mirror of
https://github.com/moby/moby.git
synced 2026-08-04 15:11:00 +00:00
rootless: keep the cgroup mount for containers with --net=host
For rootless + host netns (detach-netns mode), the daemon replaces the /sys mounts, as sysfs cannot be mounted without owning the netns. This removed the /sys/fs/cgroup mount as well, so such containers could not see their own cgroup at all (e.g. /sys/fs/cgroup/pids.max was ENOENT even when a pids limit was set and enforced). Unlike sysfs, mounting cgroup2 does not require the netns to be owned by the userns, so the cgroup mount can be kept when running with cgroup v2. runc mounts it with the container's cgroup namespace view, so the container sees its own limits, e.g.: $ docker run --net=host --pids-limit 32 busybox cat /sys/fs/cgroup/pids.max 32 This fixes TestUpdatePidsLimit in the rootless-systemd CI mode, which was the last remaining failure there: it is the only test that reads back cgroup limits from inside a container with --net=host (used for speed), and it only runs when the daemon reports a cgroup driver other than "none" (i.e. rootless + systemd + delegation). The cgroup limits themselves were applied correctly all along (every container scope had the pids controller and the correct pids.max on the host side); only the container's view was missing. Fixes the remaining part of the issue 44084. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/cgroups/v3"
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/moby/v2/daemon/internal/rootless"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
@@ -244,6 +245,15 @@ func removeSysfs(s *specs.Spec) error {
|
||||
var mounts []specs.Mount // nolint: prealloc
|
||||
for _, mount := range s.Mounts {
|
||||
if strings.HasPrefix(mount.Destination, "/sys") {
|
||||
// Unlike sysfs, mounting cgroup2 does not require the netns to
|
||||
// be owned by the userns, so the cgroup mount can be kept when
|
||||
// running with cgroup v2. Without it, the container cannot see
|
||||
// its own limits (e.g. /sys/fs/cgroup/pids.max), although they
|
||||
// are still enforced.
|
||||
// https://github.com/moby/moby/issues/44084
|
||||
if cgroups.Mode() == cgroups.Unified && path.Clean(mount.Destination) == "/sys/fs/cgroup" {
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, mount)
|
||||
|
||||
43
daemon/internal/rootless/specconv/specconv_linux_test.go
Normal file
43
daemon/internal/rootless/specconv/specconv_linux_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package specconv
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/cgroups/v3"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
// TestRemoveSysfs checks that the cgroup mount is retained when replacing
|
||||
// the /sys mounts for rootless + host netns, so that the container can
|
||||
// still see its own resource limits (e.g. /sys/fs/cgroup/pids.max).
|
||||
// https://github.com/moby/moby/issues/44084
|
||||
func TestRemoveSysfs(t *testing.T) {
|
||||
skip.If(t, cgroups.Mode() != cgroups.Unified, "test requires cgroup v2")
|
||||
|
||||
spec := &specs.Spec{
|
||||
Mounts: []specs.Mount{
|
||||
{Destination: "/proc", Type: "proc", Source: "proc"},
|
||||
{Destination: "/sys", Type: "sysfs", Source: "sysfs", Options: []string{"nosuid", "noexec", "nodev", "ro"}},
|
||||
{Destination: "/sys/fs/cgroup", Type: "cgroup", Source: "cgroup", Options: []string{"ro", "nosuid", "noexec", "nodev"}},
|
||||
},
|
||||
}
|
||||
assert.NilError(t, removeSysfs(spec))
|
||||
assert.Check(t, is.DeepEqual([]specs.Mount{
|
||||
{Destination: "/proc", Type: "proc", Source: "proc"},
|
||||
{Destination: "/sys/fs/cgroup", Type: "cgroup", Source: "cgroup", Options: []string{"ro", "nosuid", "noexec", "nodev"}},
|
||||
}, spec.Mounts))
|
||||
}
|
||||
|
||||
// TestRemoveSysfsCustomSysMount checks that a user-specified /sys mount is
|
||||
// left alone.
|
||||
func TestRemoveSysfsCustomSysMount(t *testing.T) {
|
||||
mounts := []specs.Mount{
|
||||
{Destination: "/sys", Type: "bind", Source: "/sys", Options: []string{"rbind", "ro"}},
|
||||
}
|
||||
spec := &specs.Spec{Mounts: mounts}
|
||||
assert.NilError(t, removeSysfs(spec))
|
||||
assert.Check(t, is.DeepEqual(mounts, spec.Mounts))
|
||||
}
|
||||
Reference in New Issue
Block a user