Pārlūkot izejas kodu

Configure shims from runtime config

In dockerd we already have a concept of a "runtime", which specifies the
OCI runtime to use (e.g. runc).
This PR extends that config to add containerd shim configuration.
This option is only exposed within the daemon itself (cannot be
configured in daemon.json).
This is due to issues in supporting unknown shims which will require
more design work.

What this change allows us to do is keep all the runtime config in one
place.

So the default "runc" runtime will just have it's already existing shim
config codified within the runtime config alone.
I've also added 2 more "stock" runtimes which are basically runc+shimv1
and runc+shimv2.
These new runtime configurations are:

- io.containerd.runtime.v1.linux - runc + v1 shim using the V1 shim API
- io.containerd.runc.v2 - runc + shim v2

These names coincide with the actual names of the containerd shims.

This allows the user to essentially control what shim is going to be
used by either specifying these as a `--runtime` on container create or
by setting `--default-runtime` on the daemon.

For custom/user-specified runtimes, the default shim config (currently
shim v1) is used.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Brian Goff 5 gadi atpakaļ
vecāks
revīzija
f63f73a4a8

+ 10 - 0
api/types/types.go

@@ -511,6 +511,16 @@ type Checkpoint struct {
 type Runtime struct {
 type Runtime struct {
 	Path string   `json:"path"`
 	Path string   `json:"path"`
 	Args []string `json:"runtimeArgs,omitempty"`
 	Args []string `json:"runtimeArgs,omitempty"`
+
+	// This is exposed here only for internal use
+	// It is not currently supported to specify custom shim configs
+	Shim *ShimConfig `json:"-"`
+}
+
+// ShimConfig is used by runtime to configure containerd shims
+type ShimConfig struct {
+	Binary string
+	Opts   interface{}
 }
 }
 
 
 // DiskUsage contains response of Engine API:
 // DiskUsage contains response of Engine API:

+ 22 - 7
daemon/config/config.go

@@ -36,9 +36,6 @@ const (
 	// maximum number of attempts that
 	// maximum number of attempts that
 	// may take place at a time for each pull when the connection is lost.
 	// may take place at a time for each pull when the connection is lost.
 	DefaultDownloadAttempts = 5
 	DefaultDownloadAttempts = 5
-	// StockRuntimeName is the reserved name/alias used to represent the
-	// OCI runtime being shipped with the docker daemon package.
-	StockRuntimeName = "runc"
 	// DefaultShmSize is the default value for container's shm size
 	// DefaultShmSize is the default value for container's shm size
 	DefaultShmSize = int64(67108864)
 	DefaultShmSize = int64(67108864)
 	// DefaultNetworkMtu is the default value for network MTU
 	// DefaultNetworkMtu is the default value for network MTU
@@ -47,8 +44,24 @@ const (
 	DisableNetworkBridge = "none"
 	DisableNetworkBridge = "none"
 	// DefaultInitBinary is the name of the default init binary
 	// DefaultInitBinary is the name of the default init binary
 	DefaultInitBinary = "docker-init"
 	DefaultInitBinary = "docker-init"
+
+	// StockRuntimeName is the reserved name/alias used to represent the
+	// OCI runtime being shipped with the docker daemon package.
+	StockRuntimeName = "runc"
+	// LinuxV1RuntimeName is the runtime used to specify the containerd v1 shim with the runc binary
+	// Note this is different than io.containerd.runc.v1 which would be the v1 shim using the v2 shim API.
+	// This is specifically for the v1 shim using the v1 shim API.
+	LinuxV1RuntimeName = "io.containerd.runtime.v1.linux"
+	// LinuxV2RuntimeName is the runtime used to specify the containerd v2 runc shim
+	LinuxV2RuntimeName = "io.containerd.runc.v2"
 )
 )
 
 
+var builtinRuntimes = map[string]bool{
+	StockRuntimeName:   true,
+	LinuxV1RuntimeName: true,
+	LinuxV2RuntimeName: true,
+}
+
 // flatOptions contains configuration keys
 // flatOptions contains configuration keys
 // that MUST NOT be parsed as deep structures.
 // that MUST NOT be parsed as deep structures.
 // Use this to differentiate these options
 // Use this to differentiate these options
@@ -571,10 +584,12 @@ func Validate(config *Config) error {
 		return err
 		return err
 	}
 	}
 
 
