c8d: Compute container's layer size

Co-authored-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
This commit is contained in:
Laura Brehm 2023-03-06 16:02:37 +01:00 committed by Sebastiaan van Stijn
parent 6f719c74a9
commit 45ee4d7c78
No known key found for this signature in database
GPG key ID: 76698F39D527CE8C
13 changed files with 96 additions and 23 deletions

View file

@ -49,7 +49,7 @@ type stateBackend interface {
// monitorBackend includes functions to implement to provide containers monitoring functionality. // monitorBackend includes functions to implement to provide containers monitoring functionality.
type monitorBackend interface { type monitorBackend interface {
ContainerChanges(name string) ([]archive.Change, error) 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) 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 ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error) ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error)

View file

@ -12,7 +12,7 @@ func (s *containerRouter) getContainersByName(ctx context.Context, w http.Respon
displaySize := httputils.BoolValue(r, "size") displaySize := httputils.BoolValue(r, "size")
version := httputils.VersionFromContext(ctx) 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 { if err != nil {
return err return err
} }

View file

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

View file

@ -361,7 +361,7 @@ func (c *containerAdapter) start(ctx context.Context) error {
} }
func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, 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 { if ctx.Err() != nil {
return types.ContainerJSON{}, ctx.Err() return types.ContainerJSON{}, ctx.Err()
} }

View file

@ -2,8 +2,10 @@ package containerd
import ( import (
"context" "context"
"encoding/json"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/remotes/docker" "github.com/containerd/containerd/remotes/docker"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
@ -13,6 +15,9 @@ import (
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
"github.com/docker/docker/registry" "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" "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. // 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) {
panic("not implemented") 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
} }

View file

@ -57,7 +57,7 @@ type ImageService interface {
GetLayerMountID(cid string) (string, error) GetLayerMountID(cid string) (string, error)
ReleaseLayer(rwlayer layer.RWLayer) error ReleaseLayer(rwlayer layer.RWLayer) error
LayerDiskUsage(ctx context.Context) (int64, 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 Mount(ctx context.Context, container *container.Container) error
Unmount(ctx context.Context, container *container.Container) error Unmount(ctx context.Context, container *container.Container) error

View file

@ -4,6 +4,8 @@
package images // import "github.com/docker/docker/daemon/images" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"context"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
"github.com/sirupsen/logrus" "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. // 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 ( var (
sizeRw, sizeRootfs int64 sizeRw, sizeRootfs int64
err error err error
@ -27,7 +29,7 @@ func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64)
rwlayer, err := i.layerStore.GetRWLayer(containerID) rwlayer, err := i.layerStore.GetRWLayer(containerID)
if err != nil { if err != nil {
logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err) 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) defer i.layerStore.ReleaseRWLayer(rwlayer)
@ -46,5 +48,5 @@ func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64)
sizeRootfs += sizeRw sizeRootfs += sizeRw
} }
} }
return sizeRw, sizeRootfs return sizeRw, sizeRootfs, nil
} }

View file

@ -1,6 +1,8 @@
package images package images
import ( import (
"context"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
@ -8,9 +10,9 @@ import (
) )
// GetContainerLayerSize returns real size & virtual size // 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 // TODO Windows
return 0, 0 return 0, 0, nil
} }
// GetLayerFolders returns the layer folders from an image RootFS // GetLayerFolders returns the layer folders from an image RootFS

View file

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

View file

@ -1,6 +1,8 @@
package daemon // import "github.com/docker/docker/daemon" package daemon // import "github.com/docker/docker/daemon"
import ( import (
"context"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/versions/v1p19" "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. // 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) ctr, err := daemon.GetContainer(name)
if err != nil { if err != nil {
return nil, err return nil, err

View file

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

View file

@ -238,7 +238,10 @@ func (daemon *Daemon) reducePsContainer(ctx context.Context, container *containe
// release lock because size calculation is slow // release lock because size calculation is slow
if filter.Size { 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.SizeRw = sizeRw
newC.SizeRootFs = sizeRootFs newC.SizeRootFs = sizeRootFs
} }

View file

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