没有使用docker buildx,是否可以手动构建多架构的Docker镜像?

huangapple go评论86阅读模式
英文:

Is it possible to (manually) build multi-arch docker image without docker buildx?

问题

我有一个使用Golang构建的项目,已经构建并放入了scratch镜像中:

# 构建二进制文件
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

# 创建镜像
FROM scratch
COPY --from=build-env /app/myapp /
ENTRYPOINT ["/myapp"]

由于Golang允许交叉编译多个架构(而且比在模拟环境中构建要快得多),我想做类似这样的事情:

# 构建二进制文件
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 32位
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o myapp_linux_x86_64 ./cmd/myapp/main.go         # Linux 64位
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(也适用于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 64位
RUN env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o myapp_darwin_x86_64 ./cmd/myapp/main.go       # Darwin 64位
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 32位
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 64位

但是,我应该如何创建和推送多架构的Docker镜像呢?

# 创建镜像
...
英文:

I have a Golang project which is built and put into scratch image:

# 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"]

Since Golang allows to cross-compile for multiple architectures (and it's way faster than building in emulated environments), I want to do something like this:

# 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

But then how do I create and push multi-arch docker images?

# Create image
...

答案1

得分: 4

从问题中可以看出,你似乎不想避免使用buildx,而是想避免在解释器下运行编译器,而是使用Go的内置交叉编译功能。这已经内置在buildx中:

# 构建二进制文件
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

# 创建镜像
FROM scratch
COPY --from=build-env /app/myapp /
ENTRYPOINT ["/myapp"]

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

docker buildx build \
  --platform="linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x" \
  -t registry/image:tag \
  .

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


除此之外,还有各种方法可以推送多平台的映像。docker 内置了 docker manifest 命令。或者你可以自己生成 manifest,并使用 API 调用将其推送到注册表(如果你想了解底层细节,请参阅 OCI 的 image-spec 和 distribution-spec)。但如果你使用 docker,buildx 是最简单的方法。

英文:

From the question, it sounds less like you want to avoid buildx, and more that you want to avoid running the compiler under an interpreter, and instead use Go's builtin cross compiling capabilities. That's built into 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"]

The --platform runs the first stage on the platform where you are building. And the TARGETOS and TARGETARCH are builtin args with buildkit. You can then build with:

docker buildx build \
  --platform="linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x" \
  -t registry/image:tag \
  .

Note that there's no MacOS support for docker, it runs in a Linux VM on that platform. And I don't believe windows/386 is valid either, I've only ever seen docker on amd64 and with possible support for arm64.


Beyond that, there are various ways to push multi-platform manifest. There's docker manifest built into docker. Or you can generate the manifest yourself and push it to a registry with API calls (see OCI's image-spec and distribution-spec if you want to get to the low level details). But if you have docker, buildx is the easy button.

huangapple
  • 本文由 发表于 2022年3月7日 02:04:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/71372947.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定