Skip to content

Commit 84965c0

Browse files
Charity Kathureolljanat
andcommitted
Windows: Run containerd as managed process
Signed-off-by: Charity Kathure <[email protected]> Co-authored-by: Olli Janatuinen <[email protected]>
1 parent 4f4e34f commit 84965c0

12 files changed

Lines changed: 132 additions & 89 deletions

File tree

.github/workflows/.windows.yml

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -349,33 +349,12 @@ jobs:
349349
$ErrorActionPreference = "Stop"
350350
Write-Host "Service removed"
351351
}
352-
-
353-
name: Starting containerd
354-
if: matrix.runtime == 'containerd'
355-
run: |
356-
Write-Host "Generating config"
357-
& "${{ env.BIN_OUT }}\containerd.exe" config default | Out-File "$env:TEMP\ctn.toml" -Encoding ascii
358-
Write-Host "Creating service"
359-
New-Item -ItemType Directory "$env:TEMP\ctn-root" -ErrorAction SilentlyContinue | Out-Null
360-
New-Item -ItemType Directory "$env:TEMP\ctn-state" -ErrorAction SilentlyContinue | Out-Null
361-
Start-Process -Wait "${{ env.BIN_OUT }}\containerd.exe" `
362-
-ArgumentList "--log-level=debug", `
363-
"--config=$env:TEMP\ctn.toml", `
364-
"--address=\\.\pipe\containerd-containerd", `
365-
"--root=$env:TEMP\ctn-root", `
366-
"--state=$env:TEMP\ctn-state", `
367-
"--log-file=$env:TEMP\ctn.log", `
368-
"--register-service"
369-
Write-Host "Starting service"
370-
Start-Service -Name containerd
371-
Start-Sleep -Seconds 5
372-
Write-Host "Service started successfully!"
373352
-
374353
name: Starting test daemon
375354
run: |
376355
Write-Host "Creating service"
377356
If ("${{ matrix.runtime }}" -eq "containerd") {
378-
$runtimeArg="--containerd=\\.\pipe\containerd-containerd"
357+
$runtimeArg="--default-runtime=io.containerd.runhcs.v1"
379358
echo "DOCKER_WINDOWS_CONTAINERD_RUNTIME=1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
380359
}
381360
New-Item -ItemType Directory "$env:TEMP\moby-root" -ErrorAction SilentlyContinue | Out-Null
@@ -415,6 +394,17 @@ jobs:
415394
Start-Sleep -Seconds 1
416395
}
417396
Write-Host "Test daemon started and replied!"
397+
If ("${{ matrix.runtime }}" -eq "containerd") {
398+
$containerdProcesses = Get-Process -Name containerd -ErrorAction:SilentlyContinue
399+
If (-not $containerdProcesses) {
400+
Throw "containerd process is not running"
401+
} else {
402+
foreach ($process in $containerdProcesses) {
403+
$processPath = (Get-Process -Id $process.Id -FileVersionInfo).FileName
404+
Write-Output "Running containerd instance binary Path: $($processPath)"
405+
}
406+
}
407+
}
418408
env:
419409
DOCKER_HOST: npipe:////./pipe/docker_engine
420410
-
@@ -479,19 +469,6 @@ jobs:
479469
& "${{ env.BIN_OUT }}\docker" info
480470
env:
481471
DOCKER_HOST: npipe:////./pipe/docker_engine
482-
-
483-
name: Stop containerd
484-
if: always() && matrix.runtime == 'containerd'
485-
run: |
486-
$ErrorActionPreference = "SilentlyContinue"
487-
Stop-Service -Force -Name containerd
488-
$ErrorActionPreference = "Stop"
489-
-
490-
name: Containerd logs
491-
if: always() && matrix.runtime == 'containerd'
492-
run: |
493-
Copy-Item "$env:TEMP\ctn.log" -Destination ".\bundles\containerd.log"
494-
Get-Content "$env:TEMP\ctn.log" | Out-Host
495472
-
496473
name: Stop daemon
497474
if: always()

cmd/dockerd/daemon.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,10 @@ func (cli *daemonCLI) getContainerdDaemonOpts() ([]supervisor.DaemonOpt, error)
784784
opts = append(opts, supervisor.WithCRIDisabled())
785785
}
786786

787+
if runtime.GOOS == "windows" {
788+
opts = append(opts, supervisor.WithDetectLocalBinary())
789+
}
790+
787791
return opts, nil
788792
}
789793

@@ -1024,3 +1028,30 @@ func overrideProxyEnv(name, val string) {
10241028
}
10251029
_ = os.Setenv(name, val)
10261030
}
1031+
1032+
func (cli *daemonCLI) initializeContainerd(ctx context.Context) (func(time.Duration) error, error) {
1033+
systemContainerdAddr, ok, err := systemContainerdRunning(honorXDG)
1034+
if err != nil {
1035+
return nil, errors.Wrap(err, "could not determine whether the system containerd is running")
1036+
}
1037+
if ok {
1038+
// detected a system containerd at the given address.
1039+
cli.ContainerdAddr = systemContainerdAddr
1040+
return nil, nil
1041+
}
1042+
1043+
log.G(ctx).Info("containerd not running, starting managed containerd")
1044+
opts, err := cli.getContainerdDaemonOpts()
1045+
if err != nil {
1046+
return nil, errors.Wrap(err, "failed to generate containerd options")
1047+
}
1048+
1049+
r, err := supervisor.Start(ctx, filepath.Join(cli.Root, "containerd"), filepath.Join(cli.ExecRoot, "containerd"), opts...)
1050+
if err != nil {
1051+
return nil, errors.Wrap(err, "failed to start containerd")
1052+
}
1053+
cli.ContainerdAddr = r.Address()
1054+
1055+
// Try to wait for containerd to shutdown
1056+
return r.WaitTimeout, nil
1057+
}

cmd/dockerd/daemon_unix.go

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ import (
1111
"strconv"
1212
"time"
1313

14-
"github.com/containerd/log"
1514
"github.com/docker/docker/daemon"
1615
"github.com/docker/docker/daemon/config"
17-
"github.com/docker/docker/libcontainerd/supervisor"
1816
"github.com/docker/docker/libnetwork/portallocator"
1917
"github.com/docker/docker/pkg/homedir"
2018
"github.com/pkg/errors"
@@ -122,28 +120,5 @@ func (cli *daemonCLI) initContainerd(ctx context.Context) (func(time.Duration) e
122120
return nil, nil
123121
}
124122

125-
systemContainerdAddr, ok, err := systemContainerdRunning(honorXDG)
126-
if err != nil {
127-
return nil, errors.Wrap(err, "could not determine whether the system containerd is running")
128-
}
129-
if ok {
130-
// detected a system containerd at the given address.
131-
cli.ContainerdAddr = systemContainerdAddr
132-
return nil, nil
133-
}
134-
135-
log.G(ctx).Info("containerd not running, starting managed containerd")
136-
opts, err := cli.getContainerdDaemonOpts()
137-
if err != nil {
138-
return nil, errors.Wrap(err, "failed to generate containerd options")
139-
}
140-
141-
r, err := supervisor.Start(ctx, filepath.Join(cli.Root, "containerd"), filepath.Join(cli.ExecRoot, "containerd"), opts...)
142-
if err != nil {
143-
return nil, errors.Wrap(err, "failed to start containerd")
144-
}
145-
cli.ContainerdAddr = r.Address()
146-
147-
// Try to wait for containerd to shutdown
148-
return r.WaitTimeout, nil
123+
return cli.initializeContainerd(ctx)
149124
}

cmd/dockerd/daemon_windows.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,18 @@ func newCgroupParent(config *config.Config) string {
100100
return ""
101101
}
102102

103-
func (cli *daemonCLI) initContainerd(_ context.Context) (func(time.Duration) error, error) {
104-
system.InitContainerdRuntime(cli.ContainerdAddr)
105-
return nil, nil
103+
func (cli *daemonCLI) initContainerd(ctx context.Context) (func(time.Duration) error, error) {
104+
defer func() { system.EnableContainerdRuntime(cli.ContainerdAddr) }()
105+
106+
if cli.ContainerdAddr != "" {
107+
return nil, nil
108+
}
109+
110+
if cli.DefaultRuntime != config.WindowsV2RuntimeName {
111+
return nil, nil
112+
}
113+
114+
return cli.initializeContainerd(ctx)
106115
}
107116

108117
func validateCPURealtimeOptions(_ *config.Config) error {

daemon/config/config_windows.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ const (
1313
// default value. On Windows keep this empty so the value is auto-detected
1414
// based on other options.
1515
StockRuntimeName = ""
16+
17+
WindowsV1RuntimeName = "com.docker.hcsshim.v1"
18+
WindowsV2RuntimeName = "io.containerd.runhcs.v1"
1619
)
1720

21+
var builtinRuntimes = map[string]bool{
22+
WindowsV1RuntimeName: true,
23+
WindowsV2RuntimeName: true,
24+
}
25+
1826
// BridgeConfig is meant to store all the parameters for both the bridge driver and the default bridge network. On
1927
// Windows: 1. "bridge" in this context reference the nat driver and the default nat network; 2. the nat driver has no
2028
// specific parameters, so this struct effectively just stores parameters for the default nat network.

daemon/daemon_windows.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ const (
4040
windowsMaxCPUShares = 10000
4141
windowsMinCPUPercent = 1
4242
windowsMaxCPUPercent = 100
43-
44-
windowsV1RuntimeName = "com.docker.hcsshim.v1"
45-
windowsV2RuntimeName = "io.containerd.runhcs.v1"
4643
)
4744

4845
// Windows containers are much larger than Linux containers and each of them
@@ -563,22 +560,22 @@ func (daemon *Daemon) initLibcontainerd(ctx context.Context, cfg *config.Config)
563560
rt := cfg.DefaultRuntime
564561
if rt == "" {
565562
if cfg.ContainerdAddr == "" {
566-
rt = windowsV1RuntimeName
563+
rt = config.WindowsV1RuntimeName
567564
} else {
568-
rt = windowsV2RuntimeName
565+
rt = config.WindowsV2RuntimeName
569566
}
570567
}
571568

572569
switch rt {
573-
case windowsV1RuntimeName:
570+
case config.WindowsV1RuntimeName:
574571
daemon.containerd, err = local.NewClient(
575572
ctx,
576573
daemon.containerdClient,
577574
filepath.Join(cfg.ExecRoot, "containerd"),
578575
cfg.ContainerdNamespace,
579576
daemon,
580577
)
581-
case windowsV2RuntimeName:
578+
case config.WindowsV2RuntimeName:
582579
if cfg.ContainerdAddr == "" {
583580
return fmt.Errorf("cannot use the specified runtime %q without containerd", rt)
584581
}

daemon/start_windows.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package daemon // import "github.com/docker/docker/daemon"
33
import (
44
"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
55
"github.com/docker/docker/container"
6+
"github.com/docker/docker/daemon/config"
67
"github.com/docker/docker/pkg/system"
78
)
89

910
func (daemon *Daemon) getLibcontainerdCreateOptions(*configStore, *container.Container) (string, interface{}, error) {
1011
if system.ContainerdRuntimeSupported() {
1112
opts := &options.Options{}
12-
return "io.containerd.runhcs.v1", opts, nil
13+
return config.WindowsV2RuntimeName, opts, nil
1314
}
1415
return "", nil, nil
1516
}

libcontainerd/supervisor/remote_daemon.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/containerd/containerd"
1313
"github.com/containerd/containerd/defaults"
14+
"github.com/containerd/containerd/pkg/dialer"
1415
"github.com/containerd/containerd/services/server/config"
1516
"github.com/containerd/log"
1617
"github.com/docker/docker/pkg/pidfile"
@@ -29,7 +30,6 @@ const (
2930
shutdownTimeout = 15 * time.Second
3031
startupTimeout = 15 * time.Second
3132
configFile = "containerd.toml"
32-
binaryName = "containerd"
3333
pidFile = "containerd.pid"
3434
)
3535

@@ -40,9 +40,13 @@ type remote struct {
4040
// file is saved.
4141
configFile string
4242

43-
daemonPid int
44-
pidFile string
45-
logger *log.Entry
43+
// daemonPath is the binary to execute, and can be either a basename (to use
44+
// a binary installed in the system's $PATH), or the full path to the binary
45+
// to use.
46+
daemonPath string
47+
daemonPid int
48+
pidFile string
49+
logger *log.Entry
4650

4751
daemonWaitCh chan struct{}
4852
daemonStartCh chan error
@@ -75,6 +79,7 @@ func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Da
7579
},
7680
},
7781
configFile: filepath.Join(stateDir, configFile),
82+
daemonPath: binaryName,
7883
daemonPid: -1,
7984
pidFile: filepath.Join(stateDir, pidFile),
8085
logger: log.G(ctx).WithField("module", "libcontainerd"),
@@ -156,7 +161,8 @@ func (r *remote) startContainerd() error {
156161
return err
157162
}
158163

159-
cmd := exec.Command(binaryName, "--config", cfgFile)
164+
r.logger.WithField("binary", r.daemonPath).Debug("starting containerd binary")
165+
cmd := exec.Command(r.daemonPath, "--config", cfgFile)
160166
// redirect containerd logs to docker logs
161167
cmd.Stdout = os.Stdout
162168
cmd.Stderr = os.Stderr
@@ -262,7 +268,9 @@ func (r *remote) monitorDaemon(ctx context.Context) {
262268
}
263269
}
264270

265-
os.RemoveAll(r.GRPC.Address)
271+
if err := os.RemoveAll(r.GRPC.Address); err != nil {
272+
r.logger.WithError(err).Error("failed to remove old gRPC address")
273+
}
266274
if err := r.startContainerd(); err != nil {
267275
if !started {
268276
r.daemonStartCh <- err
@@ -273,16 +281,19 @@ func (r *remote) monitorDaemon(ctx context.Context) {
273281
continue
274282
}
275283

284+
gopts := []grpc.DialOption{
285+
grpc.WithTransportCredentials(insecure.NewCredentials()),
286+
grpc.WithContextDialer(dialer.ContextDialer),
287+
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
288+
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
289+
grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor),
290+
grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor),
291+
}
292+
276293
client, err = containerd.New(
277294
r.GRPC.Address,
278295
containerd.WithTimeout(60*time.Second),
279-
containerd.WithDialOpts([]grpc.DialOption{
280-
grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor),
281-
grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor),
282-
grpc.WithTransportCredentials(insecure.NewCredentials()),
283-
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
284-
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
285-
}),
296+
containerd.WithDialOpts(gopts),
286297
)
287298
if err != nil {
288299
r.logger.WithError(err).Error("failed connecting to containerd")

libcontainerd/supervisor/remote_daemon_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
)
1111

1212
const (
13+
binaryName = "containerd"
1314
sockFile = "containerd.sock"
1415
debugSockFile = "containerd-debug.sock"
1516
)

libcontainerd/supervisor/remote_daemon_options.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package supervisor // import "github.com/docker/docker/libcontainerd/supervisor"
22

33
import (
4+
"os"
5+
"path/filepath"
6+
47
"github.com/containerd/log"
8+
"github.com/pkg/errors"
59
)
610

711
// WithLogLevel defines which log level to start containerd with.
@@ -33,3 +37,31 @@ func WithCRIDisabled() DaemonOpt {
3337
return nil
3438
}
3539
}
40+
41+
// WithDetectLocalBinary checks if a containerd binary is present in the same
42+
// directory as the dockerd binary, and overrides the path of the containerd
43+
// binary to start if found. If no binary is found, no changes are made.
44+
func WithDetectLocalBinary() DaemonOpt {
45+
return func(r *remote) error {
46+
dockerdPath, err := os.Executable()
47+
if err != nil {
48+
return errors.Wrap(err, "looking up binary path")
49+
}
50+
51+
localBinary := filepath.Join(filepath.Dir(dockerdPath), binaryName)
52+
fi, err := os.Stat(localBinary)
53+
if err != nil {
54+
if !errors.Is(err, os.ErrNotExist) {
55+
return err
56+
}
57+
return nil
58+
}
59+
if fi.IsDir() {
60+
return errors.Errorf("local containerd path found (%s), but is a directory", localBinary)
61+
}
62+
r.daemonPath = localBinary
63+
r.logger.WithField("daemon path", r.daemonPath).Debug("Local containerd daemon found.")
64+
65+
return nil
66+
}
67+
}

0 commit comments

Comments
 (0)