CI/CD 流水线工程化与发布安全治理

流水线不仅是“编译后自动部署”的脚本集合,它承担代码验证、依赖治理、制品生产、权限审批和发布审计。设计不当时,Runner 会变成内网跳板,缓存会污染构建,密钥会进入日志,错误制品会被重复部署。本文以 GitLab CI、Jenkins 和 GitHub Actions 等通用机制为背景,给出生产级治理框架。

一、先划分流水线阶段

提交
 ↓
静态检查与单元测试
 ↓
依赖与安全扫描
 ↓
可复现构建
 ↓
制品签名与入库
 ↓
测试环境部署与验证
 ↓
生产审批
 ↓
灰度发布
 ↓
监控、回滚与审计

构建与部署必须解耦:同一个经过验证并签名的制品从测试晋级到生产,而不是每个环境重新编译。

二、流水线即代码

流水线定义、脚本和模板进入 Git,接受代码评审。需要明确:

  • 谁能修改流水线;
  • 谁能修改共享模板;
  • 谁能触发生产部署;
  • 哪些分支和标签受保护;
  • 变量在哪个环境可见;
  • 手工审批能否被绕过;
  • 变更记录如何关联工单。

共享模板应版本化引用,禁止长期跟随不可控的 latest。模板升级与应用升级一样需要测试和回滚。

三、Runner与执行器隔离

Runner 会执行仓库中的代码,应视为高风险计算节点。

隔离原则:

  • 公共、内部、生产项目使用不同 Runner 池;
  • 非受信任合并请求不能使用生产网络和生产密钥;
  • Runner 使用临时虚拟机或容器,用后销毁;
  • 禁止把宿主机 Docker Socket 暴露给不可信任务;
  • 限制出站网络和云元数据访问;
  • Runner 服务账号最小权限;
  • 工作目录、缓存和凭据在任务后清理;
  • 管理面与业务面网络隔离。

共享 Shell Runner 风险最高:前一个任务可能留下进程、文件或凭据供后续任务读取。

四、依赖与构建可复现

使用锁文件固定直接和间接依赖,记录编译器、基础镜像和系统包版本。依赖下载经过可信代理或制品仓库,并校验哈希和签名。

可复现构建关注:

  • 固定时区、locale 和构建时间来源;
  • 禁止构建阶段访问不受控地址;
  • 基础镜像使用 digest;
  • 构建参数和工具链版本进入元数据;
  • 相同源代码应生成可比较制品;
  • 构建日志不包含密钥。

五、缓存与制品不是一回事

缓存用于加速,可被丢弃;制品是发布对象,需要不可变、校验、保留和权限控制。

缓存风险:

  • 不同分支共享可写缓存导致污染;
  • 依赖锁文件变化但缓存键未变化;
  • 缓存包含凭据或配置;
  • 未受信任务写入主分支缓存。

制品应包含:

  • 版本和 Git commit;
  • 构建流水线 ID;
  • 哈希;
  • SBOM;
  • 签名和证明;
  • 扫描结果;
  • 发布环境和时间。

六、密钥与变量治理

密钥存放在平台 Secret、Vault、云密钥管理服务或 OIDC 短期凭据中。优先使用按任务签发的短期身份,减少长期 Access Key。

必须防止:

  • set -x 打印命令和变量;
  • 错误堆栈打印完整请求头;
  • 把 .env 作为制品;
  • Fork/MR 获得受保护变量;
  • 密钥进入 Docker layer;
  • 命令行参数被进程列表读取;
  • 调试日志绕过掩码。

变量掩码只是最后防线,不能替代最小权限和短期凭据。

七、测试分层

流水线应快速失败:

  1. 格式与基础静态检查;
  2. 单元测试;
  3. 依赖和密钥扫描;
  4. 构建;
  5. 组件/契约测试;
  6. 集成测试;
  7. 端到端测试;
  8. 性能与安全专项;
  9. 发布后探测。

不是所有测试都阻断所有发布。需要定义质量门禁、允许例外的角色、有效期和补救计划。

八、供应链安全

生产链路至少执行:

  • SAST;
  • 依赖漏洞与许可证扫描;
  • Secret 扫描;
  • 容器镜像扫描;
  • IaC 和 Kubernetes 配置扫描;
  • SBOM 生成;
  • 制品签名;
  • 发布时验证签名和来源;
  • 构建环境来源证明。

扫描结果要结合是否可利用、是否部署和业务暴露面评估,不能只按 CVSS 一刀切,也不能无限延期高风险漏洞。

九、分支、标签与版本

保护主分支和发布标签:

  • 必须通过评审和状态检查;
  • 禁止强推;
  • 发布标签由受控流水线生成;
  • 版本与制品一一对应;
  • 热修复流程保留同等审计;
  • 删除或覆盖已发布制品受到限制。

