Building and pushing multiple manifests

I’m trying to build multiarch docker images. I’d like to sand it down as much as possible. In a better world, that would mean just one simple command. It’s not.

Here’s the first error I got from my simple script:

podman build --platform linux/amd64 . -t "${TAG}-amd64"
podman build --platform linux/arm64/v8 . -t "${TAG}-arm64"
podman manifest create "$TAG" "${TAG}-amd64" "${TAG}-arm64"

Error: setting up to read manifest and configuration from "docker://account.dkr.ecr.us-east-1.amazonaws.com/image:tag": reading manifest docker://account.dkr.ecr.us-east-1.amazonaws.com/image:tag: manifest unknown: Requested image not found

This didn’t work, and it turned out the reason was quite simple if obtuse – podman manifest wants to build from the real repositories. As I hadn’t pushed those images, it couldn’t find them on the remote repository.

I spent some time searching for a solution to build images locally, then build them into a manifest, and then finally tag them. I found a couple of things that should work, but didn’t:

podman manifest add MANIFEST containers-storage:image:tag
reference "[overlay@/home/ted/.local/share/containers/storage+/run/user/1000/containers]docker.io/library/image:tag" does not resolve to an image ID: identifier is not an image

I don’t know why this didn’t work. According to the docs on transports, containers-storage is the transport we can use to inspect local images. This is somewhat consistent in behavior:

podman build image:tag
podman tag 
podman inspect image:tag
...
podman  inspect containers-storage:repo/image:tag
...
podman inspect containers-storage:image:tag
Error: no such object: "containers-storage:image:tag
podman inspect containers-storage:localhost/image:tag
...

Containers-storage somewhat works, but you have to supply a hostname, which is “localhost” for otherwise unspecified images.

Another angle I tried is building both in a singular tag with the manifest flag. This seems like it should work.

podman build --platform linux/amd64,linux/arm64/v8 . --manifest image:tag

This actually worked – I didn’t realize it at first, but it built both architectures:

podman manifest inspect image:tag
{
    "schemaVersion": 2,
    "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
    "manifests": [
        {
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "size": 2444,
            "digest": "sha256:ea95462b074c650e6c477f8bf88bcfa0b6a021de7c550e2faca25c7f833bdc5f",
            "platform": {
                "architecture": "amd64",
                "os": "linux"
            }
        },
        {
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "size": 2444,
            "digest": "sha256:f1eb75a71b89b3655b845acd79076bc8d640d3db8fb0f24367748fb50b2e6001",
            "platform": {
                "architecture": "arm64",
                "os": "linux",
                "variant": "v8"
            }
        }
    ]
}

However, when I pushed my image, the wrong format was downloaded on my k8s nodes:

podman push image:tag

Containers:
  loadtest:
    Container ID:  containerd://5d157712c742aa63220c34eb2b5213b0cf580a50c5768406ff434910700a2638
    Image:         image:tag
    Image ID:      image:tag@sha256:d0345fbc0ec7c38fdcbedfb90e7b21986e2e9642856e7e2a62a0591d68d48f85

A significant amount of consternation later, I realized that because I was using podman push, the image was being resolved first, and then just the one architecture was pushed (but with tag for the whole . What I needed to do instead was podman manifest push, which pushed the whole manifest and all sub-images.