-	if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" && defaultRuntime != StockRuntimeName {
-		runtimes := config.GetAllRuntimes()
-		if _, ok := runtimes[defaultRuntime]; !ok {
-			return fmt.Errorf("specified default runtime '%s' does not exist", defaultRuntime)
+	if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" {
+		if !builtinRuntimes[defaultRuntime] {
+			runtimes := config.GetAllRuntimes()
+			if _, ok := runtimes[defaultRuntime]; !ok {
+				return fmt.Errorf("specified default runtime '%s' does not exist", defaultRuntime)
+			}
 		}
 		}
 	}
 	}
 
 

+ 6 - 2
daemon/daemon.go

@@ -932,7 +932,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 			}
 			}
 		}
 		}
 
 
-		return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, config.ContainerdPluginNamespace, m, d.useShimV2())
+		var rt types.Runtime
+		if runtime := config.GetRuntime(config.GetDefaultRuntimeName()); runtime != nil {
+			rt = *runtime
+		}
+		return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, config.ContainerdPluginNamespace, m, rt)
 	}
 	}
 
 
 	// Plugin system initialization should happen before restore. Do not change order.
 	// Plugin system initialization should happen before restore. Do not change order.
@@ -1081,7 +1085,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 
 
 	go d.execCommandGC()
 	go d.execCommandGC()
 
 
-	d.containerd, err = libcontainerd.NewClient(ctx, d.containerdCli, filepath.Join(config.ExecRoot, "containerd"), config.ContainerdNamespace, d, d.useShimV2())
+	d.containerd, err = libcontainerd.NewClient(ctx, d.containerdCli, filepath.Join(config.ExecRoot, "containerd"), config.ContainerdNamespace, d)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 11 - 63
daemon/daemon_unix.go

@@ -30,7 +30,6 @@ import (
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
-	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
@@ -78,10 +77,6 @@ const (
 	cgroupFsDriver      = "cgroupfs"
 	cgroupFsDriver      = "cgroupfs"
 	cgroupSystemdDriver = "systemd"
 	cgroupSystemdDriver = "systemd"
 	cgroupNoneDriver    = "none"
 	cgroupNoneDriver    = "none"
-
-	// DefaultRuntimeName is the default runtime to be used by
-	// containerd if none is specified
-	DefaultRuntimeName = "runc"
 )
 )
 
 
 type containerGetter interface {
 type containerGetter interface {
@@ -729,55 +724,11 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
 		}
 		}
 	}
 	}
 
 
-	return warnings, nil
-}
-
-func (daemon *Daemon) loadRuntimes() error {
-	return daemon.initRuntimes(daemon.configStore.Runtimes)
-}
-
-func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) {
-	runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes")
-	// Remove old temp directory if any
-	os.RemoveAll(runtimeDir + "-old")
-	tmpDir, err := ioutils.TempDir(daemon.configStore.Root, "gen-runtimes")
-	if err != nil {
-		return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
+	if hostConfig.Runtime == config.LinuxV1RuntimeName || (hostConfig.Runtime == "" && daemon.configStore.DefaultRuntime == config.LinuxV1RuntimeName) {
+		warnings = append(warnings, fmt.Sprintf("Configured runtime %q is deprecated and will be removed in the next release.", config.LinuxV1RuntimeName))
 	}
 	}
-	defer func() {
-		if err != nil {
-			if err1 := os.RemoveAll(tmpDir); err1 != nil {
-				logrus.WithError(err1).WithField("dir", tmpDir).
-					Warn("failed to remove tmp dir")
-			}
-			return
-		}
-
-		if err = os.Rename(runtimeDir, runtimeDir+"-old"); err != nil {
-			return
-		}
-		if err = os.Rename(tmpDir, runtimeDir); err != nil {
-			err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start")
-			return
-		}
-		if err = os.RemoveAll(runtimeDir + "-old"); err != nil {
-			logrus.WithError(err).WithField("dir", tmpDir).
-				Warn("failed to remove old runtimes dir")
-		}
-	}()
 
 
-	for name, rt := range runtimes {
-		if len(rt.Args) == 0 {
-			continue
-		}
-
-		script := filepath.Join(tmpDir, name)
-		content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
-		if err := ioutil.WriteFile(script, []byte(content), 0700); err != nil {
-			return err
-		}
-	}
-	return nil
+	return warnings, nil
 }
 }
 
 
 // verifyDaemonSettings performs validation of daemon config struct
 // verifyDaemonSettings performs validation of daemon config struct
