Skip to content
New issue

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

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

Already on GitHub? # to your account

--load with moby containerd store should use the oci exporter #1813

Merged
merged 1 commit into from
Jun 7, 2023

Conversation

jedevc
Copy link
Collaborator

@jedevc jedevc commented May 18, 2023

Moby with the containerd store supports importing multi-platform OCI images, which means we should be able to add functionality to have --load work with multi-platform images.

Before:

$ buildx build . --platform linux/amd64,linux/arm64 --builder container --load
[+] Building 0.1s (1/1) FINISHED                                                                                         docker-container:container
 => [internal] connecting to local controller                                                                                                  0.0s
ERROR: failed to build: docker exporter does not currently support exporting manifest lists

After:

$ buildx build . --platform linux/amd64,linux/arm64 --builder container --load
[+] Building 4.0s (24/24) FINISHED                                                                                       docker-container:container
 => [internal] connecting to local controller                                                                                                  0.0s
 => [internal] load build definition from Dockerfile                                                                                           0.0s
 => => transferring dockerfile: 2.20kB                                                                                                         0.0s
...
 => exporting to docker image format                                                                                                              0.6s
 => => exporting layers                                                                                                                        0.0s
 => => exporting manifest sha256:c0dfaa2978d8ea698f0af7ce2a36373789ed20deffd837a4504891f04c28e0f4                                              0.0s
 => => exporting config sha256:6c835b10e81d195b8795ae810667bff2c0c6565430f16f3649f5e4d4f2b6a650                                                0.0s
 => => exporting attestation manifest sha256:b11b99e5db8890a7e66713e4476d20d2d6fa622519140830d3097ef3112d7ed3                                  0.0s
 => => exporting manifest sha256:ecae44f665a1727aea6c7a2adff17ebc2f954ae5aad957443e5aa35b24277983                                              0.0s
 => => exporting config sha256:dbca1a6373dd85c3bb3ea08a6d980fc7674976126ddcfdbcc2bd61c2b57dfebe                                                0.0s
 => => exporting manifest list sha256:c2b6927cbd1a651e05e462294d11e546d83a4b1c4146d0c8195c234d8f98d490                                         0.0s
 => => sending tarball                                                                                                                         0.6s
 => importing to docker  

This patch works by translating the docker exporter to an oci exporter (which supports multi-platform exports), just as we were previously translating the docker exporter to the moby exporter for the moby case.

I think this should just work? I've done some basic tests with some sample bake files, and build commands from my history, and get the expected results (and don't get any different behavior on non-containerd moby) 🎉

