亚马逊AWS官方博客

基于Amazon Code系列的多架构容器镜像构建流水线

一、多架构镜像概述

Amazon Graviton处理器是Amazon基于64位Arm Neoverse核心定制化的自研处理器。相比于传统的64位X86架构处理器,Graviton实例具有更高的性价比,对于追求灵活性和极致成本节省的用户来说非常适合。

容器化本身是共享主机内核的,这意味着在容器内运行的代码必须与主机的架构兼容;也就是说,X86容器只能运行在X86主机上,而Arm容器只能运行在Arm主机上。多架构镜像通过将同一应用程序的多个变体打包到单个镜像中的方式来解决此类问题。当您将多平台镜像推送到镜像仓库时,镜像仓库会存储清单列表(manifest list)以及所有单独的清单(manifest)。当您拉取镜像时,镜像仓库会返回清单列表,Docker 会根据主机的架构自动选择正确的版本。

Amazon ECR容器镜像仓库已经支持多架构镜像(X86 and Arm)。我们可以通过docker manifest inspect的命令来查看存储在ECR的容器镜像manifest list:

如果使用EC2实例来构建多架构镜像,效率低下,且需要大量重复动作,不利于开发过程中的快速迭代需求。本文中,我们介绍了使用Amazon Code系列服务自动化构建多架构容器镜像的方法,可以标准化,并且能大幅提升镜像构建效率。

二、Docker Buildx简介

Docker Buildx 是一个 Docker CLI 插件,用于通过 BuildKit 扩展构建功能。

从Docker 20.10版本开始,docker buildx已内置,不需要额外安装。

Docker Buildx 是 Docker 官方提供的扩展构建工具,基于 BuildKit 引擎,支持比传统 docker build 更强大的功能。它在多架构镜像构建方面尤为突出,主要特性包括:

  • 多架构镜像构建:支持一次构建同时生成并推送 linux/amd64、arm64、ppc64le、s390x 等多架构镜像(manifest list)。
  • 跨架构构建能力:通过 binfmt_misc + QEMU,可在单一架构主机上模拟构建和测试其他架构镜像。
  • 远程与分布式构建:支持创建多个 builder,将构建任务分发到远程或集群节点以利用原生硬件加速。
  • 镜像管理与导出:提供 imagetools管理多架构 manifest,支持导出为 Docker、OCI 或 tar 等格式。
  • 高级构建特性:支持多阶段构建、构建缓存(本地/远程)、构建参数及多种构建上下文来源。

在本文中,我们将通过在CodeBuild中使用docker buildx的方式,来构建多架构容器镜像并推送到ECR镜像仓库。这个步骤是通过buildspec.yml文件来定义的。

接下来,我们将介绍完整的流水线步骤。

三、使用场景

  • 场景一:X86 Arm 混合算力的多架构部署

为兼顾性能、成本与稳定性,企业可构建包含 X86 与 Arm 的多架构算力资源池,使业务服务在不同架构节点上统一运行。该模式在保障 X86 稳定性和性能的同时,引入 Arm 的成本优势,并支持通过容量规划实现业务在不同架构间的无感迁移。

以 Amazon EKS 为例,业务可通过一个 Service 关联运行在 X86 和 Arm 节点上的两个 Deployment,且统一使用同一容器镜像 URI。部署时,节点会根据自身架构自动拉取对应架构的镜像,无需业务侧改造。

  • 场景二:核心业务发布频繁,对一致性和回滚要求极高

对于发布频繁、对一致性和回滚要求极高的核心业务,如果在混合节点池中仍然使用多套镜像,在应用发布和回滚过程中将面临较高的版本管理复杂度。

通过采用统一的多架构镜像,可以屏蔽底层算力差异,显著降低版本管理成本,从而更好地保障应用的稳定性和一致性。

  • 场景三:业务需要支持多区域或多集群部署

在多区域或多集群部署场景下,不同地域和集群可用的算力架构可能存在差异。

通过使用统一的多架构镜像,业务可以在各区域和集群中保持一致的部署方式和发布节奏,避免因底层架构差异带来的部署复杂性。

该模式有助于降低跨区域运维成本,提升整体交付效率和系统稳定性。

四、方案简介

如上图所示,方案描述如下:

1、EC2 Linux实例,用作操作终端;

    • 安装Docker, Git等工具软件;
    • 创建IAM Role,附加必要的IAM策略,并绑定到EC2实例;

