使用 Kubernetes 构建 CI 作业及处理通用进程——第 1 部分

Table of Content

Kubernetes,想必大家都不陌生。据 CNCF 2020 年的调查数据,Kubernetes 的发展势不可挡,本文的大部分读者也很可能正在使用,或正在将工作负载转移到 Kubernetes。在运行工作负载,准备、或设置开发环境时, Kubernetes 的优势很明显。另外,它也能帮助其他进程任务和构建作业。对于任何 DevOps 工程师来说,Kubernetes 集群原始、按需配用的计算能力都是工作中不可或缺的部分。

Kubernetes 内容系列由上下两部分组成,主要向大家展示如何在不同用例中使用 Kubernetes。本篇博客内容是的第一部分,第二部分的内容,请点击链接查看。

另外,如果你感兴趣的话,还有一些 Kubernetes 的入门内容:Docker vs Kubernetes ——对立还是统一

我们的目标

在深入了解技术细节之前,先要厘清目标。我们真正想要实现什么?以及为什么?

首先需要了解我们希望运行的进程作业类型。最简单的例子是构建作业,需要为不同的操作系统架构编译代码。通过并行运行每个操作系统架构编译,此类作业的并行化可轻松实现。构建作业的例子可以进一步扩展到 DevOps 工程师可能遇到的其他进程作业,如通用数据进程任务或其他耗时进程。

在我之前的博客中,我写了一些简单的,主要适用于单机并行化的解决方案,以及如何利用 CI 系统的特性进行并行化。这些方法基本能满足大部分的 CI 需求来说,但在面对 CI 系统的上下文时,可能会有所不足。对比之下,Kubernetes 解决方案完全不受限制,而且在功能和计算能力方面更加灵活。因此,Kubernetes 是一个很实用的工具,值得花时间去琢磨,以便在需要时能用上。

在这篇文章中,我将回顾 Kubernetes 的基本构建块——Docker容器和Kubernetes pod。在下一篇博客中,我将使用 Kubernetes Job 对象演示更强大、更丰富的抽象功能。本文的讲解,我先假定大家只对 Linux、Git 和 Bash 脚本有一些基础知识,但并不了解 Docker Kubernetes

运行示例代码

运行代码需要提前完成以下操作,建议你在阅读本文时运行这些命令:

以上所有命令都应该从存储库的根目录运行。因此,在拆分代码存储库之后,需要先进行复制,并在代码存储库根目录下打开一个终端。

To make the code samples easier to run, set the following environment variables in your shell (replace YourGitHub* values with your relevant details):

为了使代码示例更容易运行,请在 shell 中设置以下环境变量(用相关具体信息替换 YourGitHub* 值):

/export GITHUB_TOKEN=YourGitHhubPersonalAccessToken 
export GITHUB_USER=YourGitHubUserName/

同时,设置以下环境变量,让示例代码更加简洁,也更容易处理。

/export BUILDER_IMAGE=ghcr.io/orihoch/k8s-ci-processing-jobs-builder/

基本的工作单元——Docker 映像/容器

Kubernetes 的基本工作单元是 Docker 映像,用于在集群上运行容器。用户需要为要单独为各个类型的作业定义映像,可以是简单的构建作业(如本例),或者是更复杂的测试、集成、部署任务。

我们的示例将使用一个简单的 Go 构建作业,为各操作系统架构构建 Hello-World 二进制文件。注意,虽然我只在示例中选择了 Go 构建作业,但这个作用也可以是 C++ 或任何其他的构建或进程任务。Docker 映像是通过 Dockerfile 设置的,我们可以在这里为任务安装所需的系统依赖项。针对本示例,我使用了 golang 的一个基础映像,其中包含了所有必需的依赖项。然后,添加需要进一步编译的 Golang 源代码和 entrypoint.sh 脚本,该脚本是一个 Bash 脚本,用于处理编译:

# builder/Dockerfile 
FROM golang:1.16 
RUN apt-get update && apt-get install -y jq
COPY main.go src/ 
COPY entrypoint.sh ./ 
ENV GOOS=linux ENV GOARCH=amd64 
ENTRYPOINT ["./entrypoint.sh"]

entrypoint.sh 脚本有两个功能:一是列举可用的操作系统架构,二是构建作业:

