饮墨

子安饮墨馀三斗,留与卿儿作赋来

Kamal 2 实战:不用 Kubernetes 也能优雅部署容器化应用

不是所有团队都需要 Kubernetes。当你的服务只有 3-10 台机器,K8s 的学习成本和运维复杂度远超收益。Kamal 2 让你用一条命令把 Docker 容器部署到任意 Linux 服务器,零停机、自动 SSL、滚动发布——没有 etcd,没有 kubelet,没有 YAML 地狱。


痛点:小规模部署的尴尬处境

  1. Kubernetes 太重 —— 3 台机器跑个 API 服务,为此搭一套 K8s 控制面(etcd + apiserver + scheduler + controller-manager),运维成本远超业务本身
  2. 裸 Docker 太原始 —— docker-compose up -d 能跑,但没有滚动发布、健康检查、零停机切换,每次部署都是赌
  3. 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 值得一试。

您还没有登录,请登录后发表评论。