构建多架构Docker镜像并分开推送到Dockerhub

前提

我平时使用的PostgreSQL和Caddy的Docker镜像都是自己构建的,因为会用到一些额外的插件。 我使用的服务器多数是x86架构,也有一些是ARM架构的,所以需要构建多架构的镜像,如果直接分开构建、push,Dockerhub上只会保留最后一次的push,使用的错误的架构,程序是无法启动的。

调查

其实Docker支持使用docker buildx build --platform=linux/arm64,linux/amd64的方式同时构建多架构镜像,但是异构镜像是使用qemu这样的虚拟机技术实现的,效率可想而知,特别是在性能有限的vps上,构建很慢,而且有时候还会报错。

最好的办法是分开构建,在ARM机器和x86机器上分开构建,这样效率最高。

经过研究发现Docker提供了docker manifest 这个命令用于解决这个问题。

使用

使用很简单,分开构建并推送,然后创建manifest并推送,如果你原来的镜像是lerrybin/pg16:latest,那么,
1. 在x86机器上构建一个名为lerrybin/pg16-amd64:latest的镜像并推送到Dockerhub,镜像名称相比原来的名称多了一个架构后缀用于区分。
2. 在ARM机器上构建一个名为lerrybin/pg16-arm64:latest的镜像,同样推送到Dockerhub。
3. 创建manifest docker manifest create lerrybin/pg16:latest lerrybin/pg16-amd64:latest lerrybin/pg16-arm64:latest
4. push manifest docker manifest push lerrybin/pg16:latest

此时拉去镜像就会自动获取相应架构的镜像,需要注意的是,推送manifest之前要确保对应的镜像已经推送到Dockerhub,不然会报错。

Makefile分享

另外附上我在用的一个Makefile,以供参考

REPOSITORY := lerrybin
NAME := caddy-with-cloudflare
TAG := latest
CURRENT_ARCH := $(shell uname -m)


ifeq ($(CURRENT_ARCH),x86_64)
    BUILD_PLATFORM := linux/amd64
	SURFIX := amd64
else
    BUILD_PLATFORM := linux/arm64
	SURFIX := arm64
endif



# Pull the base caddy image
update:
	@echo "Pulling the base caddy image..."
	@docker pull caddy:2-builder

# Build the Docker image
build: update
	@echo "Building the $(NAME) image..."
	@docker build --platform=$(BUILD_PLATFORM) -t $(REPOSITORY)/$(NAME)-${SURFIX}:$(TAG) . --push
	@docker manifest create -a $(REPOSITORY)/$(NAME):$(TAG) $(REPOSITORY)/$(NAME)-amd64:$(TAG) $(REPOSITORY)/$(NAME)-arm64:$(TAG)
	@docker manifest push $(REPOSITORY)/$(NAME):$(TAG)

all: update build

.PHONY: update build
.DEFAULT_GOAL := all

使用过程中遇到问题,可以查看文档 https://docs.docker.com/reference/cli/docker/manifest/#manifest-create