/#!/usr/bin/env bash
# builder/entrypoint.sh
if [ "${1}" == "--list" ]; then
exec go tool dist list
else
export GOOS="$(echo "${1}" | cut -d"/" -f1)" &&
export GOARCH="$(echo "${1}" | cut -d"/" -f2)" &&
REPO_USER="${2}" &&
TAG="${3}" &&
if [ "${GOOS}" == "windows" ]; then EXT=.exe; fi &&
echo GOOS=$GOOS GOARCH=$GOARCH REPO_USER=$REPO_USER TAG=$TAG EXT=$EXT &&
UPLOAD_URL="$(curl -s -u "${REPO_USER}:${TOKEN}" -H "Accept: application/vnd.github.v3+json"
"https://api.github.com/repos/${REPO_USER}/k8s-ci-processing-jobs-examples/releases/tags/${TAG}" | jq -r .upload_url | cut -d'{' -f1)" &&
echo UPLOAD_URL=$UPLOAD_URL &&
echo Compiling... &&
go build -o bin/main src/main.go &&
echo OK &&
echo Publishing... &&
curl -s -u "${REPO_USER}:${TOKEN}" -X POST -H "Accept: application/vnd.github.v3+json" 
-H "Content-Type: application/x-executable" --data-binary @bin/main 
"${UPLOAD_URL}?name=hello-world-${GOOS}-${GOARCH}${EXT}" &&
echo OK
fi/

你可以在  builder/ 目录下查看代码存储库中的所有映像文件。

映像可以在本地运行,进行功能测试,并在部署到 Kubernetes 之前确保脚本是正确的。映像将发布到 GitHub Docker 注册表(你可以点击链接,查看执行这个操作的 CI 脚本)。

运行以下命令,列出生成脚本支持的操作系统架构:

/docker run $BUILDER_IMAGE --list/

docker run 命令根据 $BUILDER_IMAGE (ghcr.io/orihoch/k8s-ci-processing-job -builder) 中定义的映像创建一个容器。-list 参数传递到容器,该容器由映像控制,并列出兼容的操作系统架构(点击链接,可查看进行该项操作的代码)。

要构建和发布二进制文件,首先需要在 GitHub 存储库中进行版本发布(这是你之前拆分的 k8s-ci-processing-jobs-builder 存储库),假设我们发布了一个名为“v0.0.1”的版本。

以下命令将使用不同的操作系统架构,按顺序运行几个构建:

/docker run -e TOKEN=$GITHUB_TOKEN $BUILDER_IMAGE linux/386 $GITHUB_USER v0.0.1 &&
docker run -e TOKEN=$GITHUB_TOKEN $BUILDER_IMAGE linux/amd64 $GITHUB_USER v0.0.1 &&
docker run -e TOKEN=$GITHUB_TOKEN $BUILDER_IMAGE darwin/arm64 $GITHUB_USER v0.0.1 &&
docker run -e TOKEN=$GITHUB_TOKEN $BUILDER_IMAGE windows/arm $GITHUB_USER v0.0.1/

几个参数的具体解释:

  • -e TOKEN=$GITHUB: 我们将一个环境变量传递到包含 GitHub 令牌的容器中,以便构建器脚本可以将已编译的资产添加到发行版中。
  • $BUILDER_IMAGE windows/arm $GITHUB_USER v0.0.1: Builder image 后面是指需要编译的操作系统架构参数,以及需要添加资产的 github 用户和发布名称。

一旦容器完成运行,我们就能在 GitHub 存储库版本中看到已发布的工件。

使用 Kubectl 将容器部署到 Pod

一旦你有了 Docker  映像,你需要找到方法以在集群上运行。Kubernetes 集群中,任何工作负载的最基本构建块都是 pod。利用 kubectl CLI,你可以在集群上快速部署容器。

首先,在 GitHub 存储库上创建一个新版本,假设我们将其命名为“v0.0.2”,这样我们就可以看到已发布的工件。

Kubectl run 命令可以快速将 pod 部署到集群。运行以下命令,在集群上部署单个 pod

/kubectl run builder-linux-386 --restart=Never --env=TOKEN=$GITHUB_TOKEN 
--image=$BUILDER_IMAGE -- linux/386 $GITHUB_USER v0.0.2/