2、ECR镜像仓库,名为multiarch-app;

3、EC2实例上传示例应用代码(包含yml), 并提交到CodeCommit;

4、创建自动化构建流水线:

    • CodeBuild中创建构建项目, 项目中以buidspec的方式来定义了应用代码打包成容器镜像的完整过程;
    • CodePipeline中创建包含CodeCommit代码仓库和CodeBuild构建项目的自动化管道,实现代码提交后自动触发构建,并上传到ECR仓库。

5、流水线自动构建,或通过提交代码到CodeCommit触发新的构建;

6、使用X86和Arm EC2实例来测试镜像是否可用。

五、代码说明

本文中使用的代码分为三部分:

1、应用代码:

    • js:Node.js应用主入口文件,负责启动服务并定义核心业务逻辑;
    • json:项目的配置清单,定义应用信息、启动脚本及依赖的库;
    • package-lock.json:锁定项目所有依赖的精确版本,确保环境一致;

2、Docker镜像构建配置文件

    • Dockerfile:定义了应用的Docker构建流程;
    • .dockerignore:Docker构建镜像的忽略规则,用于排除不必要的文件;

3、BuildSpec:

    • buildspec.yml:用于定义CodeBuild构建流程,指定安装、构建打包应用的各个阶段和命令。

文件结构如下图所示:

六、环境准备

1、创建Linux实例

在控制台中创建一台Linux实例,用作操作终端,该终端要求如下:

    • AMI:Amazon Linux 2023 kernel-6.1 AMI
    • 实例类型:medium
    • 配置存储:20GB

创建完成后,需要给该机器绑定IAM角色,该角色需要包含必要的IAM权限。这里我们直接附加Amazon托管IAM策略:AmazonEC2ContainerRegistryFullAccess

说明:本文中的所有命令行操作均在这台Linux操作终端上执行。

2、安装Git和Docker

使用如下命令在操作终端上安装Git和Docker:

sudo yum update -y
sudo yum install -y git
sudo yum install -y docker
sudo service docker start
sudo usermod -a -G docker ec2-user

退出当前会话并重新ssh连接到实例, 执行如下命令确认当前用户(ec2-user)具有执行Docker命令的权限:

docker ps

3、创建ECR镜像仓库

使用如下命令在ECR中创建镜像仓库:

aws ecr create-repository --repository-name multiarch-app \
--region cn-northwest-1

七、创建示例应用代码

1、创建名为multiarch-build-repo的目录

mkdir multiarch-build-repo & cd multiarch-build-repo

2、创建package.json

{
  "name": "multi-arch-app",
  "version": "1.0.0",
  "description": "Node.js on Docker"
}

3、创建package-lock.json

npm install

4、创建app.js

const http = require('http');
const port = 3000;
const server = http.createServer((req, res) => {
      res.statusCode = 200;
      res.setHeader('Content-Type', 'text/plain');
      res.end(`Hello World! This web app is running on ${process.arch} processor architecture` );
});
server.listen(port, () => {
      console.log(`Server running on ${process.arch} architecture.`);
});

5、创建Dockerfile

FROM public.ecr.aws/docker/library/node:18.20-alpine

WORKDIR /usr/src/app

COPY package*.json app.js ./
RUN npm install --production

EXPOSE 3000
CMD ["node", "app.js"]

6、创建.dockerignore

node_modules
npm-debug.log

7、创建buildspec.yml文件,这个文件记录了镜像构建的关键过程:

    • docker run –privileged tonistiigi/binfmt:latest –install all:这条命令通过 binfmt_misc + QEMU,为宿主机启用跨 CPU 架构支持,是 Docker buildx 多架构构建的前置条件;
    • docker buildx create –name multiarch-builder –use:创建并启用buildx构建器;
    • docker buildx build –platform linux/amd64,linux/arm64 –tag $IMAGE_URI –push . :一次性构建并推送amd64和arm64的多架构 Docker 镜像。

说明:buildx默认使用的是dockerhub镜像,在中国区域可能会遇到镜像无法拉取的问题,可以考虑在buildspec.yml中修改容器镜像地址,或者将镜像下载到本地后上传到私有ECR仓库,来加速构建过程。

version: 0.2

