前言
如果你经历过这样的场景——开发人员写完代码,用 FTP 把文件传到服务器上,手动重启应用,然后在浏览器里刷新看看有没有跑起来——你就会理解 CI/CD 出现的意义。这篇文章带你从零开始理解 CI/CD 的本质,以及它如何从一次次深夜上线的痛苦中诞生。
一、部署的黑暗时代:手动操作有多痛
在没有自动化之前,一次典型的部署流程是这样的:
1. 开发人员本地跑通代码,提交到 SVN/Git
2. 运维人员从仓库拉取代码,本地编译打包
3. 停掉线上服务,替换旧的 jar/war 包
4. 重启服务,手动检查日志确认是否正常
5. 如果出问题,再手动回滚到旧版本
这套流程的问题:
- **速度慢**:一次部署动辄半小时到几小时
- **不可重复**:每次操作步骤略有不同,取决于"谁来操作"
- **容易出错**:漏传一个文件、配错一个参数,就是一次线上事故
- **回滚困难**:旧版本在哪里?配置文件改了哪些?一问三不知
- **无法审计**:谁在什么时间改了什么,全凭记忆
**踩坑提示**:很多团队至今还在用"FTP + 手动重启"的方式部署,不是因为他们不知道 CI/CD,而是觉得"项目就两三个人,不值得搞自动化"。但事实是,越是小团队,越需要用自动化来减少人为错误。
二、CI/CD 是什么:拆解三个字母
CI/CD 是一个缩写,实际上包含了两个(或三个)概念:
CI:Continuous Integration(持续集成)
核心思想:开发人员把代码频繁地(每天多次)合并到主干分支,每次合并都自动触发构建和测试。
解决的问题:代码冲突积攒到发版时才解决,合并地狱(merge hell)。
CI 的关键要素:
代码提交 → 自动拉取 → 自动构建 → 自动测试 → 结果反馈 ↑ 失败则通知,成功则继续CD:Continuous Delivery(持续交付)
核心思想:在 CI 的基础上,保证代码随时可以发布到生产环境。每次通过测试的代码都处于"可发布"状态,但发布动作需要人手动触发。
解决的问题:发版前要准备很久,不确定当前代码到底能不能上线。
CD:Continuous Deployment(持续部署)
核心思想:在持续交付的基础上,更进一步——通过测试的代码自动部署到生产环境,无需人工干预。
**注意区分**:Continuous Delivery(持续交付)和 Continuous Deployment(持续部署)的缩写都是 CD,区别在于部署是否需要人工点击"发布"按钮。下一篇会详细讲。
三、CI/CD 流水线的标准形态
一条完整的 CI/CD 流水线(Pipeline)通常包含以下阶段:
[代码提交] → [编译构建] → [单元测试] → [代码扫描] → [打包镜像] → [部署测试环境] → [集成测试] → [部署预发环境] → [验收测试] → [部署生产环境] ↑ ↓ └────────────────────────────── 失败则中断并通知 ────────────────────────────────────────────────────┘每个阶段的作用:
| 阶段 | 做什么 | 失败的后果 |
|------|--------|-----------|
| 代码提交 | 触发流水线 | 流水线不启动 |
| 编译构建 | 编译代码,检查语法错误 | 立即中断,通知开发 |
| 单元测试 | 运行自动化测试 | 中断,代码有逻辑问题 |
| 代码扫描 | 静态分析,检查安全漏洞和代码规范 | 警告或中断(取决于规则) |
| 打包镜像 | 构建 Docker 镜像并推送到仓库 | 无法进入部署阶段 |
| 部署测试环境 | 自动部署到测试环境 | 测试无法进行 |
| 集成测试 | 端到端功能验证 | 中断,通知测试团队 |
| 部署预发环境 | 部署到与生产同构的环境 | 需人工排查 |
| 验收测试 | 业务验收 | 通知产品团队 |
| 部署生产环境 | 正式上线 | 回滚机制启动 |
**培训要点**:新手搭建流水线时,不需要一开始就把所有阶段都加上。建议从"构建 → 单元测试 → 部署测试环境"三步开始,逐步增加阶段。
四、一个最小 CI/CD 示例:GitHub Actions
用一个最简的例子感受 CI/CD 的运作方式。以下是一个 GitHub Actions 的 Workflow 文件:
# .github/workflows/ci.yml name: CI Pipeline on: push: branches: [ main ] jobs: build-and-test: runs-on: ubuntu-latest steps: # 第一步:拉取代码 - name: Checkout code uses: actions/checkout@v4 # 第二步:配置 Java 环境 - name: Setup JDK uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' # 第三步:编译打包 - name: Build with Maven run: mvn clean package -DskipTests # 第四步:运行单元测试 - name: Run tests run: mvn test # 第五步:构建 Docker 镜像并推送 - name: Build and push Docker image run: | docker build -t myapp:${{ github.sha }} . docker push myregistry.com/myapp:${{ github.sha }} # 第六步:部署到测试环境 - name: Deploy to test environment run: | ssh deploy@test-server "docker pull myregistry.com/myapp:${{ github.sha }} && docker-compose up -d"这段配置文件做的事情:每次有代码推送到main分支,自动拉取代码 → 配置环境 → 编译打包 → 跑测试 → 构建镜像 → 推送到仓库 → 部署到测试服务器。全程无人值守。
**踩坑提示**:新手常犯的错误是把敏感信息(数据库密码、SSH 私钥)直接写在配置文件里。正确做法是使用 CI/CD 工具提供的 Secrets 管理功能(GitHub 的 Repository Secrets、GitLab 的 CI Variables、Jenkins 的 Credentials)。
五、CI/CD 带来的改变
引入 CI/CD 前后的对比:
| 维度 | 引入前 | 引入后 |
|------|--------|--------|
| 部署频率 | 一周一次甚至一个月一次 | 一天多次 |
| 部署耗时 | 30分钟-2小时 | 3-10分钟 |
| 部署成功率 | 70-80% | 99%+ |
| 回滚方式 | 手动找旧包、手动替换 | 一键回滚到上一版本 |
| 团队状态 | 上线日通宵、精神紧绷 | 随时部署、心态从容 |
| 问题发现时机 | 线上用户投诉 | 自动化测试阶段就拦截 |
关键变化不只是速度提升,更重要的是信心——每次部署前,你已经知道代码通过了编译、测试和扫描,而不是"祈祷它能跑起来"。
六、本篇要点回顾
1. CI/CD 是从手动部署的痛苦中诞生的自动化实践
2. CI = 持续集成(频繁合并代码+自动构建测试),CD = 持续交付/持续部署
3. 流水线由多个阶段串联,失败即中断
4. 从最小的三步流水线开始(构建→测试→部署),逐步增加阶段
5. 敏感信息必须使用 Secrets 管理,不要硬编码
下一篇预告:《核心概念全解:构建、测试、集成、交付、部署与回滚》——我们将深入每个阶段的实际含义和操作细节,为后续工具实战打下认知基础。