daemon/c8d: Unmount container fs after unclean shutdown
BaseFS is not serialized and is lost after an unclean shutdown. Unmount method in the containerd image service implementation will not work correctly in that case. This patch will allow Unmount to restore the BaseFS if the target is still mounted. The reason it works with graphdrivers is that it doesn't directly operate on BaseFS. It uses RWLayer, which is explicitly restored immediately as soon as container is loaded. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
parent
54fcd40aa4
commit
203bac0ec4
2 changed files with 44 additions and 3 deletions
|
@ -2,6 +2,7 @@ package containerd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/log"
|
||||
|
@ -30,11 +31,21 @@ func (i *ImageService) Mount(ctx context.Context, container *container.Container
|
|||
|
||||
// Unmount unmounts the container base filesystem
|
||||
func (i *ImageService) Unmount(ctx context.Context, container *container.Container) error {
|
||||
root := container.BaseFS
|
||||
baseFS := container.BaseFS
|
||||
if baseFS == "" {
|
||||
target, err := i.refCountMounter.Mounted(container.ID)
|
||||
if err != nil {
|
||||
log.G(ctx).WithField("containerID", container.ID).Warn("failed to determine if container is already mounted")
|
||||
}
|
||||
if target == "" {
|
||||
return errors.New("BaseFS is empty")
|
||||
}
|
||||
baseFS = target
|
||||
}
|
||||
|
||||
if err := i.refCountMounter.Unmount(root); err != nil {
|
||||
if err := i.refCountMounter.Unmount(baseFS); err != nil {
|
||||
log.G(ctx).WithField("container", container.ID).WithError(err).Error("error unmounting container")
|
||||
return fmt.Errorf("failed to unmount %s: %w", root, err)
|
||||
return fmt.Errorf("failed to unmount %s: %w", baseFS, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/docker/docker/daemon/graphdriver"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/moby/locker"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
)
|
||||
|
||||
// List of known filesystems that can't be re-mounted or have shared layers
|
||||
|
@ -22,6 +23,8 @@ type Mounter interface {
|
|||
Mount(mounts []mount.Mount, containerID string) (string, error)
|
||||
// Unmount unmounts the container rootfs
|
||||
Unmount(target string) error
|
||||
// Mounted returns a target mountpoint if it's already mounted
|
||||
Mounted(containerID string) (string, error)
|
||||
}
|
||||
|
||||
// inSlice tests whether a string is contained in a slice of strings or not.
|
||||
|
@ -110,6 +113,23 @@ func (m *refCountMounter) Unmount(target string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *refCountMounter) Mounted(containerID string) (string, error) {
|
||||
mounted, err := m.base.Mounted(containerID)
|
||||
if err != nil || mounted == "" {
|
||||
return mounted, err
|
||||
}
|
||||
|
||||
target := m.base.target(containerID)
|
||||
|
||||
// Check if the refcount is non-zero.
|
||||
m.rc.Increment(target)
|
||||
if m.rc.Decrement(target) > 0 {
|
||||
return mounted, nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type mounter struct {
|
||||
home string
|
||||
snapshotter string
|
||||
|
@ -137,6 +157,16 @@ func (m mounter) Unmount(target string) error {
|
|||
return unmount(target)
|
||||
}
|
||||
|
||||
func (m mounter) Mounted(containerID string) (string, error) {
|
||||
target := m.target(containerID)
|
||||
|
||||
mounted, err := mountinfo.Mounted(target)
|
||||
if err != nil || !mounted {
|
||||
return "", err
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func (m mounter) target(containerID string) string {
|
||||
return filepath.Join(m.home, "rootfs", m.snapshotter, containerID)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue