From acc3f74d5cf1df66cae6f39ec5eaefe56fb75393 Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Wed, 25 Oct 2017 20:42:32 +0000 Subject: [PATCH] Also unpack image during creation. Signed-off-by: Lantao Liu --- pkg/opts/container.go | 33 +++++++++++++++++++++++++++++++-- pkg/server/container_create.go | 1 + pkg/server/image_pull.go | 10 ++++++++-- pkg/server/restart.go | 2 +- pkg/server/sandbox_run.go | 2 ++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/pkg/opts/container.go b/pkg/opts/container.go index ea7abb9b0..3921a2245 100644 --- a/pkg/opts/container.go +++ b/pkg/opts/container.go @@ -21,12 +21,41 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/errdefs" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/system" + "github.com/opencontainers/image-spec/identity" "github.com/pkg/errors" "golang.org/x/sys/unix" ) +// WithImageUnpack guarantees that the image used by the container is unpacked. +func WithImageUnpack(i containerd.Image) containerd.NewContainerOpts { + return func(ctx context.Context, client *containerd.Client, c *containers.Container) error { + if c.Snapshotter == "" { + return errors.New("no snapshotter set for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + diffIDs, err := i.RootFS(ctx) + if err != nil { + return errors.Wrap(err, "get image diff IDs") + } + chainID := identity.ChainID(diffIDs) + _, err = snapshotter.Stat(ctx, chainID.String()) + if err == nil { + return nil + } + if !errdefs.IsNotFound(err) { + return errors.Wrap(err, "stat snapshot") + } + // Unpack the snapshot. + if err := i.Unpack(ctx, c.Snapshotter); err != nil { + return errors.Wrap(err, "unpack snapshot") + } + return nil + } +} + // WithVolumes copies ownership of volume in rootfs to its corresponding host path. // It doesn't update runtime spec. // The passed in map is a host path to container path map for all volumes. @@ -34,10 +63,10 @@ import ( func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts { return func(ctx context.Context, client *containerd.Client, c *containers.Container) error { if c.Snapshotter == "" { - return errors.Errorf("no snapshotter set for container") + return errors.New("no snapshotter set for container") } if c.SnapshotKey == "" { - return errors.Errorf("rootfs not created for container") + return errors.New("rootfs not created for container") } snapshotter := client.SnapshotService(c.Snapshotter) mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) diff --git a/pkg/server/container_create.go b/pkg/server/container_create.go index 9e5d36550..fd1e49f6d 100644 --- a/pkg/server/container_create.go +++ b/pkg/server/container_create.go @@ -150,6 +150,7 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C // Set snapshotter before any other options. opts := []containerd.NewContainerOpts{ containerd.WithSnapshotter(c.config.ContainerdConfig.Snapshotter), + customopts.WithImageUnpack(image.Image), // Prepare container rootfs. This is always writeable even if // the container wants a readonly rootfs since we want to give // the runtime (runc) a chance to modify (e.g. to create mount diff --git a/pkg/server/image_pull.go b/pkg/server/image_pull.go index 528e0483a..a717b1f73 100644 --- a/pkg/server/image_pull.go +++ b/pkg/server/image_pull.go @@ -99,14 +99,20 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma // TODO(mikebrow): add truncIndex for image id image, err := c.client.Pull(ctx, ref, - containerd.WithPullUnpack, containerd.WithSchema1Conversion, containerd.WithResolver(resolver), - containerd.WithPullSnapshotter(c.config.ContainerdConfig.Snapshotter), ) if err != nil { return nil, fmt.Errorf("failed to pull image %q: %v", ref, err) } + + // Do best effort unpack. + glog.V(4).Info("Unpack image %q", imageRef) + if err := image.Unpack(ctx, c.config.ContainerdConfig.Snapshotter); err != nil { + glog.Warningf("Failed to unpack image %q: %v", imageRef, err) + // Do not fail image pulling. Unpack will be retried before container creation. + } + repoDigest, repoTag := getRepoDigestAndTag(namedRef, image.Target().Digest, isSchema1) for _, r := range []string{repoTag, repoDigest} { if r == "" { diff --git a/pkg/server/restart.go b/pkg/server/restart.go index fa7203d17..bfa40f645 100644 --- a/pkg/server/restart.go +++ b/pkg/server/restart.go @@ -391,7 +391,7 @@ func loadImages(ctx context.Context, cImages []containerd.Image, provider conten // Checking existence of top-level snapshot for each image being recovered. if _, err := snapshotter.Stat(ctx, image.ChainID); err != nil { glog.Warningf("Failed to stat the top-level snapshot for image %+v: %v", image, err) - continue + // TODO(random-liu): Consider whether we should try unpack here. } images = append(images, image) } diff --git a/pkg/server/sandbox_run.go b/pkg/server/sandbox_run.go index 9dc853d2e..7b4223a17 100644 --- a/pkg/server/sandbox_run.go +++ b/pkg/server/sandbox_run.go @@ -33,6 +33,7 @@ import ( "golang.org/x/sys/unix" "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" + customopts "github.com/kubernetes-incubator/cri-containerd/pkg/opts" sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox" "github.com/kubernetes-incubator/cri-containerd/pkg/util" ) @@ -143,6 +144,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run opts := []containerd.NewContainerOpts{ containerd.WithSnapshotter(c.config.ContainerdConfig.Snapshotter), + customopts.WithImageUnpack(image.Image), containerd.WithNewSnapshot(id, image.Image), containerd.WithSpec(spec, specOpts...), containerd.WithContainerLabels(map[string]string{containerKindLabel: containerKindSandbox}),