mirror of
https://github.com/moby/buildkit.git
synced 2026-06-30 19:57:39 +00:00
Nydus image is a container accelerated image format provided by the Dragonfly image-service project, which offers the ability to pull image data on demand, without waiting for the entire image pull to complete and then start the container. It has been put in production usage and shown vast improvements over the old OCI image format in terms of container launching speed, image space, and network bandwidth efficiency, as well as data integrity. Nydus image can be flexibly configured as a FUSE-based user-space filesystem or in-kernel EROFS (from Linux kernel v5.16) with Nydus daemon in user-space, integrating with VM-based container runtime like KataContainers is much easier. Nydus has provided a conversion tool Nydusify for converting OCIv1 image to Nydus image and integrated into Harbor Acceld as a conversion driver, which assumes that the OCI image is already available in the registry, but a better way would be to build the Nydus images directly from the build system instead of using the conversion tool, which would increase the speed of the image export, so we experimentally integrated the Nydus export in Buildkit. Unlike other compression formats (gzip, estargz, etc.) in OCI image, nydus is divided into two types of layer, blob, and bootstrap, where blob serves as the data part of each layer of the image, and bootstrap serves as the metadata of the whole image, the bootstrap is equivalent to the view of the whole image filesystem after all layers overlay. For example, for an OCI image with 3 layers, the corresponding nydus image is 4 layers (3 layers of blob + 1 layer of bootstrap). The nydus-snapshotter project provides a package to do the actual layer compression, this commit imports the package to implement the export of nydus compression type. Signed-off-by: Yan Song <imeoer@linux.alibaba.com>
117 lines
3.9 KiB
Go
117 lines
3.9 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package cache
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"io"
|
|
|
|
"github.com/containerd/containerd/content"
|
|
"github.com/containerd/containerd/errdefs"
|
|
"github.com/containerd/containerd/mount"
|
|
"github.com/moby/buildkit/util/bklog"
|
|
"github.com/moby/buildkit/util/compression"
|
|
"github.com/moby/buildkit/util/overlay"
|
|
digest "github.com/opencontainers/go-digest"
|
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var emptyDesc = ocispecs.Descriptor{}
|
|
|
|
// computeOverlayBlob provides overlayfs-specialized method to compute
|
|
// diff between lower and upper snapshot. If the passed mounts cannot
|
|
// be computed (e.g. because the mounts aren't overlayfs), it returns
|
|
// an error.
|
|
func (sr *immutableRef) tryComputeOverlayBlob(ctx context.Context, lower, upper []mount.Mount, mediaType string, ref string, compressorFunc compression.Compressor) (_ ocispecs.Descriptor, ok bool, err error) {
|
|
// Get upperdir location if mounts are overlayfs that can be processed by this differ.
|
|
upperdir, err := overlay.GetUpperdir(lower, upper)
|
|
if err != nil {
|
|
// This is not an overlayfs snapshot. This is not an error so don't return error here
|
|
// and let the caller fallback to another differ.
|
|
return emptyDesc, false, nil
|
|
}
|
|
|
|
cw, err := sr.cm.ContentStore.Writer(ctx,
|
|
content.WithRef(ref),
|
|
content.WithDescriptor(ocispecs.Descriptor{
|
|
MediaType: mediaType, // most contentstore implementations just ignore this
|
|
}))
|
|
if err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to open writer")
|
|
}
|
|
defer func() {
|
|
if cw != nil {
|
|
if cerr := cw.Close(); cerr != nil {
|
|
bklog.G(ctx).WithError(cerr).Warnf("failed to close writer %q", ref)
|
|
}
|
|
}
|
|
}()
|
|
|
|
bufW := bufio.NewWriterSize(cw, 128*1024)
|
|
var labels map[string]string
|
|
if compressorFunc != nil {
|
|
dgstr := digest.SHA256.Digester()
|
|
compressed, err := compressorFunc(bufW, mediaType)
|
|
if err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to get compressed stream")
|
|
}
|
|
// Close ensure compressorFunc does some finalization works.
|
|
defer compressed.Close()
|
|
if err := overlay.WriteUpperdir(ctx, io.MultiWriter(compressed, dgstr.Hash()), upperdir, lower); err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to write compressed diff")
|
|
}
|
|
if err := compressed.Close(); err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to close compressed diff writer")
|
|
}
|
|
if labels == nil {
|
|
labels = map[string]string{}
|
|
}
|
|
labels[containerdUncompressed] = dgstr.Digest().String()
|
|
} else {
|
|
if err = overlay.WriteUpperdir(ctx, bufW, upperdir, lower); err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to write diff")
|
|
}
|
|
}
|
|
|
|
if err := bufW.Flush(); err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to flush diff")
|
|
}
|
|
var commitopts []content.Opt
|
|
if labels != nil {
|
|
commitopts = append(commitopts, content.WithLabels(labels))
|
|
}
|
|
dgst := cw.Digest()
|
|
if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil {
|
|
if !errdefs.IsAlreadyExists(err) {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to commit")
|
|
}
|
|
}
|
|
if err := cw.Close(); err != nil {
|
|
return emptyDesc, false, err
|
|
}
|
|
cw = nil
|
|
cinfo, err := sr.cm.ContentStore.Info(ctx, dgst)
|
|
if err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "failed to get info from content store")
|
|
}
|
|
if cinfo.Labels == nil {
|
|
cinfo.Labels = make(map[string]string)
|
|
}
|
|
// Set uncompressed label if digest already existed without label
|
|
if _, ok := cinfo.Labels[containerdUncompressed]; !ok {
|
|
cinfo.Labels[containerdUncompressed] = labels[containerdUncompressed]
|
|
if _, err := sr.cm.ContentStore.Update(ctx, cinfo, "labels."+containerdUncompressed); err != nil {
|
|
return emptyDesc, false, errors.Wrap(err, "error setting uncompressed label")
|
|
}
|
|
}
|
|
|
|
return ocispecs.Descriptor{
|
|
MediaType: mediaType,
|
|
Size: cinfo.Size,
|
|
Digest: cinfo.Digest,
|
|
}, true, nil
|
|
}
|