Does anyone build multi-platform Docker containers...
# general
l
Does anyone build multi-platform Docker containers that are then used as a base image for other images in the project? I’m using the 2.19 RC to get buildkit working, which has allowed me to build a multi-platform image. However, when I try to build on top of it as a base, Docker can’t seem to find the just-built image. This seems like more of a Docker problem than a pants-specific issue, but I’m hoping someone else will have seen it. I’m using Docker Desktop v4.26.0, and I enabled the
containerd
storage backend to get the
docker
output working. My
pants.toml
looks like:
Copy code
[docker]
use_buildx = true
and then I’m building a couple of image targets from a Dockerfile:
Copy code
docker_image(
    name="runtime",
    repository=IMAGE_REPO_NAME,
    target_stage="runtime",
    image_tags=[
        "latest",
    ],
    dependencies=CONTEXT_INCLUDES,
    build_platform=[
        "linux/amd64",
        "linux/arm64",
    ],
)
This works as expected, and after
pants package :runtime
I have an image for both platforms (visible in
docker image ls
). Yay! However, when I build a second Dockerfile in the same project referencing my earlier output:
Copy code
docker_image(
    name="image",
    repository=IMAGE_REPO,
    image_tags=[IMAGE_TAG], # We're using "test" instead of "latest" here to avoid Docker's default of _always_ re-pulling the image
    build_platform=[
        "linux/amd64",
    ],
    dependencies=[
        ":install_smoke_test_packages",
        "src/docker/u21base:runtime",
    ],
)
with this Dockerfile:
Copy code
ARG BASE=src/docker/u21base:runtime

FROM $BASE AS upstream


# The pants environment feature does require _some_ tools
# to be present in the image that you want to use, so we can add
# them here. This is a bit of a hack, but it works.
COPY src/docker/smoke_tests/smoketest_packages/install /
RUN ["ln", "-s", "/bin/bash", "/bin/sh"]
RUN ["ln", "-s", "/busybox/env", "/usr/bin/env"]
RUN ["ln", "-s", "/busybox/tar", "/usr/bin/tar"]
Docker complains that it can’t find my base image and seems to be trying to pull it from docker hub:
Copy code
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 476B done
#1 DONE 0.0s

#2 [internal] load metadata for <http://docker.io/library/u21base:runtime|docker.io/library/u21base:runtime>
#2 ...

#3 [auth] library/u21base:pull token for <http://registry-1.docker.io|registry-1.docker.io>
#3 DONE 0.0s

#2 [internal] load metadata for <http://docker.io/library/u21base:runtime|docker.io/library/u21base:runtime>
#2 ERROR: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
------
 > [internal] load metadata for <http://docker.io/library/u21base:runtime|docker.io/library/u21base:runtime>:
------
Dockerfile:3
--------------------
   1 |     ARG BASE=src/docker/u21base:runtime
   2 |     
   3 | >>> FROM $BASE AS upstream
   4 |     
   5 |     
--------------------
ERROR: failed to solve: u21base:debug: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
Any tricks to getting this working? Should I use a different image output type and just make my secondary image depend on an adhoc tool that runs
docker load
instead so I don’t have to rely on
containerd
? Parameterize the platform so I get single-arch targets that I can upload on their own?
b
It’s not totally clear to me that
BASE
has the correct value in your dependent Dockerfile. Based on the values that you set when building the base image, I would expect
BASE
should contain
{IMAGE_REPO_NAME}:latest
? What is the name of the base image that gets built when you run
docker image ls
?
l
Yes sorry, you’re right. I copied the error output from a branch with a different tag name. The issue remains though, more generally I can build
u21base:latest
and have it show up in `docker image ls`:
Copy code
pants package :runtime
14:37:50.85 [INFO] Initialization options changed: reinitializing scheduler...
14:37:54.63 [INFO] Scheduler initialized.
14:38:35.61 [INFO] Completed: Building docker image u21base:latest
14:38:35.64 [INFO] Wrote dist/src.docker.u21base/runtime.docker-info.json
Built docker image: u21base:latest
Docker image ID: <unknown>

▶ docker image ls
REPOSITORY      TAG               IMAGE ID       CREATED          SIZE
u21base         latest            79f37846a460   15 seconds ago   162MB
u21base         latest            79f37846a460   15 seconds ago   170MB
Then building my derived image, bypassing any pants dependency inference and just using
u21base:latest
directly:
Copy code
> [internal] load metadata for <http://docker.io/library/u21base:latest|docker.io/library/u21base:latest>:
------
Dockerfile:1
--------------------
   1 | >>> FROM u21base:latest AS upstream
   2 |
   3 |
--------------------
ERROR: failed to solve: u21base:latest: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
This all worked when I was building single-platform images without buildkit, so I suspect it’s a
containerd
issue
b
Must be; unfortunately we don’t use buildx so I don’t have much to add on the
containerd
side
l
Yeah. Frustratingly,
docker run u21base:latest
also works.
I wonder if it’s because I’m using a
docker_container
driver for buildx, actually.
looks like this is expected behaviour of
buildx
😞
😞 1
n
Yes, this is a known issue right now due to that. I was going to take a look at some ways to workaround in Pants, but won't get to it until the new year.
l
I ended up just moving my secondary image into the same dockerfile and using
target_stage
as a workaround.
👍 1
n