tags: containerd, container

How to Build Containerd

containerd没有提供编译环境,本文分析如何使用官方的构建环境编译containerd。

1. Standard

根据官方文档, 一步一步来即可编译containerd。

1. 安装依赖

安装golang, btrfs等依赖

2. 下载代码

git clone https://github.com/containerd/containerd.git
cd containerd
git checkout v1.6.7

3. 修改代码

sed -i 's/app.Name = "containerd"/app.Name = "containerd-st0n3"/g' cmd/containerd/command/main.go

4. 编译

make

5. binary

编译好的二进制文件,位于bin目录

# ./bin/containerd -v
containerd-st0n3 github.com/containerd/containerd v1.6.7.m 0197261a30bf81f1ee8e6a4dd2dea0ef95d67ccb.m

2. Build in a container

2.1 Github action release

上文描述的是自行搭建编译环境进行编译的过程,但可能与官方release环境不同,所以我们需要理解官方是在什么环境中编译containerd。

containerd使用github action自动进行release版本的构建:

https://github.com/containerd/containerd/runs/7681874106?check_suite_focus=true

github action的源码核心部分如下:

https://github.com/containerd/containerd/blob/v1.6.7/.github/workflows/release.yml

on:
  push:
    tags:
      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10

name: Containerd Release

jobs:
  ...
  build:
    ...
    steps:
      ...
      - name: Make
        shell: bash
        run: |
          cache="--cache-from=type=gha,scope=containerd-release --cache-to=type=gha,scope=containerd-release"
          if [[ "${PLATFORM}" =~ "windows" ]]; then
            # For Windows the cni build script generates a config but shells out to powershell (and also assume it is running on windows) to get a gateway and subnet.
            # The values provided here are taken from packages that we previously generated.
            export GATEWAY=172.21.16.1
            export PREFIX_LEN=12
            BUILD_ARGS="--build-arg GATEWAY --build-arg PREFIX_LEN"
          fi
          docker buildx build ${cache} --build-arg RELEASE_VER --build-arg GO_VERSION ${BUILD_ARGS} -f .github/workflows/release/Dockerfile --platform=${PLATFORM} -o releases/ .
          echo PLATFORM_CLEAN=${PLATFORM/\//-} >> $GITHUB_ENV
          # Remove symlinks since we don't want these in the release Artifacts
          find ./releases/ -maxdepth 1 -type l | xargs rm          
        working-directory: src/github.com/containerd/containerd
        env:
          GO_VERSION: '1.17.13'
          PLATFORM: ${{ matrix.platform }}
      - name: Save Artifacts
        uses: actions/upload-artifact@v2
        with:
          name: release-tars-${{env.PLATFORM_CLEAN}}
          path: src/github.com/containerd/containerd/releases/*.tar.gz*

    ...

注意到具体的编译命令为docker buildx build ${cache} --build-arg RELEASE_VER --build-arg GO_VERSION ${BUILD_ARGS} -f .github/workflows/release/Dockerfile --platform=${PLATFORM} -o releases/ ., 其中Dockerfile为.github/workflows/release/Dockerfile

https://github.com/containerd/containerd/blob/v1.6.7/.github/workflows/release/Dockerfile

...
FROM ${TARGETOS} AS target
...
RUN \
	--mount=type=bind,from=go,source=/usr/local/go,target=/usr/local/go \
	--mount=type=cache,target=/root/.cache/go-build \
	--mount=type=cache,target=/go/pkg \
	export CC=$(xx-info)-gcc && xx-go --wrap && \
	make release cri-release cri-cni-release && \
	for f in $(find bin -executable -type f); do xx-verify $f; done
...
FROM scratch AS release
COPY --from=target /go/src/github.com/containerd/containerd/releases/ /

因此,理论上我们可以使用该Dockerfile和对应的参数,自行构造一套编译环境。

2.2 Custom

cat << EOF > build.sh
#!/bin/bash
set -x
export cache="--cache-from=type=gha,scope=containerd-release --cache-to=type=gha,scope=containerd-release"
export RELEASE_VER="v1.6.7"
export GO_VERSION="1.17.13"
export BUILD_ARGS=""
export PLATFORM="linux/amd64"
docker buildx build ${cache} --build-arg RELEASE_VER --build-arg GO_VERSION ${BUILD_ARGS} -f .github/workflows/release/Dockerfile --platform=${PLATFORM} -o releases/ .
EOF
git add . && git commit -m "add build.sh"
./build.sh

编译后位于containerd/release目录。

注意: Dockerfile中有校验git状态,如果修改了源码,应该在build前commit。

https://github.com/containerd/containerd/blob/v1.6.7/.github/workflows/release/Dockerfile#L54-L56

...
RUN \
	export GIT_STATUS_OUTPUT=$(git status --porcelain) && \
	test -z $GIT_STATUS_OUTPUT || (echo $GIT_STATUS_OUTPUT && exit 1)
...

3. Advance

3.1 修改编译命令

阅读Makefile,可以容易找到具体的编译命令。

搜索$(GO) build可以找到以下几处涉及具体编译的命令:

https://github.com/containerd/containerd/blob/v1.6.7/Makefile

...
build: ## build the go packages
	@echo "$(WHALE) $@"
	@$(GO) build ${DEBUG_GO_GCFLAGS} ${GO_GCFLAGS} ${GO_BUILD_FLAGS} ${EXTRA_FLAGS} ${GO_LDFLAGS} ${PACKAGES}
...
define BUILD_BINARY
@echo "$(WHALE) $@"
@$(GO) build ${DEBUG_GO_GCFLAGS} ${GO_GCFLAGS} ${GO_BUILD_FLAGS} -o $@ ${GO_LDFLAGS} ${GO_TAGS}  ./$<
endef

# Build a binary from a cmd.
bin/%: cmd/% FORCE
	$(call BUILD_BINARY)

bin/containerd-shim: cmd/containerd-shim FORCE # set !cgo and omit pie for a static shim build: https://github.com/golang/go/issues/17789#issuecomment-258542220
	@echo "$(WHALE) $@"
	@CGO_ENABLED=${SHIM_CGO_ENABLED} $(GO) build ${GO_BUILD_FLAGS} -o $@ ${SHIM_GO_LDFLAGS} ${GO_TAGS} ./cmd/containerd-shim

bin/containerd-shim-runc-v1: cmd/containerd-shim-runc-v1 FORCE # set !cgo and omit pie for a static shim build: https://github.com/golang/go/issues/17789#issuecomment-258542220
	@echo "$(WHALE) $@"
	@CGO_ENABLED=${SHIM_CGO_ENABLED} $(GO) build ${GO_BUILD_FLAGS} -o $@ ${SHIM_GO_LDFLAGS} ${GO_TAGS} ./cmd/containerd-shim-runc-v1

bin/containerd-shim-runc-v2: cmd/containerd-shim-runc-v2 FORCE # set !cgo and omit pie for a static shim build: https://github.com/golang/go/issues/17789#issuecomment-258542220
	@echo "$(WHALE) $@"
	@CGO_ENABLED=${SHIM_CGO_ENABLED} $(GO) build ${GO_BUILD_FLAGS} -o $@ ${SHIM_GO_LDFLAGS} ${GO_TAGS} ./cmd/containerd-shim-runc-v2
...

修改这些命令,即可自定义编译命令。