tags: container,buildx

How to Build buildx

1. Buildx

buildx 是 docker 的一个插件,docker buildx 子命令会执行 buildx 二进制文件。本文分析如何自己编译 buildx 。

2. Standard Build

官方文档提供了标准的编译方法:https://github.com/docker/buildx#building

# Buildx 0.6+
$ docker buildx bake "https://github.com/docker/buildx.git"
$ mkdir -p ~/.docker/cli-plugins
$ mv ./bin/build/buildx ~/.docker/cli-plugins/docker-buildx

# Docker 19.03+
$ DOCKER_BUILDKIT=1 docker build --platform=local -o . "https://github.com/docker/buildx.git"
$ mkdir -p ~/.docker/cli-plugins
$ mv buildx ~/.docker/cli-plugins/docker-buildx

# Local 
$ git clone https://github.com/docker/buildx.git && cd buildx
$ make install

3. 编译流程分析

下面以 make binaries命令为入口,分析编译流程。

执行 docker buildx bake binaries 编译。

https://github.com/docker/buildx/blob/v0.10.2/Makefile#L22-L24

...
export BUILDX_CMD ?= docker buildx
...
.PHONY: binaries
binaries:
	$(BUILDX_CMD) bake binaries
...

bake的配置如下,设置目标为binaries阶段,编译后输出到 ./bin/build目录。

https://github.com/docker/buildx/blob/v0.10.2/docker-bake.hcl#L101-L106

...
variable "DESTDIR" {
  default = "./bin"
}
...
target "binaries" {
  inherits = ["_common"]
  target = "binaries"
  output = ["${DESTDIR}/build"]
  platforms = ["local"]
}
...

在 buildx-build 阶段调用 hack/build 编译。

https://github.com/docker/buildx/blob/v0.10.2/Dockerfile#L60


# syntax=docker/dockerfile-upstream:1.5.0

ARG GO_VERSION=1.19
ARG XX_VERSION=1.1.2
ARG DOCKERD_VERSION=20.10.14

FROM docker:$DOCKERD_VERSION AS dockerd-release

# xx is a helper for cross-compilation
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS golatest

FROM golatest AS gobase
COPY --from=xx / /
RUN apk add --no-cache file git
ENV GOFLAGS=-mod=vendor
ENV CGO_ENABLED=0
WORKDIR /src

FROM gobase AS buildx-version
RUN --mount=type=bind,target=. <<EOT
  set -e
  mkdir /buildx-version
  echo -n "$(./hack/git-meta version)" | tee /buildx-version/version
  echo -n "$(./hack/git-meta revision)" | tee /buildx-version/revision
EOT

FROM gobase AS buildx-build
ARG TARGETPLATFORM
RUN --mount=type=bind,target=. \
  --mount=type=cache,target=/root/.cache \
  --mount=type=cache,target=/go/pkg/mod \
  --mount=type=bind,from=buildx-version,source=/buildx-version,target=/buildx-version <<EOT
  set -e
  xx-go --wrap
  DESTDIR=/usr/bin VERSION=$(cat /buildx-version/version) REVISION=$(cat /buildx-version/revision) GO_EXTRA_LDFLAGS="-s -w" ./hack/build
  xx-verify --static /usr/bin/docker-buildx
EOT

...

FROM scratch AS binaries-unix
COPY --link --from=buildx-build /usr/bin/docker-buildx /buildx

...

FROM binaries-unix AS binaries-linux

...

FROM binaries-$TARGETOS AS binaries
# enable scanning for this stage
ARG BUILDKIT_SBOM_SCAN_STAGE=true

...

hack/build 调用 go build

https://github.com/docker/buildx/blob/v0.10.2/hack/build

#!/usr/bin/env sh

set -e

: "${DESTDIR=./bin/build}"
: "${PACKAGE=github.com/docker/buildx}"
: "${VERSION=$(./hack/git-meta version)}"
: "${REVISION=$(./hack/git-meta revision)}"

: "${CGO_ENABLED=0}"
: "${GO_PKG=github.com/docker/buildx}"
: "${GO_LDFLAGS=-X ${GO_PKG}/version.Version=${VERSION} -X ${GO_PKG}/version.Revision=${REVISION} -X ${GO_PKG}/version.Package=${PACKAGE}}"
: "${GO_EXTRA_LDFLAGS=}"

set -x
CGO_ENABLED=$CGO_ENABLED go build -mod vendor -trimpath -ldflags "${GO_LDFLAGS} ${GO_EXTRA_LDFLAGS}" -o "${DESTDIR}/docker-buildx" ./cmd/buildx

4. Custom

最终的编译命令位于hack/build文件

https://github.com/docker/buildx/blob/v0.10.2/hack/build#L16

#!/usr/bin/env sh

set -e

: "${DESTDIR=./bin/build}"
: "${PACKAGE=github.com/docker/buildx}"
: "${VERSION=$(./hack/git-meta version)}"
: "${REVISION=$(./hack/git-meta revision)}"

: "${CGO_ENABLED=0}"
: "${GO_PKG=github.com/docker/buildx}"
: "${GO_LDFLAGS=-X ${GO_PKG}/version.Version=${VERSION} -X ${GO_PKG}/version.Revision=${REVISION} -X ${GO_PKG}/version.Package=${PACKAGE}}"
: "${GO_EXTRA_LDFLAGS=}"

set -x
CGO_ENABLED=$CGO_ENABLED go build -mod vendor -trimpath -ldflags "${GO_LDFLAGS} ${GO_EXTRA_LDFLAGS}" -o "${DESTDIR}/docker-buildx" ./cmd/buildx

5. 远程调试

关闭编译优化,内联,trimpath

$ sed -i 's/go build/go build -gcflags="all=-N -l"/g' hack/build
$ sed -i 's/${GO_EXTRA_LDFLAGS}//g' hack/build
$ sed -i 's/-trimpath//g' hack/build
$ make binaries
docker buildx bake binaries
[+] Building 7.3s (21/21) FINISHED
...
 => => copying files 76.04MB
$ ls -lah bin/build/buildx 
-rwxr-xr-x 1 root root 73M Feb 14 12:52 bin/build/buildx