@@ -808,14 +759,15 @@ func verifyDaemonSettings(conf *config.Config) error {
 		return fmt.Errorf("exec-opt native.cgroupdriver=systemd requires cgroup v2 for rootless mode")
 		return fmt.Errorf("exec-opt native.cgroupdriver=systemd requires cgroup v2 for rootless mode")
 	}
 	}
 
 
-	if conf.DefaultRuntime == "" {
-		conf.DefaultRuntime = config.StockRuntimeName
-	}
-	if conf.Runtimes == nil {
-		conf.Runtimes = make(map[string]types.Runtime)
+	configureRuntimes(conf)
+	if rtName := conf.GetDefaultRuntimeName(); rtName != "" {
+		if conf.GetRuntime(rtName) == nil {
+			return fmt.Errorf("specified default runtime '%s' does not exist", rtName)
+		}
+		if rtName == config.LinuxV1RuntimeName {
+			logrus.Warnf("Configured default runtime %q is deprecated and will be removed in the next release.", config.LinuxV1RuntimeName)
+		}
 	}
 	}
-	conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: DefaultRuntimeName}
-
 	return nil
 	return nil
 }
 }
 
 
@@ -1756,10 +1708,6 @@ func (daemon *Daemon) setupSeccompProfile() error {
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) useShimV2() bool {
-	return cgroups.IsCgroup2UnifiedMode()
-}
-
 // RawSysInfo returns *sysinfo.SysInfo .
 // RawSysInfo returns *sysinfo.SysInfo .
 func (daemon *Daemon) RawSysInfo(quiet bool) *sysinfo.SysInfo {
 func (daemon *Daemon) RawSysInfo(quiet bool) *sysinfo.SysInfo {
 	var opts []sysinfo.Opt
 	var opts []sysinfo.Opt

+ 5 - 0
daemon/info.go

@@ -11,6 +11,7 @@ import (
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli/debug"
 	"github.com/docker/docker/cli/debug"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/fileutils"
@@ -78,6 +79,10 @@ func (daemon *Daemon) SystemInfo() *types.Info {
 	daemon.fillSecurityOptions(v, sysInfo)
 	daemon.fillSecurityOptions(v, sysInfo)
 	daemon.fillLicense(v)
 	daemon.fillLicense(v)
 
 
+	if v.DefaultRuntime == config.LinuxV1RuntimeName {
+		v.Warnings = append(v.Warnings, fmt.Sprintf("Configured default runtime %q is deprecated and will be removed in the next release.", config.LinuxV1RuntimeName))
+	}
+
 	return v
 	return v
 }
 }
 
 

+ 134 - 0
daemon/runtime_unix.go

@@ -0,0 +1,134 @@
+// +build !windows
+
+package daemon
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+
+	"github.com/containerd/containerd/runtime/linux/runctypes"
+	v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/daemon/config"
+	"github.com/docker/docker/pkg/ioutils"
+	"github.com/opencontainers/runc/libcontainer/cgroups"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+)
+
+const (
+	defaultRuntimeName = "runc"
+
+	linuxShimV1 = "io.containerd.runtime.v1.linux"
+	linuxShimV2 = "io.containerd.runc.v2"
+)
+
+func configureRuntimes(conf *config.Config) {
+	if conf.DefaultRuntime == "" {
+		conf.DefaultRuntime = config.StockRuntimeName
+	}
+	if conf.Runtimes == nil {
+		conf.Runtimes = make(map[string]types.Runtime)
+	}
+	conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: getShimConfig(conf, defaultRuntimeName)}
+	conf.Runtimes[config.LinuxV1RuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: defaultV1ShimConfig(conf, defaultRuntimeName)}
+	conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: defaultV2ShimConfig(conf, defaultRuntimeName)}
+}
+
+func getShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig {
+	if cgroups.IsCgroup2UnifiedMode() {
+		return defaultV2ShimConfig(conf, runtimePath)
+	}
+	return defaultV1ShimConfig(conf, runtimePath)
+}
+
+func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig {
+	return &types.ShimConfig{
+		Binary: linuxShimV2,
+		Opts: &v2runcoptions.Options{
+			BinaryName:    runtimePath,
+			Root:          filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName),
+			SystemdCgroup: UsingSystemd(conf),
+			NoPivotRoot:   os.Getenv("DOCKER_RAMDISK") != "",
+		},
+	}
+}
+
+func defaultV1ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig {
+	return &types.ShimConfig{
+		Binary: linuxShimV1,
+		Opts: &runctypes.RuncOptions{
+			Runtime:       runtimePath,
+			RuntimeRoot:   filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName),
+			SystemdCgroup: UsingSystemd(conf),
+		},
+	}
+}
+
+func (daemon *Daemon) loadRuntimes() error {
+	return daemon.initRuntimes(daemon.configStore.Runtimes)
+}
+
+func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) {
+	runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes")
+	// Remove old temp directory if any
+	os.RemoveAll(runtimeDir + "-old")
+	tmpDir, err := ioutils.TempDir(daemon.configStore.Root, "gen-runtimes")
+	if err != nil {
+		return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
+	}
+	defer func() {
+		if err != nil {
+			if err1 := os.RemoveAll(tmpDir); err1 != nil {
+				logrus.WithError(err1).WithField("dir", tmpDir).
+					Warn("failed to remove tmp dir")
+			}
+			return
+		}
+
+		if err = os.Rename(runtimeDir, runtimeDir+"-old"); err != nil {
+			return
+		}
+		if err = os.Rename(tmpDir, runtimeDir); err != nil {
+			err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start")
+			return
+		}
+		if err = os.RemoveAll(runtimeDir + "-old"); err != nil {
+			logrus.WithError(err).WithField("dir", tmpDir).
+				Warn("failed to remove old runtimes dir")
+		}
+	}()
+
+	for name, rt := range runtimes {
+		if len(rt.Args) == 0 {
+			continue
+		}
+
+		script := filepath.Join(tmpDir, name)
+		content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
+		if err := ioutil.WriteFile(script, []byte(content), 0700); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// rewriteRuntimePath is used for runtimes which have custom arguments supplied.
+// This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments.
+// To support this case, the daemon wraps the specified runtime in a script that passes through those arguments.
+func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) (string, error) {
+	if len(args) == 0 {
+		return p, nil
+	}
+
+	// Check that the runtime path actually exists here so that we can return a well known error.
+	if _, err := exec.LookPath(p); err != nil {
+		return "", errors.Wrap(err, "error while looking up the specified runtime path")
+	}
+
+	return filepath.Join(daemon.configStore.Root, "runtimes", name), nil
+}

+ 3 - 3
daemon/start.go

@@ -175,7 +175,7 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 		}
 		}
 	}
 	}
 
 
