Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for building overlaybd images #3867

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

HileQAQ
Copy link

@HileQAQ HileQAQ commented May 12, 2023

Overlaybd is a remote image format based on the block device, which can load data on demand. You can see the paper for more information.

In order to build overlaybd images using buildkit, some changes have been made that are separate from the original process. Please refer to the docs/overlaybd.md for guidance on building overlaybd images.

Signed-off-by: Haoqi Miao [email protected]

@HileQAQ
Copy link
Author

HileQAQ commented May 14, 2023

@tonistiigi @AkihiroSuda @ktock
We add some code to support building overlaybd images with buildkit. Do you have time to review it?

@AkihiroSuda
Copy link
Member

Looks good but needs rebase

cache/manager.go Outdated
@@ -615,6 +616,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 {
rwLabels := make(map[string]string)
rwLabels["containerd.io/snapshot/overlaybd.writable"] = "dev"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What’s “dev”?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a type of upper layer in overlaybd config that can specify by label, maybe we should provide a const value in overlaybd code.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the "containerd.io/snapshot/overlaybd.writable" has been set as "dev", Snapshotter will create a R/W block device directly as rootfs without overlayfs // local directory (upper) + RO overlaybd (lower)
Just like devmapper :D

https://github.com/containerd/accelerated-container-image/blob/6dff475f080557cf8a8b5564a29ddee118258f56/pkg/snapshot/overlay.go#LL909C4-L909C4

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Could you add that explanation in the code comment lines?

cache/refs.go Outdated
@@ -1232,7 +1291,8 @@ 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)
opts := snapshots.WithLabels(map[string]string{"containerd.io/snapshot.ref": string(desc.Digest)})
Copy link
Collaborator

@ktock ktock May 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be enabled by default.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, now this label is only for overlaybd.

cache/blobs.go Outdated
if snStat.Labels[obdlabel.LocalOverlayBDPath] != "" {
isOverlaybd = true
}
mediaType = ocispecs.MediaTypeImageLayer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is overwritten regardless if isOverlaybd is true/false, that's correct?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, mediaType should be overwritten only when isOverlaybd is true. It is now fixed.

cache/blobs.go Outdated
@@ -105,7 +121,7 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool
lowerRef = sr.layerParent
}
var lower []mount.Mount
if lowerRef != nil {
if !isOverlaybd && lowerRef != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is becoming quite complex with these conditions added. Quite some branches are excluded if !isOverlaybd, and stripping away that code shows that there's actually some of the other branches that are "dead code" in that case.

Perhaps out of scope for this PR, but I wonder if we need to branch earlier if isOverlaybd is true, instead of all these "micro-conditions".

I did a very quick / naive remove of all the !isOverlaybd paths, to get a "rough" idea what parts were unused, which gave me;

diff --git a/cache/blobs.go b/cache/blobs.go
index a8af0a96f..b3abd863b 100644
--- a/cache/blobs.go
+++ b/cache/blobs.go
@@ -7,7 +7,6 @@ import (
 	"io"
 	"os"
 	"path"
-	"strconv"
 
 	obdlabel "github.com/containerd/accelerated-container-image/pkg/label"
 	obdcmd "github.com/containerd/accelerated-container-image/pkg/utils"
@@ -113,97 +112,11 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool
 					}
 					mediaType = ocispecs.MediaTypeImageLayer
 				}
-				var lowerRef *immutableRef
-				switch sr.kind() {
-				case Diff:
-					lowerRef = sr.diffParents.lower
-				case Layer:
-					lowerRef = sr.layerParent
-				}
 				var lower []mount.Mount
-				if !isOverlaybd && lowerRef != nil {
-					m, err := lowerRef.Mount(ctx, true, s)
-					if err != nil {
-						return nil, err
-					}
-					var release func() error
-					lower, release, err = m.Mount()
-					if err != nil {
-						return nil, err
-					}
-					if release != nil {
-						defer release()
-					}
-				}
-
-				var upperRef *immutableRef
-				switch sr.kind() {
-				case Diff:
-					upperRef = sr.diffParents.upper
-				default:
-					upperRef = sr
-				}
 				var upper []mount.Mount
-				if !isOverlaybd && upperRef != nil {
-					m, err := upperRef.Mount(ctx, true, s)
-					if err != nil {
-						return nil, err
-					}
-					var release func() error
-					upper, release, err = m.Mount()
-					if err != nil {
-						return nil, err
-					}
-					if release != nil {
-						defer release()
-					}
-				}
-
 				var desc ocispecs.Descriptor
 				var err error
 
