瀏覽代碼

Merge pull request #44934 from laurazard/c8d-layer-size

containerd integration: compute containers layer size
Sebastiaan van Stijn 2 年之前
父節點
當前提交
d7e5708bb4

+ 1 - 1
api/server/router/container/backend.go

@@ -49,7 +49,7 @@ type stateBackend interface {
 // monitorBackend includes functions to implement to provide containers monitoring functionality.
 type monitorBackend interface {
 	ContainerChanges(name string) ([]archive.Change, error)
-	ContainerInspect(name string, size bool, version string) (interface{}, error)
+	ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error)
 	ContainerLogs(ctx context.Context, name string, config *types.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
 	ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
 	ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error)

+ 1 - 1
api/server/router/container/inspect.go

@@ -12,7 +12,7 @@ func (s *containerRouter) getContainersByName(ctx context.Context, w http.Respon
 	displaySize := httputils.BoolValue(r, "size")
 
 	version := httputils.VersionFromContext(ctx)
-	json, err := s.backend.ContainerInspect(vars["name"], displaySize, version)
+	json, err := s.backend.ContainerInspect(ctx, vars["name"], displaySize, version)
 	if err != nil {
 		return err
 	}

+ 1 - 1
daemon/cluster/executor/backend.go

@@ -45,7 +45,7 @@ type Backend interface {
 	ActivateContainerServiceBinding(containerName string) error
 	DeactivateContainerServiceBinding(containerName string) error
 	UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error
-	ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error)
+	ContainerInspectCurrent(ctx context.Context, name string, size bool) (*types.ContainerJSON, error)
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerKill(name string, sig string) error

+ 1 - 1
daemon/cluster/executor/container/adapter.go

@@ -361,7 +361,7 @@ func (c *containerAdapter) start(ctx context.Context) error {
 }
 
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
-	cs, err := c.backend.ContainerInspectCurrent(c.container.name(), false)
+	cs, err := c.backend.ContainerInspectCurrent(ctx, c.container.name(), false)
 	if ctx.Err() != nil {
 		return types.ContainerJSON{}, ctx.Err()
 	}

+ 57 - 2
daemon/containerd/service.go

@@ -2,8 +2,10 @@ package containerd
 
 import (
 	"context"
+	"encoding/json"
 
 	"github.com/containerd/containerd"
+	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/plugin"
 	"github.com/containerd/containerd/remotes/docker"
 	"github.com/containerd/containerd/snapshots"
@@ -13,6 +15,9 @@ import (
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
+	"github.com/opencontainers/image-spec/identity"
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 )
 
@@ -139,6 +144,56 @@ func (i *ImageService) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer)
 }
 
 // GetContainerLayerSize returns the real size & virtual size of the container.
-func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) {
-	panic("not implemented")
+func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) {
+	ctr := i.containers.Get(containerID)
+	if ctr == nil {
+		return 0, 0, nil
+	}
+	cs := i.client.ContentStore()
+
+	imageManifestBytes, err := content.ReadBlob(ctx, cs, *ctr.ImageManifest)
+	if err != nil {
+		return 0, 0, err
+	}
+
+	var manifest ocispec.Manifest
+	if err := json.Unmarshal(imageManifestBytes, &manifest); err != nil {
+		return 0, 0, err
+	}
+
+	imageConfigBytes, err := content.ReadBlob(ctx, cs, manifest.Config)
+	if err != nil {
+		return 0, 0, err
+	}
+	var img ocispec.Image
+	if err := json.Unmarshal(imageConfigBytes, &img); err != nil {
+		return 0, 0, err
+	}
+
+	snapshotter := i.client.SnapshotService(i.snapshotter)
+	usage, err := snapshotter.Usage(ctx, containerID)
+	if err != nil {
+		return 0, 0, err
+	}
+
+	sizeCache := make(map[digest.Digest]int64)
+	snapshotSizeFn := func(d digest.Digest) (int64, error) {
+		if s, ok := sizeCache[d]; ok {
+			return s, nil
+		}
+		u, err := snapshotter.Usage(ctx, d.String())
+		if err != nil {
+			return 0, err
+		}
+		sizeCache[d] = u.Size
+		return u.Size, nil
+	}
+
+	chainIDs := identity.ChainIDs(img.RootFS.DiffIDs)
+	virtualSize, err := computeVirtualSize(chainIDs, snapshotSizeFn)
+	if err != nil {
+		return 0, 0, err
+	}
+
+	return usage.Size, usage.Size + virtualSize, nil
 }

+ 1 - 1
daemon/image_service.go

@@ -57,7 +57,7 @@ type ImageService interface {
 	GetLayerMountID(cid string) (string, error)
 	ReleaseLayer(rwlayer layer.RWLayer) error
 	LayerDiskUsage(ctx context.Context) (int64, error)
-	GetContainerLayerSize(containerID string) (int64, int64)
+	GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error)
 	Mount(ctx context.Context, container *container.Container) error
 	Unmount(ctx context.Context, container *container.Container) error
 

