Building multi-architecture container images for AWS Graviton

Andreas Wittig – 01 Jun 2022

What do my MacBook Pro and my container workload running on ECS and Fargate have in common? They both run amazingly well on the ARM processor architecture. However, building Docker images for Apple Silicon and AWS Graviton is challenging. Because a container image made for the X86_64 architecture -which is good old Intel and AMD processors- does not run on the ARM processor architecture out of the box. Therefore, you will learn how to build multi-architecture images for X86_64 as well as for ARM64 in the following.

Building multi-architecture container images for AWS Graviton

Watch the following video to learn how to build multi-architecture images locally and with AWS CodeBuild. Besides that, we will show you how to deploy a container image to ECS and Fargate running on X86_64 and ARM64 (AWS Graviton).

You will find the commands and code snippets from the video in the following.

The commands and code snippets are excerpts of our book Rapid Docker on AWS which includes complete examples for PHP, Ruby, Python, Java, and Node.js web applications.

How to build a multi-architecture Docker image

To build a multi-architecture image, start with creating an ECR repository.

aws ecr create-repository --repository-name nodejs-express
aws ecr get-login-password | docker login --username AWS --password-stdin 486555357186.dkr.ecr.eu-central-1.amazonaws.com

Next, create a new builder instance.

docker buildx create --use

The following docker buildx build command builds two container images, creates a manifest, and pushes all of that to the ECR repository.

docker buildx build \
--platform linux/amd64,linux/arm64 \
--push -t 486555357186.dkr.ecr.eu-central-1.amazonaws.com/nodejs-express \
-f docker/Dockerfile .

To test the image on your local machine, use the docker run command.

docker run 486555357186.dkr.ecr.eu-central-1.amazonaws.com/nodejs-express

That’s how to build a multi-architecture image locally. But how to do so as part of a CI/CD pipeline?


Looking for a new challenge?

  • tecRacer

    Cloud Consultant

    tecRacer • Premier AWS Consulting Partner • Germany, Austria, Portugal, and Switzerland
    AWS only Infrastructure as Code EC2 Containers Serverless
  • tecRacer

    Cloud Migration Specialist

    tecRacer • Premier AWS Consulting Partner • Germany, Austria, Portugal, and Switzerland
    Lift&Shift Transformation EC2 RDS VPC

How to build a multi-arch container image with AWS CodeBuild

The following snippets give you an idea of how to build a multi-architecture image with the help of AWS CodeBuild as part of a deployment pipeline.

The following snippet shows a CloudFormation resource to configure a CodeBuild project. Check out the comments for explanations.

Project:
Type: 'AWS::CodeBuild::Project'
Properties:
Artifacts:
Type: NO_ARTIFACTS
Environment:
ComputeType: BUILD_GENERAL1_SMALL # The build job itself runs on X86_64 but builds a multi-arch image
EnvironmentVariables: # Some environment variables needed for the build
- Name: ACCOUNT_ID
Type: PLAINTEXT
Value: !Ref 'AWS::AccountId'
- Name: REGION
Type: PLAINTEXT
Value: !Ref 'AWS::Region'
# ...
Image: 'aws/codebuild/standard:5.0' # CodeBuild provides Docker images to run the build
PrivilegedMode: true # Required to build Docker images
Type: LINUX_CONTAINER
LogsConfig:
CloudWatchLogs:
GroupName: !Ref ProjectLogGroup
Status: ENABLED
Name: !Ref 'AWS::StackName'
ServiceRole: !GetAtt 'ProjectRole.Arn'
Source: # Run the build job for each commit pushed to your CodeCommit repository
Location: !Sub 'https://git-codecommit.${AWS::Region}.amazonaws.com/v1/repos/${CodeCommitRepositoryName}'
Type: CODECOMMIT
TimeoutInMinutes: 45

The buildspec.yml file is used to define the build job. The aws/codebuild/standard:5.0 does not ship with the buildx plugin. That’s why we need to add the plugin during the install phase.

version: '0.2'
phases:
install:
commands:
- 'echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin'
- wget https://github.com/docker/buildx/releases/download/v0.8.2/buildx-v0.8.2.linux-amd64
- mkdir -p ~/.docker/cli-plugins
- mv buildx-v0.8.2.linux-amd64 ~/.docker/cli-plugins/docker-buildx
- chmod a+rx ~/.docker/cli-plugins/docker-buildx
- docker run --privileged --rm tonistiigi/binfmt --install arm64,amd64
pre_build:
commands:
- 'aws ecr get-login-password | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com'
- 'echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin'
build:
commands:
- 'bash build.sh'

Last but not least, the build.sh script uses the same commands that we used to build a multi-architecture container image locally.

#!/bin/bash

set -e

NAME_TAG="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/\
${REPO_NAME}:${CODEBUILD_RESOLVED_SOURCE_VERSION}"
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 \
--push -t "${NAME_TAG}" -f docker/Dockerfile .

As you can now build multi-architecture images locally and with CodeBuild, one step is missing: deploying the image to ECS and Fargate.

How to run X86_64 and ARM64 container images on ECS and Fargate

The following snippet shows a CloudFormation template using cfn-modules to deploy an ECS service.

AppService:
Type: 'AWS::CloudFormation::Stack'
Properties:
Parameters:
VpcModule: !GetAtt 'Vpc.Outputs.StackName'
ClusterModule: !GetAtt 'Cluster.Outputs.StackName'
TargetModule: !GetAtt 'AppTarget.Outputs.StackName'
AlertingModule: !GetAtt 'Alerting.Outputs.StackName'
ClientSgModule1: !GetAtt 'AuroraServerlessClientSg.Outputs.StackName'
AppImage: !Ref AppImage
AppPort: '8080'
# ...
Cpu: '0.25'
Memory: '0.5'
DesiredCount: '2'
MaxCapacity: '4'
MinCapacity: '2'
LogsRetentionInDays: '14'
CpuArchitecture: 'ARM64' # or X86_64
TemplateURL: './node_modules/@cfn-modules/fargate-service/module.yml'

Unfortunately, the AWS Management Console does not show whether a container runs on X86_64 or ARM64. Use the following AWS CLI command to fetch more detailed information about a task, including the processor architecture.

aws ecs describe-tasks --tasks 122e8587c57c43f38ae1c5618c656624 \
--cluster nodejs-express-Cluster-1PM1YCI3HOWAH-Cluster-ZIVSeEmXEi1M

That’s it. Enjoy the power and efficiency of the ARM processor architecture!

Become a cloudonaut supporter

Andreas Wittig

Andreas Wittig ( Email, Twitter, or LinkedIn )

We launched the cloudonaut blog in 2015. Since then, we have published 350 articles, 45 podcast episodes, and 38 videos. It's all free and means a lot of work in our spare time. We enjoy sharing our AWS knowledge with you.

Please support us

Have you learned something new by reading, listening, or watching our content? With your help, we can spend enough time to keep publishing great content in the future. Learn more

$
Amount must be a multriply of 5. E.g, 5, 10, 15.

Thanks to Alan Leech, Alex DeBrie, ANTHONY RAITI, Christopher Hipwell, Jaap-Jan Frans, Jason Yorty, Jeff Finley, Jens Gehring, jhoadley, Johannes Grumböck, John Culkin, Jonas Mellquist, Juraj Martinka, Kamil Oboril, Ken Snyder, Ross Mohan, Ross Mohan, sam onaga, Satyendra Sharma, Shawn Tolidano, Thorsten Hoeger, Todd Valentine, Victor Grenu, and all anonymous supporters for your help! We also want to thank all supporters who purchased a cloudonaut t-shirt.