Kubernetes 构建过程
1、构建方式
Kubernetes 构建方式可以分为 3 种,分别是本地环境构建、容器环境构建、Bazel环境构建。
1.1 本地环境
make
make all执行上述语句会编译 Kubernetes 的所有组件,组件二进制文件输出的相对路径是 _output/bin/。如果我们需要对 Makefile 的执行过程进行调试,可以在命令后面加 -n 参数,输出但不执行所有执行命令,这样可以展示更详细的构建过程。
如果只需要构建单个组件,需要添加 WHAT 参数指定:
make WHAT=cmd/kubectlMakefile 文件
使用自动化工具来构建和测试程序是一个良好的约束规范,在 Kubernetes 的源码根目录中,有两个与 Makefile 相关的文件:
Makefile:描述了整个项目所有代码文件的编译顺序、编译规则及编译后的二进制输出等;
Makefile.generated_files:描述了代码生成的逻辑。
下面是本地环境构建的 makefile 文件定义,执行命令会去调用 hack/make-rules/build.sh 脚本完成构建。
define ALL_HELP_INFO
# Build code.
endef
.PHONY: all
ifeq ($(PRINT_HELP),y)
all:
echo "$$ALL_HELP_INFO"
else
all:
hack/make-rules/build.sh $(WHAT)
endif1.2 容器环境
make release
make quick-release前者构建所有的目标平台(Darwin、Linux、Windows),构建过程会比较久,并同时执行单元测试过程;后者只构建当前平台,并略过单元测试过程。
define RELEASE_HELP_INFO
# Build a release
endef
.PHONY: release release-in-a-container
ifeq ($(PRINT_HELP),y)
release release-in-a-container:
echo "$$RELEASE_HELP_INFO"
else
release release-in-a-container: KUBE_BUILD_CONFORMANCE = y
release:
build/release.sh
release-in-a-container:
build/release-in-a-container.sh
endif
define RELEASE_SKIP_TESTS_HELP_INFO
# Build a release, but skip tests
endef
.PHONY: release-skip-tests quick-release
ifeq ($(PRINT_HELP),y)
release-skip-tests quick-release:
echo "$$RELEASE_SKIP_TESTS_HELP_INFO"
else
release-skip-tests quick-release: KUBE_RELEASE_RUN_TESTS = n # 将其设为 n 则跳过运行单元测试
release-skip-tests quick-release: KUBE_FASTBUILD = true # 将其设为 true 则跳过跨平台交叉编译
release-skip-tests quick-release:
build/release.sh
endif整个构建过程主要分为五步:
verify_prereqs
进行构建环境的配置及验证。该过程会检查本机是否安装了 Docker 容器环境,而对于 Darwin(一种类 Unix 作业系统,是 macOS 和 iOS 操作环境的作业系统部份)平台,该过程会检查本机是否安装了 docker-machine 环境。
build_image
使用
build/build-image/Dockerfile文件来构建镜像。# Set up the context directory for the kube-build image and build it. function kube::build::build_image() { # 创建构建镜像的文件夹 mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}" # Make sure the context directory owned by the right user for syncing sources to container. chown -R "${USER_ID}":"${GROUP_ID}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/" chmod u+w "${LOCAL_OUTPUT_BUILD_CONTEXT}/localtime" # 复制构建所需的文件信息 cp "${KUBE_ROOT}/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" cp "${KUBE_ROOT}/build/build-image/rsyncd.sh" "${LOCAL_OUTPUT_BUILD_CONTEXT}/" dd if=/dev/urandom bs=512 count=1 2>/dev/null | LC_ALL=C tr -dc 'A-Za-z0-9' | dd bs=32 count=1 2>/dev/null > "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" chmod go= "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" # 构建容器镜像 kube::build::docker_build "${KUBE_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false' "--build-arg=KUBE_CROSS_IMAGE=${KUBE_CROSS_IMAGE} --build-arg=KUBE_CROSS_VERSION=${KUBE_CROSS_VERSION}" # Clean up old versions of everything kube::build::docker_delete_old_containers "${KUBE_BUILD_CONTAINER_NAME_BASE}" "${KUBE_BUILD_CONTAINER_NAME}" kube::build::docker_delete_old_containers "${KUBE_RSYNC_CONTAINER_NAME_BASE}" "${KUBE_RSYNC_CONTAINER_NAME}" kube::build::docker_delete_old_containers "${KUBE_DATA_CONTAINER_NAME_BASE}" "${KUBE_DATA_CONTAINER_NAME}" kube::build::docker_delete_old_images "${KUBE_BUILD_IMAGE_REPO}" "${KUBE_BUILD_IMAGE_TAG_BASE}" "${KUBE_BUILD_IMAGE_TAG}" # 运行存储容器并挂载卷 kube::build::ensure_data_container # ,运行同步容器并挂载存储容器的卷,然后通过 rsync 命令同步 Kubernetes 源码到存储容器的卷中 kube::build::sync_to_container }kube::build::run_build_command make cross
运行构建容器并在构建容器内部执行构建 Kubernetes 源码的操作。
copy_output
使用同步容器,将编译后的代码文件复制到主机上。
package_tarballs
最终,代码文件以
tar.gz压缩包的形式输出至_output/release-tars文件夹。
1.3 Bazel环境
Bazel 是 Google 公司开源的一个自动化软件构建和测试工具,其使用分布式缓存和增量构建方法,使构建更加快速,并且支持包括运行编译器和链接器以生成可执行程序和库。Bazel 在构建速度、可扩展性、灵活性及跨语言和对不同平台的支持上比其他例如 maven 等构建工具更加出色。
2、代码生成器
# TODO(thockin): Remove this in v1.29.
.PHONY: generated_files
generated_files:
echo "'make generated_files' is deprecated. Please use hack/update-codegen.sh instead."
ifneq ($(PRINT_HELP), y)
false
endif在 update-codegen 文件中有 13 个 codegen:: 开头的方法,用于代码的生成,其中 codegen::conversions、codegen::deepcopy、codegen::defaults、codegen::openapi 用于源码的构建。
2.1 Tags
代码生成器通过 Tags 来识别一个包是否需要生成代码及确定生成代码的方式,Kubernetes 提供的 Tags 可以分为如下两种:
全局:定义在每个包的 doc.go(
pkg/apis/<group>/<version>/doc.go) 文件中,对整个包中的类型自动生成代码。// +k8s:deepcopy-gen=package package core // import "k8s.io/kubernetes/pkg/apis/core"局部:定义在 Go 语言的类型声明上方(要与类型声明至少空格一行)。
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 为 PersistentVolumeList 生成返回类型为 runtime.Object 的 DeepCopyObject 函数 // PersistentVolumeList represents a list of PVs type PersistentVolumeList struct { metav1.TypeMeta // +optional metav1.ListMeta Items []PersistentVolume }
2.2 deepcopy-gen
一个自动生成 DeepCopy 函数的代码生成器。给定一个包的目录路径作为输入源,它可以为其生成 DeepCopy 相关函数,这些函数可以有效地执行每种类型的深复制操作。
deepcopy-gen /
--v 2 / # 日志等级
--bounding-dirs . / # 依赖的包并为其生成深复制的类型
-i github.com/lt90s/deepcopy-gen-demo/types / # 需要进行代码生成的包
-O zz_generated.deepcopy # 生成的文件名称2.3 defaulter-gen
一个自动生成 Defaulter 函数的代码生成器。给定一个包的目录路径作为输入源,它可以为其生成 Defaulter 相关函数,这些函数可以为资源对象生成默认值(初始值)。属于全局 Tags,没有局部 Tags,其值可以为 TypeMeta 等类型,表示为当前类型生成初始值。
# // +k8s:defaulter-gen=TypeMeta
defaulter-gen /
--v 2 / # 日志等级
--bounding-dirs . / # 依赖的包并为其生成深复制的类型
-i github.com/lt90s/deepcopy-gen-demo/types / # 需要进行代码生成的包
-O zz_generated.defaults # 生成的文件名称2.4 conversion-gen
一个自动生成 Convert 函数的代码生成器。给定一个包的目录路径作为输入源,它可以为其生成 Convert 相关函数,这些函数可以为对象在内部和外部类型之间提供转换函数。例如从v1beta1转换为internal内部版本,从internal内部版本转换为v1版本。
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/core 转换包
// +k8s:conversion-gen-external-types=k8s.io/api/core/v1 目标包2.5 openapi-gen
一个自动生成 OpenAPI 定义文件(OpenAPI Definition File)的代码生成器,给定一个包的目录路径作为输入源,它可以为其生成 OpenAPI 定义文件,该文件用于 kube-apiserver 服务上的 OpenAPI 规范的生成。
3、gengo
Kubernetes 的代码生成器都是在 k8s.io/gengo 包的基础上实现的,通过一个输入包路径(--input-dirs)参数,根据 gengo 的词法分析、抽象语法树等操作,最终生成代码并输出(--output-file-base)。通过 tree 命令可以很好的查看目录结构(需要注意的是使用 apt 安装而非 snap 安装,不然会导致切出用户目录报错无法使用)。
tree vendor/k8s.io/gengo -L 1
vendor/k8s.io/gengo
├── args # 代码生成器的通用命令行参数
├── examples # 包含 deepcopy-gen、defaulter-gen 等代码生成器的生成逻辑
├── generator # 代码生成器通用接口Generator
├── LICENSE
├── namer # 命名管理,支持创建不同类型的名称
├── parser # 代码解析器,用来构造抽象语法树
└── types # 类型系统,用于数据类型的定义及类型检查算法的实现
如上,其生成逻辑分为五步,下面逐个分析。
3.1 Gather The Info
GO 语言标准库提供了 go/build 工具,该工具支持 Go 语言的构建标签机制来构建约束条件。类似于 //+build linux darwin 的包注释信息,这就是 Go 语言编译时的约束条件,其也被称为条件编译。
构建标签:在源码里添加注释信息,比如 //+build linux,表示源码文件只在 Linux 平台上才会被编译;
文件后缀:改变代码文件的后缀,比如 foo_linux.go,该后缀决定了源码文件只在 Linux 平台上才会被编译。
该过程就是通过指定的包路径,将代码文件全部加载进内存当中。
3.2 Lexer/Parser
3.3 AST Generator
3.4 Type Checker
3.5 Code Generation
- 感谢你赐予我前进的力量