-				// Determine differ and error/log handling according to the platform, envvar and the snapshotter.
-				var enableOverlay, fallback, logWarnOnErr bool
-				if forceOvlStr := os.Getenv("BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF"); forceOvlStr != "" && sr.kind() != Diff {
-					enableOverlay, err = strconv.ParseBool(forceOvlStr)
-					if err != nil {
-						return nil, errors.Wrapf(err, "invalid boolean in BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF")
-					}
-					fallback = false // prohibit fallback on debug
-				} else if !isTypeWindows(sr) {
-					enableOverlay, fallback = true, true
-					switch sr.cm.Snapshotter.Name() {
-					case "overlayfs", "stargz":
-						// overlayfs-based snapshotters should support overlay diff except when running an arbitrary diff
-						// (in which case lower and upper may differ by more than one layer), so print warn log on unexpected
-						// failure.
-						logWarnOnErr = sr.kind() != Diff
-					case "fuse-overlayfs", "native":
-						// not supported with fuse-overlayfs snapshotter which doesn't provide overlayfs mounts.
-						// TODO: add support for fuse-overlayfs
-						enableOverlay = false
-					}
-				}
-				if !isOverlaybd && enableOverlay {
-					computed, ok, err := sr.tryComputeOverlayBlob(ctx, lower, upper, mediaType, sr.ID(), compressorFunc)
-					if !ok || err != nil {
-						if !fallback {
-							if !ok {
-								return nil, errors.Errorf("overlay mounts not detected (lower=%+v,upper=%+v)", lower, upper)
-							}
-							if err != nil {
-								return nil, errors.Wrapf(err, "failed to compute overlay diff")
-							}
-						}
-						if logWarnOnErr {
-							bklog.G(ctx).Warnf("failed to compute blob by overlay differ (ok=%v): %v", ok, err)
-						}
-					}
-					if ok {
-						desc = computed
-					}
-				}
-
 				if isOverlaybd {
 					if err := commitOverlaybd(ctx, sr, &desc); err != nil {
 						return nil, err

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has been modified, because desc will be overwritten in commitOverlaybd, so there is no effect if !isOverlaybd is not added.

cache/blobs.go Outdated
@@ -96,7 +102,17 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool

compressorFunc, finalize := comp.Type.Compress(ctx, comp)
mediaType := comp.Type.MediaType()

var isOverlaybd = false
if sr.cm.Snapshotter.Name() == "overlaybd" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a const somewhere for overlaybd ? (not sure what the best place is for it though)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's irrelevant since stargz does the same. And it doesn't make the code harder to understand:)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, definitely not a "show-stopper", and if we do, we should consider doing it for all the snapshotters (which would be out of scope for this PR).

I agree with you ("in general") that a const is not always needed for these, and sometimes a const may make ik even harder to understand the code (so .. "it depends").

The reason I was looking if we should add a const is that there's various code-paths in multiple packages, where the code-flow depends on the snapshotter used. Having a const for these could help discovery of those flows (i.e., find all uses of the const in the code). If these consts would also have a strong type, it could also act as an "enum" to help discovery of "accepted values". (See #3694 for something similar).

@HileQAQ HileQAQ force-pushed the main branch 4 times, most recently from fa544ab to 5cc5636 Compare May 30, 2023 06:13
@HileQAQ
Copy link
Author

HileQAQ commented May 30, 2023

@AkihiroSuda @thaJeztah @ktock
The code has been modified according to the opinions of the comments, and it has been rebased to the latest version. Please review it again. Thanks.

@AkihiroSuda
Copy link
Member

Compilation is failing for Windows

Error: vendor\github.com\containerd\accelerated-container-image\pkg\snapshot\overlay.go:1066:30: undefined: syscall.Stat_t
Error: vendor\github.com\containerd\accelerated-container-image\pkg\snapshot\storage.go:396:29: undefined: unix.MS_RDONLY
Error: vendor\github.com\containerd\accelerated-container-image\pkg\snapshot\storage.go:433:21: undefined: unix.Mount

@HileQAQ
Copy link
Author

HileQAQ commented Jun 5, 2023

Compilation is failing for Windows

Error: vendor\github.com\containerd\accelerated-container-image\pkg\snapshot\overlay.go:1066:30: undefined: syscall.Stat_t
Error: vendor\github.com\containerd\accelerated-container-image\pkg\snapshot\storage.go:396:29: undefined: unix.MS_RDONLY
Error: vendor\github.com\containerd\accelerated-container-image\pkg\snapshot\storage.go:433:21: undefined: unix.Mount

@AkihiroSuda We remove a package that only uses in Unix, now it can pass all checks.

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! New version looks a lot better; I left some comments/questions and was drafting along some ideas, but not sure if they're correct / make sense. I'll open a PR against your fork for consideration.

cache/blobs.go Outdated
@@ -96,7 +102,17 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool

compressorFunc, finalize := comp.Type.Compress(ctx, comp)
mediaType := comp.Type.MediaType()

var isOverlaybd = false
if sr.cm.Snapshotter.Name() == "overlaybd" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, definitely not a "show-stopper", and if we do, we should consider doing it for all the snapshotters (which would be out of scope for this PR).

I agree with you ("in general") that a const is not always needed for these, and sometimes a const may make ik even harder to understand the code (so .. "it depends").

The reason I was looking if we should add a const is that there's various code-paths in multiple packages, where the code-flow depends on the snapshotter used. Having a const for these could help discovery of those flows (i.e., find all uses of the const in the code). If these consts would also have a strong type, it could also act as an "enum" to help discovery of "accepted values". (See #3694 for something similar).

cache/blobs.go Outdated
}
if snStat.Labels[obdlabel.LocalOverlayBDPath] != "" {
isOverlaybd = true
mediaType = ocispecs.MediaTypeImageLayer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not new, as it was already defined at the top, but looks like mediaType is not used until line 188, so we could move this block closer to where it's used.

cache/blobs.go Outdated
Comment on lines 111 to 112
if snStat.Labels[obdlabel.LocalOverlayBDPath] != "" {
isOverlaybd = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if it's a silly question, but what happens if the label isn't set? Is that an expected (valid) situation, or should that be an error condition?

I'm also looking at this code, and possibly part of it could be moved at the end (where it's used).

cache/blobs.go Outdated
Comment on lines 181 to 185
case "fuse-overlayfs", "native", "overlaybd":
// not supported with fuse-overlayfs snapshotter which doesn't provide overlayfs mounts.
// TODO: add support for fuse-overlayfs
enableOverlay = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious; is the enableOverlay supported with this snapshotter? I see there's code above that allows it to be forced, and I wonder if that is a valid case (so if the if enableOverlay code below should be executed also for overlaybd if that env-var is set.

buildkit/cache/blobs.go

Lines 151 to 156 in 13cca5a

if forceOvlStr := os.Getenv("BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF"); forceOvlStr != "" && sr.kind() != Diff {
enableOverlay, err = strconv.ParseBool(forceOvlStr)
if err != nil {
return nil, errors.Wrapf(err, "invalid boolean in BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF")
}
fallback = false // prohibit fallback on debug

Because if it's not needed, we can combine the isOverlayBD code from above, and the if isOverlaybd code from below.

cache/blobs.go Outdated
@@ -96,7 +102,17 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool

compressorFunc, finalize := comp.Type.Compress(ctx, comp)
mediaType := comp.Type.MediaType()

var isOverlaybd = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit; probably should be isOverlayBD

cache/blobs.go Outdated
@@ -462,3 +485,45 @@ func ensureCompression(ctx context.Context, ref *immutableRef, comp compression.
})
return err
}

