Преглед изворни кода

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 година
родитељ
комит
f63f73a4a8

+ 10 - 0
api/types/types.go

@@ -511,6 +511,16 @@ type Checkpoint struct {
 type Runtime struct {
 	Path string   `json:"path"`
 	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:

+ 22 - 7
daemon/config/config.go

@@ -36,9 +36,6 @@ const (
 	// maximum number of attempts that
 	// may take place at a time for each pull when the connection is lost.
 	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 = int64(67108864)
 	// DefaultNetworkMtu is the default value for network MTU
@@ -47,8 +44,24 @@ const (
 	DisableNetworkBridge = "none"
 	// DefaultInitBinary is the name of the default init binary
 	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
 // that MUST NOT be parsed as deep structures.
 // Use this to differentiate these options
@@ -571,10 +584,12 @@ func Validate(config *Config) error {
 		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.
@@ -1081,7 +1085,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 
 	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 {
 		return nil, err
 	}

+ 11 - 63
daemon/daemon_unix.go

@@ -30,7 +30,6 @@ import (
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/idtools"
-	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/pkg/sysinfo"
@@ -78,10 +77,6 @@ const (
 	cgroupFsDriver      = "cgroupfs"
 	cgroupSystemdDriver = "systemd"
 	cgroupNoneDriver    = "none"
-
-	// DefaultRuntimeName is the default runtime to be used by
-	// containerd if none is specified
-	DefaultRuntimeName = "runc"
 )
 
 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
@@ -808,14 +759,15 @@ func verifyDaemonSettings(conf *config.Config) error {
 		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
 }
 
@@ -1756,10 +1708,6 @@ func (daemon *Daemon) setupSeccompProfile() error {
 	return nil
 }
 
-func (daemon *Daemon) useShimV2() bool {
-	return cgroups.IsCgroup2UnifiedMode()
-}
-
 // RawSysInfo returns *sysinfo.SysInfo .
 func (daemon *Daemon) RawSysInfo(quiet bool) *sysinfo.SysInfo {
 	var opts []sysinfo.Opt

+ 5 - 0
daemon/info.go

@@ -11,6 +11,7 @@ import (
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli/debug"
+	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/pkg/fileutils"
@@ -78,6 +79,10 @@ func (daemon *Daemon) SystemInfo() *types.Info {
 	daemon.fillSecurityOptions(v, sysInfo)
 	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
 }
 

+ 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 {
 		return err
 	}
@@ -187,7 +187,7 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 		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 errdefs.IsConflict(err) {
 			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) {
 				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 {
 			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"
 
 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/errdefs"
+	"github.com/opencontainers/runc/libcontainer/cgroups"
 	"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
-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
 	if container.HostConfig.Runtime == "" {
 		container.HostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName()
 		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"
 )
 
-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.
 	if system.ContainerdRuntimeSupported() {
 		opts := &options.Options{Debug: true}
-		return opts, nil
+		return "", opts, nil
 	}
 
 	// TODO (containerd) - Probably need to revisit LCOW options here
@@ -22,7 +22,7 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain
 	if container.OS == "linux" {
 		config := &client.Config{}
 		if err := config.GenerateDefault(daemon.configStore.GraphOptions); err != nil {
-			return nil, err
+			return "", nil, err
 		}
 		// Override from user-supplied options.
 		for k, v := range container.HostConfig.StorageOpt {
@@ -34,11 +34,11 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain
 			}
 		}
 		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) {
 	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
 }
 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
-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
-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() {
 		// useShimV2 is ignored for windows
 		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"
 // 	},
 // }
-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 {
 		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
 	oomMu           sync.Mutex
 	oom             map[string]bool
-	useShimV2       bool
 	v2runcoptionsMu sync.Mutex
 	// v2runcoptions is used for copying options specified on Create() to Start()
 	v2runcoptions map[string]v2runcoptions.Options
 }
 
 // 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{
 		client:        cli,
 		stateDir:      stateDir,
@@ -65,7 +64,6 @@ func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string,
 		ns:            ns,
 		backend:       b,
 		oom:           make(map[string]bool),
-		useShimV2:     useShimV2,
 		v2runcoptions: make(map[string]v2runcoptions.Options),
 	}
 
@@ -129,17 +127,13 @@ func (c *client) Restore(ctx context.Context, id string, attachStdio libcontaine
 	}, 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)
 	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
 
-	rt := runtimeName
-	if c.useShimV2 {
-		rt = shimV2RuntimeName
-	}
 	newOpts := []containerd.NewContainerOpts{
 		containerd.WithSpec(ociSpec),
-		containerd.WithRuntime(rt, runtimeOptions),
+		containerd.WithRuntime(shim, runtimeOptions),
 		WithBundle(bdir, ociSpec),
 	}
 	opts = append(opts, newOpts...)
@@ -151,12 +145,10 @@ func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, run
 		}
 		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
 }
@@ -218,17 +210,12 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 
 	if runtime.GOOS != "windows" {
 		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.IoGid = uint32(gid)
-				opts.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
 				info.Options = &opts
 			} else {
 				info.Options = &runctypes.CreateOptions{
@@ -237,7 +224,6 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 					NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
 				}
 			}
-
 			return nil
 		})
 	} else {

+ 0 - 5
libcontainerd/remote/client_linux.go

@@ -16,11 +16,6 @@ import (
 	"github.com/sirupsen/logrus"
 )
 
-const (
-	runtimeName       = "io.containerd.runtime.v1.linux"
-	shimV2RuntimeName = "io.containerd.runc.v2"
-)
-
 func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) {
 	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)
 
-	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)
 	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)

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

@@ -3,12 +3,11 @@ package containerd // import "github.com/docker/docker/plugin/executor/container
 import (
 	"context"
 	"io"
-	"path/filepath"
 	"sync"
 
 	"github.com/containerd/containerd"
 	"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/libcontainerd"
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
@@ -26,13 +25,14 @@ type ExitHandler interface {
 }
 
 // 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{
 		rootDir:     rootDir,
 		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 {
 		return nil, errors.Wrap(err, "error creating containerd exec client")
 	}
@@ -45,6 +45,7 @@ type Executor struct {
 	rootDir     string
 	client      libcontainerdtypes.Client
 	exitHandler ExitHandler
+	runtime     types.Runtime
 }
 
 // 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
 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()
-	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 {
 		status, err2 := e.client.Status(ctx, id)
 		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) {
 					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)
 			}
 		}