diff --git a/cache/blobs.go b/cache/blobs.go index 1736eb887759..95684b28fb5d 100644 --- a/cache/blobs.go +++ b/cache/blobs.go @@ -1,12 +1,18 @@ package cache import ( + "bufio" "context" "fmt" + "io" "maps" "os" + "path" "strconv" + obdlabel "github.com/containerd/accelerated-container-image/pkg/label" + obdcmd "github.com/containerd/accelerated-container-image/pkg/utils" + "github.com/containerd/containerd/v2/core/content" "github.com/containerd/containerd/v2/core/diff" "github.com/containerd/containerd/v2/core/leases" "github.com/containerd/containerd/v2/core/mount" @@ -108,7 +114,6 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool }() compressorFunc, finalize := comp.Type.Compress(ctx, comp) - mediaType := comp.Type.MediaType() var lowerRef *immutableRef switch sr.kind() { @@ -180,6 +185,21 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool enableOverlay = false } } + + mediaType := comp.Type.MediaType() + if sr.cm.Snapshotter.Name() == "overlaybd" { + snStat, err := sr.cm.Snapshotter.Stat(ctx, sr.getSnapshotID()) + if err != nil { + return nil, errors.Wrapf(err, "failed to Stat overlaybd") + } + if bdPath := snStat.Labels[obdlabel.LocalOverlayBDPath]; bdPath != "" { + if err := commitOverlayBD(ctx, sr, &desc); err != nil { + return nil, err + } + mediaType = desc.MediaType + enableOverlay = false + } + } if enableOverlay { computed, ok, err := sr.tryComputeOverlayBlob(ctx, lower, upper, mediaType, sr.ID(), compressorFunc) if !ok || err != nil { @@ -499,3 +519,50 @@ func ensureCompression(ctx context.Context, ref *immutableRef, comp compression. } return nil } + +func commitOverlayBD(ctx context.Context, sr *immutableRef, desc *ocispecs.Descriptor) error { + snStat, err := sr.cm.Snapshotter.Stat(ctx, sr.getSnapshotID()) + if err != nil { + return errors.Wrapf(err, "failed to Stat overlaybd") + } + bdPath := snStat.Labels[obdlabel.LocalOverlayBDPath] + if bdPath == "" { + return errors.New("missing overlaybd path label") + } + dir := path.Dir(bdPath) + commitPath := path.Join(dir, "overlaybd.commit") + err = obdcmd.Commit(ctx, dir, dir, true, "-t", "-z", "-f") + if err != nil { + return errors.Wrapf(err, "failed to overlaybd-commit") + } + cw, err := sr.cm.ContentStore.Writer(ctx, content.WithRef(sr.ID())) + if err != nil { + return errors.Wrapf(err, "failed to open writer") + } + fi, err := os.Open(commitPath) + if err != nil { + return errors.Wrapf(err, "failed to open overlaybd commit file") + } + sz, err := io.Copy(cw, bufio.NewReader(fi)) + if err != nil { + return errors.Wrapf(err, "failed to do io.Copy()") + } + dgst := cw.Digest() + labels := map[string]string{ + labels.LabelUncompressed: dgst.String(), + obdlabel.OverlayBDBlobDigest: dgst.String(), + obdlabel.OverlayBDBlobSize: fmt.Sprintf("%d", sz), + } + err = cw.Commit(ctx, sz, dgst, content.WithLabels(labels)) + if err != nil { + return errors.Wrapf(err, "failed to do cw.Commit") + } + desc.Digest = dgst + desc.Size = sz + desc.MediaType = ocispecs.MediaTypeImageLayer + desc.Annotations = map[string]string{ + obdlabel.OverlayBDBlobDigest: string(desc.Digest), + obdlabel.OverlayBDBlobSize: fmt.Sprintf("%d", desc.Size), + } + return nil +} diff --git a/cache/manager.go b/cache/manager.go index f5af2c6a97d2..c2ffa608fb96 100644 --- a/cache/manager.go +++ b/cache/manager.go @@ -8,9 +8,11 @@ import ( "sync" "time" + obdlabel "github.com/containerd/accelerated-container-image/pkg/label" "github.com/containerd/containerd/v2/core/content" "github.com/containerd/containerd/v2/core/diff" "github.com/containerd/containerd/v2/core/leases" + "github.com/containerd/containerd/v2/core/snapshots" "github.com/containerd/containerd/v2/pkg/filters" "github.com/containerd/containerd/v2/pkg/gc" "github.com/containerd/containerd/v2/pkg/labels" @@ -626,6 +628,10 @@ func (cm *cacheManager) New(ctx context.Context, s ImmutableRef, sess session.Gr }); rerr != nil { return nil, rerr } + } else if cm.Snapshotter.Name() == "overlaybd" && parent != nil { + // Snapshotter will create a R/W block device directly as rootfs with this label + rwLabels := map[string]string{obdlabel.SupportReadWriteMode: "dev"} + err = cm.Snapshotter.Prepare(ctx, snapshotID, parentSnapshotID, snapshots.WithLabels(rwLabels)) } else { err = cm.Snapshotter.Prepare(ctx, snapshotID, parentSnapshotID) } diff --git a/cache/refs.go b/cache/refs.go index aa2b3f380984..872067442267 100644 --- a/cache/refs.go +++ b/cache/refs.go @@ -10,6 +10,7 @@ import ( "sync" "time" + obdlabel "github.com/containerd/accelerated-container-image/pkg/label" "github.com/containerd/containerd/v2/core/content" "github.com/containerd/containerd/v2/core/images" "github.com/containerd/containerd/v2/core/leases" @@ -41,7 +42,7 @@ import ( "golang.org/x/sync/errgroup" ) -var additionalAnnotations = append(compression.EStargzAnnotations, labels.LabelUncompressed) +var additionalAnnotations = append(append(compression.EStargzAnnotations, obdlabel.OverlayBDAnnotations...), labels.LabelUncompressed) // Ref is a reference to cacheable objects. type Ref interface { @@ -419,7 +420,11 @@ func (cr *cacheRecord) mount(ctx context.Context) (_ snapshot.Mountable, rerr er // Return the mount direct from View rather than setting it using the Mounts call below. // The two are equivalent for containerd snapshotters but the moby snapshotter requires // the use of the mountable returned by View in this case. - mnts, err := cr.cm.Snapshotter.View(ctx, mountSnapshotID, cr.getSnapshotID()) + labels := make(map[string]string) + if cr.cm.Snapshotter.Name() == "overlaybd" { + labels["containerd.io/snapshot/overlaybd.writable"] = "dev" + } + mnts, err := cr.cm.Snapshotter.View(ctx, mountSnapshotID, cr.getSnapshotID(), snapshots.WithLabels(labels)) if err != nil && !cerrdefs.IsAlreadyExists(err) { return nil, err } @@ -1014,6 +1019,10 @@ func (sr *immutableRef) Extract(ctx context.Context, s session.Group) (rerr erro return err } return rerr + } else if sr.cm.Snapshotter.Name() == "overlaybd" { + if rerr = sr.prepareRemoteSnapshotsOverlaybdMode(ctx); rerr == nil { + return sr.unlazy(ctx, sr.descHandlers, sr.progress, s, true, false) + } } return sr.unlazy(ctx, sr.descHandlers, sr.progress, s, true, false) @@ -1141,6 +1150,55 @@ func (sr *immutableRef) prepareRemoteSnapshotsStargzMode(ctx context.Context, s return err } +func (sr *immutableRef) prepareRemoteSnapshotsOverlaybdMode(ctx context.Context) error { + _, err := g.Do(ctx, sr.ID()+"-prepare-remote-snapshot", func(ctx context.Context) (_ *leaseutil.LeaseRef, rerr error) { + dhs := sr.descHandlers + for _, r := range sr.layerChain() { + r := r + snapshotID := r.getSnapshotID() + if _, err := r.cm.Snapshotter.Stat(ctx, snapshotID); err == nil { + continue + } + dh := dhs[digest.Digest(r.getBlob())] + if dh == nil { + // We cannot prepare remote snapshots without descHandler. + return nil, nil + } + defaultLabels := snapshots.FilterInheritedLabels(dh.SnapshotLabels) + if defaultLabels == nil { + defaultLabels = make(map[string]string) + } + defaultLabels["containerd.io/snapshot.ref"] = snapshotID + // Prepare remote snapshots + var ( + key = fmt.Sprintf("tmp-%s %s", identity.NewID(), r.getChainID()) + opts = []snapshots.Opt{ + snapshots.WithLabels(defaultLabels), + } + ) + parentID := "" + if r.layerParent != nil { + parentID = r.layerParent.getSnapshotID() + } + if err := r.cm.Snapshotter.Prepare(ctx, key, parentID, opts...); err != nil { + if cerrdefs.IsAlreadyExists(err) { + // Check if the targeting snapshot ID has been prepared as + // a remote snapshot in the snapshotter. + _, err := r.cm.Snapshotter.Stat(ctx, snapshotID) + if err == nil { // usable as remote snapshot without unlazying. + // Try the next layer as well. + continue + } + } + } + // This layer and all upper layers cannot be prepared without unlazying. + break + } + return nil, nil + }) + return err +} + func makeTmpLabelsStargzMode(labels map[string]string, s session.Group) (fields []string, res map[string]string) { res = make(map[string]string) // Append unique ID to labels for avoiding collision of labels among calls @@ -1310,7 +1368,12 @@ func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, pg pr key := fmt.Sprintf("extract-%s %s", identity.NewID(), sr.getChainID()) - err = sr.cm.Snapshotter.Prepare(ctx, key, parentID) + if sr.cm.Snapshotter.Name() == "overlaybd" { + err = sr.cm.Snapshotter.Prepare(ctx, key, parentID, + snapshots.WithLabels(map[string]string{"containerd.io/snapshot.ref": string(sr.getChainID())})) + } else { + err = sr.cm.Snapshotter.Prepare(ctx, key, parentID) + } if err != nil { return err } diff --git a/cache/remotecache/registry/registry.go b/cache/remotecache/registry/registry.go index 111af04583d0..327bc571141c 100644 --- a/cache/remotecache/registry/registry.go +++ b/cache/remotecache/registry/registry.go @@ -165,6 +165,7 @@ func (dsl *withDistributionSourceLabel) SnapshotLabels(descs []ocispecs.Descript labels = make(map[string]string) } maps.Copy(labels, estargz.SnapshotLabels(dsl.ref, descs, index)) + labels["containerd.io/snapshot/image-ref"] = dsl.ref return labels } diff --git a/docs/overlaybd.md b/docs/overlaybd.md new file mode 100644 index 000000000000..d24f1f6407bd --- /dev/null +++ b/docs/overlaybd.md @@ -0,0 +1,39 @@ +# Overlaybd + +[Overlaybd](https://github.com/containerd/overlaybd) is a novel layering block-level image format, which is design for container, secure container and applicable to virtual machine. And it is an open-source implementation of paper [DADI: Block-Level Image Service for Agile and Elastic Application Deployment. USENIX ATC'20"](https://www.usenix.org/conference/atc20/presentation/li-huiba). + +## Build Overlaybd Images + +Before building overlaybd images, ensure that `overlaybd-tcmu` and `overlaybd-snapshotter` are active by referring to the [QUICKSTART](https://github.com/containerd/accelerated-container-image/blob/main/docs/QUICKSTART.md#install) guide. + +To use buildkit to build overlaybd images, you should specify `--oci-worker-snapshotter=overlaybd` and `--oci-worker-proxy-snapshotter-path=/run/overlaybd-snapshotter/overlaybd.sock` when start buildkitd: + +```bash +buildkitd --oci-worker-snapshotter=overlaybd --oci-worker-proxy-snapshotter-path=/run/overlaybd-snapshotter/overlaybd.sock +``` +If an overlaybd image is used in the FROM instruction of a Dockerfile, the build will produce a new overlaybd image. It is essential to include `--oci-mediatypes=true` and `--compression=uncompressed` while running buildctl: + +```bash +buildctl build ... \ +--output type=image,name=,push=true,oci-mediatypes=true,compression=uncompressed +``` + +## Performance + +In our test case Dockerfile, we used a 5GB OCI image (and corresponding overlaybd format), wrote some new layers of identical size, and recorded the time cost of image pull (as **pull** in the table below), building all lines in Dockerfile (as **build**), and exporting to image and pushing (as **push**). + +OCI: + +| **size per layer** | **layers** | **pull** | **build** | **push** | **total** | +| -------- | ---- | ---- | ----- | ---- | ---- | +| 4GB | 1 | 105.7| 23.5 | 219.4| 348.6| +| 1GB | 4 | 88.5 | 34.0 | 123.8| 246.3| +| 256MB | 10 | 92.1 | 20.7 | 63.6 | 176.4| + +Overlaybd: + +| **size per layer** | **layers** | **pull** | **build** | **push** | **total** | +| -------- | ---- | ---- | ----- | ---- | ---- | +| 4GB | 1 | 0.9 | 21.5 | 166.2| 188.6| +| 1GB | 4 | 0.9 | 24.9 | 72.9 | 98.7 | +| 256MB | 10 | 0.7 | 18.4 | 48.9 | 68.0 | \ No newline at end of file diff --git a/go.mod b/go.mod index a2668ea05384..7c6583df9b0e 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/aws/aws-sdk-go-v2/credentials v1.17.27 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.8 github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 + github.com/containerd/accelerated-container-image v1.2.3 github.com/containerd/console v1.0.4 github.com/containerd/containerd/api v1.8.0 github.com/containerd/containerd/v2 v2.0.2 diff --git a/go.sum b/go.sum index 8f6f586e867d..368d561edcf5 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= +github.com/containerd/accelerated-container-image v1.2.3 h1:tAIoP7Z7b2xGhb7QCM5Fa+2xqWfPqRmyi5lodbsGGRA= +github.com/containerd/accelerated-container-image v1.2.3/go.mod h1:EvKVWor6ZQNUyYp0MZm5hw4k21ropuz7EegM+m/Jb/Q= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= diff --git a/source/containerimage/pull.go b/source/containerimage/pull.go index 9809a96f0d7e..b8c659884bfd 100644 --- a/source/containerimage/pull.go +++ b/source/containerimage/pull.go @@ -152,6 +152,7 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach labels = make(map[string]string) } maps.Copy(labels, estargz.SnapshotLabels(p.manifest.Ref, p.manifest.Descriptors, i)) + labels["containerd.io/snapshot/image-ref"] = p.manifest.Ref p.descHandlers[desc.Digest] = &cache.DescHandler{ Provider: p.manifest.Provider, diff --git a/vendor/github.com/containerd/accelerated-container-image/LICENSE b/vendor/github.com/containerd/accelerated-container-image/LICENSE new file mode 100644 index 000000000000..f49a4e16e68b --- /dev/null +++ b/vendor/github.com/containerd/accelerated-container-image/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/vendor/github.com/containerd/accelerated-container-image/pkg/label/label.go b/vendor/github.com/containerd/accelerated-container-image/pkg/label/label.go new file mode 100644 index 000000000000..7df5fc033f78 --- /dev/null +++ b/vendor/github.com/containerd/accelerated-container-image/pkg/label/label.go @@ -0,0 +1,133 @@ +/* + Copyright The Accelerated Container Image Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package label + +// support on-demand loading by the labels +const ( + // TargetSnapshotRef is the interface to know that Prepare + // action is to pull image, not for container Writable snapshot. + // + // NOTE: Only available in >= containerd 1.4.0 and containerd.Pull + // with Unpack option. + // + // FIXME(fuweid): With containerd design, we don't know that what purpose + // snapshotter.Prepare does for. For unpacked image, prepare is for + // container's rootfs. For pulling image, the prepare is for committed. + // With label "containerd.io/snapshot.ref" in preparing, snapshotter + // author will know it is for pulling image. It will be useful. + // + // The label is only propagated during pulling image. So, is it possible + // to propagate by image.Unpack()? + TargetSnapshotRef = "containerd.io/snapshot.ref" + + // TargetImageRef is the label to mark where the snapshot comes from. + // + // TODO(fuweid): Is it possible to use it in upstream? + TargetImageRef = "containerd.io/snapshot/image-ref" + + // OverlayBDBlobDigest is the annotation key in the manifest to + // describe the digest of blob in OverlayBD format. + // + // NOTE: The annotation is part of image layer blob's descriptor. + OverlayBDBlobDigest = "containerd.io/snapshot/overlaybd/blob-digest" + + // OverlayBDBlobSize is the annotation key in the manifest to + // describe the size of blob in OverlayBD format. + // + // NOTE: The annotation is part of image layer blob's descriptor. + OverlayBDBlobSize = "containerd.io/snapshot/overlaybd/blob-size" + + // OverlayBDBlobFsType is the annotation key in the manifest to + // describe the filesystem type to be mounted as of blob in OverlayBD format. + // + // NOTE: The annotation is part of image layer blob's descriptor. + OverlayBDBlobFsType = "containerd.io/snapshot/overlaybd/blob-fs-type" + + // AccelerationLayer is the annotation key in the manifest to indicate + // whether a top layer is acceleration layer or not. + AccelerationLayer = "containerd.io/snapshot/overlaybd/acceleration-layer" + + // RecordTrace tells snapshotter to record trace + RecordTrace = "containerd.io/snapshot/overlaybd/record-trace" + + // RecordTracePath is the file path to record trace + RecordTracePath = "containerd.io/snapshot/overlaybd/record-trace-path" + + // ZFileConfig is the config of ZFile + ZFileConfig = "containerd.io/snapshot/overlaybd/zfile-config" + + // OverlayBD virtual block device size + OverlayBDVsize = "containerd.io/snapshot/overlaybd/vsize" + + // CRIImageRef is the image-ref from cri + CRIImageRef = "containerd.io/snapshot/cri.image-ref" + + // TurboOCIDigest is the index annotation key for image layer digest + FastOCIDigest = "containerd.io/snapshot/overlaybd/fastoci/target-digest" // legacy + TurboOCIDigest = "containerd.io/snapshot/overlaybd/turbo-oci/target-digest" + + // TurboOCIMediaType is the index annotation key for image layer media type + FastOCIMediaType = "containerd.io/snapshot/overlaybd/fastoci/target-media-type" // legacy + TurboOCIMediaType = "containerd.io/snapshot/overlaybd/turbo-oci/target-media-type" + + // DownloadRemoteBlob is a label for download remote blob + DownloadRemoteBlob = "containerd.io/snapshot/overlaybd/download-remote-blob" + + RemoteLabel = "containerd.io/snapshot/remote" + RemoteLabelVal = "remote snapshot" + + // OverlayBDVersion is the version number of overlaybd blob + OverlayBDVersion = "containerd.io/snapshot/overlaybd/version" + + // LayerToTurboOCI is used to convert local layer to turboOCI with tar index + LayerToTurboOCI = "containerd.io/snapshot/overlaybd/convert2turbo-oci" + + SnapshotType = "containerd.io/snapshot/type" + + // RootfsQuotaLabel sets container rootfs diskquota + RootfsQuotaLabel = "containerd.io/snapshot/disk_quota" +) + +// used in filterAnnotationsForSave (https://github.com/moby/buildkit/blob/v0.11/cache/refs.go#L882) +var OverlayBDAnnotations = []string{ + LocalOverlayBDPath, + OverlayBDBlobDigest, + OverlayBDBlobSize, + OverlayBDBlobFsType, +} + +// interface +const ( + // SupportReadWriteMode is used to support writable block device + // for active snapshotter. + // + // By default, multiple active snapshotters can share one block device + // from parent snapshotter(committed). Like image builder and + // sandboxed-like container runtime(KataContainer, Firecracker), those + // cases want to use the block device alone or as writable. + // There are two ways to provide writable devices: + // - 'dir' mark the snapshotter + // as wriable block device and mount it on rootfs. + // - 'dev' mark the snapshotter + // as wriable block device without mount. + SupportReadWriteMode = "containerd.io/snapshot/overlaybd.writable" + + // LocalOverlayBDPath is used to export the commit file path. + // + // NOTE: Only used in image build. + LocalOverlayBDPath = "containerd.io/snapshot/overlaybd.localcommitpath" +) diff --git a/vendor/github.com/containerd/accelerated-container-image/pkg/types/types.go b/vendor/github.com/containerd/accelerated-container-image/pkg/types/types.go new file mode 100644 index 000000000000..52cadb193689 --- /dev/null +++ b/vendor/github.com/containerd/accelerated-container-image/pkg/types/types.go @@ -0,0 +1,45 @@ +/* + Copyright The Accelerated Container Image Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package types + +// OverlayBDBSConfig is the config of overlaybd target. +type OverlayBDBSConfig struct { + RepoBlobURL string `json:"repoBlobUrl"` + Lowers []OverlayBDBSConfigLower `json:"lowers"` + Upper OverlayBDBSConfigUpper `json:"upper"` + ResultFile string `json:"resultFile"` + AccelerationLayer bool `json:"accelerationLayer,omitempty"` + RecordTracePath string `json:"recordTracePath,omitempty"` +} + +// OverlayBDBSConfigLower +type OverlayBDBSConfigLower struct { + GzipIndex string `json:"gzipIndex,omitempty"` + File string `json:"file,omitempty"` + Digest string `json:"digest,omitempty"` + TargetFile string `json:"targetFile,omitempty"` + TargetDigest string `json:"targetDigest,omitempty"` + Size int64 `json:"size,omitempty"` + Dir string `json:"dir,omitempty"` +} + +type OverlayBDBSConfigUpper struct { + Index string `json:"index,omitempty"` + Data string `json:"data,omitempty"` + Target string `json:"target,omitempty"` + GzipIndex string `json:"gzipIndex,omitempty"` +} diff --git a/vendor/github.com/containerd/accelerated-container-image/pkg/utils/cmd.go b/vendor/github.com/containerd/accelerated-container-image/pkg/utils/cmd.go new file mode 100644 index 000000000000..a08965e0e4a7 --- /dev/null +++ b/vendor/github.com/containerd/accelerated-container-image/pkg/utils/cmd.go @@ -0,0 +1,258 @@ +/* + Copyright The Accelerated Container Image Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package utils + +import ( + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + + sn "github.com/containerd/accelerated-container-image/pkg/types" + "github.com/containerd/log" + "github.com/pkg/errors" +) + +const ( + obdBinCreate = "/opt/overlaybd/bin/overlaybd-create" + obdBinCommit = "/opt/overlaybd/bin/overlaybd-commit" + obdBinApply = "/opt/overlaybd/bin/overlaybd-apply" + obdBinTurboOCIApply = "/opt/overlaybd/bin/turboOCI-apply" + + dataFile = "writable_data" + idxFile = "writable_index" + sealedFile = "overlaybd.sealed" + commitTempFile = "overlaybd.commit.temp" + commitFile = "overlaybd.commit" +) + +type ConvertOption struct { + // src options + // (TODO) LayerPath string // path of layer.tgz or layer.tar + TarMetaPath string // path of layer.tar.meta + + Config sn.OverlayBDBSConfig + Workdir string + + // output options + Ext4FSMetaPath string + // (TODO) GzipIndexPath string +} + +var defaultServiceTemplate = ` +{ + "registryFsVersion": "v2", + "logPath": "", + "logLevel": 1, + "cacheConfig": { + "cacheType": "file", + "cacheDir": "%s", + "cacheSizeGB": 4 + }, + "gzipCacheConfig": { + "enable": false + }, + "credentialConfig": { + "mode": "file", + "path": "" + }, + "ioEngine": 0, + "download": { + "enable": false + }, + "enableAudit": false +} +` + +func Create(ctx context.Context, dir string, opts ...string) error { + dataPath := path.Join(dir, dataFile) + indexPath := path.Join(dir, idxFile) + os.RemoveAll(dataPath) + os.RemoveAll(indexPath) + args := append([]string{dataPath, indexPath}, opts...) + log.G(ctx).Debugf("%s %s", obdBinCreate, strings.Join(args, " ")) + out, err := exec.CommandContext(ctx, obdBinCreate, args...).CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to overlaybd-create: %s", out) + } + return nil +} + +func Seal(ctx context.Context, dir, toDir string, opts ...string) error { + args := append([]string{ + "--seal", + path.Join(dir, dataFile), + path.Join(dir, idxFile), + }, opts...) + log.G(ctx).Debugf("%s %s", obdBinCommit, strings.Join(args, " ")) + out, err := exec.CommandContext(ctx, obdBinCommit, args...).CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to seal writable overlaybd: %s", out) + } + if err := os.Rename(path.Join(dir, dataFile), path.Join(toDir, sealedFile)); err != nil { + return errors.Wrapf(err, "failed to rename sealed overlaybd file") + } + os.RemoveAll(path.Join(dir, idxFile)) + return nil +} + +func Commit(ctx context.Context, dir, toDir string, sealed bool, opts ...string) error { + var args []string + if sealed { + args = append([]string{ + "--commit_sealed", + path.Join(dir, sealedFile), + path.Join(toDir, commitTempFile), + }, opts...) + } else { + args = append([]string{ + path.Join(dir, dataFile), + path.Join(dir, idxFile), + path.Join(toDir, commitFile), + }, opts...) + } + log.G(ctx).Debugf("%s %s", obdBinCommit, strings.Join(args, " ")) + out, err := exec.CommandContext(ctx, obdBinCommit, args...).CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to overlaybd-commit: %s", out) + } + if sealed { + return os.Rename(path.Join(toDir, commitTempFile), path.Join(toDir, commitFile)) + } + return nil +} + +func ApplyOverlaybd(ctx context.Context, dir string, opts ...string) error { + + args := append([]string{ + path.Join(dir, "layer.tar"), + path.Join(dir, "config.json")}, opts...) + log.G(ctx).Debugf("%s %s", obdBinApply, strings.Join(args, " ")) + out, err := exec.CommandContext(ctx, obdBinApply, args...).CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to overlaybd-apply[native]: %s", out) + } + return nil +} + +func ApplyTurboOCI(ctx context.Context, dir, gzipMetaFile string, opts ...string) error { + + args := append([]string{ + path.Join(dir, "layer.tar"), + path.Join(dir, "config.json"), + "--gz_index_path", path.Join(dir, gzipMetaFile)}, opts...) + log.G(ctx).Debugf("%s %s", obdBinApply, strings.Join(args, " ")) + out, err := exec.CommandContext(ctx, obdBinTurboOCIApply, args...).CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to overlaybd-apply[turboOCI]: %s", out) + } + return nil +} + +func GenerateTarMeta(ctx context.Context, srcTarFile string, dstTarMeta string) error { + + if _, err := os.Stat(srcTarFile); os.IsNotExist(err) { + return nil + } else if err != nil { + return fmt.Errorf("error stating tar file: %w", err) + } + log.G(ctx).Infof("generate layer meta for %s", srcTarFile) + if err := exec.Command(obdBinTurboOCIApply, srcTarFile, dstTarMeta, "--export").Run(); err != nil { + return fmt.Errorf("failed to convert tar file to overlaybd device: %w", err) + } + return nil +} + +// ConvertLayer produce a turbooci layer, target is path of ext4.fs.meta +func ConvertLayer(ctx context.Context, opt *ConvertOption, fs_type string) error { + if opt.Workdir == "" { + opt.Workdir = "tmp_conv" + } + + if err := os.MkdirAll(opt.Workdir, 0755); err != nil { + return fmt.Errorf("failed to create work dir: %w", err) + } + + pathWritableData := filepath.Join(opt.Workdir, "writable_data") + pathWritableIndex := filepath.Join(opt.Workdir, "writable_index") + pathFakeTarget := filepath.Join(opt.Workdir, "fake_target") + pathService := filepath.Join(opt.Workdir, "service.json") + pathConfig := filepath.Join(opt.Workdir, "config.v1.json") + + // overlaybd-create + args := []string{pathWritableData, pathWritableIndex, "256", "-s", "--turboOCI"} + if fs_type != "erofs" && len(opt.Config.Lowers) == 0 { + args = append(args, "--mkfs") + } + if out, err := exec.CommandContext(ctx, obdBinCreate, args...).CombinedOutput(); err != nil { + return fmt.Errorf("failed to overlaybd-create: %w, output: %s", err, out) + } + file, err := os.Create(pathFakeTarget) + if err != nil { + return fmt.Errorf("failed to create fake target: %w", err) + } + file.Close() + opt.Config.Upper = sn.OverlayBDBSConfigUpper{ + Data: pathWritableData, + Index: pathWritableIndex, + Target: pathFakeTarget, + } + + // turboOCI-apply + if err := os.WriteFile(pathService, []byte(fmt.Sprintf(defaultServiceTemplate, + filepath.Join(opt.Workdir, "cache"))), 0644, + ); err != nil { + return fmt.Errorf("failed to write service.json: %w", err) + } + configBytes, err := json.Marshal(opt.Config) + if err != nil { + return fmt.Errorf("failed to marshal overlaybd config: %w", err) + } + if err := os.WriteFile(pathConfig, configBytes, 0644); err != nil { + return fmt.Errorf("failed to write overlaybd config: %w", err) + } + args = []string{ + opt.TarMetaPath, pathConfig, + "--service_config_path", pathService, + "--fstype", fs_type, + } + if fs_type != "erofs" { + args = append(args, "--import") + } + + log.G(ctx).Debugf("%s %s", obdBinTurboOCIApply, strings.Join(args, " ")) + if out, err := exec.CommandContext(ctx, obdBinTurboOCIApply, + args..., + ).CombinedOutput(); err != nil { + return fmt.Errorf("failed to turboOCI-apply: %w, output: %s", err, out) + } + + // overlaybd-commit + if out, err := exec.CommandContext(ctx, obdBinCommit, + pathWritableData, + pathWritableIndex, + opt.Ext4FSMetaPath, + "-z", "--turboOCI", + ).CombinedOutput(); err != nil { + return fmt.Errorf("failed to overlaybd-commit: %w, output: %s", err, out) + } + return nil +} diff --git a/vendor/github.com/containerd/accelerated-container-image/pkg/utils/du_unix.go b/vendor/github.com/containerd/accelerated-container-image/pkg/utils/du_unix.go new file mode 100644 index 000000000000..6b916ede8edf --- /dev/null +++ b/vendor/github.com/containerd/accelerated-container-image/pkg/utils/du_unix.go @@ -0,0 +1,83 @@ +//go:build !windows + +/* + Copyright The Accelerated Container Image Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Based on https://github.com/containerd/continuity/blob/main/fs/du_unix.go +// Used to calculate the usage of the block dir, excluding the block/mountpoint dir. + +package utils + +import ( + "context" + "os" + "path/filepath" + "syscall" + + "github.com/containerd/continuity/fs" +) + +const blocksUnitSize = 512 + +type inode struct { + dev, ino uint64 +} + +func newInode(stat *syscall.Stat_t) inode { + return inode{ + dev: uint64(stat.Dev), //nolint: unconvert // dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd + ino: uint64(stat.Ino), //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd + } +} + +func DiskUsageWithoutMountpoint(ctx context.Context, roots ...string) (fs.Usage, error) { + var ( + size int64 + inodes = map[inode]struct{}{} // expensive! + ) + + for _, root := range roots { + if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if fi.Name() == "mountpoint" { + return filepath.SkipDir + } + if err != nil { + return err + } + + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + stat := fi.Sys().(*syscall.Stat_t) + inoKey := newInode(stat) + if _, ok := inodes[inoKey]; !ok { + inodes[inoKey] = struct{}{} + size += stat.Blocks * blocksUnitSize + } + + return nil + }); err != nil { + return fs.Usage{}, err + } + } + + return fs.Usage{ + Inodes: int64(len(inodes)), + Size: size, + }, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c77a78ac1c82..e49ee7d3d6c8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -261,6 +261,11 @@ github.com/cenkalti/backoff/v4 # github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 +# github.com/containerd/accelerated-container-image v1.2.3 +## explicit; go 1.22.0 +github.com/containerd/accelerated-container-image/pkg/label +github.com/containerd/accelerated-container-image/pkg/types +github.com/containerd/accelerated-container-image/pkg/utils # github.com/containerd/cgroups/v3 v3.0.3 ## explicit; go 1.18 github.com/containerd/cgroups/v3/cgroup1/stats