Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support running containers without CGroups #3581

Merged
merged 1 commit into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/podman/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"cgroupns", "host",
"cgroup namespace to use",
)
createFlags.String(
"cgroups", "enabled",
"control container cgroup configuration",
)
createFlags.String(
"cgroup-parent", "",
"Optional parent cgroup for the container",
Expand Down
1 change: 1 addition & 0 deletions cmd/podman/shared/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
CapDrop: c.StringSlice("cap-drop"),
CidFile: c.String("cidfile"),
Cgroupns: c.String("cgroupns"),
Cgroups: c.String("cgroups"),
CgroupParent: c.String("cgroup-parent"),
Command: command,
UserCommand: userCommand,
Expand Down
2 changes: 2 additions & 0 deletions cmd/podman/shared/intermediate.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes
m["blkio-weight-device"] = newCRStringSlice(c, "blkio-weight-device")
m["cap-add"] = newCRStringSlice(c, "cap-add")
m["cap-drop"] = newCRStringSlice(c, "cap-drop")
m["cgroupns"] = newCRString(c, "cgroupns")
m["cgroups"] = newCRString(c, "cgroups")
m["cgroup-parent"] = newCRString(c, "cgroup-parent")
m["cidfile"] = newCRString(c, "cidfile")
m["conmon-pidfile"] = newCRString(c, "conmon-pidfile")
Expand Down
1 change: 1 addition & 0 deletions contrib/cirrus/container_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ if [ $install -eq 1 ]; then
make TAGS="${TAGS}" install.bin PREFIX=/usr ETCDIR=/etc
make TAGS="${TAGS}" install.man PREFIX=/usr ETCDIR=/etc
make TAGS="${TAGS}" install.cni PREFIX=/usr ETCDIR=/etc
make TAGS="${TAGS}" install.config PREFIX=/usr ETCDIR=/etc
make TAGS="${TAGS}" install.systemd PREFIX=/usr ETCDIR=/etc
fi

Expand Down
2 changes: 2 additions & 0 deletions contrib/cirrus/integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ case "$SPECIALMODE" in
export OCI_RUNTIME=/usr/bin/crun
make
make install PREFIX=/usr ETCDIR=/etc
make install.config PREFIX=/usr
make test-binaries
make local${TESTSUITE}
;;
Expand All @@ -57,6 +58,7 @@ case "$SPECIALMODE" in
none)
make
make install PREFIX=/usr ETCDIR=/etc
make install.config PREFIX=/usr
make test-binaries
if [[ "$TEST_REMOTE_CLIENT" == "true" ]]
then
Expand Down
6 changes: 6 additions & 0 deletions docs/podman-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ Set the cgroup namespace mode for the container, by default **host** is used.
**private**: create a new cgroup namespace.
**ns:<PATH>**: join the namespace at the specified path.

**--cgroups**=*mode*

Determines whether the container will create CGroups.
Valid values are *enabled* and *disabled*, which the default being *enabled*.
The *disabled* option will force the container to not create CGroups, and thus conflicts with CGroup options (**--cgroupns** and **--cgroup-parent**).

**--cgroup-parent**=*path*

Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
Expand Down
6 changes: 6 additions & 0 deletions docs/podman-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ Set the cgroup namespace mode for the container, by default **host** is used.
**private**: create a new cgroup namespace.
**ns:<PATH>**: join the namespace at the specified path.

**--cgroups**=*mode*

Determines whether the container will create CGroups.
Valid values are *enabled* and *disabled*, which the default being *enabled*.
The *disabled* option will force the container to not create CGroups, and thus conflicts with CGroup options (**--cgroupns** and **--cgroup-parent**).

**--cgroup-parent**=*cgroup*

Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
Expand Down
4 changes: 4 additions & 0 deletions libpod.conf
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ runtime = "runc"
# libpod will use it for reporting nicer errors.
runtime_supports_json = ["crun", "runc"]

# List of all the OCI runtimes that support --cgroup-manager=disable to disable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment does not match the above --no-cgroups, although I prefer --cgroupns=none.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OCI runtime command that needs to be supported is --cgroup-manager=disable (or at least, it is on crun - we'll see what runc has to say.