-	createOptions, err := daemon.getLibcontainerdCreateOptions(container)
+	shim, createOptions, err := daemon.getLibcontainerdCreateOptions(container)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -187,7 +187,7 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 		return err
 		return err
 	}
 	}
 
 
-	err = daemon.containerd.Create(ctx, container.ID, spec, createOptions, withImageName(imageRef.String()))
+	err = daemon.containerd.Create(ctx, container.ID, spec, shim, createOptions, withImageName(imageRef.String()))
 	if err != nil {
 	if err != nil {
 		if errdefs.IsConflict(err) {
 		if errdefs.IsConflict(err) {
 			logrus.WithError(err).WithField("container", container.ID).Error("Container not cleaned up from containerd from previous run")
 			logrus.WithError(err).WithField("container", container.ID).Error("Container not cleaned up from containerd from previous run")
@@ -196,7 +196,7 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 			if err := daemon.containerd.Delete(ctx, container.ID); err != nil && !errdefs.IsNotFound(err) {
 			if err := daemon.containerd.Delete(ctx, container.ID); err != nil && !errdefs.IsNotFound(err) {
 				logrus.WithError(err).WithField("container", container.ID).Error("Error cleaning up stale containerd container object")
 				logrus.WithError(err).WithField("container", container.ID).Error("Error cleaning up stale containerd container object")
 			}
 			}
-			err = daemon.containerd.Create(ctx, container.ID, spec, createOptions, withImageName(imageRef.String()))
+			err = daemon.containerd.Create(ctx, container.ID, spec, shim, createOptions, withImageName(imageRef.String()))
 		}
 		}
 		if err != nil {
 		if err != nil {
 			return translateContainerdStartErr(container.Path, container.SetExitCode, err)
 			return translateContainerdStartErr(container.Path, container.SetExitCode, err)

+ 15 - 50
daemon/start_unix.go

@@ -3,70 +3,35 @@
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
-	"fmt"
-	"os/exec"
-	"path/filepath"
-
-	"github.com/containerd/containerd/runtime/linux/runctypes"
-	v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
+	"github.com/opencontainers/runc/libcontainer/cgroups"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 )
 )
 
 
-func (daemon *Daemon) getRuntimeScript(container *container.Container) (string, error) {
-	name := container.HostConfig.Runtime
-	rt := daemon.configStore.GetRuntime(name)
-	if rt == nil {
-		return "", errdefs.InvalidParameter(errors.Errorf("no such runtime '%s'", name))
-	}
-
-	if len(rt.Args) > 0 {
-		// First check that the target exist, as using it in a script won't
-		// give us the right error
-		if _, err := exec.LookPath(rt.Path); err != nil {
-			return "", translateContainerdStartErr(container.Path, container.SetExitCode, err)
-		}
-		return filepath.Join(daemon.configStore.Root, "runtimes", name), nil
-	}
-	return rt.Path, nil
-}
-
 // getLibcontainerdCreateOptions callers must hold a lock on the container
 // getLibcontainerdCreateOptions callers must hold a lock on the container
-func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (interface{}, error) {
+func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (string, interface{}, error) {
 	// Ensure a runtime has been assigned to this container
 	// Ensure a runtime has been assigned to this container
 	if container.HostConfig.Runtime == "" {
 	if container.HostConfig.Runtime == "" {
 		container.HostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
 		container.HostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
 		container.CheckpointTo(daemon.containersReplica)
 		container.CheckpointTo(daemon.containersReplica)
 	}
 	}
 
 
-	path, err := daemon.getRuntimeScript(container)
-	if err != nil {
-		return nil, err
-	}
-	if daemon.useShimV2() {
-		opts := &v2runcoptions.Options{
-			BinaryName: path,
-			Root: filepath.Join(daemon.configStore.ExecRoot,
-				fmt.Sprintf("runtime-%s", container.HostConfig.Runtime)),
+	rt := daemon.configStore.GetRuntime(container.HostConfig.Runtime)
+	if rt.Shim == nil {
+		p, err := daemon.rewriteRuntimePath(container.HostConfig.Runtime, rt.Path, rt.Args)
+		if err != nil {
+			return "", nil, translateContainerdStartErr(container.Path, container.SetExitCode, err)
 		}
 		}
-
-		if UsingSystemd(daemon.configStore) {
-			opts.SystemdCgroup = true
-		}
-
-		return opts, nil
-
+		rt.Shim = getShimConfig(daemon.configStore, p)
 	}
 	}
-	opts := &runctypes.RuncOptions{
-		Runtime: path,
-		RuntimeRoot: filepath.Join(daemon.configStore.ExecRoot,
-			fmt.Sprintf("runtime-%s", container.HostConfig.Runtime)),
-	}
-
-	if UsingSystemd(daemon.configStore) {
-		opts.SystemdCgroup = true
+	if rt.Shim.Binary == linuxShimV1 {
+		if cgroups.IsCgroup2UnifiedMode() {
+			return "", nil, errdefs.InvalidParameter(errors.Errorf("runtime %q is not supported while cgroups v2 (unified hierarchy) is being used", container.HostConfig.Runtime))
+		}
+		logrus.Warnf("Configured runtime %q is deprecated and will be removed in the next release", container.HostConfig.Runtime)
 	}
 	}
 
 
-	return opts, nil
+	return rt.Shim.Binary, rt.Shim.Opts, nil
 }
 }

+ 6 - 6
daemon/start_windows.go

@@ -7,12 +7,12 @@ import (
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 )
 )
 
 
-func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (interface{}, error) {
+func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (string, interface{}, error) {
 
 
 	// Set the runtime options to debug regardless of current logging level.
 	// Set the runtime options to debug regardless of current logging level.
 	if system.ContainerdRuntimeSupported() {
 	if system.ContainerdRuntimeSupported() {
 		opts := &options.Options{Debug: true}
 		opts := &options.Options{Debug: true}
-		return opts, nil
+		return "", opts, nil
 	}
 	}
 
 
 	// TODO (containerd) - Probably need to revisit LCOW options here
 	// TODO (containerd) - Probably need to revisit LCOW options here
@@ -22,7 +22,7 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain
 	if container.OS == "linux" {
 	if container.OS == "linux" {
 		config := &client.Config{}
 		config := &client.Config{}
 		if err := config.GenerateDefault(daemon.configStore.GraphOptions); err != nil {
 		if err := config.GenerateDefault(daemon.configStore.GraphOptions); err != nil {
-			return nil, err
+			return "", nil, err
 		}
 		}
 		// Override from user-supplied options.
 		// Override from user-supplied options.
 		for k, v := range container.HostConfig.StorageOpt {
 		for k, v := range container.HostConfig.StorageOpt {
@@ -34,11 +34,11 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain
 			}
 			}
 		}
 		}
 		if err := config.Validate(); err != nil {
 		if err := config.Validate(); err != nil {
-			return nil, err
+			return "", nil, err
 		}
 		}
 
 
-		return config, nil
+		return "", config, nil
 	}
 	}
 
 
-	return nil, nil
+	return "", nil, nil
 }
 }