(I can add some integration tests once #1770 merges)

cc @tonistiigi @vvoland @rumpl

Related issues

@jedevc jedevc added this to the v0.11.0 milestone May 18, 2023
@jedevc jedevc added area/moby kind/enhancement New feature or request labels May 18, 2023
@jedevc jedevc force-pushed the oci-export-on-containerd branch from 55a5193 to 76f7bf4 Compare May 18, 2023 15:57
@jedevc jedevc requested review from crazy-max and tonistiigi and removed request for crazy-max May 18, 2023 15:58
@tonistiigi
Copy link
Member

tonistiigi commented May 18, 2023

Allow --output type=oci to act as in --output type=docker when no dest argument is provided, thereby loading the result into moby.
Change buildx build --load to default to using --output type=oci

I don't think this is necessarily what we want. I think we shouldn't change the behavior of -o type=oci or make --load dynamic. Instead, we should change -o type=docker to get the tarball in OCI format if the current Docker supports the OCI format. -o type=docker means "output to Docker". If Docker supports OCI then it should just output to Docker using OCI format internally, which doesn't have the quirks about not supporting multi-platform etc.

@TBBle
Copy link

TBBle commented May 19, 2023

I like the idea that when pushing to Docker, it automatically pushes in OCI format if supported. For --load (like the PR's approach) this "just works", which for me is the UX goal. It also means that if people are writing --load longhand (--output=type=docker) then it "just works" with manifest lists. (I'm not as tied to that as a UX goal personally.)

However, this latter approach means --output type=docker and --output type=docker,dest=- | docker load behave differently, in that the latter still fails for an image index/manifest list.

-o type=docker means "output to Docker".

I think this would be surprising to users, and isn't what the --output documentation emphasises, although the --load documentation implies this by being a fairly simple alias.

It's not inconsistent with the docs, but if taking this latter approach, we might also want to rework the --output docs a little to make it clear that type=docker is focussed on somehow getting it into a Docker instance, not on the Docker image tarball file format (which would be legacy under this construction). Right now, it is written as a direct contrast with the oci exporter, and is (apart from the note in dest) all about the tarballs.

@tonistiigi
Copy link
Member

I think the confusion may come from that in buildx and buildctl -o type=docker do not behave the same. buildctl does export a "docker-style" tar (that actually is OCI compatible as well) while buildx behavior is to perform a docker load. With my suggestion, no current commands should break, and --load works as expected. While in current form this PR breaks some current users of type=oci. I agree that docs may need clarification.

@tonistiigi
Copy link
Member

means --output type=docker and --output type=docker,dest=- | docker load behave differently, in that the latter still fails for an image index/manifest list.

First of all, there is no need for anyone to write --output type=docker,dest=- | docker load today. If anyone does, it would still work the same way as it works today. We could document that "dest" is an optional parameter, if provided, instead of loading tar, it will be exported in the path, with maximum compatibility with older Docker versions(1.10+). This would not upgrade automatically for a manifest list, but users would usually know if they export a manifest list and can easily change type=oci to strictly control the tar format if they need that.

Another thing is that you can imagine that there will be other ways to get build results into docker in the future, eg. moby/moby#44369 . The behavior of the exporter should not be defined by a specific implementation but the expected end goal.

For bake, note that load is not bake property, it is only a shortcut override flag. So if exporters are not defined precisely the intended behavior of "load into docker" can't be defined in a file.

@jedevc jedevc force-pushed the oci-export-on-containerd branch from 76f7bf4 to 931feca Compare May 19, 2023 09:50
@jedevc
Copy link
Collaborator Author

jedevc commented May 19, 2023

I don't think this is necessarily what we want. I think we shouldn't change the behavior of -o type=oci or make --load dynamic. Instead, we should change -o type=docker to get the tarball in OCI format if the current Docker supports the OCI format. -o type=docker means "output to Docker".

Should be good now 🎉

However, we actually need to pass the oci exporter to buildkit, since the docker exporter explicitly does not support multi-platform images: https://github.com/moby/buildkit/blob/4e4b7a8ff8a5700b0c170185dce4ca86e3a4e33d/exporter/oci/export.go#L115-L117

I wonder if we should maybe change this requirement in the next version of buildkit? We have safeguards to prevent this in buildx now (since v0.6 #582).

Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

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

cc @rumpl

// rely on oci importer if available (which supports
// multi-platform images), otherwise fall back to docker
opt.Exports[i].Type = "oci"
} else if len(opt.Platforms) > 1 || len(attests) > 0 {
return nil, nil, errors.Errorf("docker exporter does not currently support exporting manifest lists")
}
if e.Output == nil {
if nodeDriver.IsMobyDriver() {
Copy link
Member

Choose a reason for hiding this comment

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

Not changing in this PR but shouldn't this also have a "context" check?

@@ -63,6 +63,28 @@ func (c *Client) LoadImage(ctx context.Context, name string, status progress.Wri
}, nil
}

func (c *Client) Features(ctx context.Context, name string) (map[Feature]bool, error) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In discussion with @crazy-max about #1846, we noticed that this probably needs a similar cache to ensure that we don't create a new docker client in each loop of the toSolveOpt call.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can try and do this as a follow-up though, would be good to not block another RC on this.

@jedevc jedevc force-pushed the oci-export-on-containerd branch 2 times, most recently from aa4afa7 to e89ed57 Compare May 26, 2023 15:37
@jedevc
Copy link
Collaborator Author

jedevc commented May 26, 2023

Aha, reworked the Features call, we shouldn't allow errors from it to propagate to the user - if no docker daemon is available, we should just assume the OCIImporter isn't either. This got detected in the new tests we have 🎉

@rumpl
Copy link
Member

rumpl commented May 26, 2023

I have a couple of questions:

@jedevc jedevc force-pushed the oci-export-on-containerd branch from e89ed57 to b3d573d Compare May 26, 2023 16:10
@jedevc
Copy link
Collaborator Author

jedevc commented May 26, 2023

Does the OCI exporter unpack the image?

Not directly, no. We take the output of the OCI exporter and pipe it into the ImageLoad docker API, so I think that should do the unpack operation?

did you try and run the built images?

An important point. This doesn't work when doing moby-buildkit for whatever reason for me... 🤔 (but that's using the type=image exporter, not the oci exporter).

Starting from an empty state (docker system prune -a) - for some reason, I can get into a state where I can't reproduce this, I wonder if there's something about intermediate layers not being loaded from multiplatform images or something?

$ docker buildx build . --target shell --builder desktop-linux --output type=docker -t buildx-shell --platform linux/amd64,linux/arm64
...
$ docker image ls
REPOSITORY                 TAG       IMAGE ID       CREATED         SIZE
buildx-shell               latest    6a1a88e8cb50   7 seconds ago   111MB
buildx-shell               latest    6a1a88e8cb50   7 seconds ago   104MB
$ docker image inspect buildx-shell | head -n 5
[
    {
        "Id": "sha256:6a1a88e8cb5056f4c9802ee8251a9f7eb7118a0a14a71d5b4c867909d045ff0d",
        "RepoTags": [
            "buildx-shell:latest"
$ docker run -it buildx-shell
Unable to find image 'buildx-shell:latest' locally

(note, single platform builds work fine)

This does work when using the container driver though (which with this patch uses oci exporter):

$ buildx build . --target shell --builder container -t buildx-shell-2 --platform linux/amd64,linux/arm64
...
$ docker image ls
REPOSITORY                 TAG       IMAGE ID       CREATED         SIZE
buildx-shell-2             latest    114e993fbed0   4 minutes ago   427MB
buildx-shell-2             latest    114e993fbed0   4 minutes ago   410MB
$ docker image inspect buildx-shell-2 | head -n 5
[
    {
        "Id": "sha256:114e993fbed0fba938a3c9f6c38b76fcb6b7ca7d6599f56dbbae9fc385a14f97",
        "RepoTags": [
            "buildx-shell-2:latest"
$ docker run -it buildx-shell-2
/work # 

Slightly confused at the difference in sizes of images... is something maybe not getting unpacked?

@jedevc
Copy link
Collaborator Author

jedevc commented May 26, 2023

Could potentially be related to moby/buildkit#3891?

@tonistiigi
Copy link
Member

This looks more like issue with the load command on dockerd with containerd enabled rather than build-side. We basically just issue docker load. All else is dockerd.

@jedevc
Copy link
Collaborator Author

jedevc commented May 30, 2023

The case with docker load (using a non-docker driver) works fine - it's when we use the type=image exporter with buildkit (using the docker driver) that we have the issue.

@rumpl
Copy link
Member

rumpl commented May 30, 2023

The case with docker load (using a non-docker driver) works fine - it's when we use the type=image exporter with buildkit (using the docker driver) that we have the issue.

Are we still talking about the ERROR: failed to build: docker exporter does not currently support exporting manifest lists issue or are you talking about something else?

@tonistiigi
Copy link
Member

it's when we use the type=image exporter with buildkit (using the docker driver) that we have the issue.

That case should be working since #1262

Signed-off-by: Justin Chadwell <me@jedevc.com>
@jedevc
Copy link
Collaborator Author

jedevc commented May 31, 2023

Done some more investigation - I think this out of scope for this PR, so we probably shouldn't block on it.

I can reproduce the same issue with the type=moby exporter (equivalent to type=image,unpack=true) when building a multi-platform image, so something seems incorrect here - which is covered by moby/buildkit#3891.

@jedevc jedevc requested a review from tonistiigi May 31, 2023 15:09
@jedevc jedevc force-pushed the oci-export-on-containerd branch from b3d573d to 183a73a Compare May 31, 2023 15:35
@jedevc
Copy link
Collaborator Author

jedevc commented Jun 6, 2023

@tonistiigi PTAL

@@ -63,6 +63,23 @@ func (c *Client) LoadImage(ctx context.Context, name string, status progress.Wri
}, nil
}

func (c *Client) Features(ctx context.Context, name string) map[Feature]bool {
features := make(map[Feature]bool)
if dapi, err := c.API(name); err == nil {
Copy link
Member

Choose a reason for hiding this comment

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

In a follow-up this function could do caching these return values for bake and if we add any more Feature values we probably would need to ask for a specific one to make sure expensive call like info is not done needlessly.

@jedevc jedevc requested a review from rumpl June 6, 2023 15:57
Copy link
Member

@rumpl rumpl left a comment

Choose a reason for hiding this comment

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

Just tested it, seems to work great, awesome work @jedevc, thanks!

@Felixoid
Copy link

Felixoid commented Aug 7, 2023

Hello, is there a way to use OCI exported in case of --push? Our command looks like docker buildx build --push --output=type=image,push-by-digest=true, but it's very slow for low-power instances.

@jedevc
Copy link
Collaborator Author

jedevc commented Aug 7, 2023

@Felixoid this doesn't seem related to this PR, which is about --load - if you have other questions around it, can you open a separate issue?

In any case, if looks like you might want --output type=image,push-by-digest=true,oci-mediatypes=true.

@Felixoid
Copy link

Felixoid commented Aug 7, 2023

Thank you, I'll try the parameter oci-mediatypes=true. It may solve our issue!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
area/moby kind/enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request: --load equivalent for the OCI exporter
5 participants