持续集成和持续交付系统
一直以来,我总是感觉持续集成和持续交付系统(CI/CD)往往对于个人是多余的,自己在本地系统里用项目的包系统或者简单的 Makefile 运行一下就得到产物了。随着我接触越来越多复杂的项目,我意识到这类系统确实是在试图用软件工程的方式节省时间,规范产品发布和部署流程,但它们也总是顺手把这一套流程锁定在自家的平台设施上。最终的目的,就是把原本属于开发流程的一部分,变成可以持续收费、逐步加价,并且让人不太容易迁出的基础设施产品。
CI/CD 到底在做什么
把各种名词先放下,绝大多数 CI/CD 系统做的事情其实都差不多:监听代码仓库里的某种事件,然后按照仓库中的工作流文件,把一连串步骤交给某台 runner 或者 agent 去执行。触发条件可能是 push、pull request、tag、定时任务,或者手动点击;执行内容则从最基础的拉取代码、安装依赖、运行测试,到打包镜像、发布 release、上传制品、部署线上服务。
所以不管界面包装得多么不同,这类系统一般都离不开几个部件:接收 webhook 的 server,解析工作流文件的调度层,真正运行命令的 runner 或 agent,以及保存日志、密钥和构建状态的存储层。你也可以把它理解成一台远程的自动化构建机,只不过这台机器顺便还决定了你的密钥交给谁、算力从哪里买、日志和制品最后落在哪个平台里。
CI/CD 101: GitHub Actions
如果把 GitHub 当作最先入门 CI/CD 的平台,那么 GitHub Actions 的优点确实几乎不用介绍:写一个 YAML 文件就能跑,和 pull request、release、packages、secret 全部紧密打通,入门体验非常顺手。GitHub 当前的定价页面里,免费版私有仓库仍然给每月 2000 分钟,团队版给 3000 分钟,看起来还算慷慨。
问题恰好也出在这种便利性上。你越是习惯在 GitHub 里完成代码托管、流水线、制品管理和部署,迁出的成本就越高。GitHub 在 2025 年 12 月 15 日更新了一篇 GitHub Actions 定价调整公告 里,页面摘要里目前写的是“暂缓”自托管 runner 的计费变更,同时从 2026 年 1 月 1 日起把在 GitHub 托管的 runner 的价格下调最多 39%。但同一页面保留的原始公告正文又明确写过:他们打算对所有 Actions 工作流引入每分钟 0.002 美元的平台费用,自托管 runner 也不例外,原计划从 2026 年 3 月 1 日起生效。
这份公告说是“听到了反馈”,说是“为客户降价”,实际还是要把平台的营利逻辑说得很明白:控制面、调度服务、可靠性改造、企业级扩缩容,这些东西最后都要折算回账单里。免费层此前一直都比较克制,服务质量也总是以平台优先;当产品方向和财报目标一致时,个人开发者得到的“慷慨额度”往往只是一个早期促销价。
更好笑的是 GitHub 对 Copilot 的拆分定价。按照我写这篇文章时 GitHub 官方的 Copilot 价格页,个人版已经分成了 Pro 每月 10 美元、Pro+ 每月 39 美元、Max 每月 100 美元,还额外配了一套 AI Credits 的额度说法,甚至这些个人付费计划的新开通一度还是“暂时暂停”。一个原本主打自动补全的产品,最后长成这种像云账单一样的分层菜单,本身就挺有黑色幽默的。
GitHub 一宣布这一轮 Actions 计费,围绕 GitHub 生态吃饭的公司也立刻跟上了。Blacksmith 那篇 The GitHub Actions control plane is no longer free 基本上就是一边解释 GitHub 新增的平台费,一边顺手推销他们更快的机器、持久化的 Docker layer cache 和每月 3000 免费分钟。这当然很合理,但本质上他们和 GitHub 并没有什么根本区别:不是在帮你摆脱 GitHub Actions,而是在 GitHub 开始收过路费之后,试图从 runner 这部分预算里抢走一些客户。
Woodpecker: 轻量而保守的附带服务
和 GitHub 这种大一统平台相比,Woodpecker CI、Forgejo Actions 这一类工具明显要克制得多。它们往往更像是某个代码托管平台的配套能力,或者一个可以单独部署的小服务,而不是一套企图包办整个研发流程的商业平台。它们的设计目标通常也很直接:把代码拉下来,放进容器里跑若干步骤,然后把结果回传给 forge。
这类系统的优点是轻,概念也相对朴素。缺点则是“轻量”和“保守”常常是一体两面。你很难指望它们像 GitHub 那样把各种附加服务都提前喂到嘴边,但反过来说,这种工具至少不会把“方便”包装成一种难以迁移的依赖。对个人和小团队而言,我反而觉得这是一种优点。这一工具的表现就是做成核心服务的一个插件或者扩展,为打算托管核心服务的维护者提供
Jenkins: 大企业重产品
Jenkins 我没有真正部署和维护过,所以这里最多只能谈印象。但只要稍微接触一下它周围那套 controller、agent、插件、权限、升级和运维话语,就能感觉到一种很明显的大企业内部工具气质。你可以说它成熟、经典、生态庞大,也可以说它厚重、历史包袱多,而且有一股相当典型的 Java 式臃肿。
如果一个团队本来就有专门的人长期维护这套系统,Jenkins 当然有它的合理性。但对我这种只是想搭一套个人可控流水线的人来说,它看起来更像是要把“写代码”升级成“兼职维护企业中间件”。
最后: Crow CI
Crow CI 最吸引我的地方,是它没有假装自己是另一个 GitHub。官方文档里列出来的前几项特点,再加上一个我自己比较在意的能力,大致可以概括成下面四点:
- 多代码托管平台支持:GitHub、GitLab、Forgejo、Gitea、Bitbucket 都能接。
- 容器后端可以选择 Docker、Podman,也支持 Kubernetes。
- 运行内存需求大约 150 MB。
- 构建失败时可以发送邮件通知。
这几句话翻得再直白一点,就是:它提供多个 forge 支持,灵活的 runner 的后端,自定义的机器规格,以及满足不同部署环境要求。这意味着流水线本身会跟着某个平台一起锁死。
除了这些表面的功能点,Crow 还有一个设计:它是一个彻底的容器化工具。官方文档直接把它描述成一个 container-only application,也就是流水线天然以容器为核心,而不是先把任务跑在宿主机上,再把容器当作一个附属选项。这和 GitHub Actions、GitLab Runner、Jenkins 那种“host-first”的默认心智不太一样。对于个人自托管场景,这种思路更容易形成一套边界清楚、可迁移的配置。
Crow Autoscaler 和部署上的一点经验
Crow 的安装文档把组件拆得很清楚:server 负责 UI、API、webhook 和流水线解析,agent 负责真正执行任务,而 autoscaler 则是可选组件,用来按需拉起或销毁云服务商的机器。文档里把复杂的、可选的和耗资源的组件都说得比较明白。
如果只是单机自托管,最朴素的方式就是把 crow-server 和 crow-agent 用容器分开跑起来,再让两边共享同一个 CROW_AGENT_SECRET。数据库默认可以先用 SQLite 起步,但如果打算长期运行,还是更适合 PostgreSQL 或者 MariaDB。这个思路和我在上一篇 Forgejo 部署里做的选择很像:先让服务跑起来当然重要,但只要你准备长期维护,就该尽早把状态数据放到一个你愿意长期照看的地方。
我自己的经验是,Crow 的 server 部署其实不算难,文档在大部分过程都介绍了比较详细, 只是不像 GitHub 那样藏掉了复杂度。如果在文档里查不到,在 AI 的帮助下理解一些源码里的配置也是可行的。agent 如果频繁重启,最好把配置文件持久化,不然容易反复注册出新的 agent;如果 server 和 agent 分开放在不同机器上,内网直连当然最省事,走公网就得老老实实给 gRPC 通道配 TLS 和反向代理。
官方还提供了一个单独的 Crow Autoscaler 文档。文档里提到,一个 server 可以同时挂多个 autoscaler,每个 autoscaler 都有自己的注册 token、provider 配置和扩缩容限制,理论上可以把不同的云服务商、区域和机器架构拼在一起。我这里虽然已经配置了一个 autoscaler,但因为还没有给它真正接上云服务的 provider,所以目前还没有实际跑过,只能算是先把接口预留好了。
技术特点,以及为什么需要重新适应
Crow 一个很好的特点,是它允许你组合不同层级的配置。代码托管可以接不同 forge,执行后端可以选 Docker、Kubernetes 甚至实验性的 Podman,agent 可以是一直在线的小机器,也可以和 autoscaler 配合成按需启动的大机器。autoscaler 文档里还专门写了 hybrid setup:让静态 agent 处理轻量任务,让 autoscaled agent 处理重型任务,再用 label 把不同工作流分流出去。这种设计对个人项目尤其友好,因为它可以先按“够用”和“便宜”来设计,而不是一上来就照着企业方案部署 Kubernetes 或者昂贵的 AWS 服务。
扩展能力主要来自 插件系统。Crow 把插件看成预定义的流水线步骤,用来封装复杂操作,也顺便减少在普通 shell step 里直接暴露敏感凭据的机会。它兼容 Woodpecker 插件,并部分兼容 Drone 插件,所以生态并不是从零开始。
另外一个比较有意思的点,是它支持用 Jsonnet 代替 YAML 来写流水线。相比只会复制粘贴锚点和别名的 YAML,Jsonnet 至少真的有变量、函数、条件分支和 import,更适合把重复的构建矩阵、镜像标签和多仓库公用配置抽出来。当然,这也意味着你得把流水线当成“配置程序”去理解,而不是继续把它当成一坨越写越长的 YAML。
代价也很明确。你一旦不用 GitHub Actions,就不能再沿用那套“仓库、action、marketplace、托管 runner 全都已经替我准备好了”的平台托管。Crow 不是把 GitHub Actions 换一个皮照抄过来,而是需要重新理解 webhook、agent、label、secret、pipeline 编译和调度这些流程。对只想省脑力的人来说,这当然是缺点;但对想拿回控制权的人来说,这也正是。
已经迁过去的一个例子
目前我已经把之前那个轻量 Git 仓库 simple-git-server 的构建迁到了 Crow CI 上。对应代码仓库在 Forgejo,流水线页面也已经挂在 Crow CI 实例 上。
这至少说明一件事:对于我现在这种规模的项目,Crow CI 并不是一个只能拿来写博文的“替代品”,而是真的可以把日常构建接过去。至于后面要不要继续把 配置邮件、生产项目自动化也逐步迁过去,那就是下一阶段慢慢折腾的问题了。
评论系统尚未配置。请在 .env 中填写 giscus 所需的环境变量。