phases:
  pre_build:
    commands:
      - echo "Installing QEMU"
      - docker version
      - echo "Logging in to Amazon ECR"
      - aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com.cn      
      - docker run --privileged --rm tonistiigi/binfmt:latest --install all
      - docker buildx create --name multiarch-builder --use
      - docker buildx inspect --bootstrap
  
  build:
    commands:
      - echo "Building multi-architecture Docker image"
      -IMAGE_URI=$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com.cn/$IMAGE_REPO_NAME:$IMAGE_TAG
      - docker buildx build --platform linux/amd64,linux/arm64 --tag $IMAGE_URI --push .
      - echo "Inspecting multi-architecture image"
      - docker buildx imagetools inspect $IMAGE_URI
  
  post_build:
    commands:
      - echo "Build completed on $(date)"
      - echo "Image URI - $IMAGE_URI"
      - printf '{"ImageURI":"%s"}' $IMAGE_URI > imageDetail.json

artifacts:
  files:
    - imageDetail.json

八、代码提交到CodeCommit仓库

1、在CodeCommit中创建名为multiarch-build-repo的仓库:

2、在IAM用户下,创建HTTPS Git凭证,推送代码会使用该凭证:

3、在操作终端执行如下命令,将代码文件提交到CodeCommit仓库:

git init
git add .
git commit -m "first commit"
git remote add origin https://git-codecommit.$REGION.amazonaws.com.cn/v1/repos/multiarch-build-repo
git branch -M main
git push -u origin main

4、后续如果代码有变更,只需要重复如下命令继续提交即可:

git add .
git commit -m “some comments”
git push -u origin main

 

 

九、CodeBuild中创建构建项目

1、构建项目按照如下配置创建:

    • 项目名称:multiarch-build-project
    • 源提供商:Amazon CodeCommit; 存储库:multiarch-build-repo
    • 构建环境,选择托管镜像:Amazon Linux 2 Standard
    • 服务角色,选择“新服务角色”,需要注意:该角色需要额外添加ECR访问策略,否则构建时会报错:
    • 其他配置,参考下图完成:
    • 构建规范,选择“使用buildspec文件”
    • 构件类型,选择“无构件”;其他保持默认。

构建项目创建完成:

十、CodePipeline创建自动化管道

1、选择管道设置,执行模式选择“已排队”:

2、源阶段配置,选择CodeCommit中已创建的仓库:

3、构建阶段配置,选择CodeBuild中已创建的项目:

4、添加部署阶段,由于我们推送到ECR,选择“跳过部署阶段”即可:

5、最后创建管道成功,可以看到管道会自动执行构建:

6、构建过程可以查看日志,如果报错也会在日志中显示出来:

接下来我们就可以使用唯一的容器镜像URI来部署容器应用了。

十一、镜像测试

1、使用X86实例测试

    • 创建1台EC2 X86 Linux实例,满足如下要求:
      • 实例规格选择micro;
      • 实例AMI:Amazon Linux 2023 kernel-6.1 AMI,架构:64位(x86);
      • 实例需绑定IAM角色,该角色绑定IAM托管策略:AmazonEC2ContainerRegistryFullAccess;
      • 实例安全组入站规则开放HTTP 80端口。
    • 实例安装Docker;
    • 拉取容器镜像;
# 配置环境变量
ACCOUNT_ID=01234567890
REGION=cn-northwest-1

# 登录ECR镜像仓库
aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com.cn

# 拉取容器镜像
docker pull ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com.cn/ multiarch-app:latest
    • 以后台模式(detached)运行测试容器
docker run -dp 80:3000 ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com.cn/multiarch-app:latest
    • 浏览器打开实例80端口(http://instance-public-ip)测试

2、使用Arm实例测试

    • 创建1台EC2 Arm Linux实例,满足如下要求:
      • 实例规格选择micro;
      • 其他配置与X86实例相同。
    • 重复1小节中的2-6步骤,并使用实例80端口测试

十二、总结

本文采用了Amazon Code系列服务来自动化构建多架构容器镜像,这套方案具备成本低廉、容易上手、按需扩展等优点,有利于开发人员快速迭代应用版本。 CodeCommit, CodeBuild, CodePipeline等服务是全托管服务,客户无需提前预置底层基础设施,按需使用并付费,是构建自动化流水线的不二之选。

不过,本方案也存在着一些不足之处,例如更多应用类型、多容器同时并行构建、构建时间压缩等等,这些将在未来持续优化,并迭代到后面的版本中。

*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。

本篇作者

戴涛

13年+ IT专业服务经验,云计算项目经验丰富,擅长系统架构、容器/云原生领域。


AWS 架构师中心: 云端创新的引领者

探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用