是否可以在没有 docker buildx 的情况下(手动)构建多架构 docker 镜像?

Eri*_*kas 5 go docker docker-registry

我有一个 Golang 项目,已构建并放入scratch镜像中:

# Build binary
FROM golang:1.17-alpine AS build-env
ADD . /app
WORKDIR /app
RUN env CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp ./cmd/myapp/main.go

# Create image
FROM scratch
COPY --from=build-env /app/myapp /
ENTRYPOINT ["/myapp"]
Run Code Online (Sandbox Code Playgroud)

由于 Golang 允许针对多种架构进行交叉编译(并且比在模拟环境中构建要快得多),所以我想做这样的事情:

# Build binary
FROM golang:1.17-alpine AS build-env
ADD . /app
WORKDIR /app
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o myapp_linux_i386 ./cmd/myapp/main.go             # Linux 32bit
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o myapp_linux_x86_64 ./cmd/myapp/main.go         # Linux 64bit
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags="-s -w" -o myapp_linux_arm ./cmd/myapp/main.go      # Linux armv5/armel/arm (it also works on armv6)
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w" -o myapp_linux_armhf ./cmd/myapp/main.go    # Linux armv7/armhf
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o myapp_linux_aarch64 ./cmd/myapp/main.go        # Linux armv8/aarch64
RUN env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags="-s -w" -o myapp_freebsd_x86_64 ./cmd/myapp/main.go     # FreeBSD 64bit
RUN env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o myapp_darwin_x86_64 ./cmd/myapp/main.go       # Darwin 64bit
RUN env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o myapp_darwin_aarch64 ./cmd/myapp/main.go      # Darwin armv8/aarch64
RUN env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w" -o myapp_windows_i386.exe ./cmd/myapp/main.go     # Windows 32bit
RUN env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o myapp_windows_x86_64.exe ./cmd/myapp/main.go # Windows 64bit
Run Code Online (Sandbox Code Playgroud)

但是如何创建和推送多架构 docker 镜像呢?

# Create image
...
Run Code Online (Sandbox Code Playgroud)

BMi*_*tch 7

从问题来看,听起来不太像您想避免 buildx,而更像是您想避免在解释器下运行编译器,而是使用 Go 的内置交叉编译功能。这是内置于 buildx 中的:

# Build binary
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS build-env
ADD . /app
WORKDIR /app
ARG TARGETOS
ARG TARGETARCH
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
    go build -ldflags="-s -w" -o myapp ./cmd/myapp/main.go

# Create image
FROM scratch
COPY --from=build-env /app/myapp /
ENTRYPOINT ["/myapp"]
Run Code Online (Sandbox Code Playgroud)

--platform在您正在构建的平台上运行第一阶段。TARGETOS 和 TARGETARCH 是 buildkit 的内置参数。然后您可以使用以下命令进行构建:

# Build binary
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS build-env
ADD . /app
WORKDIR /app
ARG TARGETOS
ARG TARGETARCH
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
    go build -ldflags="-s -w" -o myapp ./cmd/myapp/main.go

# Create image
FROM scratch
COPY --from=build-env /app/myapp /
ENTRYPOINT ["/myapp"]
Run Code Online (Sandbox Code Playgroud)

请注意,docker 不支持 MacOS,它在该平台上的 Linux VM 中运行。而且我也不相信 windows/386 是有效的,我只见过 amd64 上的 docker 并且可能支持 arm64。


除此之外,还有多种方式可以推送多平台清单。docker 内置了docker manifest。或者您可以自己生成清单并使用 API 调用将其推送到注册表(如果您想了解底层详细信息,请参阅 OCI 的图像规范和分发规范)。但如果你有 docker,buildx 就是最简单的按钮。