Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build a Docker image on a specific architecture with Docker Hub?

I have these automated builds on Docker Hub:

docker hub automated builds

And for reference, here are both Dockerfiles:

  • https://github.com/rfgamaral/docker-gphotos-uploader/blob/master/Dockerfile
  • https://github.com/rfgamaral/docker-gphotos-uploader/blob/master/Dockerfile.arm32v7

And here's the build log for the arm32v7 build:

Building in Docker Cloud's infrastructure...
Cloning into '.'...
Warning: Permanently added the RSA host key for IP address '192.30.253.113' to the list of known hosts.
Reset branch 'master'
Your branch is up-to-date with 'origin/master'.
Executing build hook...
Sending build context to Docker daemon 88.06kB
Step 1/17 : ARG ALPINE_VERSION="3.8"
Step 2/17 : ARG S6_OVERLAY_VERSION="1.21.7.0"
Step 3/17 : FROM golang:1.11-alpine${ALPINE_VERSION} AS builder
1.11-alpine3.8: Pulling from library/golang
169185f82c45: Pulling fs layer
34c29055ee42: Pulling fs layer
29802c64cdfc: Pulling fs layer
dd82873a5b09: Pulling fs layer
b711937b138a: Pulling fs layer
dd82873a5b09: Waiting
b711937b138a: Waiting
29802c64cdfc: Verifying Checksum
29802c64cdfc: Download complete
34c29055ee42: Verifying Checksum
34c29055ee42: Download complete
b711937b138a: Verifying Checksum
b711937b138a: Download complete
169185f82c45: Verifying Checksum
169185f82c45: Download complete
169185f82c45: Pull complete
34c29055ee42: Pull complete
29802c64cdfc: Pull complete
dd82873a5b09: Verifying Checksum
dd82873a5b09: Download complete
dd82873a5b09: Pull complete
b711937b138a: Pull complete
Digest: sha256:9657ef82d7ead12e0c88c7f4708e78b50c5fd3c1893ac0f2f0924ab98873aad8
Status: Downloaded newer image for golang:1.11-alpine3.8
---> be1230a1b343
Step 4/17 : RUN apk update && apk add --no-cache --virtual build-dependencies git && go get -u github.com/nmrshll/gphotos-uploader-cli/cmd/gphotos-uploader-cli && cd /go/src/github.com/nmrshll && rm -rf gphotos-uploader-cli && git clone https://github.com/rfgamaral/gphotos-uploader-cli.git --branch docker && rm -rf oauth2-noserver && git clone https://github.com/rfgamaral/oauth2-noserver.git --branch docker && cd gphotos-uploader-cli/cmd/gphotos-uploader-cli && GOOS=linux GOARCH=amd64 go build -ldflags='-w -s' -o /go/bin/gphotos-uploader-cli && apk del build-dependencies
---> Running in 79c1d68ad8b0
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
v3.8.2-53-g53558ad6fc [http://dl-cdn.alpinelinux.org/alpine/v3.8/main]
v3.8.2-53-g53558ad6fc [http://dl-cdn.alpinelinux.org/alpine/v3.8/community]
OK: 9544 distinct packages available
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/7) Installing nghttp2-libs (1.32.0-r0)
(2/7) Installing libssh2 (1.8.0-r3)
(3/7) Installing libcurl (7.61.1-r1)
(4/7) Installing expat (2.2.5-r0)
(5/7) Installing pcre2 (10.31-r0)
(6/7) Installing git (2.18.1-r0)
(7/7) Installing build-dependencies (0)
Executing busybox-1.28.4-r3.trigger
OK: 19 MiB in 21 packages
Cloning into 'gphotos-uploader-cli'...
Cloning into 'oauth2-noserver'...
(1/7) Purging build-dependencies (0)
(2/7) Purging git (2.18.1-r0)
(3/7) Purging libcurl (7.61.1-r1)
(4/7) Purging nghttp2-libs (1.32.0-r0)
(5/7) Purging libssh2 (1.8.0-r3)
(6/7) Purging expat (2.2.5-r0)
(7/7) Purging pcre2 (10.31-r0)
Executing busybox-1.28.4-r3.trigger
OK: 5 MiB in 14 packages
Removing intermediate container 79c1d68ad8b0
---> 17b221a9ee49
Step 5/17 : FROM amd64/alpine:${ALPINE_VERSION}
3.8: Pulling from amd64/alpine
169185f82c45: Already exists
Digest: sha256:616d0d0ff1583933ed10a7b3b4492899942016c0577d43a1c506c0aad8ab4da8
Status: Downloaded newer image for amd64/alpine:3.8
---> 491e0ff7a8d5
Step 6/17 : LABEL maintainer="[email protected]"
---> Running in e58b7fcdb220
Removing intermediate container e58b7fcdb220
---> c525e340a42d
Step 7/17 : ARG BUILD_DATE
---> Running in 0a9417e1adcd
Removing intermediate container 0a9417e1adcd
---> 9f6c69125803
Step 8/17 : ARG S6_OVERLAY_VERSION
---> Running in 93a8cd6996b9
Removing intermediate container 93a8cd6996b9
---> 6034d93430da
Step 9/17 : ARG VCS_REF
---> Running in 8f6fc7d81c71
Removing intermediate container 8f6fc7d81c71
---> 74180d38dbc0
Step 10/17 : LABEL org.label-schema.build-date="${BUILD_DATE}" org.label-schema.description="Mass upload media folders to your Google Photos account with this Docker image." org.label-schema.name="rfgamaral/gphotos-uploader" org.label-schema.schema-version="1.0" org.label-schema.vcs-ref="${VCS_REF}" org.label-schema.vcs-url="https://github.com/rfgamaral/docker-gphotos-uploader.git"
---> Running in 08cf19c6f46a
Removing intermediate container 08cf19c6f46a
---> 106104e2ef17
Step 11/17 : ENV GPU_SCHEDULE="0 */8 * * *"
---> Running in edea63c892b9
Removing intermediate container edea63c892b9
---> d69ae92742d2
Step 12/17 : ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-amd64.tar.gz /tmp/
---> a7448cda217f
Step 13/17 : RUN apk update && apk add --no-cache curl && tar xzf /tmp/s6-overlay-amd64.tar.gz -C / && rm -rf /tmp/*
---> Running in 5d9ee7e3941d
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
v3.8.2-53-g53558ad6fc [http://dl-cdn.alpinelinux.org/alpine/v3.8/main]
v3.8.2-53-g53558ad6fc [http://dl-cdn.alpinelinux.org/alpine/v3.8/community]
OK: 9544 distinct packages available
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ca-certificates (20171114-r3)
(2/5) Installing nghttp2-libs (1.32.0-r0)
(3/5) Installing libssh2 (1.8.0-r3)
(4/5) Installing libcurl (7.61.1-r1)
(5/5) Installing curl (7.61.1-r1)
Executing busybox-1.28.4-r3.trigger
Executing ca-certificates-20171114-r3.trigger
OK: 6 MiB in 18 packages
Removing intermediate container 5d9ee7e3941d
---> 14fc569893de
Step 14/17 : COPY --from=builder /go/bin/gphotos-uploader-cli /usr/local/bin/gphotos-uploader-cli
---> 32fa657de51c
Step 15/17 : COPY rootfs/ /
---> 1639f6e639b4
Step 16/17 : VOLUME ["/config", "/photos"]
---> Running in 440d1d13cd60
Removing intermediate container 440d1d13cd60
---> fd730f9c1ebb
Step 17/17 : ENTRYPOINT ["/init"]
---> Running in 197c889006b2
Removing intermediate container 197c889006b2
---> 4e66fc7b481d
Successfully built 4e66fc7b481d
Successfully tagged rfgamaral/gphotos-uploader:latest-arm32v7
Pushing index.docker.io/rfgamaral/gphotos-uploader:latest-arm32v7...
Done!
Build finished

As you can see, the log only references amd64 and not arm32v7 or armhf which are clearly there in the Dockerfile.arm32v7 file. Why is Docker Hub changing:

  • arm32v7/alpine:${ALPINE_VERSION} to amd64/alpine:${ALPINE_VERSION}
  • s6-overlay-armhf.tar.gz to s6-overlay-amd64.tar.gz
  • GOARCH=arm GOARM=7 to GOARCH=amd64

It's like it's using Dockerfile instead of Dockerfile.arm32v7 but a) that's not what I've picked for "Dockerfile location" in the automated builds configuration and b) Docker Hub Builds section as a "Dockerfile" tab to show the Dockerfile used for the build and it displays the right one.

Is this a bug on Docker Hub or am I doing something wrong?

like image 308
rfgamaral Avatar asked Feb 07 '19 16:02

rfgamaral


People also ask

Can a docker image run on any architecture?

Docker images can support multiple architectures, which means that a single image may contain variants for different architectures, and sometimes for different operating systems, such as Windows.

How do I create a docker image for multiple architectures?

You can build a multi-arch image by creating the individual images for each architecture, pushing them to Docker Hub, and entering docker manifest to combine them within a tagged manifest list. You can then push the manifest list to Docker Hub.

Is Docker Hub used for building docker images?

Docker Hub provides the following major features: Repositories: Push and pull container images. Teams & Organizations: Manage access to private repositories of container images. Docker Official Images: Pull and use high-quality container images provided by Docker.

How do I deploy an image to Docker Hub?

To push an image to Docker Hub, you must first name your local image using your Docker Hub username and the repository name that you created through Docker Hub on the web. You can add multiple images to a repository by adding a specific :<tag> to them (for example docs/base:testing ).


2 Answers

I solved my own issue after a bit of research... First, I was making a stupid mistake and second, I was forgetting a very important thing. Here's how I fixed my issues:

The Stupid Mistake

Although I specified different Dockerfiles for each automated build, I also had a build hook which was overwriting the docker build command and it was defaulting to Dockerfile for all builds instead of picking the right file.

Fixed build hook file:

#!/bin/bash

docker build \
    --file "${DOCKERFILE_PATH}" \
    --build-arg BUILD_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
    --build-arg VCS_REF="$(git rev-parse --short HEAD)" \
    --tag "$IMAGE_NAME" \
    .

The Important Thing

Like @JanGaraj mentioned on his answer, Docker Hub runs on amd64 so it can't run binaries for other architectures. How does one build multi-arch images with Docker Hub Automated Builds? With the help of qemu-user-static and more hooks. I found the answer on this GitHub issue but I'll post here the complete answer to my specific use case:

My sample project tree:

.
├── Dockerfile
├── Dockerfile.aarch64
├── Dockerfile.armhf
└── hooks
    ├── build
    ├── post_checkout
    └── pre_build

The post_checkout hook file:

#!/bin/bash

BUILD_ARCH=$(echo "${DOCKERFILE_PATH}" | cut -d '.' -f 2)

[ "${BUILD_ARCH}" == "Dockerfile" ] && \
    { echo 'qemu-user-static: Download not required for current arch'; exit 0; }

QEMU_USER_STATIC_ARCH=$([ "${BUILD_ARCH}" == "armhf" ] && echo "${BUILD_ARCH::-2}" || echo "${BUILD_ARCH}")
QEMU_USER_STATIC_DOWNLOAD_URL="https://github.com/multiarch/qemu-user-static/releases/download"
QEMU_USER_STATIC_LATEST_TAG=$(curl -s https://api.github.com/repos/multiarch/qemu-user-static/tags \
    | grep 'name.*v[0-9]' \
    | head -n 1 \
    | cut -d '"' -f 4)

curl -SL "${QEMU_USER_STATIC_DOWNLOAD_URL}/${QEMU_USER_STATIC_LATEST_TAG}/x86_64_qemu-${QEMU_USER_STATIC_ARCH}-static.tar.gz" \
    | tar xzv

The pre_build hook file:

#!/bin/bash

BUILD_ARCH=$(echo "${DOCKERFILE_PATH}" | cut -d '.' -f 2)

[ "${BUILD_ARCH}" == "Dockerfile" ] && \
    { echo 'qemu-user-static: Registration not required for current arch'; exit 0; }

docker run --rm --privileged multiarch/qemu-user-static:register --reset

The Dockerfile file:

FROM amd64/alpine:3.8
(...)

The Dockerfile.aarch64 file:

FROM arm64v8/alpine:3.8
COPY qemu-aarch64-static /usr/bin/
(...)

The Dockerfile.armhf file:

FROM arm32v6/alpine:3.8
COPY qemu-arm-static /usr/bin/
(...)

That's it!

like image 147
rfgamaral Avatar answered Oct 05 '22 15:10

rfgamaral


Nowadays you can also use docker buildx to build for different architectures without having to maintain a separate Dockerfile for each. Building Multi-Architecture Docker Images With Buildx has a description of how that works. Basically, you

  1. register your QEMU simulator in binfmt_misc on the host with the fix-binary flag so that it can run in the container without having to copy it into the container file system
  2. use docker buildx build --platform linux/arm/v7 ... to instruct the builder to build for an architecture different from your build host's
like image 45
Artur Klauser Avatar answered Oct 05 '22 17:10

Artur Klauser