
{"id":4966,"date":"2024-04-16T10:40:58","date_gmt":"2024-04-16T14:40:58","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=4966"},"modified":"2024-04-16T10:40:58","modified_gmt":"2024-04-16T14:40:58","slug":"docker-multi-platform-images","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=4966","title":{"rendered":"Docker multi-platform images"},"content":{"rendered":"<h1>Invalid Exec Format<\/h1>\n<p>They gave me a MacBook Pro at work. I built a docker image locally. I pushed it to Amazon cloud to run. I got &#8220;<code>invalid exec format<\/code>&#8221; error. What gives?<\/p>\n<p>Compatibility problem! The Mac has Apple M3 chip based on the ARM technology, while the computer in the Amazon cloud has a chip with x64 instruction set. Oops&#8230; Docker images are processor-architecture and OS specific. An image built for ARM cannot run on x64 and vice versa. Fortunately, Docker provides tools for building cross-platform and multi-platform images. Unfortunately, this is still an area under development, so the situation changes quickly and the information about it is often outdated or confusing.<\/p>\n<h1>Terminology<\/h1>\n<p>&#8220;OS&#8221; is an operating system, like Windows or Linux.<\/p>\n<p>&#8220;Architecture&#8221; is a family of compatible processors. Intel and AMD compatible processors are labeled <code>amd64<\/code> or <code>x86_64<\/code>. 64-bit ARM processors are labeled <code>arm64<\/code>, <code>aarch64<\/code>, or <code>armv8<\/code>. Here <code>amd64<\/code> and <code>arm64<\/code>&#8221; are &#8216;standard&#8217; names, and others are aliases. E.g. if you ask docker to build an <code>aarch64<\/code> image, it obliges, but then reports image architecture as <code>arm64<\/code>&#8216;.<\/p>\n<p>&#8220;Platform&#8221; is a combination of OS and architecture, e.g. &#8220;<code>linux\/amd64<\/code>&#8220;.<\/p>\n<h1>Inspecting Image Platform<\/h1>\n<p>Each &#8220;regular&#8221; image has an OS and an architecture associated with it. These can be viewed using the following command<\/p>\n<p><code>docker inspect --format='{{.Os}}\/{{.Architecture}}' <i>image_tag<\/i><\/code><\/p>\n<p>The output would be something like this: <code>'linux\/arm64'<\/code><\/p>\n<h1>Local Images are Single Platform<\/h1>\n<p>At the time of writing (April 2024) local Docker image storage only supports single-platform images. I.e. each local\u00a0 image will have a specific architecture and platform. However, it is possible to build an image for another platform, not the same as the local computer.<\/p>\n<p><code>docker build . --platform linux\/amd64 -t ikriv\/testimage:1.0-intel<\/code><\/p>\n<p><code>docker build . --platform linux\/arm64 -t ikriv\/testimage:1.0-mac<\/code><\/p>\n<p>Images built in this way can be pushed to DockerHub.<\/p>\n<p>The trouble is that when pulling an image, one has to know which image to pick for the current platform, which may be inconvenient and error-prone.<\/p>\n<h1>Multi-Platform Solutions<\/h1>\n<p>It would be nice to have a single image tag that produces an image for the right platform when pulled.<\/p>\n<p>E.g. I want to be able to do<\/p>\n<p><code>docker pull httpd:latest<\/code><\/p>\n<p>and pull the latest Apache image that works on my machine, without worrying which platform it is.<\/p>\n<p>Docker offers two solutions to this problem: manifests and multi-platform images.<\/p>\n<h1>Manifests<\/h1>\n<p>Manifests video: <a href=\"https:\/\/www.youtube.com\/watch?v=AQeGdMuJWIM&amp;ab_channel=AntonPutra\">https:\/\/www.youtube.com\/watch?v=AQeGdMuJWIM&amp;ab_channel=AntonPutra<\/a><\/p>\n<p>Manifest is a special entity that acts as a collection of images. Note that at the time of writing manifests cannot be created locally, they always live on a remote server. Thus, manifests can only contain images If I built two platform specific images <code>ikriv\/testimage:1.0-intel<\/code> and <code>ikriv\/testimage:1.0-mac<\/code>, I can create a multi-platform manifest as follows:<\/p>\n<p><code>docker push ikriv\/testimage:1.0-intel<\/code><\/p>\n<p><code>docker push ikriv\/testimage:1.0-mac<br \/>\n<\/code><\/p>\n<p><code>docker manifest create ikriv\/testimage:1.0 ikriv\/testimage:1.0-intel ikriv\/testimage:1.0-mac<\/code><\/p>\n<p><code>docker manifest annotate ikriv\/testimage:1.0 ikriv\/testimage:1.0-intel --os linux --arch amd64<\/code><\/p>\n<p><code>docker manifest annotate ikriv\/testimage:1.0 ikriv\/testimage:1.0-mac --os linux --arch arm64<\/code><\/p>\n<p><code>docker manifest push ikriv\/testimage:1.0<\/code><\/p>\n<p>This way, when someone does <code>docker pull ikriv\/testimage:1.0<\/code>, they will get either &#8220;intel&#8221; or &#8220;mac&#8221; version, depending on their platform. All three entities, <code>testimage:1.0<\/code>, <code>testimage:1.0-intael<\/code>, and <code>testimage:1.0-mac<\/code> will be visible on Dockerhub.<\/p>\n<h1>Multi-Platform Images<\/h1>\n<p>Multi-platform images video: <a href=\"https:\/\/www.youtube.com\/watch?v=ZxsAXNCiSZw\">https:\/\/www.youtube.com\/watch?v=ZxsAXNCiSZw<\/a><\/p>\n<p>It is possible to build a single image that contains multiple platform-specific sub-images using <code>docker buildx<\/code> command. To do so, one needs to create a named docker builder:<\/p>\n<p><code>docker buildx create --name mybuilder<\/code><\/p>\n<p>Then we make this builder default:<\/p>\n<p><code>docker buildx use mybuilder<\/code><\/p>\n<p>Then we build our multi-platform image and push it to the remote server in the same command. Keep in mind, a multi-platform image cannot be stored in the local image storage:<\/p>\n<p><code>docker buildx build --platform linux\/amd64,linux\/arm64 -t ikriv\/testbuildx --push .<\/code><\/p>\n<p>This would create a single entity <code>ikriv\/testbuildx:latest<\/code> on the DockerHub server. It contain images for two platforms. <code>docker pull<\/code> will download the image for the current platform.<\/p>\n<p>For an example, inspect <a href=\"https:\/\/hub.docker.com\/_\/httpd\/tags\">httpd image from Apache<\/a>.<\/p>\n<h1>What&#8217;s the difference?<\/h1>\n<p>Images create using <code>docker build --platform<\/code> and using <code>docker buildx<\/code> produce slightly different results. In my test, buildx produced smaller images, but took longer time. The nature of this difference is subject of further research. However, functionally these images behave identically when pulled.<\/p>\n<h1>Conclusion<\/h1>\n<p>Docker multi-platform support is a new area that is still developing. You need to worry about platforms if your dev platform is different from your hosting platform. Local storage currently does not support multi-platform images (but see <a href=\"https:\/\/docs.docker.com\/storage\/containerd\/\">https:\/\/docs.docker.com\/storage\/containerd\/<\/a>). Docker offers two ways to create multi-platform images on the server: manifests and <code>docker buildx<\/code> command. They produce slightly different results, but seem to be functionally equivalent. Most popular images on DockerHub support multiple platforms, when you pull, you get a version that is compatible with the local machine.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Invalid Exec Format They gave me a MacBook Pro at work. I built a docker image locally. I pushed it to Amazon cloud to run. I got &#8220;invalid exec format&#8221; <a href=\"https:\/\/ikriv.com\/blog\/?p=4966\" class=\"more-link\">[&hellip;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[4],"tags":[],"class_list":["entry","author-ikriv","post-4966","post","type-post","status-publish","format-standard","category-hack"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4966","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4966"}],"version-history":[{"count":1,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4966\/revisions"}],"predecessor-version":[{"id":4967,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4966\/revisions\/4967"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4966"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4966"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4966"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}