CI/CD 构建流水线套件 — 从首个 workflow 到上线 gate
端到端搭一条真的 CI/CD:GitHub Actions / GitLab CI / Dagger,BuildKit 缓存,矩阵构建,sops + Infisical + Gitleaks 管秘钥,最后用 Kamal 接零停机上线 gate。配 AI 友好的 config 模板,方便生成、调试和压缩构建时间。
这个 pack 包含什么
大多数 CI/CD 教程甩给你一个 200 行的 .github/workflows/ci.yml 就算完事。业余项目能撑,真实流水线扛不住:要面对矩阵构建、缓存失效、秘钥泄漏、半夜两点上线失败。本 pack 走的是相反角度:十个开源工具按精心设计的顺序装,每一个都解决前一步暴露出来的真问题。
每个都是开源、活跃维护,产出的是 AI agent 能读、能生成、能调试 的配置文件。零黑盒 SaaS。装完你拿到的是:本地能跑、CI 能跑、缓存激进、扫秘钥、只上绿色构建的一条完整流水线。
推荐安装顺序
- actionlint — push 之前本地 lint workflow YAML。抓
uses:写错、if:表达式坏掉、shell 转义错 — 否则 CI 跑 4 分钟才告诉你。配 pre-commit hook,无借口。 - act — 本地 Docker 跑 GitHub Actions。CI 迭代最大的杀手就是
commit → push → 等 5 分钟 → 红 → 重来的死循环。act把循环压到几秒。和 actionlint 配套,调 workflow 像调代码。 - Super-Linter — GitHub 出的多语言 linter 聚合器,开箱即用。一个步骤塞进 workflow,背后是 50+ linter(shellcheck、hadolint、yamllint、eslint、ruff)。让 CI 成为代码风格的唯一裁判,省掉一堆 PR review 撕逼。
- BuildKit — 现代 OCI 镜像构建器,并行 layer +
--mount=type=cache支持。改用 BuildKit 之后,RUN apt-get和RUN npm ci能跨 commit 复用缓存。典型场景:一个 Node + Python 服务从 8 分钟降到 90 秒。 - Dagger — 可编程 CI/CD 引擎:流水线是真的代码(Go / Python / TypeScript),在 GitHub Actions、GitLab CI、Buildkite、你的笔记本上跑结果完全一致。当
.yml超过 300 行那一刻你就该上 Dagger,从此不回头。供应商锁定一个下午解开。 - Concourse — 容器原生 CI,声明式 pipeline,专为自托管场景。当代码不能上托管 CI(保密、合规、内网),Concourse 是理智的开源替代。pipeline 是 YAML,每一步都是容器,状态存 S3 或 PG。
- Gitleaks — 静态秘钥扫描器。流水线里一个步骤,commit 里混了 AWS key / GitHub token / JWT 直接 fail build。再小心的程序员也会有 5% 漏网,全靠它拦。
- sops — Mozilla 出的加密文件秘钥管理。
secrets.enc.yaml提交到 git,部署时用 KMS / age / PGP 解密。极小、零服务、80% 还用不到 Vault 的团队完美选择。 - Infisical — 开源秘钥管理服务,sops 撑不住时的下一站。Web UI、角色权限、GitHub Actions / GitLab / Vercel 全套 CI 集成。从 sops 到 Vault 之间的合理过渡。
- Kamal — 零停机 Docker 部署工具。CI 通过后,Kamal 在一台或多台服务器上做带健康检查的滚动部署。替代每个团队都写过又都后悔的那个
ssh && docker pull && docker restart脚本。
它们怎么协同
写 workflow.yml
│
├─ actionlint (本地, pre-commit)
│
├─ act (本地, docker-compose 跑 CI)
│
CI 跑起来 ─┐
├─ Super-Linter 步骤 (风格 + 正确性)
├─ BuildKit 步骤 (带缓存的 docker build)
├─ Dagger pipeline (跨厂商可移植)
│ └─ 或 Concourse (自托管替代)
├─ Gitleaks 扫秘钥步骤 (秘钥泄漏直接 block)
└─ sops 解密步骤 (运行时注入秘钥)
└─ Infisical (团队 > 5 人时)
│
▼
Kamal 部署 (零停机 gate)
actionlint + act + Super-Linter 三件套是「便宜但巨大」的迭代红利,大多数团队跳过,每周因此浪费几个小时。Dagger + BuildKit 组合是长期反锁定的逃生路径,YAML 觉得脆了,这就是重写方向。先 sops 后 Infisical 是正确的秘钥升级节奏,3 人团队直接上 Vault 是「纸盔甲式 YAGNI」。
你会遇到的取舍
- act vs 托管 runner —
act解决 80% 的 workflow 调试,但 hosted runner 的某些边界(矩阵展开特殊情况、OIDC token、托管专属服务)模拟不完美。本地用它快速迭代,合并 workflow 改动前必须真 CI 验一遍。 - Dagger vs 裸 YAML — Dagger 在 CI 上加一层语言(Go/Python/TS)。YAML 超过 ~300 行,或者需要同一条 pipeline 跑在多个厂商,才值得。单文件 50 行的 workflow,裸 YAML 仍然是对的。
- sops vs Infisical vs Vault — sops = 文件提交 git,零服务。Infisical = web UI + 权限,一个服务。Vault = 完整秘钥平台 + 动态凭证 + 多服务。挑能解决本季度问题的最小那个。
- Kamal vs Kubernetes — Kamal 给「想要零停机 Docker 部署但不想跑 k8s」的团队。一旦你需要自动扩缩容、定时任务、service mesh,就出了它的射程。别硬扛。
- Concourse vs GitHub Actions — 能用托管 CI 就用。Concourse 是合规 / 内网 / 成本逼得只能自托管时的选项。运维一个 Concourse 集群本身就是实打实的成本。
常见踩坑
- 缓存 key 策略错 —
actions/cache写错 key 每次都失效。哈希锁文件(package-lock.json/poetry.lock),不要哈希整个源码树。 - 矩阵构建默认
fail-fast: true— GitHub 默认第一个失败就杀整个矩阵。triage 时想看到所有 OS / 版本失败,得显式设fail-fast: false。 - 秘钥放 workflow 级 env —
env:写在 job 级别,所有步骤(包括第三方 action)都能看见。秘钥只在需要它的那一步注入。 - 没设
concurrency:— 没分组的话,同一分支两次 push 会两个部署并发跑,互相打架。加concurrency: { group: deploy-${{ github.ref }}, cancel-in-progress: true }。 - Gitleaks 没配 allowlist — 第一次跑会把测试 fixture 和样例 token 全标红。一次性整理好
.gitleaks.toml的 allowlist,之后才会一直有用而不是被静音。 - Kamal 没配 healthcheck 端点 — Kamal 的零停机靠
/up返回 200 才切流量。跳过这一步,得到的是「接近零停机」而不是真零停机。
10 个资产打包就绪
常见问题
真的需要全装吗?一个 repo 用十个工具会不会过头?
不需要。最小可用流水线是 actionlint + Super-Linter + BuildKit + Gitleaks + (sops 或 Infisical 之一) + Kamal。调 workflow 调得频繁了再加 act;YAML 痛了再上 Dagger;托管 CI 不能用时才考虑 Concourse。把清单当成成长路径,不是购物车。
GitLab CI / Buildkite 能不能用这一套?
能 — 这正是 Dagger 和 Concourse 在清单里的原因。actionlint / act 是 GitHub 专属,但 BuildKit、Super-Linter(通过 Docker)、Gitleaks、sops、Infisical、Kamal 都和 CI 平台无关。Dagger pipeline 写一次,三个厂商表现一致,锁定收缩到两个薄薄的入口 YAML 文件。
BuildKit 缓存到底能省多少 CI 时间?
一个典型 Node + Python 服务:冷构建 7-9 分钟,热构建(只改源码、依赖命中缓存)60-120 秒。如果在包管理器缓存上也用 cache-mount(--mount=type=cache,target=/root/.npm),不是简单 COPY 后再 install,收益还会叠加。再往下就是边际递减,重点该转去优化别的环节。
为啥 sops 和 Infisical 都要 — 不是一回事吗?
不同成长阶段。sops 加密文件提交 git — 零服务,独立开发或小团队完美。Infisical 跑一个带 UI、角色权限、审计日志、CI 集成的服务 — 当非工程师(PM、市场)需要在不动 git 的前提下轮换 API key,就该上了。先 sops,等摩擦真正出现再迁。
怎么让 AI agent(Claude / Copilot / Cursor)真的在 CI 配置上有用?
三个习惯。一是每个 repo 保持一份规范的 workflow 文件 + 注释头描述各阶段,agent 读了就知道往哪个步骤写。二是失败时把 actionlint -format '{{json .}}' 的输出喂给它,让它修根因不是修症状。三是跨厂商迁移时把 Dagger pipeline 文件(普通代码)而不是 YAML 喂给它,它生成各厂商入口文件正确率高得多。