从零开始构建你的第一个 CI/CD 流水线
构建你的第一个 CI/CD 流水线可能看起来令人生畏,尤其是在工作环境复杂且难以进行实验的情况下。然而,核心原则其实非常简单:流水线就是一个自动化流程,用于在干净、可复现的环境中安装依赖、运行测试并构建你的应用程序。本指南将带你了解基本概念和实际步骤,教你如何从零开始构建 CI/CD 流水线,将手动且易出错的部署转变为可靠的自动化工作流。
你不需要 Kubernetes 或生产级系统来练习 CI/CD。从一个小的项目、一个 Git 仓库和一个 CI 工具(如 GitHub Actions、GitLab CI 或 Jenkins)开始。最关键的一步是在编写任何流水线配置之前,确保你的构建在本地是可复现的。
基础:可复现性是关键
在编写第一个流水线文件之前,你的项目必须能够从零开始干净地构建。如果在一个全新的仓库克隆上构建失败,那么在 CI 中也会失败。
步骤 1:让你的本地构建可复现
黄金法则是确保你的应用程序在本地机器上从干净状态成功构建。例如:
- Node.js:
npm ci && npm test && npm run build - Python:
pip install -r requirements.txt && pytest - Go:
go test ./... && go build
步骤 2:提交锁定文件
锁定文件(如 package-lock.json、go.sum 或 Pipfile.lock)对于确定性构建至关重要。没有它们,你的 CI 流水线可能会安装与你本地不同的依赖版本,导致不可预测的测试失败。在 Node.js 中,npm ci 会严格执行锁定文件,防止这种“依赖漂移”。
编写你的第一个流水线
有了可复现的构建,你现在可以将其自动化。一个基本的流水线包含三个阶段:构建、测试和部署。
步骤 3:选择你的 CI/CD 工具
你的选择通常取决于代码托管的位置。以下是三种常见选项:
| 工具 | 最适合 | 流水线定义 | 自托管选项 |
|---|---|---|---|
| GitHub Actions | 已在 GitHub 上的项目,公共仓库(免费)。 | .github/workflows/*.yml |
是(自托管运行器) |
| GitLab CI | 集成了 Git 托管的全功能 DevOps 平台。 | .gitlab-ci.yml |
是(GitLab CE) |
| Jenkins | 高度可定制,成熟的生态系统,复杂的构建逻辑。 | Jenkinsfile(Groovy) |
是(基于 Java,插件驱动) |
步骤 4:创建最小流水线配置
流水线定义是仓库根目录下的一个 YAML 或 Groovy 文件,告诉 CI 运行器要做什么。
示例:.gitlab-ci.yml(GitLab CI)
这定义了一个流水线,用于构建 Go 应用程序,将二进制文件保存为制品,然后在后续阶段执行它。
default:
image: golang
stages:
- build
- run
build go:
stage: build
script:
- go build
artifacts:
paths:
- array
run go:
stage: run
script:
- ./array
示例:Jenkinsfile(声明式流水线)
这做同样的事情,使用 Docker 镜像运行 Node.js 应用程序。
pipeline {
agent { docker { image 'node:20-alpine' } }
stages {
stage('Build') {
steps { sh 'npm ci && npm run build' }
}
stage('Test') {
steps { sh 'npm run test' }
}
}
}
⚠️ 安全警告: 切勿在仓库中硬编码环境变量,如数据库 URL 或 API 密钥。使用 CI 工具内置的密钥管理器(GitLab CI Variables、Jenkins Credentials 或 GitHub Actions Secrets)将它们注入到你的作业中。
在阶段之间共享数据
现代流水线的一个关键特性是能够在作业之间共享数据。如果没有这个功能,你必须在每个阶段重新构建应用程序,浪费时间和资源。
步骤 5:使用制品
制品是从一个作业保存并可供后续作业使用的文件或目录。在 GitLab CI 示例中,build go 作业保存了 array 二进制文件。run go 作业随后自动下载该二进制文件,无需重新编译即可使用。
自动化部署(可选)
一旦 CI 正常工作,你可以添加持续部署。原则很简单:部署只应在测试通过后进行。
步骤 6:实现基本部署策略
对于简单的应用程序,SSH 命令可以将文件复制到你的服务器。
示例:使用 rsync 部署
deploy:
stage: deploy
image: alpine:latest
only:
- main
script:
- apk add --no-cache openssh-client rsync
- rsync -avz --delete dist/ deploy@yourserver:/var/www/app/
如果你使用容器,部署阶段通常构建 Docker 镜像,推送到注册表,然后使用 SSH 在生产服务器上触发 docker compose pull && docker compose up -d。
实践与故障排除
仅仅阅读 CI/CD 是不够的。要真正学习,你必须运行流水线并故意破坏它们。
步骤 7:故意破坏以学习
这是真正理解发生的地方。尝试以下实验:
- 删除一个依赖声明。CI 会失败吗?为什么?
- 删除锁定文件。版本漂移会破坏构建吗?
- 添加一个不稳定的测试。它在本地通过但在 CI 中失败吗?这通常暴露了干净 CI 环境中的差异。
步骤 8:使用测试报告和日志
CI 应提供哪些测试失败、哪些慢、哪些不稳定的可见性。使用日志不仅是为了查找错误,还要理解整个过程:依赖安装需要多长时间?构建具体在哪里失败?
常见问题
构建第一个 CI/CD 流水线需要多长时间? 对于一个简单的项目,你可以在不到一小时内运行一个基本流水线。GitLab CI 实践实验室估计基础流水线需要 15 分钟。时间通常花在编写配置文件和解决可复现性问题。
初学者应该选择 Jenkins、GitHub Actions 还是 GitLab CI? 如果你的代码在 GitHub 上,GitHub Actions 是自然的选择。对于一体化平台,GitLab CI 非常出色。Jenkins 非常强大,但学习曲线较陡,需要更多维护,因此通常更适合更大、更复杂的设置。
CI/CD 中的制品和缓存有什么区别?
制品用于在阶段之间传递文件(例如,将编译后的二进制文件从构建阶段传递到部署阶段)。缓存用于通过重用不同流水线运行中的文件(如依赖项 node_modules)来加速构建。
为什么我的测试在本地通过但在 CI 流水线中失败? 这通常是环境漂移的迹象。你的本地机器可能有不同的软件版本或环境变量。这就是为什么提交锁定文件至关重要,以及在本地从干净克隆运行项目是必要的第一步。
构建 CI/CD 流水线需要 Docker 吗? 不严格需要,但强烈推荐。在 Docker 容器内运行作业可确保隔离性和可复现性。它保证每个作业在干净、已知的环境中运行,防止作业相互干扰或污染主机服务器。
— Editorial Team
暂无评论。