+ 5 - 3
daemon/images/image_unix.go

@@ -4,6 +4,8 @@
 package images // import "github.com/docker/docker/daemon/images"
 
 import (
+	"context"
+
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/sirupsen/logrus"
@@ -16,7 +18,7 @@ func (i *ImageService) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer)
 }
 
 // GetContainerLayerSize returns the real size & virtual size of the container.
-func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) {
+func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) {
 	var (
 		sizeRw, sizeRootfs int64
 		err                error
@@ -27,7 +29,7 @@ func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64)
 	rwlayer, err := i.layerStore.GetRWLayer(containerID)
 	if err != nil {
 		logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err)
-		return sizeRw, sizeRootfs
+		return sizeRw, sizeRootfs, nil
 	}
 	defer i.layerStore.ReleaseRWLayer(rwlayer)
 
@@ -46,5 +48,5 @@ func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64)
 			sizeRootfs += sizeRw
 		}
 	}
-	return sizeRw, sizeRootfs
+	return sizeRw, sizeRootfs, nil
 }

+ 4 - 2
daemon/images/image_windows.go

@@ -1,6 +1,8 @@
 package images
 
 import (
+	"context"
+
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/system"
@@ -8,9 +10,9 @@ import (
 )
 
 // GetContainerLayerSize returns real size & virtual size
-func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) {
+func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) {
 	// TODO Windows
-	return 0, 0
+	return 0, 0, nil
 }
 
 // GetLayerFolders returns the layer folders from an image RootFS

+ 9 - 5
daemon/inspect.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"time"
@@ -19,19 +20,19 @@ import (
 // ContainerInspect returns low-level information about a
 // container. Returns an error if the container cannot be found, or if
 // there is an error getting the data.
-func (daemon *Daemon) ContainerInspect(name string, size bool, version string) (interface{}, error) {
+func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error) {
 	switch {
 	case versions.LessThan(version, "1.20"):
-		return daemon.containerInspectPre120(name)
+		return daemon.containerInspectPre120(ctx, name)
 	case versions.Equal(version, "1.20"):
 		return daemon.containerInspect120(name)
 	}
-	return daemon.ContainerInspectCurrent(name, size)
+	return daemon.ContainerInspectCurrent(ctx, name, size)
 }
 
 // ContainerInspectCurrent returns low-level information about a
 // container in a most recent api version.
-func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error) {
+func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string, size bool) (*types.ContainerJSON, error) {
 	ctr, err := daemon.GetContainer(name)
 	if err != nil {
 		return nil, err
@@ -78,7 +79,10 @@ func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.Co
 	ctr.Unlock()
 
 	if size {
-		sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(base.ID)
+		sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, base.ID)
+		if err != nil {
+			return nil, err
+		}
 		base.SizeRw = &sizeRw
 		base.SizeRootFs = &sizeRootFs
 	}

+ 3 - 1
daemon/inspect_linux.go

@@ -1,6 +1,8 @@
 package daemon // import "github.com/docker/docker/daemon"
 
 import (
+	"context"
+
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/versions/v1p19"
@@ -18,7 +20,7 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON
 }
 
 // containerInspectPre120 gets containers for pre 1.20 APIs.
-func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, error) {
+func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (*v1p19.ContainerJSON, error) {
 	ctr, err := daemon.GetContainer(name)
 	if err != nil {
 		return nil, err

+ 4 - 2
daemon/inspect_windows.go

@@ -1,6 +1,8 @@
 package daemon // import "github.com/docker/docker/daemon"
 
 import (
+	"context"
+
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/container"
@@ -12,8 +14,8 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON
 }
 
 // containerInspectPre120 get containers for pre 1.20 APIs.
-func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) {
-	return daemon.ContainerInspectCurrent(name, false)
+func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (*types.ContainerJSON, error) {
+	return daemon.ContainerInspectCurrent(ctx, name, false)
 }
 
 func inspectExecProcessConfig(e *container.ExecConfig) *backend.ExecProcessConfig {

+ 4 - 1
daemon/list.go

@@ -238,7 +238,10 @@ func (daemon *Daemon) reducePsContainer(ctx context.Context, container *containe
 
 	// release lock because size calculation is slow
 	if filter.Size {
-		sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(newC.ID)
+		sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, newC.ID)
+		if err != nil {
+			return nil, err
+		}
 		newC.SizeRw = sizeRw
 		newC.SizeRootFs = sizeRootFs
 	}

+ 5 - 2
daemon/prune.go

@@ -73,9 +73,12 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
 			if !matchLabels(pruneFilters, c.Config.Labels) {
 				continue
 			}
-			cSize, _ := daemon.imageService.GetContainerLayerSize(c.ID)
+			cSize, _, err := daemon.imageService.GetContainerLayerSize(ctx, c.ID)
+			if err != nil {
+				return nil, err
+			}
 			// TODO: sets RmLink to true?
-			err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
+			err = daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
 			if err != nil {
 				logrus.Warnf("failed to prune container %s: %v", c.ID, err)
 				continue