Merge pull request #1402 from ktock/remote-snapshotter
Support stargz snapshotter for dev stagesv0.8
commit
a57f8a2dcc
14
Dockerfile
14
Dockerfile
|
@ -11,6 +11,7 @@ ARG ROOTLESSKIT_VERSION=v0.9.5
|
|||
ARG CNI_VERSION=v0.8.6
|
||||
ARG SHADOW_VERSION=4.8.1
|
||||
ARG FUSEOVERLAYFS_VERSION=v1.1.2
|
||||
ARG STARGZ_SNAPSHOTTER_VERSION=5aca593bd474015005b8832cf9763685d9d4db61
|
||||
|
||||
# git stage is used for checking out remote repository sources
|
||||
FROM --platform=$BUILDPLATFORM alpine AS git
|
||||
|
@ -173,6 +174,15 @@ RUN --mount=target=/root/.cache,type=cache \
|
|||
CGO_ENABLED=0 go build -o /rootlesskit ./cmd/rootlesskit && \
|
||||
file /rootlesskit | grep "statically linked"
|
||||
|
||||
FROM gobuild-base AS stargz-snapshotter
|
||||
RUN git clone https://github.com/containerd/stargz-snapshotter.git /go/src/github.com/containerd/stargz-snapshotter
|
||||
WORKDIR /go/src/github.com/containerd/stargz-snapshotter
|
||||
ARG STARGZ_SNAPSHOTTER_VERSION
|
||||
RUN git checkout -q "$STARGZ_SNAPSHOTTER_VERSION" && \
|
||||
mkdir /out && CGO_ENABLED=0 PREFIX=/out/ make && \
|
||||
file /out/containerd-stargz-grpc | grep "statically linked" && \
|
||||
file /out/ctr-remote | grep "statically linked"
|
||||
|
||||
FROM --platform=$BUILDPLATFORM alpine AS fuse-overlayfs
|
||||
RUN apk add --no-cache curl
|
||||
ARG FUSEOVERLAYFS_VERSION
|
||||
|
@ -225,7 +235,7 @@ RUN curl -Ls https://github.com/containernetworking/plugins/releases/download/$C
|
|||
|
||||
FROM buildkit-base AS integration-tests-base
|
||||
ENV BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR="1000:1000"
|
||||
RUN apt-get --no-install-recommends install -y uidmap sudo vim iptables \
|
||||
RUN apt-get --no-install-recommends install -y uidmap sudo vim iptables fuse \
|
||||
&& useradd --create-home --home-dir /home/user --uid 1000 -s /bin/sh user \
|
||||
&& echo "XDG_RUNTIME_DIR=/run/user/1000; export XDG_RUNTIME_DIR" >> /home/user/.profile \
|
||||
&& mkdir -m 0700 -p /run/user/1000 \
|
||||
|
@ -233,6 +243,8 @@ RUN apt-get --no-install-recommends install -y uidmap sudo vim iptables \
|
|||
&& update-alternatives --set iptables /usr/sbin/iptables-legacy
|
||||
# musl is needed to directly use the registry binary that is built on alpine
|
||||
ENV BUILDKIT_INTEGRATION_CONTAINERD_EXTRA="containerd-1.3=/opt/containerd-alt/bin"
|
||||
ENV BUILDKIT_INTEGRATION_CONTAINERD_STARGZ=1
|
||||
COPY --from=stargz-snapshotter /out/* /usr/bin/
|
||||
COPY --from=rootlesskit /rootlesskit /usr/bin/
|
||||
COPY --from=containerd-alt /out/containerd* /opt/containerd-alt/bin/
|
||||
COPY --from=registry /bin/registry /usr/bin
|
||||
|
|
|
@ -385,12 +385,17 @@ func (cm *cacheManager) getRecord(ctx context.Context, id string, opts ...RefOpt
|
|||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parent refs are possibly lazy so keep it hold the description handlers.
|
||||
var dhs DescHandlers
|
||||
if mutable.parent != nil {
|
||||
dhs = mutable.parent.descHandlers
|
||||
}
|
||||
rec := &cacheRecord{
|
||||
mu: &sync.Mutex{},
|
||||
cm: cm,
|
||||
refs: make(map[ref]struct{}),
|
||||
// mutable refs are always non-lazy, so we can set parent desc handlers to nil
|
||||
parent: mutable.parentRef(false, nil),
|
||||
mu: &sync.Mutex{},
|
||||
cm: cm,
|
||||
refs: make(map[ref]struct{}),
|
||||
parent: mutable.parentRef(false, dhs),
|
||||
md: md,
|
||||
equalMutable: &mutableRef{cacheRecord: mutable},
|
||||
}
|
||||
|
@ -535,7 +540,12 @@ func (cm *cacheManager) New(ctx context.Context, s ImmutableRef, opts ...RefOpti
|
|||
|
||||
cm.records[id] = rec // TODO: save to db
|
||||
|
||||
return rec.mref(true, nil), nil
|
||||
// parent refs are possibly lazy so keep it hold the description handlers.
|
||||
var dhs DescHandlers
|
||||
if parent != nil {
|
||||
dhs = parent.descHandlers
|
||||
}
|
||||
return rec.mref(true, dhs), nil
|
||||
}
|
||||
|
||||
func (cm *cacheManager) GetMutable(ctx context.Context, id string, opts ...RefOption) (MutableRef, error) {
|
||||
|
|
|
@ -9,8 +9,9 @@ import (
|
|||
)
|
||||
|
||||
type DescHandler struct {
|
||||
Provider content.Provider
|
||||
Progress progress.Controller
|
||||
Provider content.Provider
|
||||
Progress progress.Controller
|
||||
SnapshotLabels map[string]string
|
||||
}
|
||||
|
||||
type DescHandlers map[digest.Digest]*DescHandler
|
||||
|
|
|
@ -391,15 +391,69 @@ func (sr *immutableRef) Extract(ctx context.Context) (rerr error) {
|
|||
ctx = winlayers.UseWindowsLayerMode(ctx)
|
||||
}
|
||||
|
||||
if _, err := sr.prepareRemoteSnapshots(ctx, sr.descHandlers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sr.extract(ctx, sr.descHandlers)
|
||||
}
|
||||
|
||||
func (sr *immutableRef) prepareRemoteSnapshots(ctx context.Context, dhs DescHandlers) (bool, error) {
|
||||
ok, err := sr.sizeG.Do(ctx, sr.ID()+"-prepare-remote-snapshot", func(ctx context.Context) (_ interface{}, rerr error) {
|
||||
snapshotID := getSnapshotID(sr.md)
|
||||
if _, err := sr.cm.Snapshotter.Stat(ctx, snapshotID); err == nil {
|
||||
return true, nil
|
||||
}
|
||||
desc, err := sr.ociDesc()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
dh := dhs[desc.Digest]
|
||||
if dh == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
parentID := ""
|
||||
if sr.parent != nil {
|
||||
if ok, err := sr.parent.prepareRemoteSnapshots(ctx, dhs); !ok {
|
||||
return false, err
|
||||
}
|
||||
parentID = getSnapshotID(sr.parent.md)
|
||||
}
|
||||
|
||||
// Hint labels to the snapshotter
|
||||
labels := dh.SnapshotLabels
|
||||
if labels == nil {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
labels["containerd.io/snapshot.ref"] = snapshotID
|
||||
opt := snapshots.WithLabels(labels)
|
||||
|
||||
// Try to preapre the remote snapshot
|
||||
key := fmt.Sprintf("tmp-%s %s", identity.NewID(), sr.Info().ChainID)
|
||||
if err = sr.cm.Snapshotter.Prepare(ctx, key, parentID, opt); err != nil {
|
||||
if errdefs.IsAlreadyExists(err) {
|
||||
// Check if the targeting snapshot ID has been prepared as a remote
|
||||
// snapshot in the snapshotter.
|
||||
if _, err := sr.cm.Snapshotter.Stat(ctx, snapshotID); err == nil {
|
||||
// We can use this remote snapshot without unlazying.
|
||||
// Try the next layer as well.
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This layer cannot be prepared without unlazying.
|
||||
return false, nil
|
||||
})
|
||||
return ok.(bool), err
|
||||
}
|
||||
|
||||
func (sr *immutableRef) extract(ctx context.Context, dhs DescHandlers) error {
|
||||
_, err := sr.sizeG.Do(ctx, sr.ID()+"-extract", func(ctx context.Context) (_ interface{}, rerr error) {
|
||||
snapshotID := getSnapshotID(sr.md)
|
||||
if _, err := sr.cm.Snapshotter.Stat(ctx, snapshotID); err == nil {
|
||||
queueBlobOnly(sr.md, false)
|
||||
return nil, sr.md.Commit()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
eg, egctx := errgroup.WithContext(ctx)
|
||||
|
|
|
@ -35,7 +35,7 @@ func (sr *immutableRef) GetRemote(ctx context.Context, createIfNeeded bool, comp
|
|||
return nil, err
|
||||
}
|
||||
|
||||
mprovider := lazyMultiProvider{mprovider: contentutil.NewMultiProvider(nil)}
|
||||
mprovider := &lazyMultiProvider{mprovider: contentutil.NewMultiProvider(nil)}
|
||||
remote := &solver.Remote{
|
||||
Provider: mprovider,
|
||||
}
|
||||
|
@ -115,18 +115,19 @@ type lazyMultiProvider struct {
|
|||
plist []lazyRefProvider
|
||||
}
|
||||
|
||||
func (mp lazyMultiProvider) Add(p lazyRefProvider) {
|
||||
func (mp *lazyMultiProvider) Add(p lazyRefProvider) {
|
||||
mp.mprovider.Add(p.desc.Digest, p)
|
||||
mp.plist = append(mp.plist, p)
|
||||
}
|
||||
|
||||
func (mp lazyMultiProvider) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
func (mp *lazyMultiProvider) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
return mp.mprovider.ReaderAt(ctx, desc)
|
||||
}
|
||||
|
||||
func (mp lazyMultiProvider) Unlazy(ctx context.Context) error {
|
||||
func (mp *lazyMultiProvider) Unlazy(ctx context.Context) error {
|
||||
eg, egctx := errgroup.WithContext(ctx)
|
||||
for _, p := range mp.plist {
|
||||
p := p
|
||||
eg.Go(func() error {
|
||||
return p.Unlazy(egctx)
|
||||
})
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
@ -119,6 +120,7 @@ func TestIntegration(t *testing.T) {
|
|||
testSourceMap,
|
||||
testSourceMapFromRef,
|
||||
testLazyImagePush,
|
||||
testStargzLazyPull,
|
||||
}, mirrors)
|
||||
|
||||
integration.Run(t, []integration.Test{
|
||||
|
@ -2049,6 +2051,112 @@ func testBuildPushAndValidate(t *testing.T, sb integration.Sandbox) {
|
|||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func testStargzLazyPull(t *testing.T, sb integration.Sandbox) {
|
||||
skipDockerd(t, sb)
|
||||
requiresLinux(t)
|
||||
|
||||
cdAddress := sb.ContainerdAddress()
|
||||
if cdAddress == "" || !sb.Stargz() {
|
||||
t.Skip("test requires containerd worker with stargz snapshotter")
|
||||
}
|
||||
|
||||
client, err := newContainerd(cdAddress)
|
||||
require.NoError(t, err)
|
||||
defer client.Close()
|
||||
registry, err := sb.NewRegistry()
|
||||
if errors.Is(err, integration.ErrorRequirements) {
|
||||
t.Skip(err.Error())
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// Prepare stargz image
|
||||
sgzImage := registry + "/stargz/alpine:latest"
|
||||
err = exec.Command("ctr-remote", "image", "optimize",
|
||||
"--period=1", "alpine:latest", sgzImage).Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := New(context.TODO(), sb.Address())
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
// stargz layers should be lazy even for executing something on them
|
||||
def, err := llb.Image(sgzImage).
|
||||
Run(llb.Args([]string{"/bin/touch", "/foo"})).
|
||||
Marshal(context.TODO())
|
||||
require.NoError(t, err)
|
||||
target := registry + "/buildkit/testlazyimage:latest"
|
||||
_, err = c.Solve(context.TODO(), def, SolveOpt{
|
||||
Exports: []ExportEntry{
|
||||
{
|
||||
Type: ExporterImage,
|
||||
Attrs: map[string]string{
|
||||
"name": target,
|
||||
"push": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
var (
|
||||
imageService = client.ImageService()
|
||||
contentStore = client.ContentStore()
|
||||
ctx = namespaces.WithNamespace(context.Background(), "buildkit")
|
||||
)
|
||||
|
||||
img, err := imageService.Get(ctx, target)
|
||||
require.NoError(t, err)
|
||||
|
||||
manifest, err := images.Manifest(ctx, contentStore, img.Target, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check if image layers are lazy.
|
||||
// The topmost(last) layer created by `Run` isn't lazy so we skip the check for the layer.
|
||||
var sgzLayers []ocispec.Descriptor
|
||||
for _, layer := range manifest.Layers[:len(manifest.Layers)-1] {
|
||||
_, err = contentStore.Info(ctx, layer.Digest)
|
||||
require.True(t, errors.Is(err, ctderrdefs.ErrNotFound), "unexpected error %v", err)
|
||||
sgzLayers = append(sgzLayers, layer)
|
||||
}
|
||||
require.NotEqual(t, 0, len(sgzLayers), "no layer can be used for checking lazypull")
|
||||
|
||||
// The topmost(last) layer created by `Run` shouldn't be lazy
|
||||
_, err = contentStore.Info(ctx, manifest.Layers[len(manifest.Layers)-1].Digest)
|
||||
require.NoError(t, err)
|
||||
|
||||
// clear all local state out
|
||||
err = imageService.Delete(ctx, img.Name, images.SynchronousDelete())
|
||||
require.NoError(t, err)
|
||||
checkAllReleasable(t, c, sb, true)
|
||||
|
||||
// stargz layers should be exportable
|
||||
destDir, err := ioutil.TempDir("", "buildkit")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(destDir)
|
||||
out := filepath.Join(destDir, "out.tar")
|
||||
outW, err := os.Create(out)
|
||||
require.NoError(t, err)
|
||||
_, err = c.Solve(context.TODO(), def, SolveOpt{
|
||||
Exports: []ExportEntry{
|
||||
{
|
||||
Type: ExporterOCI,
|
||||
Output: fixedWriteCloser(outW),
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check if image layers are un-lazied
|
||||
for _, layer := range sgzLayers {
|
||||
_, err = contentStore.Info(ctx, layer.Digest)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
err = c.Prune(context.TODO(), nil, PruneAll)
|
||||
require.NoError(t, err)
|
||||
checkAllRemoved(t, c, sb)
|
||||
}
|
||||
|
||||
func testLazyImagePush(t *testing.T, sb integration.Sandbox) {
|
||||
skipDockerd(t, sb)
|
||||
requiresLinux(t)
|
||||
|
|
|
@ -102,7 +102,11 @@ func testBuildContainerdExporter(t *testing.T, sb integration.Sandbox) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// NOTE: by default, it is overlayfs
|
||||
ok, err := img.IsUnpacked(ctx, "overlayfs")
|
||||
snapshotter := "overlayfs"
|
||||
if sb.Stargz() {
|
||||
snapshotter = "stargz"
|
||||
}
|
||||
ok, err := img.IsUnpacked(ctx, snapshotter)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ok, true)
|
||||
}
|
||||
|
|
|
@ -78,7 +78,8 @@ type OCIConfig struct {
|
|||
// incomplete and the intention is to make it default without config.
|
||||
UserRemapUnsupported string `toml:"userRemapUnsupported"`
|
||||
// For use in storing the OCI worker binary name that will replace buildkit-runc
|
||||
Binary string `toml:"binary"`
|
||||
Binary string `toml:"binary"`
|
||||
ProxySnapshotterPath string `toml:"proxySnapshotterPath"`
|
||||
}
|
||||
|
||||
type ContainerdConfig struct {
|
||||
|
@ -89,6 +90,7 @@ type ContainerdConfig struct {
|
|||
Namespace string `toml:"namespace"`
|
||||
GCConfig
|
||||
NetworkConfig
|
||||
Snapshotter string `toml:"snapshotter"`
|
||||
}
|
||||
|
||||
type GCPolicy struct {
|
||||
|
|
|
@ -86,6 +86,11 @@ func init() {
|
|||
Usage: "path of cni binary files",
|
||||
Value: defaultConf.Workers.Containerd.NetworkConfig.CNIBinaryPath,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "containerd-worker-snapshotter",
|
||||
Usage: "snapshotter name to use",
|
||||
Value: ctd.DefaultSnapshotter,
|
||||
},
|
||||
}
|
||||
|
||||
if defaultConf.Workers.Containerd.GC == nil || *defaultConf.Workers.Containerd.GC {
|
||||
|
@ -184,6 +189,9 @@ func applyContainerdFlags(c *cli.Context, cfg *config.Config) error {
|
|||
if c.GlobalIsSet("containerd-cni-binary-dir") {
|
||||
cfg.Workers.Containerd.NetworkConfig.CNIBinaryPath = c.GlobalString("containerd-cni-binary-dir")
|
||||
}
|
||||
if c.GlobalIsSet("containerd-worker-snapshotter") {
|
||||
cfg.Workers.Containerd.Snapshotter = c.GlobalString("containerd-worker-snapshotter")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -210,7 +218,11 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([
|
|||
},
|
||||
}
|
||||
|
||||
opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, ctd.DefaultSnapshotter, cfg.Namespace, cfg.Labels, dns, nc, ctd.WithTimeout(60*time.Second))
|
||||
snapshotter := ctd.DefaultSnapshotter
|
||||
if cfg.Snapshotter != "" {
|
||||
snapshotter = cfg.Snapshotter
|
||||
}
|
||||
opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, snapshotter, cfg.Namespace, cfg.Labels, dns, nc, ctd.WithTimeout(60*time.Second))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -6,11 +6,16 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
fuseoverlayfs "github.com/AkihiroSuda/containerd-fuse-overlayfs"
|
||||
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
|
||||
"github.com/containerd/containerd/defaults"
|
||||
"github.com/containerd/containerd/pkg/dialer"
|
||||
ctdsnapshot "github.com/containerd/containerd/snapshots"
|
||||
"github.com/containerd/containerd/snapshots/native"
|
||||
"github.com/containerd/containerd/snapshots/overlay"
|
||||
snproxy "github.com/containerd/containerd/snapshots/proxy"
|
||||
"github.com/containerd/containerd/sys"
|
||||
"github.com/moby/buildkit/cmd/buildkitd/config"
|
||||
"github.com/moby/buildkit/executor/oci"
|
||||
|
@ -22,6 +27,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -50,9 +57,13 @@ func init() {
|
|||
},
|
||||
cli.StringFlag{
|
||||
Name: "oci-worker-snapshotter",
|
||||
Usage: "name of snapshotter (overlayfs or native)",
|
||||
Usage: "name of snapshotter (overlayfs, native, etc.)",
|
||||
Value: defaultConf.Workers.OCI.Snapshotter,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "oci-worker-proxy-snapshotter-path",
|
||||
Usage: "address of proxy snapshotter socket (do not include 'unix://' prefix)",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "oci-worker-platform",
|
||||
Usage: "override supported platforms for worker",
|
||||
|
@ -193,6 +204,9 @@ func applyOCIFlags(c *cli.Context, cfg *config.Config) error {
|
|||
if c.GlobalIsSet("oci-worker-binary") {
|
||||
cfg.Workers.OCI.Binary = c.GlobalString("oci-worker-binary")
|
||||
}
|
||||
if c.GlobalIsSet("oci-worker-proxy-snapshotter-path") {
|
||||
cfg.Workers.OCI.ProxySnapshotterPath = c.GlobalString("oci-worker-proxy-snapshotter-path")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -213,7 +227,7 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
|
|||
return nil, err
|
||||
}
|
||||
|
||||
snFactory, err := snapshotterFactory(common.config.Root, cfg.Snapshotter)
|
||||
snFactory, err := snapshotterFactory(common.config.Root, cfg.Snapshotter, cfg.ProxySnapshotterPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -266,7 +280,36 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
|
|||
return []worker.Worker{w}, nil
|
||||
}
|
||||
|
||||
func snapshotterFactory(commonRoot, name string) (runc.SnapshotterFactory, error) {
|
||||
func snapshotterFactory(commonRoot, name, address string) (runc.SnapshotterFactory, error) {
|
||||
if address != "" {
|
||||
snFactory := runc.SnapshotterFactory{
|
||||
Name: name,
|
||||
}
|
||||
if _, err := os.Stat(address); os.IsNotExist(err) {
|
||||
return snFactory, errors.Wrapf(err, "snapshotter doesn't exist on %q (Do not include 'unix://' prefix)", address)
|
||||
}
|
||||
snFactory.New = func(root string) (ctdsnapshot.Snapshotter, error) {
|
||||
backoffConfig := backoff.DefaultConfig
|
||||
backoffConfig.MaxDelay = 3 * time.Second
|
||||
connParams := grpc.ConnectParams{
|
||||
Backoff: backoffConfig,
|
||||
}
|
||||
gopts := []grpc.DialOption{
|
||||
grpc.WithInsecure(),
|
||||
grpc.WithConnectParams(connParams),
|
||||
grpc.WithContextDialer(dialer.ContextDialer),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
|
||||
}
|
||||
conn, err := grpc.Dial(dialer.DialAddress(address), gopts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to dial %q", address)
|
||||
}
|
||||
return snproxy.NewSnapshotter(snapshotsapi.NewSnapshotsClient(conn), name), nil
|
||||
}
|
||||
return snFactory, nil
|
||||
}
|
||||
|
||||
if name == "auto" {
|
||||
if err := overlay.Supported(commonRoot); err == nil {
|
||||
name = "overlayfs"
|
||||
|
|
|
@ -3,7 +3,9 @@ package containerimage
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -13,6 +15,7 @@ import (
|
|||
"github.com/containerd/containerd/leases"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
|
@ -203,14 +206,29 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach
|
|||
progressController.Name = p.vtx.Name()
|
||||
}
|
||||
|
||||
descHandler := &cache.DescHandler{
|
||||
Provider: p.manifest.Remote.Provider,
|
||||
Progress: progressController,
|
||||
var layers string
|
||||
for _, desc := range p.manifest.Remote.Descriptors {
|
||||
layers += fmt.Sprintf("%s,", desc.Digest.String())
|
||||
}
|
||||
layers = strings.TrimSuffix(layers, ",")
|
||||
|
||||
p.descHandlers = cache.DescHandlers(make(map[digest.Digest]*cache.DescHandler))
|
||||
for _, desc := range p.manifest.Remote.Descriptors {
|
||||
p.descHandlers[desc.Digest] = descHandler
|
||||
|
||||
// Hints for remote/stargz snapshotter for searching for remote snapshots
|
||||
labels := snapshots.FilterInheritedLabels(desc.Annotations)
|
||||
if labels == nil {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
labels["containerd.io/snapshot/remote/stargz.reference"] = p.manifest.Ref
|
||||
labels["containerd.io/snapshot/remote/stargz.digest"] = desc.Digest.String()
|
||||
labels["containerd.io/snapshot/remote/stargz.layers"] = layers
|
||||
|
||||
p.descHandlers[desc.Digest] = &cache.DescHandler{
|
||||
Provider: p.manifest.Remote.Provider,
|
||||
Progress: progressController,
|
||||
SnapshotLabels: labels,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,12 +37,22 @@ func InitContainerdWorker() {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if s := os.Getenv("BUILDKIT_INTEGRATION_CONTAINERD_STARGZ"); s == "1" {
|
||||
Register(&containerd{
|
||||
name: "containerd-stargz",
|
||||
containerd: "containerd",
|
||||
containerdShim: "containerd-shim-runc-v2",
|
||||
stargzSnapshotter: "containerd-stargz-grpc",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type containerd struct {
|
||||
name string
|
||||
containerd string
|
||||
containerdShim string
|
||||
name string
|
||||
containerd string
|
||||
containerdShim string
|
||||
stargzSnapshotter string
|
||||
}
|
||||
|
||||
func (c *containerd) Name() string {
|
||||
|
@ -98,6 +108,26 @@ disabled_plugins = ["cri"]
|
|||
[plugins.linux]
|
||||
shim = %q
|
||||
`, filepath.Join(tmpdir, "root"), filepath.Join(tmpdir, "state"), address, filepath.Join(tmpdir, "debug.sock"), c.containerdShim)
|
||||
|
||||
var snBuildkitdArgs []string
|
||||
if c.stargzSnapshotter != "" {
|
||||
snPath, snCl, err := runStargzSnapshotter(cfg, c.stargzSnapshotter)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
deferF.append(snCl)
|
||||
|
||||
config = fmt.Sprintf(`%s
|
||||
|
||||
[proxy_plugins]
|
||||
[proxy_plugins.stargz]
|
||||
type = "snapshot"
|
||||
address = %q
|
||||
`, config, snPath)
|
||||
|
||||
snBuildkitdArgs = append(snBuildkitdArgs, "--containerd-worker-snapshotter=stargz")
|
||||
}
|
||||
|
||||
configFile := filepath.Join(tmpdir, "config.toml")
|
||||
if err := ioutil.WriteFile(configFile, []byte(config), 0644); err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -109,19 +139,19 @@ disabled_plugins = ["cri"]
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := waitUnix(address, 5*time.Second); err != nil {
|
||||
if err := waitUnix(address, 10*time.Second); err != nil {
|
||||
ctdStop()
|
||||
return nil, nil, errors.Wrapf(err, "containerd did not start up: %s", formatLogs(cfg.Logs))
|
||||
}
|
||||
deferF.append(ctdStop)
|
||||
|
||||
buildkitdArgs := []string{"buildkitd",
|
||||
buildkitdArgs := append([]string{"buildkitd",
|
||||
"--oci-worker=false",
|
||||
"--containerd-worker-gc=false",
|
||||
"--containerd-worker=true",
|
||||
"--containerd-worker-addr", address,
|
||||
"--containerd-worker-labels=org.mobyproject.buildkit.worker.sandbox=true", // Include use of --containerd-worker-labels to trigger https://github.com/moby/buildkit/pull/603
|
||||
}
|
||||
}, snBuildkitdArgs...)
|
||||
|
||||
buildkitdSock, stop, err := runBuildkitd(cfg, buildkitdArgs, cfg.Logs, 0, 0)
|
||||
if err != nil {
|
||||
|
@ -134,6 +164,7 @@ disabled_plugins = ["cri"]
|
|||
address: buildkitdSock,
|
||||
containerdAddress: address,
|
||||
rootless: false,
|
||||
stargz: c.stargzSnapshotter != "",
|
||||
}, cl, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/gofrs/flock"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
@ -37,6 +38,7 @@ type Backend interface {
|
|||
Address() string
|
||||
ContainerdAddress() string
|
||||
Rootless() bool
|
||||
Stargz() bool
|
||||
}
|
||||
|
||||
type Sandbox interface {
|
||||
|
@ -371,3 +373,50 @@ func prepareValueMatrix(tc testConf) []matrixValue {
|
|||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func runStargzSnapshotter(cfg *BackendConfig, binary string) (address string, cl func() error, err error) {
|
||||
if err := lookupBinary(binary); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
deferF := &multiCloser{}
|
||||
cl = deferF.F()
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
deferF.F()()
|
||||
cl = nil
|
||||
}
|
||||
}()
|
||||
|
||||
tmpStargzDir, err := ioutil.TempDir("", "bktest_containerd_stargz_grpc")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
deferF.append(func() error { return os.RemoveAll(tmpStargzDir) })
|
||||
|
||||
config := `insecure = ["127.0.0.1", "localhost"]`
|
||||
configFile := filepath.Join(tmpStargzDir, "config.toml")
|
||||
if err = ioutil.WriteFile(configFile, []byte(config), 0644); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
address = filepath.Join(tmpStargzDir, "containerd-stargz-grpc.sock")
|
||||
stargzRootDir := filepath.Join(tmpStargzDir, "root")
|
||||
cmd := exec.Command(binary,
|
||||
"--log-level", "debug",
|
||||
"--address", address,
|
||||
"--root", stargzRootDir,
|
||||
"--config", configFile)
|
||||
snStop, err := startCmd(cmd, cfg.Logs)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err = waitUnix(address, 10*time.Second); err != nil {
|
||||
snStop()
|
||||
return "", nil, errors.Wrapf(err, "containerd-stargz-grpc did not start up: %s", formatLogs(cfg.Logs))
|
||||
}
|
||||
deferF.append(snStop)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ type backend struct {
|
|||
address string
|
||||
containerdAddress string
|
||||
rootless bool
|
||||
stargz bool
|
||||
}
|
||||
|
||||
func (b backend) Address() string {
|
||||
|
@ -38,6 +39,10 @@ func (b backend) Rootless() bool {
|
|||
return b.rootless
|
||||
}
|
||||
|
||||
func (b backend) Stargz() bool {
|
||||
return b.stargz
|
||||
}
|
||||
|
||||
type sandbox struct {
|
||||
Backend
|
||||
|
||||
|
|
Loading…
Reference in New Issue