前言

拉取使用别人镜像时发现在不同CPU架构下都可以使用,查找后发现虽然是同一个镜像但是分不同的架构版本,拉取镜像时会拉取当前CPU架构下的镜像,所以可以全平台通用一个命令。

比如golang:1.24.1

docker pull golang:1.24.1

可以看到官方镜像是分为好多架构的

Buildx

1、Buildx 简介

Docker Buildx 是 Docker的CLI插件,来自于Moby BuildKit 。自从Docker 18.06 开始这个插件直接集成到了Docker build 中。

Buildx 是一个构建工具, 它可以帮助用户快速、高效地构建 Docker 镜像, 并支持多种平台的构建。使用 buildx, 用户可以在单个命令中构建多种架构的镜像, 例如 x86 和 ARM 架构, 而无需手动操作多个构建命令。此外, buildx 还支持 Dockerfile 的多阶段构建和缓存, 这可以大大提高镜像构建的效率和速度。

2、Buildx 安装

注意: 在Debian和Ubuntu中不要使用apt install docker.io命令安装docker,因为docker.io不是官方docker,没有内置buildx,所以安装后无法使用buildx,应该使用官方镜像安装docker才内置buildx。

debian/ubuntu官方教程:https://docs.docker.com/engine/install/debian/

添加密钥

sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

安装docker-ce和插件

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

检查版本

docker version
docker buildx version
# github.com/docker/buildx v0.16.1 34c1952

如果已经安装了docker.io,并且有正在运行的镜像且无法停机时,有两种方式可以为docker.io添加Buildx插件

1.手动下载Buildx插件

# 创建文件夹
mkdir -p ~/.docker/cli-plugins/

# 下载到指定目录 ~/.docker/cli-plugins/docker-buildx
# 注意 这是amd架构的
curl -Lo ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.11.2/buildx-v0.11.2.linux-amd64
# arm64:curl -Lo ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.16.2/buildx-v0.16.2.linux-arm64

# 赋予权限
chmod +x ~/.docker/cli-plugins/docker-buildx

# 验证
docker buildx version

开启支持,Docker Buildx属于实验性功能,默认并没有开启

# 编辑配置文件
vi /etc/docker/daemon.json

{
  "experimental": true
}

# 重启
systemctl restart docker

2.docker套docker

# 启动一个docker容器
docker run --privileged -d --name docker docker

# 进入docker容器
docker exec -it docker sh

# 1.为qemu安装所有架构
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

# 2.取最新的 tonistiigi/binfmt 镜像
docker run --privileged --rm tonistiigi/binfmt --install all

# 查看buildx版本
docker buildx version

# 登录docker账号
docker login -u javaow

# 剩下就是和普通的一样, 先创建Dockerfile.build编译即可

3、构建器管理

要构建多平台镜像就需要使用构建器构建,虽然builder有一个名为default的默认构建器,但是default的驱动器为docker而不是docker-container,所以默认的default构建器不能构建跨多平台容器,需要手动创建构建器并且指定为默认构建器才可以

查看 builder 列表

# 查看 builder 列表
docker buildx ls

NAME/NODE        DRIVER/ENDPOINT                   STATUS    BUILDKIT   PLATFORMS
multiarch*       docker-container
 \_ multiarch0    \_ unix:///var/run/docker.sock   running   v0.15.1    linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
default          docker
 \_ default       \_ default                       running   v0.15.0    linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
 
# 一个默认的default和一个我们自己创建的名为multiarch的根据器
# 其中名称后面带*的是当前正在使用的,也是默认的构建器 当我们运行 docker build 命令时就是在使用此 builder 构建镜像。

创建启动构建器 因为使用 docker 驱动程序的默认 builder 不支持使用单条命令(默认 builder 的 –platform 参数只接受单个值)构建跨平台镜像,所以我们需要使用 docker-container 驱动创建一个新的 builder

# 创建 buildx 构建器 名称为multiarch 驱动器为docker-container
# --use 指定为默认构建器
docker buildx create --name multiarch --driver docker-container --use

启动构建器

#一般默认创建了就会直接启动,如果创建了没有启动就需要手动启动构建器
# 查看构建器列表
docker buildx ls

# 如果发现构建器的STATUS为inactive,那就是还未启用
# 手动启用名为multiarch的构建器,再次ls查看状态为running就是已经启用
docker buildx inspect --bootstrap multiarch

