- 引言 —— Openship 上线那天,同一个问题又回来了
- 决定结果的五个维度
- 控制平面住在哪里
- 回滚与零停机部署的真实样子
- 密钥 —— 最容易失手的地方
- 候选方案对比
- 花掉的不是钱,而是注意力
- 结语 —— 先写下谁来负责维护
- 参考资料
引言 —— Openship 上线那天,同一个问题又回来了
Openship 登上了 GeekNews。它的说法既熟悉又雄心勃勃 —— 只要指向一个仓库,就能自动识别技术栈、构建容器、配置域名和 TLS,一路部署到底。不需要配置文件,不需要流水线,也不需要 YAML。项目采用 Apache 2.0 许可证,同时提供桌面应用、Web 控制台、CLI 和 REST API。
评论区一如既往地收敛到同样的问题上:「这和 Coolify 有什么不同?」「直接用 compose 不行吗?」「这东西谁来维护?」
本文就是为了回答这个问题而给出的决策框架。罗列功能清单式的对比没什么用 —— 候选方案宣传的大多是同一批功能:Git 推送部署、自动 TLS、数据库、备份、日志。真正决定你三个月后会不会后悔的,是五件很少写进功能清单的事。先把结论摆在前面:本文不会说「自托管更好」,也不会说「托管服务更好」。自托管是一笔用钱换注意力的交易,而这个汇率因团队而异。
决定结果的五个维度
与其看功能表,不如用下面五个问题筛选候选方案 —— 通常能把范围收窄到两个以内。
第一,控制平面住在哪里。是一台常年开着的仪表盘服务器,还是只在执行命令那一刻才存在的本地 CLI?这决定了「部署系统本身挂掉时会发生什么」。
第二,回滚需要几秒钟、几条命令。凌晨三点要回退时,走的是点一下 UI 按钮,还是要翻出之前的镜像标签手动重新部署,又或者干脆连文档都没写?
第三,密钥存在哪里、以什么形式存在。如果密钥落在平台自己的数据库里,那这个数据库的备份策略和访问权限,就直接成了密钥的安全边界。
第四,节点从一台变成多台时,什么会跟着改变。大多数工具都写着「支持多服务器」,但在实际使用中,「分别部署到多台机器」和「当成一个集群来管理」之间差距很大。
第五,用这套工具每个月要搭进去多少小时。这才是真正应该拿来和托管 PaaS 账单对比的数字。
控制平面住在哪里
在这个维度上,候选方案明显分成两大阵营。
常驻控制平面阵营—— Coolify、Dokploy、CapRover、Openship(服务器模式)。服务器上始终挂着一个仪表盘。以 Coolify 为例,控制平面负责仪表盘、内置的 PostgreSQL、接收 Git webhook、编排部署,再通过 SSH 连接工作节点来启动容器。通常的建议是,控制平面本身 2 vCPU / 4GB 就够用。
# 安装 Coolify —— 建议养成先读脚本内容再执行的习惯
curl -fsSL https://cdn.coollabs.io/coolify/install.sh -o /tmp/coolify-install.sh
less /tmp/coolify-install.sh
bash /tmp/coolify-install.sh
这一阵营的优点很明确:负责部署的人不必是后端工程师。看日志、改环境变量、重新部署,这些操作在浏览器里就能完成。用一键应用模板,几分钟就能把 Postgres、Redis、监控栈都跑起来。
缺点也同样明确。这多出了一个可能独立发生故障的部件 —— 控制平面本身。仪表盘挂了,已经在跑的应用不受影响,但那种状态下你既不能部署也不能回滚。而且仪表盘本身就是一个攻击面 —— 2026 年 Coolify 相关的 CVE 被曝出后,「把仪表盘从公网上撤下来,放到 VPN 或 IP 白名单后面」这条建议被反复提起。把自托管部署工具的仪表盘开放在 0.0.0.0 上,实际上和开放一个 root shell 没什么两样。
无常驻阵营—— Kamal 2,以及纯 compose + SSH 组合。这一派没有控制平面。它只在开发者笔记本电脑或 CI runner 执行命令的那段时间里存在,命令跑完,服务器上就只剩下容器和代理。
# config/deploy.yml — Kamal 2
service: app
image: acme/app
servers:
web:
- 10.0.0.11
- 10.0.0.12
proxy:
ssl: true
host: app.example.com
healthcheck:
path: /up
interval: 3
registry:
server: ghcr.io
username: acme-ci
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
RAILS_ENV: production
secret:
- DATABASE_URL
- SECRET_KEY_BASE
accessories:
db:
image: postgres:17
host: 10.0.0.20
env:
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
kamal setup # 仅首次执行:安装 Docker、启动代理、创建 accessories
kamal deploy # 构建 → 推送 → 按服务器逐台滚动替换
kamal app logs -f # 实时查看所有服务器的日志流
kamal rollback # 回退到上一个正常版本
kamal proxy reboot # 只重启代理
Kamal 2 甩开了 Traefik,改用自己的 kamal-proxy。因为这个代理就是为零停机部署而生的,默认行为就是等新容器通过健康检查之后才切流量。它还是 Rails 8 的默认部署工具,因此在 Rails 生态里已经事实上成了标准,不过只要能打出 Docker 镜像,用什么语言都无所谓。
这一阵营的优点是,压根没有控制平面可以出故障。缺点是凌晨三点没有 UI 可看 —— 查日志得靠 SSH 或 CLI,团队里要是有人不习惯用终端,这个人就被排除在部署流程之外了。
Openship 有意思的地方在于两种模式都提供:一种是桌面应用充当本地控制平面、通过 SSH 部署;另一种是把它常驻在服务器上,接收 Git 推送部署。不过截至本文写作时,公开版本还停留在 0.1.x,项目仓库自己也承认「核心已具备生产可用性,但仍在积极开发中」。多节点集群、私有网络、可视化 CI/CD 流水线,目前都还只是路线图上的计划项。值得在新项目里先试试看,但要拿它去迁移一个已经在跑的服务,还为时过早。
回滚与零停机部署的真实样子
这是挑选部署工具时,人们最少去确认、事后却最常后悔的一项。
零停机部署几乎所有候选方案都支持,做法也大同小异 —— 启动新容器,通过健康检查后,由代理切换流量,再关掉旧容器。这个角色在 Kamal 里由 kamal-proxy 承担,在 Coolify 和 Dokploy 里是 Traefik 系,在 CapRover 里是 Docker Swarm 的滚动更新,在 Openship 里则是基于 OpenResty 的边缘层。
差异体现在回滚上。需要确认的事情有三件。
- 上一个镜像是否还留在服务器上。如果没留下,回滚就变成「重新从镜像仓库拉一次」,一旦镜像仓库不可访问,回滚本身就做不到了。Kamal 会把上一个容器保留下来,所以
kamal rollback能立刻生效。 - 回滚所需的信息能否直接在 UI 或 CLI 里看到。如果得翻遍镜像仓库控制台才能找到上一次部署的镜像标签,那就等于没有回滚路径。
- 数据库迁移怎么办。这一点没有任何工具能替你解决。使用前后兼容的迁移方式、把部署和 schema 变更解耦,依然是应用层需要自己解决的设计问题。
第三点才是关键。就算容器回滚 30 秒就能搞定,只要 schema 回不去,实际的恢复时间就不是 30 秒。这是一个排在工具选型之前的决定。
再多说一句:从没真正演练过的回滚功能,等于不存在。应该每季度至少一次,在预发布环境故意上线一个有问题的版本,然后练习回滚。备份恢复也是同样的道理 —— 「备份在正常运行」和「真的能恢复」是两码事。
密钥 —— 最容易失手的地方
候选方案的性格差异,在这个环节暴露得最明显。
仪表盘类工具通常把密钥存在自己的数据库里:在 UI 里输入,部署时作为环境变量注入。这很方便,但结果是那个数据库变成了密钥仓库。需要确认三件事 —— 存储时是否加密、加密密钥放在哪里(如果和数据放在同一台服务器上,实际保护效果有限)、仪表盘的备份里是否原样包含了密钥。
Kamal 走的是相反的路线。它自己不保管密钥,而是在部署那一刻从本地读取,再注入容器。.kamal/secrets 文件就是这个通道,推荐做法是不要提交这个文件本身,而是从外部密钥库(vault)里取值填进去。
# .kamal/secrets —— 写的是「怎么取值」,不是值本身
KAMAL_REGISTRY_PASSWORD=$(op read "op://infra/ghcr/token")
DATABASE_URL=$(op read "op://infra/app/database-url")
SECRET_KEY_BASE=$(op read "op://infra/app/secret-key-base")
# 在 CI 里,同样的位置用 CI 密钥填充
# .kamal/secrets.production
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
DATABASE_URL=$DATABASE_URL
这种方式的好处是,密钥的唯一源头始终留在部署工具之外。就算把整个部署平台换掉,密钥管理体系也不受影响。缺点是要多配置一层,而且一旦 vault 挂了,部署就会被卡住。
不管选哪一边,该守住的底线都一样:不要把密钥打进镜像里,别让它出现在构建日志里,把轮换流程写成文档,并且亲自确认一次 —— 部署工具的备份里,密钥是不是以明文形式躺在那儿。
候选方案对比
| 工具 | 控制平面 | 回滚 | 密钥存放位置 | 多节点 | 状态 / 许可证 |
|---|---|---|---|---|---|
| Coolify | 常驻仪表盘 + SSH 工作节点 | 在 UI 里选择之前的某次部署 | 平台数据库 | 1 台控制平面 + N 台 SSH 工作节点 | 成熟,社区庞大 / Apache 2.0 |
| Dokploy | 常驻仪表盘(轻量) | 在 UI 里选择之前的某次部署 | 平台数据库 | 基于 Docker Swarm | 活跃,比 Coolify 更轻 / Apache 2.0 |
| CapRover | 常驻仪表盘 | 用之前的镜像标签重新部署 | 平台配置 | 原生 Docker Swarm | 历史悠久,稳定 / Apache 2.0 |
| Kamal 2 | 无(仅 CLI 执行期间存在) | kamal rollback 立即生效 | 部署时从外部 vault 注入 | 服务器逐个列在配置里,滚动部署 | 成熟,Rails 8 默认工具 / MIT |
| Openship | 桌面 / 服务器 / 云端三选一 | 文档中称已支持 | 平台托管 | 路线图计划项 | 0.1.x,早期阶段 / Apache 2.0 |
| compose + runner | 无(由 CI 执行) | 需要自己实现 | CI 密钥或 vault | 每台服务器单独配置 | 没有现成工具,全靠自建 |
把这张表浓缩成一句话就是:团队里只要有人不习惯用终端,就选仪表盘类工具;要是全员都是后端工程师,就选 Kamal 或 compose。光这一条标准,就能解释大部分实际满意度的差异。
纯 compose 组合也是一个值得认真考虑的选项:在服务器上放一个 docker compose 配置和一个 systemd 单元,让 GitHub Actions 的自托管 runner 或者一个 webhook 接收器执行 git pull && docker compose up -d。如果服务只有一两个、每周部署一次,这套组合用最少的概念就能撑最久。不过零停机部署和回滚得自己动手实现,不实现的话,每次部署都会中断几秒钟。能不能扛住这几秒钟的中断,就是判断标准。
花掉的不是钱,而是注意力
关于自托管的讨论里,最常见的扭曲就是只拿价格来比较成本。把每月 200 美元的托管 PaaS 和每月 40 美元的 VPS 放在一起,结论似乎一目了然。可那 200 美元里,其实包含了一堆我们原本不用亲自动手的工作。
一旦转向自托管,以下这些就变成了自己的工作。
- 操作系统的安全补丁与重启计划
- Docker 引擎和部署平台本身的升级(以及升级搞砸之后的恢复)
- 磁盘用量管理 —— 旧镜像和构建缓存把磁盘塞满、导致部署卡死,这种事真的很常见
- 备份计划,以及恢复演练
- 仪表盘访问控制,维护 VPN 或白名单
- 应对证书续期失败(哪怕是自动化的,也照样会失败)
- 以及围绕这一切的 on-call 值班
换算成时间的话因团队而异,但对于几个服务的规模,正常月份 2~4 小时、出问题的月份搭进去一整天,是比较常见的范围。工程师的时薪按多少算,会很容易让这笔账反过来。
所以诚实的结论是这样的。
- 自托管更划算的情况:团队里已经有懂基础设施的人,反正也需要服务器(后台 worker、常驻进程、GPU),存在数据存放位置方面的要求,或者相对于流量而言托管账单已经明显偏高。
- 托管更划算的情况:团队小、需要专注在产品上,流量难以预测,没人能顶 on-call,部署频繁,并且没有合规方面的硬性要求。
- 最糟的情况:转向了自托管,却没有人真正接手维护。这比继续用托管服务还要贵 —— 六个月后,生产环境会跑在一个没人能升级的平台上。
关于迁移容器运行时本身的实务问题,可以参考从 Docker 迁移到 Podman这篇文章。
结语 —— 先写下谁来负责维护
像 Openship 这样的新工具还在不断涌现,原因是这个问题至今没有被真正解决。既想要托管 PaaS 的便利,又想拥有对自己服务器的控制权,这个诉求本身是合理的,而 Coolify、Dokploy、Kamal 各自从不同的角度回应了这个诉求。
- 先用五个维度筛选候选方案 —— 控制平面的位置、回滚路径、密钥存放位置、多节点表现、每月运维时间。功能表放在这些之后再看。
- 如果选了仪表盘类工具,就把仪表盘从公网上撤下来。默认应该放在 VPN 或 IP 白名单后面。
- 如果选了 Kamal 或 compose,就把回滚和查日志的流程写成文档。没有 UI,流程本身就是团队的知识。
- Openship 目前还停留在 0.1.x。不妨先在新的个人项目里试用,等路线图上的功能真正落地之后,再考虑用在需要多节点的负载上。
- 在引入文档的第一行,写下负责升级这个平台的人的名字。如果这个名字是空的,前面所有的工具对比都没有意义。
自托管并不会让成本消失,只是把它从账单挪到了日历上。
参考资料
현재 단락 (1/115)
[Openship 登上了 GeekNews](https://github.com/oblien/openship)。它的说法既熟悉又雄心勃勃 —— 只要指向一个仓库,就能自动识别技术栈、构建容器、...