我们可以解释一下这个命令中的参数信息:

  • builder-linux-386要创建的 pod 的名称,名称不能重复,注意在同一个命名空间中不能有两个同名的 pod
  • –restart=NeverKubernetes 默认情况下会主动重启 pod,对于只需要运行一次的构建脚本,我们要禁用该功能。
  • –env=TOKEN=$GITHUB_TOKEN在容器添加一个支持的环境变量
  • –image=$BUILDER_IMAGE需要部署的映像
  • — linux/386 $GITHUB_USER v0.0.2:放在所有参数后,作为参数传递给映像。这些参数与我们在 docker run 命令中使用的参数相同。

运行下列命令,启动另外两个 pod, 进行不同的操作系统架构编译:

/kubectl run builder-linux-amd64 --restart=Never --env=TOKEN=$GITHUB_TOKEN 
--image=$BUILDER_IMAGE -- linux/amd64 $GITHUB_USER v0.0.2/
kubectl run builder-darwin-arm64 --restart=Never --env=TOKEN=$GITHUB_TOKEN 
--image=$BUILDER_IMAGE -- darwin/arm64 $GITHUB_USER v0.0.2

我们可使用下列命令检查创建的 pod 的状态:

/kubectl get pods/

你应该能看到 3 个 pod 并行运行,且状态在一直变化,直到最终的 “完成”状态。另外,你应该能够在 GitHub 上看到发布版本的二进制文件。

需要注意的是,记得在 pod 完成运行后进行删除,及时清理以防止集群出现混乱。可以使用以下命令完成:

当构建中断时会发生什么?工作负载调试

一般来说,构建很少会中断,但保险起见,我们看看如何调试 Kubernetes 中运行的 pod

首先需要部署一个 builder pod,因为它使用的是不兼容的操作系统架构,所以无法正常运行。

/kubectl run builder-linux-quantum --restart=Never --env=TOKEN=$GITHUB_TOKEN 
--image=$BUILDER_IMAGE -- linux/quantum $GITHUB_USER v0.0.2/

检查 pod 状态

/kubectl get pod builder-linux-quantum/

可以看到状态显示为‘错误’,查看 builder job 日志了解更多细节:

/kubectl logs builder-linux-quantum/

这个错误显示,是不支持 linux/quantum 架构(正如预期,目前还不支持编译quantum 计算架构)。

有时日志的信息还不够,我们需要在 pod 上运行一些代码,我们可以覆盖默认入口点,并运行交互式 shell

/kubectl run builder-linux-quantum-shell --restart=Never --env=TOKEN=$GITHUB_TOKEN 
--image=$BUILDER_IMAGE -it --command -- bash/

现在,你应该能看到 pod 内的交互式 shell 会话,接着,运行 pod 入口点脚本进行调试:

/./entrypoint.sh linux/quantum YourGitHubUserName v0.0.2/

记住使用以下命令,及时清理创建的 pod,以防止集群中出现混乱:

/./entrypoint.sh linux/quantum YourGitHubUserName v0.0.2/
kubectl delete pods builder-linux-quantum builder-linux-quantum-shell

总结

在本文中,我们学习了如何将工作负载部署到 Kubernetes。考虑到每个人在 Docker Kubernetes 的技能和经验程度参差不齐,因此大家吸收的内容估计也不一样。如果你发现上述内容有所缺失,可以阅读完整的官方文档进行深入学习。Docker 和 Kubernetes 的文档很齐全,适合不同技能程度的爱好者:

诚然,容器和 pod 这些基本构建块的作用不小,但也有一些局限性。最主要的就是都需要大量的手动操作——每个 pod 都必须亲自启动。因此,如果按照示例运行,当你需要构建44 个兼容的操作系统架构时,你必须启动 44 pod。当然,你也可以使用一些 Bash Python 脚本来完成,但是你还是要跟踪这些作业,并重试失败的作业。除了基础 pod 外,Kubernetes 还提供了几个抽象功能,这些 pod 可以弥补上面提到的不足。在下一篇博客中,我将展示如何使用 Kubernetes job object 扩展我们的示例。

Whitepaper download

Table of Content

缩短你的构建

Incredibuild 使您的团队能够提高工作效率并专注于创新。
分享

相关博客文章

CI/CD
CRA 如何防止近期供应链攻击
[4 11 月 2025]
Made CI 85% Faster
C++, CI
我们如何让 CI 快 85% —— 内部先行实践实录
[30 10 月 2025]
Written words: Agentic AI
C++, DevOps
Agentic AI 如何改变软件开发
[23 10 月 2025]
[wpml_language_selector_widget]