[lxc-devel] [lxd/master] Basic infrastructure for cgroup abstraction
stgraber on Github
lxc-bot at linuxcontainers.org
Mon Dec 9 20:16:13 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191209/499e6351/attachment.bin>
-------------- next part --------------
From f873bc3caf8fe7febf3dbfaea0e09dbab71772ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 9 Dec 2019 15:14:55 -0500
Subject: [PATCH 1/3] lxd/cgroup: Add basic cgroup abstraction
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/cgroup/abstraction.go | 31 ++++++++++++++++
lxd/cgroup/errors.go | 11 ++++++
lxd/cgroup/init.go | 74 +++++++++++++++++++++++++++++++++++++++
lxd/cgroup/load.go | 17 +++++++++
lxd/cgroup/types.go | 23 ++++++++++++
5 files changed, 156 insertions(+)
create mode 100644 lxd/cgroup/abstraction.go
create mode 100644 lxd/cgroup/errors.go
create mode 100644 lxd/cgroup/init.go
create mode 100644 lxd/cgroup/load.go
create mode 100644 lxd/cgroup/types.go
diff --git a/lxd/cgroup/abstraction.go b/lxd/cgroup/abstraction.go
new file mode 100644
index 0000000000..8144aab6a5
--- /dev/null
+++ b/lxd/cgroup/abstraction.go
@@ -0,0 +1,31 @@
+package cgroup
+
+import (
+ "fmt"
+)
+
+// CGroup represents the main cgroup abstraction.
+type CGroup struct {
+ rw ReadWriter
+}
+
+// SetMaxProcesses applies a limit to the number of processes
+func (cg *CGroup) SetMaxProcesses(max int64) error {
+ // Confirm we have the controller
+ version := cgControllers["pids"]
+ if version == Unavailable {
+ return ErrControllerMissing
+ }
+
+ // V1/V2 behavior
+ if version == V1 || version == V2 {
+ // Setting pids limits is conveniently identical on V1 and V2.
+ if max == -1 {
+ return cg.rw.Set(version, "pids", "pids.max", "max")
+ }
+
+ return cg.rw.Set(version, "pids", "pids.max", fmt.Sprintf("%d", max))
+ }
+
+ return ErrUnknownVersion
+}
diff --git a/lxd/cgroup/errors.go b/lxd/cgroup/errors.go
new file mode 100644
index 0000000000..7e86feeac3
--- /dev/null
+++ b/lxd/cgroup/errors.go
@@ -0,0 +1,11 @@
+package cgroup
+
+import (
+ "fmt"
+)
+
+// ErrControllerMissing indicates that the requested controller isn't setup on the system.
+var ErrControllerMissing = fmt.Errorf("Cgroup controller is missing")
+
+// ErrUnknownVersion indicates that a version other than those supported was detected during init.
+var ErrUnknownVersion = fmt.Errorf("Unknown cgroup version")
diff --git a/lxd/cgroup/init.go b/lxd/cgroup/init.go
new file mode 100644
index 0000000000..6cd78e24af
--- /dev/null
+++ b/lxd/cgroup/init.go
@@ -0,0 +1,74 @@
+package cgroup
+
+import (
+ "bufio"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/lxc/lxd/shared/logger"
+)
+
+var cgControllers = map[string]Backend{}
+
+func init() {
+ // Go through the list of resource controllers for LXD.
+ selfCg, err := os.Open("/proc/self/cgroup")
+ if err != nil {
+ if os.IsNotExist(err) {
+ logger.Warnf("System doesn't appear to support CGroups")
+ } else {
+ logger.Errorf("Unable to load list of cgroups: %v", err)
+ }
+
+ return
+ }
+ defer selfCg.Close()
+
+ // Go through the file line by line.
+ scanSelfCg := bufio.NewScanner(selfCg)
+ for scanSelfCg.Scan() {
+ line := strings.TrimSpace(scanSelfCg.Text())
+ fields := strings.SplitN(line, ":", 3)
+
+ // Deal with the V1 controllers.
+ if fields[1] != "" {
+ controllers := strings.Split(fields[1], ",")
+ for _, controller := range controllers {
+ cgControllers[controller] = V1
+ }
+
+ continue
+ }
+
+ // Parse V2 controllers.
+ path := fields[2]
+ hybridPath := filepath.Join(cgPath, "unified", path, "cgroup.controllers")
+ dedicatedPath := filepath.Join(cgPath, path, "cgroup.controllers")
+
+ controllers, err := os.Open(hybridPath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ logger.Errorf("Unable to load cgroup.controllers")
+ return
+ }
+
+ controllers, err = os.Open(dedicatedPath)
+ if !os.IsNotExist(err) {
+ logger.Errorf("Unable to load cgroup.controllers")
+ return
+ }
+ }
+
+ if err == nil {
+ // Record the fact that V2 is present at all.
+ cgControllers["unified"] = V2
+
+ scanControllers := bufio.NewScanner(controllers)
+ for scanControllers.Scan() {
+ line := strings.TrimSpace(scanSelfCg.Text())
+ cgControllers[line] = V2
+ }
+ }
+ }
+}
diff --git a/lxd/cgroup/load.go b/lxd/cgroup/load.go
new file mode 100644
index 0000000000..6543403218
--- /dev/null
+++ b/lxd/cgroup/load.go
@@ -0,0 +1,17 @@
+package cgroup
+
+import (
+ "fmt"
+)
+
+// New setups a new CGroup abstraction using the provided read/writer.
+func New(rw ReadWriter) (*CGroup, error) {
+ if rw == nil {
+ return nil, fmt.Errorf("A CGroup read/writer is required")
+ }
+
+ cg := CGroup{}
+ cg.rw = rw
+
+ return &cg, nil
+}
diff --git a/lxd/cgroup/types.go b/lxd/cgroup/types.go
new file mode 100644
index 0000000000..124d7e83b7
--- /dev/null
+++ b/lxd/cgroup/types.go
@@ -0,0 +1,23 @@
+package cgroup
+
+var cgPath = "/sys/fs/cgroup"
+
+// Backend indicates whether to use v1, v2 or unavailable.
+type Backend int
+
+const (
+ // Unavailable indicates the lack of controller.
+ Unavailable = Backend(0)
+
+ // V1 indicates the controller is backed by Cgroup V1.
+ V1 = Backend(1)
+
+ // V2 indicates the controller is backed by Cgroup V2.
+ V2 = Backend(2)
+)
+
+// The ReadWriter interface is used to read/write cgroup data.
+type ReadWriter interface {
+ Get(backend Backend, controller string, key string) (string, error)
+ Set(backend Backend, controller string, key string, value string) error
+}
From dc00626b9c537346a60e919730a47988b07d0cb9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 9 Dec 2019 15:15:06 -0500
Subject: [PATCH 2/3] lxd/container: Add wrapper for cgroup abstraction
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/container_lxc.go | 45 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 33d1bb1cd5..b40881b88e 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -6926,3 +6926,48 @@ func (c *containerLXC) maasDelete() error {
return c.state.MAAS.DeleteContainer(project.Prefix(c.project, c.name))
}
+
+func (c *containerLXC) cgroup(cc *lxc.Container) (*cgroup.CGroup, error) {
+ rw := lxcCgroupReadWriter{}
+ if cc != nil {
+ rw.cc = cc
+ rw.conf = true
+ } else {
+ rw.cc = c.c
+ }
+
+ return cgroup.New(&rw)
+}
+
+type lxcCgroupReadWriter struct {
+ cc *lxc.Container
+ conf bool
+}
+
+func (rw *lxcCgroupReadWriter) Get(version cgroup.Backend, controller string, key string) (string, error) {
+ if rw.conf {
+ lxcKey := fmt.Sprintf("lxc.cgroup.%s", key)
+
+ if version == cgroup.V2 {
+ lxcKey = fmt.Sprintf("lxc.cgroup2.%s", key)
+ }
+
+ return strings.Join(rw.cc.ConfigItem(lxcKey), "\n"), nil
+
+ return "", fmt.Errorf("CGroup key %s isn't set", key)
+ }
+
+ return strings.Join(rw.cc.CgroupItem(key), "\n"), nil
+}
+
+func (rw *lxcCgroupReadWriter) Set(version cgroup.Backend, controller string, key string, value string) error {
+ if rw.conf {
+ if version == cgroup.V1 {
+ return lxcSetConfigItem(rw.cc, fmt.Sprintf("lxc.cgroup.%s", key), value)
+ }
+
+ return lxcSetConfigItem(rw.cc, fmt.Sprintf("lxc.cgroup2.%s", key), value)
+ }
+
+ return rw.cc.SetCgroupItem(key, value)
+}
From c8301ed674489e9b55848c2b93667f5f312edb54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 9 Dec 2019 15:15:21 -0500
Subject: [PATCH 3/3] lxd/container: Port pids.max to cgroup abstraction
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/container_lxc.go | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index b40881b88e..a1aeef3d7a 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -693,6 +693,12 @@ func (c *containerLXC) initLXC(config bool) error {
return err
}
+ // Load cgroup abstraction
+ cg, err := c.cgroup(cc)
+ if err != nil {
+ return err
+ }
+
freeContainer := true
defer func() {
if freeContainer {
@@ -1197,7 +1203,7 @@ func (c *containerLXC) initLXC(config bool) error {
return err
}
- err = lxcSetConfigItem(cc, "lxc.cgroup.pids.max", fmt.Sprintf("%d", valueInt))
+ err = cg.SetMaxProcesses(valueInt)
if err != nil {
return err
}
@@ -2589,10 +2595,16 @@ func (c *containerLXC) Stop(stateful bool) error {
}
}
+ // Load cgroup abstraction
+ cg, err := c.cgroup(nil)
+ if err != nil {
+ return err
+ }
+
// Fork-bomb mitigation, prevent forking from this point on
if c.state.OS.CGroupPidsController {
// Attempt to disable forking new processes
- c.CGroupSet("pids.max", "0")
+ cg.SetMaxProcesses(0)
} else if c.state.OS.CGroupFreezerController {
// Attempt to freeze the container
freezer := make(chan bool, 1)
@@ -4079,6 +4091,12 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error {
return errors.Wrap(err, "Initialize LXC")
}
+ // Load cgroup abstraction
+ cg, err := c.cgroup(nil)
+ if err != nil {
+ return err
+ }
+
// If apparmor changed, re-validate the apparmor profile
if shared.StringInSlice("raw.apparmor", changedConfig) || shared.StringInSlice("security.nesting", changedConfig) {
err = apparmor.ParseProfile(c)
@@ -4401,7 +4419,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error {
}
if value == "" {
- err = c.CGroupSet("pids.max", "max")
+ err = cg.SetMaxProcesses(-1)
if err != nil {
return err
}
@@ -4411,7 +4429,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error {
return err
}
- err = c.CGroupSet("pids.max", fmt.Sprintf("%d", valueInt))
+ err = cg.SetMaxProcesses(valueInt)
if err != nil {
return err
}
More information about the lxc-devel
mailing list