四、Docker Buildx 构建支持多系统架构的Docker镜像
一、前言
1. 本文主要内容
使用 Docker Buldx 构建支持AMD64、ARM、ARM64等架构的镜像并传送到Docker Hub。
2. 环境支持
-
安装Docker >= 19.03
该版本包含 buildx,该功能仅适用于 Docker v19.03+ 版本。 -
Linux kernel >= 4.8
自该Linux内核版本 binfmt_misc 支持 fix-binary (F) flag。fix_binary 标志允许内核在容器或chroot内使用binfmt_misc注册的二进制格式处理程序,即使该处理程序二进制文件不是该容器或chroot内可见的文件系统的一部分。
3. 启用 Buildx
Docker在19.03引入了一个新的特性,使得Docker可以构建不同CPU体系结构的镜像,比如ARM镜像,这是不必引入模拟器的情况下,Docker自身所提供的原生统一构建机制,但是使用时需要进行设定才能进行使用。(从 v20.10 版本开始,Docker CLI 所有实验特性的命令均默认开启,无需再进行配置或设置系统环境变量。)
buildx 命令属于实验特性,因此首先需要开启该特性。
运行命令
docker buildx version
出现如下问题,因为没开启Buildx 特性
docker: 'buildx' is not a docker command.
See 'docker --help'
运行命令安装
docker buildx install
在此运行命令,出现版本即可
docker buildx version
github.com/docker/buildx v0.9.1-docker ed00243a0ce2a0aee75311b06e32d33b44729689
查看已有的builder 实例
docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
mybuilder * docker-container
mybuilder0 unix:///var/run/docker.sock stopped
default docker
default default running 20.10.21 linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
如果缺少一些架构,可以执行以下命令增加
docker run --privileged --rm tonistiigi/binfmt --install all
二、镜像制作准备
1. 准备应用代码
用 golang 简单的写了一个 http server,监听8000端口,默认输出helloworld,并返回操作系统、HostName以及IP地址信息
新建 main.go 保存以下代码
package main
import (
"fmt"
"log"
"net"
"net/http"
"os"
"runtime"
"strings"
)
func getHostName() string {
hostname, err := os.Hostname()
if err != nil {
log.Fatalf("Failed to open log file: %v", err)
}
return hostname
}
func getIpAddresses() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
log.Fatal(err)
return ""
}
var ips []string
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
ips = append(ips, ipnet.IP.String())
}
}
}
return strings.Join(ips, ",")
}
func handler(w http.ResponseWriter, r *http.Request) {
log.Println("received request from", r.RemoteAddr, r.URL.Path[1:])
var welcome = r.URL.Path[1:]
if len(welcome) == 0 {
welcome = "World"
}
fmt.Fprintf(w, "Hello, %s! ---ken.io \r\n", welcome)
fmt.Fprintf(w, "OS:%s/%s,Host:%s,IP:%s\r\n", runtime.GOOS, runtime.GOARCH, getHostName(), getIpAddresses())
}
func main() {
http.HandleFunc("/", handler)
log.Println("starting server on port 8000")
log.Fatal(http.ListenAndServe(":8000", nil))
}
2. 编写Dockerfile 文件
新建 Dockerfile.build 文件并保存以下内容
# 使用官方提供的 Go 镜像作为基础镜像
FROM --platform=$TARGETPLATFORM golang:1.20
# 将工作目录设置为 /app
WORKDIR /app
# 将helloworld.go复制到 /app 下
COPY main.go /app
# 设置go mod 镜像
RUN go env -w GO111MODULE=on
RUN go env -w GOPROXY=https://goproxy.cn,direct
# 导入依赖的Redis go module
RUN go mod init main
# 允许宿主机访问容器的 8000 端口
EXPOSE 8000
# 设置容器进程为:go run helloworld.go
CMD go run main.go
$TARGETPLATFORM
是内置变量,由 --platform
参数来指定其值
由于是基于 golang 的镜像来制作的,而 golang 是支持以下 7 种系统架构的,因此我们制作的镜像也就跟着支持这 7 种系统架构
linux/amd64, linux/arm/v6, linux/arm/v7, linux/arm64/v8, linux/386, linux/ppc64le, linux/s390x
这里穿插一句吐槽,简单统计了一下,ARM 的系统架构有如下各种简称:
arm64, armv8l, arm64v8, aarch64
arm, arm32, arm32v7, armv7, armv7l, armhf
arm32v6, armv6, armv6l, arm32v5, armv5, armv5l, armel, aarch32
而对比 Intel 和 AMD 的就简单多了:
x86, 386, i386, i686
x86_64, x64, amd64
三、镜像制作
1. 确认 基础镜像 支持架构
docker buildx ls
2. 登录账号
访问 https://hub.docker.com/signup 注册账号,然后在Docker Desktop登录账号,或者通过命令登录
# 登录命令
docker login
# 根据命令号交互输入注册时的账号密码即可成功登录
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: xxx
Password:
Login Succeeded
Logging in with your password grants your terminal complete access to your account.
For better security, log in with a limited-privilege personal access token. Learn more at https://docs.doc
3. 构建镜像
# 进入main.go 所在目录
cd /data/dev
# 构建镜像(默认为latest)(注意结尾一定要加.)
# 这里选择构建常用 linux/amd64,linux/arm64 的架构,如有需要可以根据支持的架构自行追加或删减
docker buildx build \
--platform linux/amd64,linux/arm64 \
-f Dockerfile.build \
-t shyurongli/go-buildx . --push
4. 查看构建结果
命令执行成功后,你就会在 Docker Hub 看到你上传的镜像啦。示例图如下:
5. 镜像测试
# 拉取镜像
docker pull shyurongli/go-buildx
# 启动容器
docker run -itd --name go-buildx -p 8000:8000 shyurongli/go-buildx
# 访问测试
curl localhost:8000
# 输出示例
Hello, World! ---ken.io
OS:linux/amd64,Host:68dc587fb24f,IP:172.17.0.3
6. 查看镜像信息
docker buildx imagetools inspect shyurongli/go-buildx
最后
在制作多系统架构的 Docker 镜像时,建议使用 CPU 比较强或者多核心的 VPS 来构建,否则会非常耗时,本篇文章主要讲的是手动进行多架构镜像的构建,也可以是用cicd工具来自动化进行构建,后续文章进行说明