部署时使用不可变版本或 digest,不使用会漂移的 latest。

十、环境与配置

应用制品保持一致,环境差异通过受控配置和密钥注入。配置也需要版本、校验和回滚。

测试环境应尽量复制生产的关键约束:网络策略、身份、依赖版本、存储类型和资源限制。完全不同的测试环境只能验证“能启动”,无法验证生产行为。

临时 Preview Environment 应设置自动过期、配额、脱敏测试数据和域名回收。

十一、生产审批

审批人需要看到:

  • 变更范围和风险;
  • commit、制品哈希和签名;
  • 测试与扫描结果;
  • 配置和数据库变更;
  • 发布批次和观察时间;
  • 回滚条件;
  • 值守人与业务确认。

审批不能只是一个无信息的“点击同意”。高风险变更可要求双人审批和职责分离。

十二、数据库变更

应用回滚不等于数据库回滚。数据库迁移应使用 expand/contract:

  1. 先增加兼容结构;
  2. 发布兼容新旧结构的应用;
  3. 后台回填;
  4. 切换读取;
  5. 观察;
  6. 最后移除旧结构。

大表 DDL、索引创建和数据回填需评估锁、日志、复制延迟和磁盘空间。流水线要能暂停,而不是让超时任务自动重试同一 DDL。

十三、灰度与发布策略

常见方式:滚动、蓝绿、金丝雀、影子流量和功能开关。

灰度判定应使用业务与技术指标:

  • 错误率和 P99;
  • 关键交易成功率;
  • 资源和下游依赖;
  • 新旧版本差异;
  • 特定用户反馈;
  • 日志异常模式。

每批设置自动暂停和最大失败阈值。若监控数据缺失,应停止推进,而不是视为成功。

十四、回滚与前滚修复

回滚必须演练。需要确认:

  • 旧制品仍可获取且签名有效;
  • 配置向后兼容;
  • 数据库结构兼容;
  • 缓存和消息格式兼容;
  • 功能开关可快速禁用;
  • 回滚不会再次触发错误迁移。

部分数据变更无法简单回滚,应准备前滚修复、补偿任务或只读降级。

十五、Jenkins专项

  • Controller 不执行普通构建;
  • Agent 临时化和标签隔离;
  • 插件最少化并定期升级;
  • Script Approval 严格管理;
  • 凭据域按 Folder 隔离;
  • 共享库固定版本;
  • 备份 JENKINS_HOME 但恢复前验证密钥;
  • 禁止随意开放 Groovy Console。

十六、GitLab CI专项

  • Protected Branch/Tag 与 Protected Variable 对齐;
  • Runner 标签不能作为唯一安全边界;
  • MR Pipeline 与默认分支 Pipeline 权限分离;
  • 环境设置保护和审批;
  • resource_group 避免同环境并发部署;
  • interruptible 与自动取消减少过期构建;
  • 制品和缓存设置保留周期;
  • 审计手工 Job 的操作者。

十七、失败分类与重试

区分:

  • 代码/测试确定性失败;
  • 基础设施瞬时失败;
  • 依赖服务失败;
  • 配额和容量失败;
  • 权限或密钥失败;
  • 人工取消。

只对明确瞬时失败有限重试。测试失败无脑重试会掩盖不稳定测试,并延长反馈时间。

十八、指标与DORA

跟踪:

  • 部署频率;
  • 变更前置时间;
  • 变更失败率;
  • 恢复时间;
  • 流水线排队和执行时间;
  • 缓存命中;
  • 不稳定测试;
  • Runner 利用率;
  • 制品晋级时间;
  • 人工审批等待。

指标用于改善系统,不应简单变成个人绩效排名,否则团队会优化数字而不是交付质量。

十九、上线清单

  • [ ] 流水线、模板和脚本全部版本化并评审;
  • [ ] 构建与部署分离,制品不可变;
  • [ ] 非受信任务与生产 Runner、网络、密钥隔离;
  • [ ] 依赖固定,基础镜像使用 digest;
  • [ ] Secret 使用短期身份且不进入日志;
  • [ ] SBOM、扫描、签名和来源证明齐全;
  • [ ] 生产审批信息完整且职责分离;
  • [ ] 数据库迁移兼容回滚;
  • [ ] 灰度有指标、暂停和自动失败阈值;
  • [ ] 回滚、平台备份和灾难恢复已演练。

二十、结语

高质量 CI/CD 的目标不是把每次提交尽快推到生产,而是在保持交付速度的同时,让制品来源可信、权限受控、风险可见、发布可暂停、失败可恢复。流水线本身就是生产系统,必须拥有与业务系统同等级的安全、监控、容量和灾备设计。

参考资料

Copyright © yan-jian.com 2023 - 2026 · Powered by GitBook更新时间: 2026-10-28 21:12:05

results matching ""

    No results matching ""