Build multi-architecture images
This document covers methods for building multi architecture images in Harness with or without Docker layer caching (DLC).
With DLC
If you're using self-hosted infrastructure, turn on the feature flag CI_ENABLE_DLC_SELF_HOSTED
. To enable this flag, contact Harness Support.
- Open your Build and Push an image to Docker Registry step.
- Select
Enable Docker Layer caching
. To learn more, go to Docker layer caching. - Open the Optional Configuration dropdown at the bottom of the step.
- Add a variable under Environment Variables.
- Enter
PLUGIN_PLATFORM
for your Key. - Enter your architectures as a comma separated list as your Value. For example,
linux/amd64,linux/arm64
.
Without DLC
If you're using Kubernetes infrastructure, turn on the feature flag CI_USE_BUILDX_ON_K8
. To enable this flag, contact Harness Support.
- Open your Build and Push an image to Docker Registry step.
- Open the Optional Configuration dropdown at the bottom of the step.
- Add two variables under Environment Variables.
- Enter
PLUGIN_PLATFORM
for your first Key. - Then, enter your architectures as a comma separated list as your Value. For example,
linux/amd64,linux/arm64
. - Enter
PLUGIN_BUILDER_DRIVER
for your second variable's Key. - Then, enter
docker-container
for your second variable's Value.
Deprecated Methods
Deprecated method of building multi-arch images on Kubernetes infrastructure
The following method for building multi-arch images is not recommended.
To build multi-architecture images in a CI pipeline, use a separate stage to build and push each architecture.
For example, the following pipeline has two stages. The two stages have similar components, but they differ according to the architecture of the image that the stage builds. Each stage has:
- A variation of a Kubernetes cluster build infrastructure. Notice that each stage uses a different Kubernetes cluster connector (
infrastructure.spec.connectorRef
) and other settings due to the different architecture requirements. - A Run step that prepares the Dockerfile.
- A Build and Push step that builds and uploads the image. If the images are uploaded to the same repository, use
tags
to differentiate them, such as1.0-linux-amd64
and1.0-linux-arm64
.
pipeline:
allowStageExecutions: true
projectIdentifier: default
orgIdentifier: default
identifier: CI_MultiArch
name: CI_MultiArch
tags:
CI: ""
properties:
ci:
codebase:
connectorRef: YOUR_CODEBASE_CONNECTOR_ID
repoName: YOUR_REPO_NAME
build: <+input>
stages:
- stage:
name: K8
identifier: upload
type: CI
spec:
cloneCodebase: true
infrastructure:
type: KubernetesDirect
spec:
connectorRef: k8Linux
namespace: <+input>
runAsUser: ""
automountServiceAccountToken: true
nodeSelector: {}
containerSecurityContext:
runAsUser: ""
os: Linux
execution:
steps:
- step:
type: Run
name: CreateDockerFile
identifier: CreateDockerFile
spec:
connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
image: alpine:latest
command: |-
touch harnessDockerfileui
cat > harnessDockerfileui <<- EOM
FROM alpine:latest AS dev-env
ARG foo
RUN echo "$foo bar"
ENTRYPOINT ["pwd"]
FROM alpine:latest AS release-env
ARG hello
RUN echo "$hello world"
ENTRYPOINT ["ls"]
EOM
cat harnessDockerfileui
resources:
limits:
memory: 100M
- step:
type: BuildAndPushDockerRegistry
name: DockerPushStep
identifier: DockerPushStep
spec:
connectorRef: YOUR_DOCKER_CONNECTOR_ID
repo: my-repo/ci-demo
tags:
- "1.0-linux-amd64"
dockerfile: harnessDockerfileui
target: dev-env
resources:
limits:
memory: 100M
- stage:
name: K8s Linux arm
identifier: CI_Golden_ARM
type: CI
spec:
cloneCodebase: true
infrastructure:
type: KubernetesDirect
spec:
connectorRef: k8sarm
namespace: ci-gold-arm-delegate
automountServiceAccountToken: true
tolerations:
- effect: NoSchedule
key: kubernetes.io/arch
operator: Equal
value: arm64
nodeSelector:
kubernetes.io/arch: arm64
os: Linux
execution:
steps:
- step:
type: Run
name: CreateDockerFile
identifier: CreateDockerFile
spec:
connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
image: alpine:latest
command: |-
touch harnessDockerfileui
cat > harnessDockerfileui <<- EOM
FROM alpine:latest AS dev-env
ARG foo
RUN echo "$foo bar"
ENTRYPOINT ["pwd"]
FROM alpine:latest AS release-env
ARG hello
RUN echo "$hello world"
ENTRYPOINT ["ls"]
EOM
cat harnessDockerfileui
resources:
limits:
memory: 100M
- step:
type: BuildAndPushDockerRegistry
name: DockerPushStep
identifier: DockerPushStep
spec:
connectorRef: YOUR_DOCKER_CONNECTOR_ID
repo: my-repo/ci-demo
tags:
- "1.0-linux-arm64"
dockerfile: harnessDockerfileui
target: dev-env
resources:
limits:
memory: 100M