引子
从 EP 07 中,我们可以看到已经有了成功的 riscv 移植工作,见此仓库. 发现其构建 riscv 采用了以下命令
# Build
rm -rf bin dist build
ARCH=riscv64 SKIP_IMAGE=true SKIP_VALIDATE=true SKIP_AIRGAP=true make
# Split
cd dist/artifacts
gzip < k3s-riscv64 | split -b 20M - k3s-riscv64.gz.
在此 pull request 中他也提到了这一路上的适配工作过程:开始他是使用qemu模拟,之后在硬件上测试并适配我们之前提到过的各种镜像.
所以这篇文章中我们要做的是,弄明白它的构建过程,包括其 Makefile 与 Dockerfile.dapper
Makefile
源码
分段解释此 Makefile
TARGETS := $(shell ls scripts | grep -v \\.sh)
GO_FILES ?= $$(find . -name '*.go' | grep -v generated)
- 通过 shell 命令列出 scripts 目录下所有不以
.sh结尾的文件 - 查找项目中所有
.go文件,排除 generated目录
.dapper:
@echo Downloading dapper
@curl -sL https://releases.rancher.com/dapper/v0.6.0/dapper-$$(uname -s)-$$(uname -m) > .dapper.tmp
@@chmod +x .dapper.tmp
@./.dapper.tmp -v
@mv .dapper.tmp .dapper
- 下载 dapper 工具,根据系统类型下载对应版本
- 添加执行权限并验证版本
- 重命名为
.dapper
$(TARGETS): .dapper
DAPPER_DEBUG=1 DAPPER_DOCKER_BUILD_ARGS="--network host" ./.dapper $@
- 为上面不以
.sh结尾的文件创建目标 - 每个目标依赖
.dapper - 执行时使用 dapper 运行脚本
.PHONY: deps
deps:
go mod tidy
- 清理整理 go 依赖
release:
./scripts/release.sh
- 执行发布脚本
.DEFAULT_GOAL := ci
.PHONY: $(TARGETS)
build/data:
mkdir -p $@
- 设置默认目标为 ci
- 声明伪目标
- 创建 build/data 目录
.PHONY: binary-size-check
binary-size-check:
scripts/binary_size_check.sh
.PHONY: image-scan
image-scan:
scripts/image_scan.sh $(IMAGE)
format:
gofmt -s -l -w $(GO_FILES)
goimports -w $(GO_FILES)
- 检查二进制文件大小
- 扫描指定的镜像
- 格式化 Go 代码
.PHONY: local
local:
DOCKER_BUILDKIT=1 docker build \
--build-arg="REPO TAG GITHUB_TOKEN GOLANG GOCOVER DEBUG" \
-t k3s-local -f Dockerfile.local --output=. .
- 使用Docker BuildKit 构建本地开发镜像
可以发现,我们可以在 make 后跟local,format,build/data,release这些参数,执行不同的运行逻辑.
执行过程
当我们执行 make 时,默认执行 make ci,而 ci 是 Target 中的目标, 正是 script/ 下的一个文件,内容如下
#!/bin/bash
set -e
SCRIPT_DIR=$(dirname $0)
pushd $SCRIPT_DIR
./download
./validate
./build
./package
popd
$SCRIPT_DIR/binary_size_check.sh
又因为 Target 依赖于 .dapper,故如果 .dapper不存在,它会执行 .dapper 的逻辑(下载dapper)
最终回到 DAPPER_DEBUG=1 DAPPER_DOCKER_BUILD_ARGS="--network host" ./.dapper ci, 关于dapper我们下面来讲.
Dockerfile.dapper
什么是 dapper
Dapper is a tool to wrap any existing build tool in an consistent environment. This allows people to build your software from source or modify it without worrying about setting up a build environment. The approach is very simple and taken from a common pattern that has adopted by many open source projects. Create a file called Dockerfile.dapper in the root of your repository. Dapper will build that Dockerfile and then execute a container based off of the resulting image. Dapper will also copy in source files and copy out resulting artifacts or will use bind mounting if you choose.
.dapper 是一个可执行的构建工具,它会运行一个构建容器,然后在容器里执行 scripts/ci;
而看到上面 ci 文件中的内容会发现,其执行了 download、validate、build、package 这几个脚本.
这不就和之前我们在 issue 中看到的情况有点像, 他直接自己本地执行的 download,build,package-cli
dapper 源码
分段解释一下 Dockerfile.dapper 的源码
ARG GOLANG=golang:1.23.3-alpine3.20
FROM ${GOLANG}
- 基于此镜像作为基础镜像
# Set proxy environment variables
ARG http_proxy
ARG https_proxy
ARG no_proxy
ENV http_proxy=${http_proxy} \
https_proxy=${https_proxy} \
no_proxy=${no_proxy}
- 传递代理设置,这里可能会引发网络问题,除非你可以保证代理地址可以从容器内访问
RUN apk -U --no-cache add \
bash git gcc musl-dev docker vim less file curl wget ca-certificates jq linux-headers \
zlib-dev tar zip squashfs-tools npm coreutils python3 py3-pip openssl-dev libffi-dev libseccomp \
libseccomp-dev libseccomp-static make libuv-static sqlite-dev sqlite-static libselinux \
libselinux-dev zlib-dev zlib-static zstd pigz alpine-sdk binutils-gold btrfs-progs-dev \
btrfs-progs-static gawk yq pipx \
&& [ "$(go env GOARCH)" = "amd64" ] && apk -U --no-cache add mingw-w64-gcc || true
- 安装编译和工具链所需的依赖
ENV TRIVY_VERSION="0.56.2"
RUN case "$(go env GOARCH)" in \
arm64) TRIVY_ARCH="ARM64" ;; \
amd64) TRIVY_ARCH="64bit" ;; \
s390x) TRIVY_ARCH="s390x" ;; \
*) TRIVY_ARCH="" ;; \
esac
RUN if [ -n "${TRIVY_ARCH}" ]; then \
wget --no-verbose "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-${TRIVY_ARCH}.tar.gz" \
&& tar -zxvf "trivy_${TRIVY_VERSION}_Linux-${TRIVY_ARCH}.tar.gz" \
&& mv trivy /usr/local/bin; \
fi
- 安装 Trivy 安全扫描工具
RUN GOPROXY=https://goproxy.cn,direct go install golang.org/x/tools/cmd/goimports@latest
RUN rm -rf /go/src /go/pkg
RUN if [ "$(go env GOARCH)" = "amd64" ]; then \
curl -sL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.55.2; \
fi
ARG SELINUX=true
ENV SELINUX=${SELINUX}
- 安装 goimports 工具
- 清理缓存
- 仅对于 amd64 进行静态分析
- 控制是否启用 SELinux
ENV DAPPER_RUN_ARGS="--privileged -v k3s-cache:/go/src/github.com/k3s-io/k3s/.cache -v trivy-cache:/root/.cache/trivy" \
DAPPER_ENV="REPO TAG DRONE_TAG IMAGE_NAME SKIP_VALIDATE SKIP_IMAGE SKIP_AIRGAP AWS_SECRET_ACCESS_KEY AWS_ACCESS_KEY_ID GITHUB_TOKEN GOLANG GOCOVER DEBUG" \
DAPPER_SOURCE="/go/src/github.com/k3s-io/k3s/" \
DAPPER_OUTPUT="./bin ./dist ./build/out ./build/static ./pkg/static ./pkg/deploy" \
DAPPER_DOCKER_SOCKET=true \
CROSS=true \
STATIC_BUILD=true
- 设置 Dapper 运行时配置
ENV HOME=${DAPPER_SOURCE}
WORKDIR ${DAPPER_SOURCE}
ENTRYPOINT ["./scripts/entry.sh"]
CMD ["ci"]
- 设置工作目录为源码中的路径
- 执行
./scripts/entry.sh ci,启动构建流程
entry.sh
#!/bin/bash
set -e
mkdir -p bin dist
if [ -e ./scripts/$1 ]; then
./scripts/"$@"
else
exec "$@"
fi
chown -R $DAPPER_UID:$DAPPER_GID .
- 如果 scripts/ci 存在,那就执行它(因为 $1 是传给 entry.sh 的第一个参数 ci)
$@是所有参数
总结
所以当我们在终端按下make 的时候,发生的是:
- make ci
- ./.dapper ci
- dapper 启动一个 Docker 容器,挂载当前代码目录,并在容器中执行构建命令(如 ci)
- 容器内执行
./scripts/entry.sh ci - 容器内执行
download,validate,build,package
在开发板上构建的时候,因为下载 dapper 这个过程可能会遇到网络问题(我没找到怎么解决)
所以我们可以跳过 dapper 这个过程,直接在网络好的本机进行 download,然后将 download 得到的 build 目录传给开发板,在开发板上执行 build、package-cli 命令. 又回到了之前这篇文章中说过的过程了,只是这次我们修改了内部的镜像并使用了私有镜像仓库.
在此之前我们 fork 出一个仓库上传到 gitee 然后在开发板上拉取源码进行构建.git clone https://gitee.com/ltxworld/k3s-riscv64.git
这里是LTX,感谢您阅读这篇博客,人生海海,和自己对话,像只蝴蝶纵横四海。