+ 1 - 1
daemon/util_test.go

@@ -28,7 +28,7 @@ func (c *MockContainerdClient) Version(ctx context.Context) (containerd.Version,
 func (c *MockContainerdClient) Restore(ctx context.Context, containerID string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, p libcontainerdtypes.Process, err error) {
 func (c *MockContainerdClient) Restore(ctx context.Context, containerID string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, p libcontainerdtypes.Process, err error) {
 	return false, 0, &mockProcess{}, nil
 	return false, 0, &mockProcess{}, nil
 }
 }
-func (c *MockContainerdClient) Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
+func (c *MockContainerdClient) Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
 	return nil
 	return nil
 }
 }
 func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) {
 func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) {

+ 2 - 2
libcontainerd/libcontainerd_linux.go

@@ -9,6 +9,6 @@ import (
 )
 )
 
 
 // NewClient creates a new libcontainerd client from a containerd client
 // NewClient creates a new libcontainerd client from a containerd client
-func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend, useShimV2 bool) (libcontainerdtypes.Client, error) {
-	return remote.NewClient(ctx, cli, stateDir, ns, b, useShimV2)
+func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
+	return remote.NewClient(ctx, cli, stateDir, ns, b)
 }
 }

+ 2 - 2
libcontainerd/libcontainerd_windows.go

