Merge pull request #45780 from neersighted/backport/45698/24.0

This commit is contained in:
Sebastiaan van Stijn 2023-06-21 17:00:44 +02:00 committed by GitHub
commit aaa8a90747
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 211 additions and 67 deletions

View file

@ -3,9 +3,7 @@ package containerd
import (
"context"
"fmt"
"os"
"github.com/containerd/containerd/mount"
"github.com/docker/docker/container"
"github.com/sirupsen/logrus"
)
@ -19,17 +17,13 @@ func (i *ImageService) Mount(ctx context.Context, container *container.Container
return err
}
// The temporary location will be under /var/lib/docker/... because
// we set the `TMPDIR`
root, err := os.MkdirTemp("", fmt.Sprintf("%s_rootfs-mount", container.ID))
if err != nil {
return fmt.Errorf("failed to create temp dir: %w", err)
}
if err := mount.All(mounts, root); err != nil {
var root string
if root, err = i.refCountMounter.Mount(mounts, container.ID); err != nil {
return fmt.Errorf("failed to mount %s: %w", root, err)
}
logrus.WithField("container", container.ID).Debugf("container mounted via snapshotter: %v", root)
container.BaseFS = root
return nil
}
@ -38,15 +32,10 @@ func (i *ImageService) Mount(ctx context.Context, container *container.Container
func (i *ImageService) Unmount(ctx context.Context, container *container.Container) error {
root := container.BaseFS
if err := mount.UnmountAll(root, 0); err != nil {
if err := i.refCountMounter.Unmount(root); err != nil {
logrus.WithField("container", container.ID).WithError(err).Error("error unmounting container")
return fmt.Errorf("failed to unmount %s: %w", root, err)
}
if err := os.Remove(root); err != nil {
logrus.WithError(err).WithField("dir", root).Error("failed to remove mount temp dir")
}
container.BaseFS = ""
return nil
}

View file

@ -15,6 +15,7 @@ import (
"github.com/docker/docker/container"
daemonevents "github.com/docker/docker/daemon/events"
"github.com/docker/docker/daemon/images"
"github.com/docker/docker/daemon/snapshotter"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
@ -35,6 +36,7 @@ type ImageService struct {
registryService RegistryConfigProvider
eventsService *daemonevents.Events
pruneRunning atomic.Bool
refCountMounter snapshotter.Mounter
}
type RegistryHostsProvider interface {
@ -47,12 +49,13 @@ type RegistryConfigProvider interface {
}
type ImageServiceConfig struct {
Client *containerd.Client
Containers container.Store
Snapshotter string
HostsProvider RegistryHostsProvider
Registry RegistryConfigProvider
EventsService *daemonevents.Events
Client *containerd.Client
Containers container.Store
Snapshotter string
HostsProvider RegistryHostsProvider
Registry RegistryConfigProvider
EventsService *daemonevents.Events
RefCountMounter snapshotter.Mounter
}
// NewService creates a new ImageService.
@ -64,6 +67,7 @@ func NewService(config ImageServiceConfig) *ImageService {
registryHosts: config.HostsProvider,
registryService: config.Registry,
eventsService: config.EventsService,
refCountMounter: config.RefCountMounter,
}
}

View file

@ -40,6 +40,7 @@ import (
"github.com/docker/docker/daemon/images"
dlogger "github.com/docker/docker/daemon/logger"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/daemon/snapshotter"
"github.com/docker/docker/daemon/stats"
"github.com/docker/docker/distribution"
dmetadata "github.com/docker/docker/distribution/metadata"
@ -1023,12 +1024,13 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
return nil, err
}
d.imageService = ctrd.NewService(ctrd.ImageServiceConfig{
Client: d.containerdCli,
Containers: d.containers,
Snapshotter: driverName,
HostsProvider: d,
Registry: d.registryService,
EventsService: d.EventsService,
Client: d.containerdCli,
Containers: d.containers,
Snapshotter: driverName,
HostsProvider: d,
Registry: d.registryService,
EventsService: d.EventsService,
RefCountMounter: snapshotter.NewMounter(config.Root, driverName, idMapping),
})
} else {
layerStore, err := layer.NewStoreFromOptions(layer.StoreOptions{

View file

@ -1394,19 +1394,13 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
if !daemon.UsesSnapshotter() {
return daemon.Mount(container)
}
return nil
return daemon.Mount(container)
}
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
if !daemon.UsesSnapshotter() {
return daemon.Unmount(container)
}
return nil
return daemon.Unmount(container)
}
// setDefaultIsolation determines the default isolation mode for the

View file

@ -718,21 +718,19 @@ func sysctlExists(s string) bool {
// WithCommonOptions sets common docker options
func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if c.BaseFS == "" && !daemon.UsesSnapshotter() {
if c.BaseFS == "" {
return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly empty")
}
linkedEnv, err := daemon.setupLinkedContainers(c)
if err != nil {
return err
}
if !daemon.UsesSnapshotter() {
s.Root = &specs.Root{
Path: c.BaseFS,
Readonly: c.HostConfig.ReadonlyRootfs,
}
if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil {
return err
}
s.Root = &specs.Root{
Path: c.BaseFS,
Readonly: c.HostConfig.ReadonlyRootfs,
}
if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil {
return err
}
cwd := c.Config.WorkingDir
if len(cwd) == 0 {
@ -1023,20 +1021,8 @@ func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (r
WithSelinux(c),
WithOOMScore(&c.HostConfig.OomScoreAdj),
coci.WithAnnotations(c.HostConfig.Annotations),
WithUser(c),
)
if daemon.UsesSnapshotter() {
s.Root = &specs.Root{
Path: "rootfs",
}
if c.Config.User != "" {
opts = append(opts, coci.WithUser(c.Config.User))
}
if c.Config.WorkingDir != "" {
opts = append(opts, coci.WithProcessCwd(c.Config.WorkingDir))
}
} else {
opts = append(opts, WithUser(c))
}
if c.NoNewPrivileges {
opts = append(opts, coci.WithNoNewPrivileges)

141
daemon/snapshotter/mount.go Normal file
View file

@ -0,0 +1,141 @@
package snapshotter
import (
"os"
"path/filepath"
"github.com/containerd/containerd/mount"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/idtools"
"github.com/moby/locker"
"github.com/sirupsen/logrus"
)
const mountsDir = "rootfs"
// List of known filesystems that can't be re-mounted or have shared layers
var refCountedFileSystems = []string{"overlayfs", "zfs", "fuse-overlayfs"}
// Mounter handles mounting/unmounting things coming in from a snapshotter
// with optional reference counting if needed by the filesystem
type Mounter interface {
// Mount mounts the rootfs for a container and returns the mount point
Mount(mounts []mount.Mount, containerID string) (string, error)
// Unmount unmounts the container rootfs
Unmount(target string) error
}
// inSlice tests whether a string is contained in a slice of strings or not.
// Comparison is case sensitive
func inSlice(slice []string, s string) bool {
for _, ss := range slice {
if s == ss {
return true
}
}
return false
}
// NewMounter creates a new mounter for the provided snapshotter
func NewMounter(home string, snapshotter string, idMap idtools.IdentityMapping) Mounter {
if inSlice(refCountedFileSystems, snapshotter) {
return &refCountMounter{
home: home,
snapshotter: snapshotter,
rc: graphdriver.NewRefCounter(checker()),
locker: locker.New(),
idMap: idMap,
}
}
return mounter{
home: home,
snapshotter: snapshotter,
idMap: idMap,
}
}
type refCountMounter struct {
home string
snapshotter string
rc *graphdriver.RefCounter
locker *locker.Locker
idMap idtools.IdentityMapping
}
func (m *refCountMounter) Mount(mounts []mount.Mount, containerID string) (target string, retErr error) {
target = filepath.Join(m.home, mountsDir, m.snapshotter, containerID)
_, err := os.Stat(target)
if err != nil && !os.IsNotExist(err) {
return "", err
}
if count := m.rc.Increment(target); count > 1 {
return target, nil
}
m.locker.Lock(target)
defer m.locker.Unlock(target)
defer func() {
if retErr != nil {
if c := m.rc.Decrement(target); c <= 0 {
if mntErr := unmount(target); mntErr != nil {
logrus.Errorf("error unmounting %s: %v", target, mntErr)
}
if rmErr := os.Remove(target); rmErr != nil && !os.IsNotExist(rmErr) {
logrus.Debugf("Failed to remove %s: %v: %v", target, rmErr, err)
}
}
}
}()
root := m.idMap.RootPair()
if err := idtools.MkdirAllAndChown(target, 0700, root); err != nil {
return "", err
}
return target, mount.All(mounts, target)
}
func (m *refCountMounter) Unmount(target string) error {
if count := m.rc.Decrement(target); count > 0 {
return nil
}
m.locker.Lock(target)
defer m.locker.Unlock(target)
if err := unmount(target); err != nil {
logrus.Debugf("Failed to unmount %s: %v", target, err)
}
if err := os.Remove(target); err != nil {
logrus.WithError(err).WithField("dir", target).Error("failed to remove mount temp dir")
}
return nil
}
type mounter struct {
home string
snapshotter string
idMap idtools.IdentityMapping
}
func (m mounter) Mount(mounts []mount.Mount, containerID string) (string, error) {
target := filepath.Join(m.home, mountsDir, m.snapshotter, containerID)
root := m.idMap.RootPair()
if err := idtools.MkdirAndChown(target, 0700, root); err != nil {
return "", err
}
return target, mount.All(mounts, target)
}
func (m mounter) Unmount(target string) error {
return unmount(target)
}

View file

@ -0,0 +1,17 @@
//go:build !windows
package snapshotter
import (
"github.com/containerd/containerd/mount"
"github.com/docker/docker/daemon/graphdriver"
"golang.org/x/sys/unix"
)
func checker() graphdriver.Checker {
return graphdriver.NewDefaultChecker()
}
func unmount(target string) error {
return mount.Unmount(target, unix.MNT_DETACH)
}

View file

@ -0,0 +1,18 @@
package snapshotter
import "github.com/containerd/containerd/mount"
type winChecker struct {
}
func (c *winChecker) IsMounted(path string) bool {
return false
}
func checker() *winChecker {
return &winChecker{}
}
func unmount(target string) error {
return mount.Unmount(target, 0)
}

View file

@ -5,7 +5,6 @@ import (
"runtime"
"time"
"github.com/containerd/containerd"
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/container"
@ -178,13 +177,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, container *container.C
return err
}
newContainerOpts := []containerd.NewContainerOpts{}
if daemon.UsesSnapshotter() {
newContainerOpts = append(newContainerOpts, containerd.WithSnapshotter(container.Driver))
newContainerOpts = append(newContainerOpts, containerd.WithSnapshot(container.ID))
}
ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions, newContainerOpts...)
ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions)
if err != nil {
return setExitCodeFromError(container.SetExitCode, err)
}