func commitOverlaybd(ctx context.Context, sr *immutableRef, desc *ocispecs.Descriptor) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: should probably be commitOverlayBD (capital BD)

@thaJeztah
Copy link
Member

opened HileQAQ#1 with things I was drafting / playing with (includes some of my suggestions)

@HileQAQ
Copy link
Author

HileQAQ commented Jun 26, 2023

@thaJeztah I made some modifications as suggested, except your suggestions, I set enableOverlay=false after commitOverlayBD for compatibility. Sorry for the late reply.

@AkihiroSuda AkihiroSuda requested a review from tonistiigi June 28, 2023 10:02
@AkihiroSuda
Copy link
Member

@tonistiigi @ktock PTAL

@shuaichang
Copy link

+1 for this very useful feature! We depend on Overlaybd image format and being able to build directly instead of doing conversion would be a major enhancement. Do we know if we have plan to merge this soon?

@AkihiroSuda
Copy link
Member

At least this needs rebasing

@BigVan
Copy link

BigVan commented Nov 26, 2024

I will solve this... This PR has been pending for a long time😂

At least this needs rebasing

@liulanzheng
Copy link
Contributor

@AkihiroSuda @BigVan I have rebased the code, PTAL

@BigVan
Copy link

BigVan commented Jan 17, 2025

The related feature has been supported in Alibaba for almost two years.
PTAL @thaJeztah @ktock and Hope you guys can approve this PR

@liulanzheng
Copy link
Contributor

@AkihiroSuda A lint error has been fixed, please help rerun the checks

@fuweid
Copy link
Contributor

fuweid commented Jan 24, 2025

ping @AkihiroSuda @thaJeztah @tonistiigi

@liulanzheng
Copy link
Contributor

These two failed checks should be unrelated to the code in this PR. The logic related to overlaybd includes appropriate checks and will not affect the existing logic. Additionally, I also encounter these failures when using the upstream code on my GitHub Actions occasionally. Could you please help me retry or provide some assistance? @AkihiroSuda

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/dependencies Pull requests that update a dependency file area/docs area/remotecache area/source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants