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>
This commit is contained in:
Brian Goff 2020-07-07 13:33:46 -07:00
parent 6fd94aa933
commit f63f73a4a8
17 changed files with 238 additions and 178 deletions

View file

@ -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:

View file

@ -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)
}
}
}

View file

@ -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
}

View file

@ -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,57 +724,13 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
}
}
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))
}
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")
}
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
}
// verifyDaemonSettings performs validation of daemon config struct
func verifyDaemonSettings(conf *config.Config) error {
if conf.ContainerdNamespace == conf.ContainerdPluginNamespace {
@ -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
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)
}
}
if conf.Runtimes == nil {
conf.Runtimes = make(map[string]types.Runtime)
}
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

View file

@ -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
daemon/runtime_unix.go Normal file
View file

@ -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
}

View file

@ -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)

View file

@ -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
rt.Shim = getShimConfig(daemon.configStore, p)
}
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))
}
return opts, nil
}
opts := &runctypes.RuncOptions{
Runtime: path,
RuntimeRoot: filepath.Join(daemon.configStore.ExecRoot,
fmt.Sprintf("runtime-%s", container.HostConfig.Runtime)),
logrus.Warnf("Configured runtime %q is deprecated and will be removed in the next release", container.HostConfig.Runtime)
}
if UsingSystemd(daemon.configStore) {
opts.SystemdCgroup = true
}
return opts, nil
return rt.Shim.Binary, rt.Shim.Opts, nil
}

View file

@ -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
}

View file

@ -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) {

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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")))
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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)

View file

@ -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)
}
}