--- id: devops.cicd.pipeline title: 流水线设计:触发、阶段、缓存、服务容器、密钥 type: lesson domain: devops stack: [cicd] tags: [流水线, GitHub Actions, 缓存, 服务容器, 密钥管理] level: basic est_hours: 2 related: [devops.cicd.release-rollback, devops.cicd.interview, devops.git.index] week: [3, 7] order: 1 --- # 流水线设计:触发、阶段、缓存、服务容器、密钥 ## 解释说明 - **CI(持续集成)**:每次推送 / 合并请求自动执行“检查 → 测试 → 构建”,尽早发现问题。**合并请求必须 CI 通过**才允许合并。 - **CD**:持续交付(自动构建出可发布的产物,人工点一下发布)和持续部署(自动发布到生产)。大部分团队是测试环境自动部署、生产环境人工审批。 - **典型阶段**: | 阶段 | 内容 | 失败时 | | --- | --- | --- | | 检查 | 格式化、lint(`go vet`、`ruff`、`eslint`)、类型检查 | 最快失败,最便宜 | | 测试 | 单元测试、集成测试(用服务容器起数据库) | 阻止合并 | | 构建 | 编译、打包前端、构建镜像 | 阻止发布 | | 安全 | 依赖漏洞扫描、镜像扫描、密钥扫描 | 视严重程度阻止 | | 部署 | 推送镜像、更新环境、健康检查 | 自动回滚 | - **触发条件**:`push` 到主分支、`pull_request`、打标签、定时、手动。 - **缓存**:依赖下载(Go 模块、pip、npm)缓存起来,按锁文件(`go.sum`、`package-lock.json`)的哈希作为缓存 key。 - **服务容器**:CI 里起一个临时 PostgreSQL / Redis,跑集成测试。 - **密钥**:放在平台的 Secrets 里,通过环境变量注入;日志里平台会自动打码,但不要主动 `echo`。合并请求来自外部 fork 时,平台默认不提供密钥。 - **流水线即代码**:配置文件和代码一起放在仓库里,可评审、可追溯。 ## 案例 前端出身转后端,最常见的流水线:合并请求触发 `lint + test + build`;合并到 main 后构建镜像(标签是提交哈希)并自动部署到测试环境;生产发布由负责人在平台上手动触发,指定镜像标签。 ## 代码示例 完整文件见 [examples/pipeline/ci.yml](examples/pipeline/ci.yml),下面是 Go 服务的测试任务(带 PostgreSQL 服务容器): ```yaml jobs: api: runs-on: ubuntu-latest services: postgres: image: postgres:17 env: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: test ports: ['5432:5432'] options: >- --health-cmd "pg_isready -U test" --health-interval 5s --health-retries 10 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod # 和 go.mod 保持一致,自带模块缓存 - run: go vet ./... - run: go test -race -count=1 ./... env: TEST_DATABASE_URL: postgres://test:test@localhost:5432/test?sslmode=disable ``` 本仓库自己的检查命令也可以放进流水线: ```yaml - run: python3 tools/build_catalog.py --check --strict # 内容规范校验 - run: python3 tools/run_examples.py # 运行示例代码 ``` Action 的版本号(`@v4`、`@v5`)会更新,以各 Action 仓库的说明为准。 ## 易错点 - **测试依赖真实外部服务**(真实的模型 API、第三方支付):不稳定、花钱、泄露密钥。单元测试用假实现,真实调用放到单独的、手动触发的任务里。 - **用 `latest` 标签部署**:无法确定线上是哪个版本,也无法回滚。 - **缓存 key 不包含锁文件哈希**:依赖更新了还在用旧缓存。 - **把密钥写在配置文件里**:即使后来删除,也留在 Git 历史中,必须轮换密钥。 - **流水线太慢没人等**:并行执行任务、按改动路径只跑相关部分、缓存依赖;目标是 10 分钟以内。 - **CI 通过不是合并的必要条件**:分支保护里要设置“必须通过检查”。 ## 面试常问 见 [CI/CD 面试题](interview.md):你们的 CI/CD 流程、怎么加速流水线、密钥怎么管理。