概述
- 介绍如何通过 Docker 的
buildx
工具实现跨架构镜像的构建,并配置 GitLab CI/CD 流水线以支持多架构镜像的自动化构建、测试和推送。内容包括:
- 创建支持多架构的 Docker Builder
- 持久化相关配置,确保重启后 Builder 依然可用
- 在 GitLab CI/CD 中配置多架构镜像的构建、测试和推送任务的示例,能够轻松构建适用于不同架构(如
amd64
、arm64
等)的 Docker 镜像,并实现自动化部署流程。
操作步骤
创建 Builder
拉取必要镜像
docker pull multiarch/qemu-user-static:latest
docker pull moby/buildkit:buildx-stable-1
启用多架构支持
docker run --rm --privileged multiarch/qemu-user-static:latest --reset -p yes
创建并激活 Builder
docker buildx create --name archbuilder --use
docker buildx use archbuilder
docker buildx inspect --bootstrap
持久化配置
- 修改
/etc/docker/daemon.json
,增加以下内容:
{
"features": {
"buildkit": true
}
}
- 重启 Docker 服务:
systemctl daemon-reload
systemctl restart docker
持久化设置
环境变量持久化
echo "export DOCKER_BUILDKIT=1" >> /root/.bashrc
配置 Systemd 服务
- 创建脚本目录:
mkdir -p /data/buildx
touch /data/buildx/buildx.sh
- 创建 Systemd 服务文件:
touch /etc/systemd/system/buildx.service
- 编辑
buildx.sh
脚本:
#!/bin/bash
# 设置环境变量
export DOCKER_BUILDKIT=1
# 启用多架构支持
docker run --rm --privileged multiarch/qemu-user-static:latest --reset -p yes
# 检查 archbuilder 是否存在
archbuilder_exists=$(docker buildx ls | grep archbuilder)
if [[ -z "$archbuilder_exists" ]]; then
echo "Docker buildx archbuilder not found. Creating archbuilder..."
docker buildx create --name archbuilder --use
else
echo "Docker buildx archbuilder already exists."
fi
docker buildx use archbuilder
docker buildx inspect --bootstrap
- 编辑
buildx.service
文件:
[Unit]
Description=Create Docker buildx Archbuilder
After=docker.service
[Service]
Type=oneshot
ExecStart=/bin/bash /data/buildx/buildx.sh
ExecStop=/bin/bash -c "docker buildx rm archbuilder || true"
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
- 启动并启用服务:
systemctl daemon-reload
systemctl enable buildx
systemctl start buildx
示例:GitLab CI/CD 配置
- 提交代码时,commit关键字中包含"镜像构建"这四个关键字即可触发Gitlab流水线
- 支持传到多个仓库,需要在Gitlab对应组织下,设置-CI/CD-变量中补充对应的仓库变量
- Dockhub
DOCKERHUB_USER
:dockerhub仓库登录用户名
DOCKERHUB_PASSWORD
:dockerhub仓库登录密码
- 阿里云容器镜像服务/ACR
ALIYUN_USER
: 阿里容器镜像仓库登录用户名
ALIYUN_PASSWORD
: 阿里容器镜像仓库登录密码
- 华为容器镜像服务/SWR
HW_USER
: 华为容器镜像仓库登录用户名
HW_PASSWORD
: 华为阿里容器镜像仓库登录密码
stages:
- build
- push
# 变量
variables:
# 镜像构建后的tag,置空则使用当前日期作为tag
VERSION: ""
# 置空则为amd64,可选为all(双架构)、amd64(x86平台)、arm64
ARCH: "amd64"
# 仅AMD架构的时候是否可以单独上传
AMD_PUSH: "true"
# 上传的镜像仓库,可选为dockerhub、aliyun、huawei
PUSH_REPO: "dockerhub aliyun huawei"
# 上传的仓库与镜像名称,假设仓库为test,镜像为testimg
IMAGE_NAME: "test/testimg"
# 镜像构建阶段
build_job:
stage: build
tags:
- build
variables:
# git clone仅克隆当前单层代码,加速代码拉取
GIT_DEPTH: "1"
rules:
# 仅在当前分支提交时message包含"镜像构建"时触发
- if: "$CI_COMMIT_MESSAGE =~ /镜像构建/"
when: on_success
- when: never
script:
- |
if [ -z "$VERSION" ]; then
export VERSION=$(date +%Y%m%d)
echo "当前默认版本为:$VERSION"
else
echo "手动设置版本为: $VERSION"
fi
if [ -z "$ARCH" ]; then
export ARCH="amd64"
echo "当前默认架构为:$ARCH"
else
echo "手动设置架构为: $ARCH"
fi
if [ ${ARCH} == "all" ]; then
echo "构建AMD镜像: $IMAGE_NAME:$VERSION-amd64"
echo "----------------------------------------"
docker build -t $IMAGE_NAME:$VERSION-amd64 --build-arg ARCH=amd64 .
echo "----------------------------------------"
echo "构建ARM镜像: $IMAGE_NAME:$VERSION-arm64"
docker buildx build --platform linux/arm64 -t $IMAGE_NAME:$VERSION-arm64 --build-arg ARCH=arm64 .
elif [ ${ARCH} == "arm64" ]; then
echo "构建ARM镜像: $IMAGE_NAME:$VERSION-arm64"
docker buildx build --platform linux/arm64 -t $IMAGE_NAME:$VERSION-arm64 --build-arg ARCH=arm64 .
else
echo "构建AMD镜像: $IMAGE_NAME:$VERSION-amd64"
docker build -t $IMAGE_NAME:$VERSION-amd64 --build-arg ARCH=amd64 .
fi
echo "export VERSION=$VERSION" > env.sh
echo "export ARCH=$ARCH" >> env.sh
artifacts:
paths:
- env.sh
expire_in: 5 minutes
# 镜像推送阶段
push_job:
stage: push
tags:
- build
dependencies:
- build_job
rules:
- if: "$CI_COMMIT_MESSAGE =~ /镜像构建/"
when: on_success
- when: never
script:
- |
source env.sh
echo "当前镜像版本为:$VERSION"
echo "当前镜像架构为:$ARCH"
echo "登录Dockerhub"
func_amd64(){
echo "上传: $IMAGE_NAME:$VERSION-amd64"
docker tag $IMAGE_NAME:$VERSION-amd64 $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-amd64
docker push $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-amd64
if [ ${AMD_PUSH} == "true" ]; then
echo "单架构上传: $IMAGE_NAME:latest"
docker tag $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-amd64 $DOCKER_REGISTRY/$IMAGE_NAME:latest
docker push $DOCKER_REGISTRY/$IMAGE_NAME:latest
fi
}
func_arm64(){
echo "上传: $IMAGE_NAME:$VERSION-arm64"
docker tag $IMAGE_NAME:$VERSION-arm64 $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-arm64
docker push $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-arm64
}
func_mix(){
echo "上传双架构镜像:$IMAGE_NAME:$VERSION"
docker manifest create $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION --amend $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-amd64 --amend $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-arm64
docker manifest push $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION
echo "上传双架构镜像:$IMAGE_NAME:latest"
docker manifest create $DOCKER_REGISTRY/$IMAGE_NAME:latest --amend $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-amd64 --amend $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION-arm64
docker manifest push $DOCKER_REGISTRY/$IMAGE_NAME:latest
}
push_repo_array=($PUSH_REPO)
for repo in ${push_repo_array[@]}; do
if [ ${repo} == "dockerhub" ]; then
DOCKER_REGISTRY=docker.io
echo $DOCKERHUB_PASSWORD | docker login --username $DOCKERHUB_USER --password-stdin $DOCKER_REGISTRY
elif [ ${repo} == "aliyun" ]; then
DOCKER_REGISTRY=registry.cn-hangzhou.aliyuncs.com
echo $ALIYUN_PASSWORD | docker login --username $ALIYUN_USER --password-stdin $DOCKER_REGISTRY
elif [ ${repo} == "huawei" ]; then
DOCKER_REGISTRY=swr.cn-north-4.myhuaweicloud.com
echo $HW_PASSWORD | docker login --username $HW_USER --password-stdin $DOCKER_REGISTRY
fi
echo "上传镜像到仓库: $DOCKER_REGISTRY/$IMAGE_NAME:$VERSION"
if [ ${ARCH} == "all" ]; then
func_amd64
func_arm64
func_mix
elif [ ${ARCH} == "arm64" ]; then
func_arm64
else
func_amd64
fi
done
评论区