@@ -11,10 +11,10 @@ import (
 )
 )
 
 
 // NewClient creates a new libcontainerd client from a containerd client
 // NewClient creates a new libcontainerd client from a containerd client
-func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend, useShimV2 bool) (libcontainerdtypes.Client, error) {
+func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
 	if !system.ContainerdRuntimeSupported() {
 	if !system.ContainerdRuntimeSupported() {
 		// useShimV2 is ignored for windows
 		// useShimV2 is ignored for windows
 		return local.NewClient(ctx, cli, stateDir, ns, b)
 		return local.NewClient(ctx, cli, stateDir, ns, b)
 	}
 	}
-	return remote.NewClient(ctx, cli, stateDir, ns, b, useShimV2)
+	return remote.NewClient(ctx, cli, stateDir, ns, b)
 }
 }

+ 1 - 1
libcontainerd/local/local_windows.go

@@ -153,7 +153,7 @@ func (c *client) Version(ctx context.Context) (containerd.Version, error) {
 // 		"ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
 // 		"ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
 // 	},
 // 	},
 // }
 // }
-func (c *client) Create(_ context.Context, id string, spec *specs.Spec, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
+func (c *client) Create(_ context.Context, id string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
 	if ctr := c.getContainer(id); ctr != nil {
 	if ctr := c.getContainer(id); ctr != nil {
 		return errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
 		return errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
 	}
 	}

+ 11 - 25
libcontainerd/remote/client.go

@@ -50,14 +50,13 @@ type client struct {
 	eventQ          queue.Queue
 	eventQ          queue.Queue
 	oomMu           sync.Mutex
 	oomMu           sync.Mutex
 	oom             map[string]bool
 	oom             map[string]bool
-	useShimV2       bool
 	v2runcoptionsMu sync.Mutex
 	v2runcoptionsMu sync.Mutex
 	// v2runcoptions is used for copying options specified on Create() to Start()
 	// v2runcoptions is used for copying options specified on Create() to Start()
 	v2runcoptions map[string]v2runcoptions.Options
 	v2runcoptions map[string]v2runcoptions.Options
 }
 }
 
 
 // NewClient creates a new libcontainerd client from a containerd client
 // NewClient creates a new libcontainerd client from a containerd client
-func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend, useShimV2 bool) (libcontainerdtypes.Client, error) {
+func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
 	c := &client{
 	c := &client{
 		client:        cli,
 		client:        cli,
 		stateDir:      stateDir,
 		stateDir:      stateDir,
@@ -65,7 +64,6 @@ func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string,
 		ns:            ns,
 		ns:            ns,
 		backend:       b,
 		backend:       b,
 		oom:           make(map[string]bool),
 		oom:           make(map[string]bool),
-		useShimV2:     useShimV2,
 		v2runcoptions: make(map[string]v2runcoptions.Options),
 		v2runcoptions: make(map[string]v2runcoptions.Options),
 	}
 	}
 
 
@@ -129,17 +127,13 @@ func (c *client) Restore(ctx context.Context, id string, attachStdio libcontaine
 	}, nil
 	}, nil
 }
 }
 
 
