From e4b70657ecf256ca4a41745c6bf3abcfbca2bd3d Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 15 Oct 2026 12:52:34 -0700 Subject: [PATCH] Add --cgroup-manager, deprecate --systemd-cgroup Add a new --cgroup-manager global option, accepting either "cgroupfs" (the default) or "systemd". It is compatible with the option of the same name crun has (except crun also has "disabled"). This helps tools which run either runc or crun, for example, crun's test suite, which can now be run against runc. Deprecate --systemd-cgroup in favor of --cgroup-manager=systemd. It is still supported (and there are no plans to remove it), but conflicts with --cgroup-manager set to anything other than systemd. Switch the integration tests to use the new option, and add a few tests for the new and the deprecated option. Signed-off-by: Kir Kolyshkin --- CHANGELOG.md | 7 +++++++ contrib/completions/bash/runc | 6 ++++++ docs/cgroup-v2.md | 4 ++-- docs/systemd.md | 6 +++--- main.go | 10 +++++++++- man/runc.8.md | 9 ++++++--- rootless_linux.go | 2 +- script/setup_host.sh | 2 +- tests/integration/bin/runc | 2 +- tests/integration/cgroups.bats | 21 +++++++++++++++++++++ utils.go | 18 ++++++++++++++++++ utils_linux.go | 2 +- 12 files changed, 76 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e249459b0..6c28b474125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,11 +12,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 says, the destination is a path inside the container, and it is now used as such. (#5556) +### Added ### +- New `--cgroup-manager` global option, to select the cgroup manager + (`cgroupfs` or `systemd`). This is compatible with crun. (#5570) + ### Deprecated ### - `libcontainer.Stats.Interfaces` and `types.Stats.NetworkInterfaces` (shown as `network_interfaces` in `runc events --stats` output) are deprecated, as they are always empty since the removal of veth network strategy in runc 1.0.0-rc6. (#5523) +- The `--systemd-cgroup` global option is deprecated in favor of + `--cgroup-manager=systemd`. It still works, and there are no plans to + remove it. (#5570) ### Changed ### - A container configuration which uses `--no-pivot` or sets diff --git a/contrib/completions/bash/runc b/contrib/completions/bash/runc index ae24c8ce54a..3277b679429 100644 --- a/contrib/completions/bash/runc +++ b/contrib/completions/bash/runc @@ -228,6 +228,7 @@ _runc_runc() { --systemd-cgroup " local options_with_args=" + --cgroup-manager --log --log-format --root @@ -255,6 +256,11 @@ _runc_runc() { return ;; + --cgroup-manager) + COMPREPLY=($(compgen -W 'cgroupfs systemd' -- "$cur")) + return + ;; + $(__runc_to_extglob "$options_with_args")) return ;; diff --git a/docs/cgroup-v2.md b/docs/cgroup-v2.md index 5cf4a90afb0..2884d1a0f51 100644 --- a/docs/cgroup-v2.md +++ b/docs/cgroup-v2.md @@ -28,7 +28,7 @@ Kernel older than 5.2 is not recommended due to lack of freezer. Notably, kernel older than 4.15 MUST NOT be used (unless you are running containers with user namespaces), as it lacks support for controlling permissions of devices. ### Systemd -On cgroup v2 hosts, it is highly recommended to run runc with the systemd cgroup driver (`runc --systemd-cgroup`), though not mandatory. +On cgroup v2 hosts, it is highly recommended to run runc with the systemd cgroup driver (`runc --cgroup-manager=systemd`), though not mandatory. The recommended systemd version is 244 or later. Older systemd does not support delegation of `cpuset` controller. @@ -45,7 +45,7 @@ On cgroup v2 hosts, rootless runc can talk to systemd to get cgroup permissions ```bash runc spec --rootless jq '.linux.cgroupsPath="user.slice:runc:foo"' config.json | sponge config.json -runc --systemd-cgroup run foo +runc --cgroup-manager=systemd run foo ``` The container processes are executed in a cgroup like `/user.slice/user-$(id -u).slice/user@$(id -u).service/user.slice/runc-foo.scope`. diff --git a/docs/systemd.md b/docs/systemd.md index 965b0c852c7..cbf090331d7 100644 --- a/docs/systemd.md +++ b/docs/systemd.md @@ -1,9 +1,9 @@ ## systemd cgroup driver By default, runc creates cgroups and sets cgroup limits on its own (this mode -is known as fs cgroup driver). When `--systemd-cgroup` global option is given -(as in e.g. `runc --systemd-cgroup run ...`), runc switches to systemd cgroup -driver. This document describes its features and peculiarities. +is known as fs cgroup driver). When `--cgroup-manager=systemd` global option is +given (as in e.g. `runc --cgroup-manager=systemd run ...`), runc switches to +systemd cgroup driver. This document describes its features and peculiarities. ### systemd unit name and placement diff --git a/main.go b/main.go index abda2372b4e..f5121609b4f 100644 --- a/main.go +++ b/main.go @@ -123,9 +123,14 @@ func main() { Value: root, Usage: "root directory for storage of container state (this should be located in tmpfs)", }, + &cli.StringFlag{ + Name: "cgroup-manager", + Value: "cgroupfs", + Usage: "cgroup manager to use ('cgroupfs' or 'systemd'); the latter expects cgroupsPath to be of form \"slice:prefix:name\" for e.g. \"system.slice:runc:434234\"", + }, &cli.BoolFlag{ Name: "systemd-cgroup", - Usage: "enable systemd cgroup support, expects cgroupsPath to be of form \"slice:prefix:name\" for e.g. \"system.slice:runc:434234\"", + Usage: "same as --cgroup-manager=systemd (deprecated)", }, &cli.StringFlag{ Name: "rootless", @@ -169,6 +174,9 @@ func main() { if err := reviseRootDir(cmd); err != nil { return ctx, err } + if err := reviseCgroupManager(cmd); err != nil { + return ctx, err + } if err := configLogrus(cmd); err != nil { return ctx, err diff --git a/man/runc.8.md b/man/runc.8.md index 7e93a401f20..23d1f3eb691 100644 --- a/man/runc.8.md +++ b/man/runc.8.md @@ -106,10 +106,13 @@ These options can be used with any command, and must precede the **command**. located on tmpfs. Default is */run/runc*, or *$XDG_RUNTIME_DIR/runc* for rootless containers. +**--cgroup-manager** **cgroupfs**|**systemd** +: Set the cgroup manager to use (default is **cgroupfs**). With **systemd**, +the container spec (_config.json_) is expected to have **cgroupsPath** value +in the *slice:prefix:name* form (e.g. **system.slice:runc:434234**). + **--systemd-cgroup** -: Enable systemd cgroup support. If this is set, the container spec -(_config.json_) is expected to have **cgroupsPath** value in the -*slice:prefix:name* form (e.g. **system.slice:runc:434234**). +: Same as **--cgroup-manager=systemd**. Deprecated. **--rootless** **true**|**false**|**auto** : Enable or disable rootless mode. Default is **auto**, meaning to auto-detect diff --git a/rootless_linux.go b/rootless_linux.go index 1f0183b3ffd..cb7805b80fd 100644 --- a/rootless_linux.go +++ b/rootless_linux.go @@ -37,7 +37,7 @@ func shouldUseRootlessCgroupManager(cmd *cli.Command) (bool, error) { // // On error, we assume we are root. An error may happen during shelling out to `busctl` CLI, // mostly when $DBUS_SESSION_BUS_ADDRESS is unset. - if cmd.Bool("systemd-cgroup") { + if cmd.String("cgroup-manager") == "systemd" { ownerUID, err := systemd.DetectUID() if err != nil { logrus.WithError(err).Debug("failed to get the OwnerUID value, assuming the value to be 0") diff --git a/script/setup_host.sh b/script/setup_host.sh index b0a8ba030f4..cf3b78b90ee 100755 --- a/script/setup_host.sh +++ b/script/setup_host.sh @@ -71,7 +71,7 @@ dnf clean all # Setup rootless user. "$SCRIPTDIR"/setup_rootless.sh -# Delegate all cgroup v2 controllers to rootless user via --systemd-cgroup +# Delegate all cgroup v2 controllers to rootless user via --cgroup-manager=systemd if [ -e /sys/fs/cgroup/cgroup.controllers ]; then mkdir -p /etc/systemd/system/user@.service.d cat >/etc/systemd/system/user@.service.d/delegate.conf <<'EOF' diff --git a/tests/integration/bin/runc b/tests/integration/bin/runc index bd854bf356c..8b8b60efd13 100755 --- a/tests/integration/bin/runc +++ b/tests/integration/bin/runc @@ -11,7 +11,7 @@ set -u -e opts=() -[ -v RUNC_USE_SYSTEMD ] && opts+=("--systemd-cgroup") +[ -v RUNC_USE_SYSTEMD ] && opts+=("--cgroup-manager=systemd") [ -n "${ROOT:-}" ] && opts+=("--root" "$ROOT/state") exec "$RUNC" "${opts[@]}" "$@" diff --git a/tests/integration/cgroups.bats b/tests/integration/cgroups.bats index 95ce6fce178..c558972270e 100644 --- a/tests/integration/cgroups.bats +++ b/tests/integration/cgroups.bats @@ -15,6 +15,27 @@ function setup() { run -0 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_permissions } +@test "runc --cgroup-manager with an invalid value fails" { + run -1 runc --cgroup-manager=foo list + assert_output --partial 'invalid --cgroup-manager value' +} + +@test "runc --systemd-cgroup conflicts with --cgroup-manager=cgroupfs" { + run -1 runc --systemd-cgroup --cgroup-manager=cgroupfs list + assert_output --partial "--systemd-cgroup conflicts with --cgroup-manager=cgroupfs" +} + +@test "runc --systemd-cgroup is the same as --cgroup-manager=systemd" { + requires systemd + [ $EUID -ne 0 ] && requires rootless_cgroup + + set_cgroups_path + + # Not using the runc wrapper, as it adds --cgroup-manager=systemd. + run -0 "$RUNC" --root "$ROOT/state" --systemd-cgroup run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_systemd + check_systemd_value "ActiveState" "active" +} + @test "runc create (rootless + no limits + cgrouppath + no permission) fails with permission error" { requires rootless rootless_no_cgroup diff --git a/utils.go b/utils.go index d035926e2b2..0b69a18e971 100644 --- a/utils.go +++ b/utils.go @@ -121,6 +121,24 @@ func reviseRootDir(cmd *cli.Command) error { return cmd.Set("root", root) } +// reviseCgroupManager checks the --cgroup-manager option argument, and +// converts the deprecated --systemd-cgroup option to --cgroup-manager=systemd. +func reviseCgroupManager(cmd *cli.Command) error { + manager := cmd.String("cgroup-manager") + switch manager { + case "cgroupfs", "systemd": + default: + return fmt.Errorf("invalid --cgroup-manager value %q (must be 'cgroupfs' or 'systemd')", manager) + } + if !cmd.Bool("systemd-cgroup") { + return nil + } + if cmd.IsSet("cgroup-manager") && manager != "systemd" { + return fmt.Errorf("--systemd-cgroup conflicts with --cgroup-manager=%s", manager) + } + return cmd.Set("cgroup-manager", "systemd") +} + // parseBoolOrAuto returns (nil, nil) if s is empty or "auto" func parseBoolOrAuto(s string) (*bool, error) { if s == "" || strings.ToLower(s) == "auto" { diff --git a/utils_linux.go b/utils_linux.go index 0966fb9e554..3b4c92e129f 100644 --- a/utils_linux.go +++ b/utils_linux.go @@ -188,7 +188,7 @@ func createContainer(cmd *cli.Command, id string, spec *specs.Spec) (*libcontain } config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{ CgroupName: id, - UseSystemdCgroup: cmd.Bool("systemd-cgroup"), + UseSystemdCgroup: cmd.String("cgroup-manager") == "systemd", NoPivotRoot: cmd.Bool("no-pivot"), NoNewKeyring: cmd.Bool("no-new-keyring"), Spec: spec,