# creation of CGroups for containers.
runtime_supports_nocgroups = ["crun"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yuck, this is getting to complicated. I would rather throw an error, or see what runc throws for an error when called in this way

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do this for other OCI runtime features. I think it's a better user experience that popping up errors from runc that we used a flag that doesn't exist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I share Dan's initial reaction but as we're already doing it for other features (see json-support above), I prefer doing that to be consistent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, it's not the best experience for package updates/upgrades as the libpod.conf will remain untouched. Just hit it on my machine testing the PR.


# Paths to look for a valid OCI runtime (runc, runv, etc)
# If the paths are empty or no valid path was found, then the `$PATH`
# environment variable will be used as the fallback.
Expand Down
3 changes: 3 additions & 0 deletions libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ type ContainerConfig struct {
StopTimeout uint `json:"stopTimeout,omitempty"`
// Time container was created
CreatedTime time.Time `json:"createdTime"`
// NoCgroups indicates that the container will not create CGroups. It is
// incompatible with CgroupParent.
NoCgroups bool `json:"noCgroups,omitempty"`
// Cgroup parent of the container
CgroupParent string `json:"cgroupParent"`
// LogPath log location
Expand Down
10 changes: 10 additions & 0 deletions libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,11 @@ type InspectContainerHostConfig struct {
// populated.
// TODO.
Cgroup string `json:"Cgroup"`
// Cgroups contains the container's CGroup mode.
// Allowed values are "default" (container is creating CGroups) and
// "disabled" (container is not creating CGroups).
// This is Libpod-specific and not included in `docker inspect`.
Cgroups string `json:"Cgroups"`
// Links is unused, and provided purely for Docker compatibility.
Links []string `json:"Links"`
// OOMScoreAdj is an adjustment that will be made to the container's OOM
Expand Down Expand Up @@ -958,6 +963,11 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
restartPolicy.Name = c.config.RestartPolicy
restartPolicy.MaximumRetryCount = c.config.RestartRetries
hostConfig.RestartPolicy = restartPolicy
if c.config.NoCgroups {
hostConfig.Cgroups = "disabled"
} else {
hostConfig.Cgroups = "default"
}

hostConfig.Dns = make([]string, 0, len(c.config.DNSServer))
for _, dns := range c.config.DNSServer {
Expand Down
8 changes: 8 additions & 0 deletions libpod/container_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,10 @@ func (c *Container) stop(timeout uint) error {

// Internal, non-locking function to pause a container
func (c *Container) pause() error {
if c.config.NoCgroups {
return errors.Wrapf(define.ErrNoCgroups, "cannot pause without using CGroups")
}

if err := c.ociRuntime.pauseContainer(c); err != nil {
return err
}
Expand All @@ -1132,6 +1136,10 @@ func (c *Container) pause() error {

// Internal, non-locking function to unpause a container
func (c *Container) unpause() error {
if c.config.NoCgroups {
return errors.Wrapf(define.ErrNoCgroups, "cannot unpause without using CGroups")
}

if err := c.ociRuntime.unpauseContainer(c); err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if err != nil {
return nil, err
}
if rootless.IsRootless() && !unified {
if (rootless.IsRootless() && !unified) || c.config.NoCgroups {
g.SetLinuxCgroupsPath("")
} else if c.runtime.config.CgroupManager == SystemdCgroupsManager {
// When runc is set to use Systemd as a cgroup manager, it
Expand Down
4 changes: 4 additions & 0 deletions libpod/container_top_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (
// Top gathers statistics about the running processes in a container. It returns a
// []string for output
func (c *Container) Top(descriptors []string) ([]string, error) {
if c.config.NoCgroups {
return nil, errors.Wrapf(define.ErrNoCgroups, "cannot run top on container %s as it did not create a cgroup", c.ID())
}

conStat, err := c.State()
if err != nil {
return nil, errors.Wrapf(err, "unable to look up state for %s", c.ID())
Expand Down
4 changes: 4 additions & 0 deletions libpod/define/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ var (
// the user.
ErrDetach = utils.ErrDetach

// ErrNoCgroups indicates that the container does not have its own
// CGroup.
ErrNoCgroups = errors.New("this container does not have a cgroup")

// ErrRuntimeStopped indicates that the runtime has already been shut
// down and no further operations can be performed on it
ErrRuntimeStopped = errors.New("runtime has already been stopped")
Expand Down
30 changes: 16 additions & 14 deletions libpod/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,20 @@ const (
// OCIRuntime represents an OCI-compatible runtime that libpod can call into
// to perform container operations
type OCIRuntime struct {
name string
path string
conmonPath string
conmonEnv []string
cgroupManager string
tmpDir string
exitsDir string
socketsDir string
logSizeMax int64
noPivot bool
reservePorts bool
supportsJSON bool
sdNotify bool
name string
path string
conmonPath string
conmonEnv []string
cgroupManager string
tmpDir string
exitsDir string
socketsDir string
logSizeMax int64
noPivot bool
reservePorts bool
supportsJSON bool
supportsNoCgroups bool
sdNotify bool
}

// ociError is used to parse the OCI runtime JSON log. It is not part of the
Expand All @@ -73,7 +74,7 @@ type ociError struct {

// Make a new OCI runtime with provided options.
// The first path that points to a valid executable will be used.
func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *RuntimeConfig, supportsJSON bool) (*OCIRuntime, error) {
func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *RuntimeConfig, supportsJSON, supportsNoCgroups bool) (*OCIRuntime, error) {
if name == "" {
return nil, errors.Wrapf(define.ErrInvalidArg, "the OCI runtime must be provided a non-empty name")
}
Expand All @@ -93,6 +94,7 @@ func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *R
// TODO: probe OCI runtime for feature and enable automatically if
// available.
runtime.supportsJSON = supportsJSON
runtime.supportsNoCgroups = supportsNoCgroups

foundPath := false
for _, path := range paths {
Expand Down
11 changes: 10 additions & 1 deletion libpod/oci_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func (r *OCIRuntime) configureConmonEnv(runtimeDir string) ([]string, []*os.File
func (r *OCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, pidPath, logPath, exitDir, ociLogPath string) []string {
// set the conmon API version to be able to use the correct sync struct keys
args := []string{"--api-version", "1"}
if r.cgroupManager == SystemdCgroupsManager {
if r.cgroupManager == SystemdCgroupsManager && !ctr.config.NoCgroups {
args = append(args, "-s")
}
args = append(args, "-c", ctr.ID())
Expand Down Expand Up @@ -307,6 +307,10 @@ func (r *OCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, pidPath
if ociLogPath != "" {
args = append(args, "--runtime-arg", "--log-format=json", "--runtime-arg", "--log", fmt.Sprintf("--runtime-arg=%s", ociLogPath))
}
if ctr.config.NoCgroups {
logrus.Debugf("Running with no CGroups")
args = append(args, "--runtime-arg", "--cgroup-manager", "--runtime-arg", "disabled")
}
return args
}

Expand Down Expand Up @@ -355,6 +359,11 @@ func startCommandGivenSelinux(cmd *exec.Cmd) error {
// moveConmonToCgroupAndSignal gets a container's cgroupParent and moves the conmon process to that cgroup
// it then signals for conmon to start by sending nonse data down the start fd
func (r *OCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec.Cmd, startFd *os.File, uuid string) error {
// If cgroup creation is disabled - just signal.
if ctr.config.NoCgroups {
return writeConmonPipeData(startFd)
}

cgroupParent := ctr.CgroupParent()
if r.cgroupManager == SystemdCgroupsManager {
unitName := createUnitName("libpod-conmon", ctr.ID())
Expand Down
4 changes: 3 additions & 1 deletion libpod/oci_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,12 @@ func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error {
}

var args []string
if rootless.IsRootless() {
if rootless.IsRootless() || ctr.config.NoCgroups {
// we don't use --all for rootless containers as the OCI runtime might use
// the cgroups to determine the PIDs, but for rootless containers there is
// not any.
// Same logic for NoCgroups - we can't use cgroups as the user
// explicitly requested none be created.
args = []string{"kill", ctr.ID(), "KILL"}
} else {
args = []string{"kill", "--all", ctr.ID(), "KILL"}
Expand Down
29 changes: 29 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,10 @@ func WithPIDNSFrom(nsCtr *Container) CtrCreateOption {
return errors.Wrapf(define.ErrInvalidArg, "container has joined pod %s and dependency container %s is not a member of the pod", ctr.config.Pod, nsCtr.ID())
}

if ctr.config.NoCgroups {
return errors.Wrapf(define.ErrInvalidArg, "container has disabled creation of CGroups, which is incompatible with sharing a PID namespace")
}

ctr.config.PIDNsCtr = nsCtr.ID()

return nil
Expand Down Expand Up @@ -1056,6 +1060,27 @@ func WithLogPath(path string) CtrCreateOption {
}
}

// WithNoCgroups disables the creation of CGroups for the new container.
func WithNoCgroups() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}

if ctr.config.CgroupParent != "" {
return errors.Wrapf(define.ErrInvalidArg, "NoCgroups conflicts with CgroupParent")
}

if ctr.config.PIDNsCtr != "" {
return errors.Wrapf(define.ErrInvalidArg, "NoCgroups requires a private PID namespace and cannot be used when PID namespace is shared with another container")
}

ctr.config.NoCgroups = true

return nil
}
}

// WithCgroupParent sets the Cgroup Parent of the new container.
func WithCgroupParent(parent string) CtrCreateOption {
return func(ctr *Container) error {
Expand All @@ -1067,6 +1092,10 @@ func WithCgroupParent(parent string) CtrCreateOption {
return errors.Wrapf(define.ErrInvalidArg, "cgroup parent cannot be empty")
}

if ctr.config.NoCgroups {
return errors.Wrapf(define.ErrInvalidArg, "CgroupParent conflicts with NoCgroups")
}

ctr.config.CgroupParent = parent

return nil
Expand Down
Loading