-func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
+func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
 	bdir := c.bundleDir(id)
 	bdir := c.bundleDir(id)
 	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
 	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
 
 
-	rt := runtimeName
-	if c.useShimV2 {
-		rt = shimV2RuntimeName
-	}
 	newOpts := []containerd.NewContainerOpts{
 	newOpts := []containerd.NewContainerOpts{
 		containerd.WithSpec(ociSpec),
 		containerd.WithSpec(ociSpec),
-		containerd.WithRuntime(rt, runtimeOptions),
+		containerd.WithRuntime(shim, runtimeOptions),
 		WithBundle(bdir, ociSpec),
 		WithBundle(bdir, ociSpec),
 	}
 	}
 	opts = append(opts, newOpts...)
 	opts = append(opts, newOpts...)
@@ -151,12 +145,10 @@ func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, run
 		}
 		}
 		return wrapError(err)
 		return wrapError(err)
 	}
 	}
-	if c.useShimV2 {
-		if x, ok := runtimeOptions.(*v2runcoptions.Options); ok {
-			c.v2runcoptionsMu.Lock()
-			c.v2runcoptions[id] = *x
-			c.v2runcoptionsMu.Unlock()
-		}
+	if x, ok := runtimeOptions.(*v2runcoptions.Options); ok {
+		c.v2runcoptionsMu.Lock()
+		c.v2runcoptions[id] = *x
+		c.v2runcoptionsMu.Unlock()
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -218,17 +210,12 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 
 
 	if runtime.GOOS != "windows" {
 	if runtime.GOOS != "windows" {
 		taskOpts = append(taskOpts, func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
 		taskOpts = append(taskOpts, func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
-			if c.useShimV2 {
-				// For v2, we need to inherit options specified on Create
-				c.v2runcoptionsMu.Lock()
-				opts, ok := c.v2runcoptions[id]
-				c.v2runcoptionsMu.Unlock()
-				if !ok {
-					opts = v2runcoptions.Options{}
-				}
+			c.v2runcoptionsMu.Lock()
+			opts, ok := c.v2runcoptions[id]
+			c.v2runcoptionsMu.Unlock()
+			if ok {
 				opts.IoUid = uint32(uid)
 				opts.IoUid = uint32(uid)
 				opts.IoGid = uint32(gid)
 				opts.IoGid = uint32(gid)
-				opts.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
 				info.Options = &opts
 				info.Options = &opts
 			} else {
 			} else {
 				info.Options = &runctypes.CreateOptions{
 				info.Options = &runctypes.CreateOptions{
@@ -237,7 +224,6 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 					NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
 					NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
 				}
 				}
 			}
 			}
-
 			return nil
 			return nil
 		})
 		})
 	} else {
 	} else {

+ 0 - 5
libcontainerd/remote/client_linux.go

@@ -16,11 +16,6 @@ import (
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 )
 )
 
 
-const (
-	runtimeName       = "io.containerd.runtime.v1.linux"
-	shimV2RuntimeName = "io.containerd.runc.v2"
-)
-
 func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) {
 func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) {
 	return &libcontainerdtypes.Summary{}, nil
 	return &libcontainerdtypes.Summary{}, nil
 }
 }

