构建准备
安装 yq 工具,避免 download
脚本执行失败:
❯ sudo pacman -S go-yq
构建 k3s 过程中可能出现如下警告:
❯ make download
./.dapper download
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
Install the buildx component to build images with BuildKit:
https://docs.docker.com/go/buildx/
需要安装 buildx:
❯ sudo pacman -S docker-buildx
❯ pacman -Ql docker-buildx
docker-buildx /usr/lib/docker/cli-plugins/
docker-buildx /usr/lib/docker/cli-plugins/docker-buildx
Docker build wrapper
https://github.com/rancher/dapper
Docker Build architecture
https://docs.docker.com/build/architecture/
buildx - Installing
https://github.com/docker/buildx#manual-download
dapper 执行过程中可能出现如下错误:
❯ make download
./.dapper download
[+] Building 0.1s (5/11)
=> ERROR [2/8] RUN apk -U --no-cache add bash
ERROR: failed to solve: process "/bin/sh -c apk -U --no-cache add bash && if [ \"$(go env GOARCH)\" = \"amd64\" ]; then apk -U --no-cache add mingw-w64-gcc; fi" did not complete successfully: failed to create endpoint qus5uzen3gyvq3zkvs4pqgw87 on network bridge: failed to add the host (veth4abe592) <=> sandbox (veth52cbefa) pair interfaces: operation not supported
FATA[0000] exit status 1
make: *** [Makefile:13: download] Error 1
记得更新内核之后重启机器。
In my case, the error appears every time I update my Linux kernel. It disappears when I restart the computer.
Docker: failed to add the pair interfaces (operation not supported)
https://serverfault.com/questions/738773/docker-failed-to-add-the-pair-interfaces-operation-not-supported
由于 go 的限制,Dockerfile.dapper 中 GOPROXY
只能设置成 direct
:
RUN GOPROXY=direct go install golang.org/x/tools/cmd/goimports@gopls/v0.11.0
否则会出现如下的错误:
❯ export GOPROXY=https://goproxy.cn
❯ go install golang.org/x/tools/cmd/goimports@gopls/v0.11.0
go: golang.org/x/tools/cmd/goimports@gopls/v0.11.0: golang.org/x/tools/cmd/goimports@gopls/v0.11.0: invalid version: version "gopls/v0.11.0" invalid: disallowed version string
cmd/go/internal/modfetch: treat malformed versions as “not found” on the proxy path
https://github.com/golang/go/issues/32955
另外建议将 dapper 的 mode
显式设置成 bind
(默认的 auto
在我的机器上是 cp
):
diff --git a/Makefile b/Makefile
index aba74a95d2..0fad11ec3c 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ GO_FILES ?= $$(find . -name '*.go' | grep -v generated)
@mv .dapper.tmp .dapper
$(TARGETS): .dapper
- ./.dapper $@
+ ./.dapper -m bind $@
正式构建
❯ cd k3s
❯ mkdir -p build/data
❯ make download
❯ make generate
make download
, make generate
执行的命令是 ./.dapper download
, ./.dapper generate
,而实际的处理流程是先用 dapper 构建一个容器镜像,然后基于这个容器镜像创建容器并执行 scripts/download
, scripts/generate
脚本。
如果 dapper 所需的容器镜像构建成功,可以使用如下命令查看容器:
❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
k3s master 215e8fb356e5 About an hour ago 1.91GB
❯ docker run -it --rm -v .:/go/src/github.com/k3s-io/k3s/ k3s:master /bin/bash
上一节提到的问题都是 dapper 带来的,因此也完全可以不使用 dapper 准备构建环境,而直接在本地执行相应的脚本:
❯ cd k3s
❯ sudo rm -rf bin build
❯ ./scripts/download
❯ ./scripts/generate
执行之后会生成如下的文件:
❯ make
注意 Makefile 中的定义 .DEFAULT_GOAL := ci
,因此 make
等同于 make ci
,等效于如下命令(不完全等同,可以阅读 scripts/ci
脚本进行理解):
❯ ./scripts/build
❯ ./scripts/package-cli
其中 package-cli
属于 package
的一部分,package
除了执行 package-cli
外,会创建基于 alpine 的 k3s agent 镜像,以及打包用于无网络环境(airgap)安装所需的其他容器镜像(如 klipper-helm,klipper-lb,local-path-provisioner 等)。
最终生成 k3s 可执行文件如下:
❯ ls dist/artifacts/k3s
dist/artifacts/k3s
需要注意的是,build
和 package-cli
都会生成 k3s 可执行文件,build
根据源文件 cmd/server/main.go
生成的 k3s 可执行文件在 bin
目录下,而 package-cli
根据源文件 cmd/k3s/main.go
生成的 k3s 可执行文件在 dist/artifacts
目录下,此外,根目录下的源文件 main.go
实际上也可以构建 k3s 可执行文件,它和 cmd/server/main.go
相比功能稍有差异,如没有 ctr
和 token
子命令且无法作为 containerd
使用,但主要的目的还是用来执行 go generate
以生成资源文件用于 go-bindata 打包。
package-cli
将整个 bin
目录打包进其生成的 k3s 可执行文件(--data-dir
选项决定解压路径,--prefer-bundled-bin
选项决定 PATH
环境变量的优先级),package-cli
生成的 k3s 可执行文件在运行时将作为父进程 exec 调用 build
生成的 k3s 可执行文件。
❯ ls -l /var/lib/rancher/k3s/data/current/bin | grep k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 containerd -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 crictl -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 ctr -> k3s
-rwxr-xr-x 1 root root 180893608 Jun 14 16:17 k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 k3s-agent -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 k3s-certificate -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 k3s-completion -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 k3s-etcd-snapshot -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 k3s-secrets-encrypt -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 k3s-server -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 k3s-token -> k3s
lrwxrwxrwx 1 root root 3 Jun 15 09:12 kubectl -> k3s
影响 build
脚本的环境变量如下:
❯ export DEBUG=true
❯ export STATIC_BUILD=true
其中 DEBUG
用于生成带调试符号的 k3s 可执行文件,STATIC_BUILD
用于静态编译,而 package-cli
脚本则没有环境变量用于控制构建,如要生成带调试符号的 k3s 可执行文件需要手工修改 package-cli
脚本:
--- a/scripts/package-cli
+++ b/scripts/package-cli
@@ -56,11 +56,10 @@ CMD_NAME=dist/artifacts/k3s${BIN_SUFFIX}
LDFLAGS="
-X github.com/k3s-io/k3s/pkg/version.Version=$VERSION
-X github.com/k3s-io/k3s/pkg/version.GitCommit=${COMMIT:0:8}
- -w -s
"
TAGS="urfave_cli_no_docs"
STATIC="-extldflags '-static'"
-CGO_ENABLED=0 "${GO}" build -tags "$TAGS" -ldflags "$LDFLAGS $STATIC" -o ${CMD_NAME} ./cmd/k3s/main.go
+CGO_ENABLED=0 "${GO}" build -tags "$TAGS" -gcflags "all=-N -l" -ldflags "$LDFLAGS $STATIC" -o ${CMD_NAME} ./cmd/k3s/main.go
根据上面对 build
和 package-cli
脚本的分析可知,如果只是开发环境进行调试的话,可以直接使用 cmd/server/main.go
进行构建并测试:
❯ ./scripts/download
❯ mkdir -p build/data
❯ mkdir -p build/static
❯ go generate
❯ go build -tags ctrd -o k3s ./cmd/server
或者直接使用根目录下的 main.go
进行构建并测试(不支持 containerd,无需指定 -tags
条件构建选项):
❯ ./scripts/download
❯ mkdir -p build/data
❯ mkdir -p build/static
❯ go generate
❯ go build
不使用 dist/artifacts/k3s
带来一个讨厌的问题是命令行自动补全没法用:
❯ ./k3s serFATA[0000] flag provided but not defined: -generate-bash-completion
❯ ./k3s --generate-bash-completion
Incorrect Usage. flag provided but not defined: -generate-bash-completion
可以简单的规避如下:
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -43,6 +43,7 @@ func main() {
os.Args[0] = cmd
app := cmds.NewApp()
+ app.EnableBashCompletion = true
app.Commands = []cli.Command{
cmds.NewServerCommand(server.Run),
cmds.NewAgentCommand(agent.Run),
diff --git a/main.go b/main.go
index ad09773f3e..36cab5a519 100644
--- a/main.go
+++ b/main.go
@@ -27,6 +27,7 @@ import (
func main() {
app := cmds.NewApp()
+ app.EnableBashCompletion = true
app.Commands = []cli.Command{
cmds.NewServerCommand(server.Run),
cmds.NewAgentCommand(agent.Run),
自动补全
zsh
❯ vi ~/.zshrc
fpath+=(~/.oh-my-zsh/completion.d)
❯ mkdir ~/.oh-my-zsh/completion.d
❯ k3s completion zsh > ~/.oh-my-zsh/completion.d/_k3s
❯ rm -f ~/.zcompdump*
注意 fpath
的修改要在 source
之前。
Not sure where to put completions with Oh My Zsh
https://github.com/ohmyzsh/ohmyzsh/discussions/10774
bash
❯ k3s completion bash -i
Autocomplete for bash added to: /home/runsisi/.bashrc
或:
❯ k3s completion bash | sudo tee /usr/share/bash-completion/completions/k3s
命令行工具
k3s 可执行文件可以作为 kubectl, crictl, ctr, containerd 等程序使用:
❯ k3s
NAME:
k3s - Kubernetes, but small and simple
USAGE:
k3s [global options] command [command options] [arguments...]
VERSION:
v1.27.2+k3s-b66a1183 (b66a1183)
COMMANDS:
server Run management server
agent Run node agent
kubectl Run kubectl
crictl Run crictl
ctr Run ctr
可以建立相应的符号链接直接使用,如:
❯ k3s ctr -v
ctr github.com/k3s-io/containerd v1.7.1-k3s1
❯ sudo ln -sr dist/artifacts/k3s /usr/local/bin/ctr
❯ /usr/local/bin/ctr -v
ctr github.com/k3s-io/containerd v1.7.1-k3s1
需要注意的是,前面提到的不使用 dist/artifacts/k3s
带来的另一个讨厌的问题是 kubectl
, crictl
, ctr
子命令都必须使用符号链接进行使用(不过 containerd
在任何时候都需要通过符号链接使用,而根目录下的 main.go
根本就不支持 containerd
)。
Creating a Multi-Call Linux Binary
https://www.redbooks.ibm.com/abstracts/tips0092.html
最后修改于 2023-06-21