Browse code

rootless: support `--exec-opt native.cgroupdriver=systemd`

Support cgroup as in Rootless Podman.

Requires cgroup v2 host with crun.
Tested with Ubuntu 19.10 (kernel 5.3, systemd 242), crun v0.12.1.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>

Akihiro Suda authored on 2020/02/10 14:37:22
Showing 4 changed files
... ...
@@ -599,15 +599,13 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, sysIn
599 599
 }
600 600
 
601 601
 func (daemon *Daemon) getCgroupDriver() string {
602
+	if UsingSystemd(daemon.configStore) {
603
+		return cgroupSystemdDriver
604
+	}
602 605
 	if daemon.Rootless() {
603 606
 		return cgroupNoneDriver
604 607
 	}
605
-	cgroupDriver := cgroupFsDriver
606
-
607
-	if UsingSystemd(daemon.configStore) {
608
-		cgroupDriver = cgroupSystemdDriver
609
-	}
610
-	return cgroupDriver
608
+	return cgroupFsDriver
611 609
 }
612 610
 
613 611
 // getCD gets the raw value of the native.cgroupdriver option, if set.
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"context"
5 5
 	"fmt"
6 6
 	"io"
7
+	"io/ioutil"
7 8
 	"os"
8 9
 	"os/exec"
9 10
 	"path/filepath"
... ...
@@ -84,8 +85,26 @@ func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
84 84
 }
85 85
 
86 86
 // WithRootless sets the spec to the rootless configuration
87
-func WithRootless(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
88
-	return specconv.ToRootless(s)
87
+func WithRootless(daemon *Daemon) coci.SpecOpts {
88
+	return func(_ context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
89
+		var v2Controllers []string
90
+		if daemon.getCgroupDriver() == cgroupSystemdDriver {
91
+			if !cgroups.IsCgroup2UnifiedMode() {
92
+				return errors.New("rootless systemd driver doesn't support cgroup v1")
93
+			}
94
+			rootlesskitParentEUID := os.Getenv("ROOTLESSKIT_PARENT_EUID")
95
+			if rootlesskitParentEUID == "" {
96
+				return errors.New("$ROOTLESSKIT_PARENT_EUID is not set (requires RootlessKit v0.8.0)")
97
+			}
98
+			controllersPath := fmt.Sprintf("/sys/fs/cgroup/user.slice/user-%s.slice/cgroup.controllers", rootlesskitParentEUID)
99
+			controllersFile, err := ioutil.ReadFile(controllersPath)
100
+			if err != nil {
101
+				return err
102
+			}
103
+			v2Controllers = strings.Fields(string(controllersFile))
104
+		}
105
+		return specconv.ToRootless(s, v2Controllers)
106
+	}
89 107
 }
90 108
 
91 109
 // WithOOMScore sets the oom score
... ...
@@ -760,6 +779,9 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
760 760
 		useSystemd := UsingSystemd(daemon.configStore)
761 761
 		if useSystemd {
762 762
 			parent = "system.slice"
763
+			if daemon.configStore.Rootless {
764
+				parent = "user.slice"
765
+			}
763 766
 		}
764 767
 
765 768
 		if c.HostConfig.CgroupParent != "" {
... ...
@@ -985,7 +1007,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e
985 985
 		opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
986 986
 	}
987 987
 	if daemon.configStore.Rootless {
988
-		opts = append(opts, WithRootless)
988
+		opts = append(opts, WithRootless(daemon))
989 989
 	}
990 990
 	return &s, coci.ApplyOpts(context.Background(), nil, &containers.Container{
991 991
 		ID: c.ID,
... ...
@@ -1,7 +1,7 @@
1 1
 #!/bin/sh
2 2
 
3
-# v0.7.1
4
-: ${ROOTLESSKIT_COMMIT:=76c4e26750da3986fa0e741464fbf0fcd55bea71}
3
+# v0.8.0
4
+: ${ROOTLESSKIT_COMMIT:=ce88a431e6a7cf891ebb68b10bfc6a5724b9ae72}
5 5
 
6 6
 install_rootlesskit() {
7 7
 	case "$1" in
... ...
@@ -8,10 +8,12 @@ import (
8 8
 )
9 9
 
10 10
 // ToRootless converts spec to be compatible with "rootless" runc.
11
-// * Remove cgroups (will be supported in separate PR when delegation permission is configured)
11
+// * Remove non-supported cgroups
12 12
 // * Fix up OOMScoreAdj
13
-func ToRootless(spec *specs.Spec) error {
14
-	return toRootless(spec, getCurrentOOMScoreAdj())
13
+//
14
+// v2Controllers should be non-nil only if running with v2 and systemd.
15
+func ToRootless(spec *specs.Spec, v2Controllers []string) error {
16
+	return toRootless(spec, v2Controllers, getCurrentOOMScoreAdj())
15 17
 }
16 18
 
17 19
 func getCurrentOOMScoreAdj() int {
... ...
@@ -26,10 +28,44 @@ func getCurrentOOMScoreAdj() int {
26 26
 	return i
27 27
 }
28 28
 
29
-func toRootless(spec *specs.Spec, currentOOMScoreAdj int) error {
30
-	// Remove cgroup settings.
31
-	spec.Linux.Resources = nil
32
-	spec.Linux.CgroupsPath = ""
29
+func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int) error {
30
+	if len(v2Controllers) == 0 {
31
+		// Remove cgroup settings.
32
+		spec.Linux.Resources = nil
33
+		spec.Linux.CgroupsPath = ""
34
+	} else {
35
+		if spec.Linux.Resources != nil {
36
+			m := make(map[string]struct{})
37
+			for _, s := range v2Controllers {
38
+				m[s] = struct{}{}
39
+			}
40
+			// Remove devices: https://github.com/containers/crun/issues/255
41
+			spec.Linux.Resources.Devices = nil
42
+			if _, ok := m["memory"]; !ok {
43
+				spec.Linux.Resources.Memory = nil
44
+			}
45
+			if _, ok := m["cpu"]; !ok {
46
+				spec.Linux.Resources.CPU = nil
47
+			}
48
+			if _, ok := m["cpuset"]; !ok {
49
+				if spec.Linux.Resources.CPU != nil {
50
+					spec.Linux.Resources.CPU.Cpus = ""
51
+					spec.Linux.Resources.CPU.Mems = ""
52
+				}
53
+			}
54
+			if _, ok := m["pids"]; !ok {
55
+				spec.Linux.Resources.Pids = nil
56
+			}
57
+			if _, ok := m["io"]; !ok {
58
+				spec.Linux.Resources.BlockIO = nil
59
+			}
60
+			if _, ok := m["rdma"]; !ok {
61
+				spec.Linux.Resources.Rdma = nil
62
+			}
63
+			spec.Linux.Resources.HugepageLimits = nil
64
+			spec.Linux.Resources.Network = nil
65
+		}
66
+	}
33 67
 
34 68
 	if spec.Process.OOMScoreAdj != nil && *spec.Process.OOMScoreAdj < currentOOMScoreAdj {
35 69
 		*spec.Process.OOMScoreAdj = currentOOMScoreAdj