+ 1 - 1
libcontainerd/types/types.go

@@ -52,7 +52,7 @@ type Client interface {
 
 
 	Restore(ctx context.Context, containerID string, attachStdio StdioCallback) (alive bool, pid int, p Process, err error)
 	Restore(ctx context.Context, containerID string, attachStdio StdioCallback) (alive bool, pid int, p Process, err error)
 
 
-	Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error
+	Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error
 	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
 	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
 	SignalProcess(ctx context.Context, containerID, processID string, signal int) error
 	SignalProcess(ctx context.Context, containerID, processID string, signal int) error
 	Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)
 	Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)

+ 7 - 9
plugin/executor/containerd/containerd.go

@@ -3,12 +3,11 @@ package containerd // import "github.com/docker/docker/plugin/executor/container
 import (
 import (
 	"context"
 	"context"
 	"io"
 	"io"
-	"path/filepath"
 	"sync"
 	"sync"
 
 
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd/cio"
 	"github.com/containerd/containerd/cio"
-	"github.com/containerd/containerd/runtime/linux/runctypes"
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libcontainerd"
 	"github.com/docker/docker/libcontainerd"
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
@@ -26,13 +25,14 @@ type ExitHandler interface {
 }
 }
 
 
 // New creates a new containerd plugin executor
 // New creates a new containerd plugin executor
-func New(ctx context.Context, rootDir string, cli *containerd.Client, ns string, exitHandler ExitHandler, useShimV2 bool) (*Executor, error) {
+func New(ctx context.Context, rootDir string, cli *containerd.Client, ns string, exitHandler ExitHandler, runtime types.Runtime) (*Executor, error) {
 	e := &Executor{
 	e := &Executor{
 		rootDir:     rootDir,
 		rootDir:     rootDir,
 		exitHandler: exitHandler,
 		exitHandler: exitHandler,
+		runtime:     runtime,
 	}
 	}
 
 
-	client, err := libcontainerd.NewClient(ctx, cli, rootDir, ns, e, useShimV2)
+	client, err := libcontainerd.NewClient(ctx, cli, rootDir, ns, e)
 	if err != nil {
 	if err != nil {
 		return nil, errors.Wrap(err, "error creating containerd exec client")
 		return nil, errors.Wrap(err, "error creating containerd exec client")
 	}
 	}
@@ -45,6 +45,7 @@ type Executor struct {
 	rootDir     string
 	rootDir     string
 	client      libcontainerdtypes.Client
 	client      libcontainerdtypes.Client
 	exitHandler ExitHandler
 	exitHandler ExitHandler
+	runtime     types.Runtime
 }
 }
 
 
 // deleteTaskAndContainer deletes plugin task and then plugin container from containerd
 // deleteTaskAndContainer deletes plugin task and then plugin container from containerd
@@ -66,11 +67,8 @@ func deleteTaskAndContainer(ctx context.Context, cli libcontainerdtypes.Client,
 
 
 // Create creates a new container
 // Create creates a new container
 func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error {
 func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error {
-	opts := runctypes.RuncOptions{
-		RuntimeRoot: filepath.Join(e.rootDir, "runtime-root"),
-	}
 	ctx := context.Background()
 	ctx := context.Background()
-	err := e.client.Create(ctx, id, &spec, &opts)
+	err := e.client.Create(ctx, id, &spec, e.runtime.Shim.Binary, e.runtime.Shim.Opts)
 	if err != nil {
 	if err != nil {
 		status, err2 := e.client.Status(ctx, id)
 		status, err2 := e.client.Status(ctx, id)
 		if err2 != nil {
 		if err2 != nil {
@@ -82,7 +80,7 @@ func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteClo
 				if err2 := e.client.Delete(ctx, id); err2 != nil && !errdefs.IsNotFound(err2) {
 				if err2 := e.client.Delete(ctx, id); err2 != nil && !errdefs.IsNotFound(err2) {
 					logrus.WithError(err2).WithField("plugin", id).Error("Error cleaning up containerd container")
 					logrus.WithError(err2).WithField("plugin", id).Error("Error cleaning up containerd container")
 				}
 				}
-				err = e.client.Create(ctx, id, &spec, &opts)
+				err = e.client.Create(ctx, id, &spec, e.runtime.Shim.Binary, e.runtime.Shim.Opts)
 			}
 			}
 		}
 		}