<h2 class="wp-block-heading">Key concepts</h2>
<p class="wp-block-paragraph"><em><strong>Container manager</strong></em>.There is an actor, a daemon, or something that pull and run the right image when instructucted to do that.<br>That means, you can use docker, docker swarm mode, kubernetes, k3s, or whatever daemon to monitor or just run container<br>based on image taken from some place, and that daemon need to understand what to pull.</p>
<p class="wp-block-paragraph"><em><strong>Architecture</strong></em>: every machine has its own instruction set, device, etc. and its own binary format.<br>This is “architecture” in this context (the context of cross building a docker image).</p>
<p class="wp-block-paragraph"><em>Binary format</em>, <code><strong>binfmt</strong></code> in linux. There is a lot of things on this topic, and too advanced.<br>Anyway, Linux kernel support a kind of pluggable executor based on magic character, characters used<br>to recognize the architecture, those magics are bounded with executor. Typically <code>/proc</code> fs or <code>/sys</code> fs is used<br>to setup and inspect kernel runtime setup, so the current binfmt status can be inspected in <code>/proc/sys/fs/binfmt_misc/*</code><br>More infos in lwn article: <a href="https://lwn.net/Articles/630727/" target="_blank" rel="noreferrer noopener">https://lwn.net/Articles/630727/</a></p>
<p class="wp-block-paragraph"><em><strong>Docker registry</strong></em> is a <em>service</em> that run somewhere and keep track of images and theirs tags.</p>
<p class="wp-block-paragraph"><em><strong>Image manifest</strong></em>. This was a new concept to me, tag is not enough for describing an image, an image is identified by<br>its manifest, that is a json structure that keep reference to the real image(s) tag(s), its architecture, and other infos.</p>
<p class="wp-block-paragraph">So the manifest, someway, overlap the concept of tag for image, but in fact that is a trick: a default manifest<br>with the same tag is created and associated to the unique image tag, when manifest is not treated explicitly.</p>
<p class="wp-block-paragraph">So manifest has a tag, like each image has a tag, but manifest refers to more images.</p>
<h2 class="wp-block-heading">Deal explicitly with manifest</h2>
<p class="wp-block-paragraph">Docker CLI, the command line interface used to communicate with dockerd, is not intended to deal with manifest,<br>I do not know why, but it can do it only enabling “experimental” feature, by editing <code>~/.docker/config.json</code> with</p>
<pre class="wp-block-preformatted">{"experimental": "enabled"}</pre>
<p class="wp-block-paragraph">Then is possible to use it</p>
<p class="wp-block-paragraph">Commands:</p>
<ul class="wp-block-list"><li><code>docker manifest inspect --verbose TAG</code></li><li><code>docker manifest create MANIFESTTAG [IMAGETAG1 [IMAGETAG2 [IMAGETAG3 […]]]]</code></li><li><code>docker manifest push MANIFESTTAG</code></li></ul>
<p class="wp-block-paragraph"><code>inspect</code> is to inspect, <code>create</code> is to create, <code>push</code> is to push the created manifest into the remote registry.</p>
<p class="wp-block-paragraph">There are limitations here, for example one can create a manifest tag and push it to the registry, but if<br>the manifest refers to image tags that are not already pushed in the registry the service will complain about it<br>(or at least I suppose, never tried)</p>
<h2 class="wp-block-heading">Real building multiarch</h2>
<p class="wp-block-paragraph">What I used to understand it as of few days ago, a Dockerfile refer to main image in FROM section.<br>But today my idea about it become more complex and I am a bit confused.<br>Maybe it refers to a Manifest? and the image is automatically selected during the <code>docker build</code><br>phase from a set of image, based on matching architecture?</p>
<p class="wp-block-paragraph">Anyway, one can explicetely refer to a specific architecture by its name, for example using alpine distro<br>it is a prefix: ‘amd64/’, ‘arm32v7/’, ‘arm64v8/’, …</p>
<p class="wp-block-paragraph">In Dockerfile is possible to define arguments and their defaults:</p>
<pre class="wp-block-code"><code>ARG ARCH=
FROM ${ARCH}alpine</code></pre>
<p class="wp-block-paragraph">and give values to arguments in command line during <code>docker build</code>:</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p><code>docker build -t IMAGETAGNAME --build-args ARCH=arm32v7/</code></p></blockquote>
<p class="wp-block-paragraph">at this point the docker daemon try to build it based on image referred in FROM: arm32v7/alpine</p>
<p class="wp-block-paragraph">This image contains a filesystem with binary in a format specific to arm32v7 architecture, so you have:</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>standard_init_linux.go:211: exec user process caused “exec format error”</p></blockquote>
<p class="wp-block-paragraph">it means there is no magic matched, so docker tried to execute it directly, but by</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p><code>docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64</code></p></blockquote>
<p class="wp-block-paragraph">the folder /proc/sys/fs/binfmt_misc is populated, i.e.:</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>cat /proc/sys/fs/binfmt_misc/qemu-arm</p></blockquote>
<pre class="wp-block-code"><code>enabled
interpreter /usr/bin/qemu-arm
flags: OCF
offset 0
magic 7f454c4601010100000000000000000002002800
mask ffffffffffffff00fffffffffffffffffeffffff</code></pre>
<p class="wp-block-paragraph">and now again</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>docker build -t IMAGETAGNAME –build-args ARCH=arm32v7/</p></blockquote>
<p class="wp-block-paragraph">will build the image in a qemu-arm machine runned <code>magically</code> for that job.</p>
<p class="wp-block-paragraph">Then the steps are:</p>
<ol class="has-medium-font-size wp-block-list"><li>built image tag for each architecture</li><li>push image tag for each architecture</li><li>create a manifest naming it with a tag and referring to all architecture created in 1. and pushed in 2.</li><li>push the manifest created in 3. to the docker registry</li></ol>
<p class="wp-block-paragraph">There is nothing complex or special, but one just need to understand the concept behind the words: image tag, manifest tag, manifest, architecture, registry, binfmt, crossbuild, …</p>
<h2 class="wp-block-heading">References</h2>
<p class="wp-block-paragraph">I found it hard to understand concepts, my learning path (I spent 5/6 hours) was to check first this document<br><a href="https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/" target="_blank" rel="noreferrer noopener">https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/</a></p>
<p class="wp-block-paragraph">From there I needed to understand github actions, and github secrets</p>
<ol class="wp-block-list"><li><em>github actions</em></li></ol>
<p class="wp-block-paragraph">An action is defined by</p>
<p class="wp-block-paragraph">i. a trigger (on: push: branch: master, for example)<br>ii. and a job, made of: name, environment, steps</p>
<p class="wp-block-paragraph">i.e. https://github.com/danielecr/uuid-provider/blob/main/.github/workflows/image.yml</p>
<ol class="wp-block-list" start="2"><li><em>github secrets</em></li></ol>
<p class="wp-block-paragraph">It is possible to define variable binded with the repo, those are secrets and environment variable</p>
<p class="wp-block-paragraph">It is possible to access those variables from github action, i.e. by ${{ secrets.MYSECRET }}</p>
<p class="wp-block-paragraph">But still I was confused about it.</p>
<p class="wp-block-paragraph">I googled more, and continuosly found reference to windows platform, and everywhere try to sell that<br>buildx as “easy way”, with result to be more confused.</p>
<p class="wp-block-paragraph">I finally found <a rel="noreferrer noopener" href="https://www.docker.com/blog/getting-started-with-docker-for-arm-on-linux/" target="_blank">https://www.docker.com/blog/getting-started-with-docker-for-arm-on-linux/</a><br>still a lot of advertising for buildx script, but reference to<a rel="noreferrer noopener" href="https://hub.docker.com/r/docker/binfmt/tags?page=1&ordering=last_updated" target="_blank"> https://hub.docker.com/r/docker/binfmt/tags?page=1&ordering=last_updated</a><br>and by guessing it <code><a rel="noreferrer noopener" href="https://github.com/docker/binfmt" target="_blank">https://github.com/docker/binfmt</a></code><br>and suggestion to use the newer <a rel="noreferrer noopener" href="https://github.com/linuxkit/linuxkit" target="_blank">https://github.com/linuxkit/linuxkit</a> (but I still need to see it)</p>
<p class="wp-block-paragraph">Just for curiosity, the only graphical description of build environment I found was in <a rel="noreferrer noopener" href="https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/" target="_blank">https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/</a> but it give little/no infos about binfmt, and it is a content specific to jetbrain device.</p>
Key concepts
Container manager.There is an actor, a daemon, or something that pull and run the right image when instructucted to do that.
That means, you can use docker, docker swarm mode, kubernetes, k3s, or whatever daemon to monitor or just run container
based on image taken from some place, and that daemon need to understand what to pull.
Architecture: every machine has its own instruction set, device, etc. and its own binary format.
This is “architecture” in this context (the context of cross building a docker image).
Binary format, binfmt in linux. There is a lot of things on this topic, and too advanced.
Anyway, Linux kernel support a kind of pluggable executor based on magic character, characters used
to recognize the architecture, those magics are bounded with executor. Typically /proc fs or /sys fs is used
to setup and inspect kernel runtime setup, so the current binfmt status can be inspected in /proc/sys/fs/binfmt_misc/*
More infos in lwn article: https://lwn.net/Articles/630727/
Docker registry is a service that run somewhere and keep track of images and theirs tags.
Image manifest. This was a new concept to me, tag is not enough for describing an image, an image is identified by
its manifest, that is a json structure that keep reference to the real image(s) tag(s), its architecture, and other infos.
So the manifest, someway, overlap the concept of tag for image, but in fact that is a trick: a default manifest
with the same tag is created and associated to the unique image tag, when manifest is not treated explicitly.
So manifest has a tag, like each image has a tag, but manifest refers to more images.
Deal explicitly with manifest
Docker CLI, the command line interface used to communicate with dockerd, is not intended to deal with manifest,
I do not know why, but it can do it only enabling “experimental” feature, by editing ~/.docker/config.json with
{"experimental": "enabled"}
Then is possible to use it
Commands:
docker manifest inspect --verbose TAGdocker manifest create MANIFESTTAG [IMAGETAG1 [IMAGETAG2 [IMAGETAG3 […]]]]docker manifest push MANIFESTTAG
inspect is to inspect, create is to create, push is to push the created manifest into the remote registry.
There are limitations here, for example one can create a manifest tag and push it to the registry, but if
the manifest refers to image tags that are not already pushed in the registry the service will complain about it
(or at least I suppose, never tried)
Real building multiarch
What I used to understand it as of few days ago, a Dockerfile refer to main image in FROM section.
But today my idea about it become more complex and I am a bit confused.
Maybe it refers to a Manifest? and the image is automatically selected during the docker build
phase from a set of image, based on matching architecture?
Anyway, one can explicetely refer to a specific architecture by its name, for example using alpine distro
it is a prefix: ‘amd64/’, ‘arm32v7/’, ‘arm64v8/’, …
In Dockerfile is possible to define arguments and their defaults:
ARG ARCH=
FROM ${ARCH}alpine
and give values to arguments in command line during docker build:
docker build -t IMAGETAGNAME --build-args ARCH=arm32v7/
at this point the docker daemon try to build it based on image referred in FROM: arm32v7/alpine
This image contains a filesystem with binary in a format specific to arm32v7 architecture, so you have:
standard_init_linux.go:211: exec user process caused “exec format error”
it means there is no magic matched, so docker tried to execute it directly, but by
docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
the folder /proc/sys/fs/binfmt_misc is populated, i.e.:
cat /proc/sys/fs/binfmt_misc/qemu-arm
enabled
interpreter /usr/bin/qemu-arm
flags: OCF
offset 0
magic 7f454c4601010100000000000000000002002800
mask ffffffffffffff00fffffffffffffffffeffffff
and now again
docker build -t IMAGETAGNAME –build-args ARCH=arm32v7/
will build the image in a qemu-arm machine runned magically for that job.
Then the steps are:
- built image tag for each architecture
- push image tag for each architecture
- create a manifest naming it with a tag and referring to all architecture created in 1. and pushed in 2.
- push the manifest created in 3. to the docker registry
There is nothing complex or special, but one just need to understand the concept behind the words: image tag, manifest tag, manifest, architecture, registry, binfmt, crossbuild, …
References
I found it hard to understand concepts, my learning path (I spent 5/6 hours) was to check first this document
https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/
From there I needed to understand github actions, and github secrets
- github actions
An action is defined by
i. a trigger (on: push: branch: master, for example)
ii. and a job, made of: name, environment, steps
i.e. https://github.com/danielecr/uuid-provider/blob/main/.github/workflows/image.yml
- github secrets
It is possible to define variable binded with the repo, those are secrets and environment variable
It is possible to access those variables from github action, i.e. by ${{ secrets.MYSECRET }}
But still I was confused about it.
I googled more, and continuosly found reference to windows platform, and everywhere try to sell that
buildx as “easy way”, with result to be more confused.
I finally found https://www.docker.com/blog/getting-started-with-docker-for-arm-on-linux/
still a lot of advertising for buildx script, but reference to https://hub.docker.com/r/docker/binfmt/tags?page=1&ordering=last_updated
and by guessing it https://github.com/docker/binfmt
and suggestion to use the newer https://github.com/linuxkit/linuxkit (but I still need to see it)
Just for curiosity, the only graphical description of build environment I found was in https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/ but it give little/no infos about binfmt, and it is a content specific to jetbrain device.