编程 Kamal 2 部署笔记:kamal-proxy 如何切换流量,deploy.yml 怎么填

2026-09-25 21:31:50

Kamal 2 部署笔记:kamal-proxy 如何切换流量,deploy.yml 怎么填

项目信息

  • 仓库:
  • 官网与文档:
  • kamal-proxy 源码:
  • 当前版本 2.12.0,MIT 许可。项目原名 MRSK。

最初是为 Rails 应用写的,任何能被 Docker 容器化的 web 应用都能用。

定位

从裸机到云 VM,用 Docker 部署 web 应用,做到零停机。kamal-proxy 负责在容器之间无缝切换请求,SSHKit 负责在多台服务器上执行命令。

设计取舍

不走 K8s/Swarm 那套声明式状态 reconcile,走的是和 Capistrano 一样的命令式(imperative)路线,可以理解成 Capistrano for Containers。服务器不需要提前精心准备:Ruby 或其它依赖都打包进 Docker 镜像,全新 Ubuntu 服务器加一个 SSH key,写进服务器列表,Kamal 会自动装 Docker 并跑起来。

相比自建 Kubernetes,Kamal 简单得多,做的事情看得见,本质就是调用基础的 Docker 命令。Heroku/Fly/Render 这类云托管平台好用但会绑定,Kamal 想在没有商业平台加价的前提下,把部署体验做到接近。37signals 用它把 HEY 等应用从云迁回自有机房。

kamal-proxy

Kamal 2 自带一个专用反向代理 kamal-proxy,是一个独立的 Go 二进制,替代应用前面单独跑的 Nginx 或 Caddy 进程。

部署时 kamal-proxy 先把连接 hold 住,等新容器起来、健康检查通过后原子切换流量,不丢请求。老容器上的在途请求会被 drain(默认 30 秒)之后才发 SIGTERM。请求如果超过这个窗口(大文件上传、报表),可能被切断,可以调大 proxy 下的 stop_wait_time:

stop_wait_time: 120

kamal-proxy run 启动代理;kamal-proxy deploy service1 --target web-1:3000 注册目标。健康检查失败会中止部署并返回非零退出码。TLS 方面,--tls 自动申请并续期证书,--tls-on-demand-url 支持按需签发。run 的选项也可以通过环境变量传入(HTTP_PORT / KAMAL_PROXY_HTTP_PORT 等)。

安装与初始化

gem install kamal
kamal init

kamal init 会生成 config/deploy.yml、.kamal/secrets、Dockerfile。

config/deploy.yml

service: myapp
image: ghcr.io/yourorg/myapp

servers:
web:
hosts:
- 192.0.2.10
- 192.0.2.11
job:
hosts:
- 192.0.2.10
cmd: bin/jobs

proxy:
ssl: true
host: myapp.example.com
app_port: 3000
healthcheck:
path: /up
interval: 2
timeout: 5
# stop_wait_time: 120

registry:
server: ghcr.io
username: yourorg
password:
- KAMAL_REGISTRY_PASSWORD

env:
clear:
RAILS_ENV: production
PORT: "3000"
secret:
- RAILS_MASTER_KEY
- DATABASE_URL

accessories:
sidekiq:
image: ghcr.io/yourorg/myapp
hosts: [192.0.2.10]
cmd: "bundle exec sidekiq"
env:
secret: [RAILS_MASTER_KEY, DATABASE_URL, REDIS_URL]

boot:
limit: 10
pause: 0

几个字段的说明:

  • proxy.host 填域名,HTTPS 由 Let's Encrypt 自动签发。用 Cloudflare 时把 SSL/TLS 加密模式设为 Full。
  • app_port 要和容器里应用监听的端口一致,默认代理连容器的 80。
  • 构建默认用最后一次 git commit 作为来源。初期调试可以设 context: . 直接用工作目录里未提交的改动,稳妥之后删掉这行。构建默认在部署机上做远程构建,也可以指定独立的 builder。
  • 多目的地:kamal deploy -d staging 会额外读取并合并 config/deploy.staging.yml。

第一次部署

kamal setup。第一台服务器、第一个应用会装 Docker、建 kamal 网络、启动 kamal-proxy;之后的应用会跳过这些步骤,直接用已经存在的 proxy。

kamal deploy 的执行顺序

  1. Build:构建镜像并推到 registry
  2. Lock:在主服务器上取部署锁,并发部署会被中止
  3. Pull:各主机拉新镜像
  4. 跑 boot 命令,例如 kamal app exec --reuse 'bin/rails db:migrate'
  5. 启动新容器(与老容器并存)
  6. 健康检查:kamal-proxy 轮询 health path,直到返回 200 或超时
  7. 切换流量:原子路由到新容器,老容器上的在途请求 drain
  8. 停老容器:SIGTERM,超时后 SIGKILL
  9. 释放锁

多台 web 主机时,3–8 步默认并行。要做真正的逐台滚动部署,用 --hosts 指定:

kamal deploy --hosts 192.0.2.10
kamal deploy --hosts 192.0.2.11

超时换算:有效启动窗口是 healthcheck.interval × healthcheck.timeout(上面例子是 2×5)。外层 boot.limit 是总墙钟预算(秒),要覆盖最坏的启动时间加上数据库迁移时间。

accessories 与单机多应用

accessories 用来跑长期存在的边车(db、cache、worker)。Rails 7.1 起自带 /up 健康端点,检查 Active Record、Action Cable adapter、Active Storage。

同一台服务器上跑多个应用时,每个应用各自一份 config/deploy.yml 和各自的 service/host,但只跑一个 kamal-proxy 实例(由第一个部署的应用安装)。查看 proxy 上注册的服务:

ssh USER@SERVER
docker exec kamal-proxy kamal-proxy list

输出列:Service / Host / Target / State / TLS。

Kamal 目前不能做重定向,www 到非 www 的跳转要在应用侧做;任何需要暴露 HTTP 端点的 accessory 其实应该做成一个独立应用。

文档

  • 安装:
  • 配置:
  • 命令:
  • 源码:
  • kamal-proxy:
复制全文 生成海报 Kamal Docker 运维 部署 kamal-proxy

推荐文章

程序员茄子在线接单