不是所有团队都需要 Kubernetes。当你的服务只有 3-10 台机器,K8s 的学习成本和运维复杂度远超收益。Kamal 2 让你用一条命令把 Docker 容器部署到任意 Linux 服务器,零停机、自动 SSL、滚动发布——没有 etcd,没有 kubelet,没有 YAML 地狱。
痛点:小规模部署的尴尬处境
- Kubernetes 太重 —— 3 台机器跑个 API 服务,为此搭一套 K8s 控制面(etcd + apiserver + scheduler + controller-manager),运维成本远超业务本身
- 裸 Docker 太原始 ——
docker-compose up -d能跑,但没有滚动发布、健康检查、零停机切换,每次部署都是赌 - Ansible/脚本难维护 —— 自己写部署脚本,时间一长逻辑混乱,新人接手直接懵
Kamal(原名 MRSK)由 37signals(Basecamp/HEY 背后的公司)开源,专门解决这个场景:用 Docker 容器的标准化打包能力,配合 SSH 直连部署,给中小规模服务提供生产级发布体验。
Kamal 2 核心能力
| 能力 | 说明 |
|---|---|
| 零停机部署 | 内置 kamal-proxy 反向代理,新容器就绪后自动切流 |
| 多服务器编排 | 一个配置文件管理 web、worker、cron 等多角色 |
| 自动 SSL | 集成 Let's Encrypt,HTTPS 开箱即用 |
| 滚动发布 | 逐台部署,失败自动回滚 |
| 环境变量管理 | 加密存储 secrets,部署时注入 |
| 无依赖 | 目标机器只需 Docker + SSH,无 Agent |
实操步骤
Step 1:安装 Kamal CLI
# 方式一:gem 安装(需 Ruby 2.7+)
gem install kamal
# 方式二:Docker 方式运行(无需本地 Ruby)
alias kamal='docker run -it --rm -v "${PWD}:/workdir" -v "${SSH_AUTH_SOCK}:/ssh-agent" -v /var/run/docker.sock:/var/run/docker.sock -e "SSH_AUTH_SOCK=/ssh-agent" ghcr.io/basecamp/kamal:latest'
# 验证
kamal version
# => Kamal 2.x.x
Step 2:初始化项目配置
kamal init
生成 config/deploy.yml,核心配置如下:
# config/deploy.yml
service: myapp
image: registry.example.com/myapp
servers:
web:
hosts:
- 10.0.1.10
- 10.0.1.11
options:
memory: 512m
worker:
hosts:
- 10.0.1.12
cmd: bundle exec sidekiq
proxy:
ssl: true
host: app.example.com
app_port: 3000
healthcheck_path: /up
registry:
server: registry.example.com
username: deploy
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
DATABASE_URL: postgres://db.internal:5432/myapp
secret:
- RAILS_MASTER_KEY
- REDIS_URL
builder:
multiarch: false # 目标机器架构一致时关闭,加速构建
Step 3:配置 Secrets
# 创建加密的 secrets 文件
kamal secrets set KAMAL_REGISTRY_PASSWORD=xxx RAILS_MASTER_KEY=yyy REDIS_URL=redis://10.0.1.20:6379/0
Secrets 加密存储在 .kamal/secrets 中,部署时通过 SSH 注入容器环境变量,不落盘到目标服务器。
Step 4:首次部署
# 首次部署(会自动安装 Docker、kamal-proxy)
kamal setup
# 后续常规部署
kamal deploy
执行流程: 1. 本地构建 Docker 镜像并 push 到 Registry 2. SSH 到各目标服务器拉取新镜像 3. 启动新容器,等待健康检查通过 4. kamal-proxy 将流量切到新容器 5. 停止旧容器
整个过程零停机,失败则自动保留旧容器继续服务。
常用运维命令
# 查看所有服务器上的容器状态
kamal details
# 查看应用日志(实时)
kamal app logs -f
# 回滚到上一个版本
kamal rollback
# 在远程服务器执行一次性命令
kamal app exec 'bin/rails db:migrate'
# 重启所有 web 容器
kamal app restart -r web
# 查看 kamal-proxy 状态
kamal proxy details
避坑指南
1. 构建缓慢:关闭 multiarch
默认 Kamal 会构建 linux/amd64 + linux/arm64 双架构镜像。如果你的服务器全是 x86,在 deploy.yml 中设置 builder.multiarch: false,构建时间从 8 分钟降到 2 分钟。
2. 健康检查超时导致部署失败
kamal-proxy 默认等待 30 秒健康检查通过。如果应用启动慢(比如 Java/Spring Boot),需要调整:
proxy:
healthcheck_path: /up
healthcheck_interval: 3 # 每 3 秒检查一次
healthcheck_timeout: 60 # 最多等 60 秒
3. SSH 连接数过多被防火墙拦截
Kamal 并行 SSH 连接所有服务器。机器超过 10 台时,可能触发 MaxStartups 限制:
# /etc/ssh/sshd_config(目标服务器)
MaxStartups 30:30:60
或在 deploy.yml 中限制并发:
servers:
web:
hosts: [...]
ssh:
max_concurrent_connects: 5
Kamal vs Kubernetes vs Docker Compose 对比
| 维度 | Kamal 2 | Kubernetes | Docker Compose |
|---|---|---|---|
| 适用规模 | 1-20 台服务器 | 10-10000+ 节点 | 单机开发/测试 |
| 零停机部署 | ✅ 内置 | ✅ 内置 | ❌ 需自行实现 |
| 自动 SSL | ✅ Let's Encrypt | 需 cert-manager | ❌ |
| 学习曲线 | 低(1 个 YAML) | 高(数十种资源对象) | 低 |
| 服务发现 | 按角色分组 | DNS/Service | links/networks |
| 自动扩缩容 | ❌ | ✅ HPA/VPA | ❌ |
| 运维依赖 | SSH + Docker | etcd + 控制面 | Docker Engine |
| 适合场景 | 中小 API/Web 服务 | 微服务集群 | 本地开发 |
总结
- 3-10 台机器的 Web/API 服务,Kamal 2 是目前最务实的选择:部署体验接近 K8s,运维复杂度接近裸机
- 核心优势:零停机、自动 SSL、无 Agent、一条命令部署,对运维人员极度友好
- 不适合的场景:需要自动扩缩容、服务网格、复杂调度策略的大规模微服务——这些场景老老实实用 K8s
- 迁移建议:如果你还在用
docker-compose + Nginx + 手动 scp部署,Kamal 是零成本升级路径,半天就能跑通
37signals 用 Kamal 部署了 HEY(百万级用户邮件服务)和 Basecamp 的全部基础设施,证明了这套方案在真实生产环境的可靠性。对于不想被 K8s 绑架、又需要生产级部署能力的团队,Kamal 2 值得一试。