In the previous post, I built a simple multiplatform container image. Now, we discuss how to customize the image contents depending on the target architecture.

When you build an image with precompiled libraries or your application needs different compilation instructions, Docker uses implicit arguments to let you know the target platform. I plan to walk you through the building of the DevOps "Swiss army knife" of a sort. This hypothetical set will demonstrate various types of installations by installing tools as below:

  • Ansible
  • Terraform
  • Helm
  • Kubernetes
  • AWS v2 Client

Let us begin with the first part of the Dockerfile

FROM --platform=${TARGETPLATFORM} python:slim
# Tools Versions
ARG  HELM_VER=v3.15.0
ARG  K8CL_VER=v1.30.1
# Builder Arguments

The declarative part defines six arguments, three of which are custom, to determine the tool version we will use for Helm, Terraform, and Kubernetes CLI, respectfully. The builder driver sets those arguments, indicating the target image platform, OS name, and processor architecture. For example, my OS (MacOS, M2) default values are:


The argument --platform in the FROM directive is redundant because the builder sets it for you by default.

Now, let's add the regular OS update and packaged installations.

# check Buidler Shell   
SHELL ["/bin/bash", "-c"]

RUN apt-get update; \
    apt-get install -y curl wget unzip && \
    python3 -m pip install --upgrade pip && \
    python3 -m pip install ansible

The same command runs the Ansible installation to save some space. Those commands are platform-agnostic and will be run in the respectful architectures during the image build.

Now, the fun begins. We are adding layers of precompiled utilities that match the target build platform.

# Terraform Install
RUN curl -L "${TRFM_VER}/terraform_${TRFM_VER}_${TARGETOS}_${TARGETARCH}.zip" -o
RUN curl -LO "${TRFM_VER}/terraform_${TRFM_VER}_SHA256SUMS"
RUN unzip
RUN install -o root -g root -m 0755 terraform /usr/local/bin/terraform
# Get latest kubectl
RUN curl -LO "${K8CL_VER}/bin/${TARGETPLATFORM}/kubectl"
RUN curl -LO "${K8CL_VER}/bin/${TARGETPLATFORM}/kubectl.sha256"
# Install helm
RUN curl -LO${HELM_VER}-${TARGETOS}-${TARGETARCH}.tar.gz  && \
tar -zxvf helm-${HELM_VER}-${TARGETOS}-${TARGETARCH}.tar.gz && \
mv ${TARGETOS}-${TARGETARCH}/helm /usr/local/bin/helm

The code above heavily uses Dockerfile arguments to download and install the right binary file for the target platform. However, not all vendors use the same language, and AWS command-line tools require name translation.

# Install AWS Client
RUN export AWSARCH=$(echo "${TARGETARCH}" | sed 's#arm#aarch#;s#amd#x86_#'); \
    curl${TARGETOS}-${AWSARCH}.zip -o && \
    unzip && \
    ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update && \
    rm -rf ./aws

CMD [/bin/bash]

Docker does not perform variable calculations, so I used BASH commands and syntax to translate the Docker architecture name into the AWS notation. It will build working images for MacOS and Linux/x86-64.

Let's recall the build command line from the previous example and build our image.

$ docker buildx build --load \
  --platform=linux/arm64,linux/amd64 \
  -t mikhailidim/devops-tools:0.1.0 .
Multiplatform image build in progress

A few takeaways:

  • As expected, the multiplatform image build is more complex but easy to learn.
  • You should be mindful of the target architecture and use builder arguments for the correct libraries and parameters.
  • The layer parameters may be non-trivial and require precalculation or shell script helpers to alter the building process.
  • Consider multi-stage builds to leave behind all unnecessary layers.