4、架构安装

为构建器安装支持架构

# 1.为qemu安装所有架构
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes


# 2.使用最新的 tonistiigi/binfmt 镜像
docker run --privileged --rm tonistiigi/binfmt --install all

5、构建

构建并推送

# --platform要构建的平台列表 --no-cache不使用缓存
# -f指定Dockerfile.build 文件位置
# -t指定构建的镜像名称和版本 这里没有指定版本所以是最新镜像
# .说明Dockerfile.build 在当前目录
# --push 构建好后直接push提交到远程仓库
docker buildx build \
--platform linux/amd64,linux/arm64 --no-cache \
-f Dockerfile \
-t javaow/go-buildx . --push

6、实战

注意: 因为go本身支持跨平台编译,所以这里的第一阶段构建使用的是宿主机CPU架构,如果语言不支持或者必须使用不同平台镜像编译 那么需要把$BUILDPLATFOR换成$TARGETPLATFORM

dockerfile

# ================= 第一阶段 =====================
# 使用官方 Go 镜像进行编译
# 镜像的架构平台取自$BUILDPLATFORM变量
# $BUILDPLATFORM变量标识宿主机的架构,因为go支持跨平台编译,所以这里统一使用一个架构镜像编译即可 在go层面使用跨平台编译
# 如果语言不支持或者必须使用不同平台镜像编译 那么需要把$BUILDPLATFOR换成$TARGETPLATFORM
# 别名为builder
FROM --platform=$BUILDPLATFORM golang:1.20 AS builder

# 设置工作目录(创建一个目录并cd过去)
WORKDIR /demo

# 将源代码复制到容器中
COPY main.go .

# 设置 Go 环境变量 切换模块源为中国Go模块代理服务器
RUN go env -w GOPROXY=https://goproxy.cn,direct

# 在当前目录初始化创建main模块并自动解析下载依赖(构建完整模块 tidy完善go.mod)
RUN go mod init main && go mod tidy

# RUN 命令想要使用变量必须提前用 ARG 进行声明,并且多个阶段中要多次声明
ARG TARGETOS
ARG TARGETARCH

# 编译go二进制文件 go层面的跨平台编译(如果是其他语言那么基础镜像就得使用不同平台镜像)
# GOARCH平台从 $TARGETARCH 变量获取
# GOOS从$TARGETOS变量获取
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /demo/main .

# ================= 第二阶段 =====================

# 使用轻量级的 Alpine 镜像
# 架构为$TARGETPLATFORM,$TARGETPLATFORM代表目标架构,由--platform指定
FROM --platform=$TARGETPLATFORM alpine:3.18

# 设置工作目录(创建一个目录并cd过去)
WORKDIR /apps

# 将第一阶段编译好的二进制文件从第一阶段复制到 Alpine 镜像中
# 复制builder阶段中的/demo/main文件到当前阶段的.目录中 .代表当前目录也就是上面WORKDIR的目录
# --from指定阶段FROM的AS别名
COPY --from=builder /demo/main .

# 设置时区为上海,并且创建全局访问的软链接
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo 'Asia/Shanghai' > /etc/timezone \
    && ln -s /apps/main /usr/bin/main

# 设置编码
ENV LANG C.UTF-8

# RUN 命令想要使用变量必须提前用 ARG 进行声明,并且多个阶段中要多次声明
ARG TARGETPLATFORM

# 根据平台不同安装不同的软件包
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
        # 创建软链接 解决二进制无法执行问题 只限制amd架构
        mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2; \
    elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
        echo "arm平台"; \

    fi

# 设置容器启动命令
CMD main
# 如果有-c等命令就使用ENTRYPOINT,会拼接,CMD只会覆盖
# ENTRYPOINT ["/usr/bin/main"]

编译并提交到远程,这里编译4个架构

# --platform就是Dockerfile.build 文件中的变量,指定不同的基础镜像
# -f指定Dockerfile.build 文件位置
# -t指定构建的镜像名称 这里没有指定版本所以是最新镜像
# .说明Dockerfile.build 在当前目录
# --push 构建好后直接push提交到远程仓库
docker buildx build \
--platform linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 --no-cache \
-f Dockerfile.build \
-t javaow/go-buildx . --push