Kamal 提供零停机部署、滚动重启、资源桥接、远程构建、附属服务管理,以及使用 Docker 在生产环境部署和管理 Web 应用所需的一切。最初为 Rails 应用打造,Kamal 适用于任何可容器化的 Web 应用。
美国 37signal 公司是一家 “小而美” 的公司,员工只有几十人但是每年有数百万利润。著名的项目管理工具 Basecamp、网络框架 Ruby on Rails 都出自他们。Kamal 也是他们的作品,用 Ruby 编写并集成到 Rails 的默认工具链中。
Kamal 的主要优势有二:一是与 Git 版本管理紧密结合,一次 commit 一个镜像,出什么问题可以立即回退;二是使用 kamal-proxy,从而带来健康检查与零停机部署,新版本实例正常上线前不会影响已有的实例。
前提条件如上所述,只要能被 Docker 打成镜像的 Web 应用都能用 Kamal 部署。同时因为 Kamal 有
Accessories的概念,事实上让 Kamal 占据了与 Docker Compose 一样的生态位。然而,你要部署的应用还要满足一些特殊条件:
- 你需要手动写一个 Dockerfile,使你的服务正常启动并向某个端口提供 HTTP 服务。
- 你需要添加
GET /up端点,该端点用于健康检查,如果应用状态正常则返回200 OK。(Kamal 最初是为 Rails 设计的,这个端点其实就是 Rails 的惯例。)对于部署目标 VPS 也有一定要求:
- 建议使用刚刚安装、仅配置好 SSH Key 远程连接的全新系统。
- 发行版建议为最新版本的 Ubuntu 或 Debian。
- SSH 开放在 22 端口且为
root账户登录。(显然 Kamal 的默认配置并不合安全的最佳实践,但是你会需要额外的配置,下文会说明)
下面准备了一个 Node.js 的例子,与一个 PHP+MySQL 的例子来说说它的基本用法。
请准备一个域名,并在 DNS 提供商中把域名解析事先设置到你的服务器上。
首先安装 Ruby 3.2+,如果你不知道怎么安装,可以用 rbenv。
然后运行:
$gem install kamal 把 Kamal 安装到本机上。
Node.js 应用
我已经准备了一个例子: GitHub - uqsme/app-size-comparison: App sizes comparison
切换到根目录,如你所见我已经写了 Dockerfile。本应用共有三个端点,根端点是一个 SPA,/api/files 是数据源,/up 是健康检查端点。
在根目录下执行:
$kamal init 此时会出现 config/deploy.yml,这就是 kamal 的配置文件。但是原配置过于冗长、注释繁琐,这里针对本例的简单应用,请直接清空然后复制粘贴以下内容:
service: app-size-comparison # 把kamal改成你Docker Hub的用户名,或你自定义registry的命名空间 kamal/app-size-comparison # 部署的目标服务器,即你ssh的IP或域名 servers: web: - 192.168.0.1 # 自动启用Let's Encrypt proxy: ssl: true # 换成你要部署的域名 host: lastname.uqs.me app_port: 8000 builder: arch: amd64 registry: password: KAMAL_REGISTRY_PASSWORD 再转到.kamal/secret:
# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets, # and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either # password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git. # Option 1: Read secrets from the environment # KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD # Option 2: Read secrets via a command # RAILS_MASTER_KEY=$(cat config/master.key) # Option 3: Read secrets via kamal secrets helpers # These will handle logging in and fetching the secrets in as few calls as possible # There are adapters for 1Password, LastPass + Bitwarden # # SECRETS=$(kamal secrets fetch --adapter 1password --account my-account --from MyVault/MyItem KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY) # KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD $SECRETS) # RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY $SECRETS) Kamal 在这个文件中存储凭据的获取方式,注释说了不仅支持环境变量、文件,还适配了密码管理器(当然也包括明文),可以直接从管理器中的条目读取。这里你需要设置你的 Docker Hub 密码,添加以下内容
KAMAL_REGISTRY_PASSWORD=你的Docker Hub密码
然后针对你的特殊情况,展开对应的部分:
使用 ghcr.io、阿里云等三方 registry以阿里云仓库服务为例,在
config/deploy.yml添加以下内容:registry: # 把这两行添加回去 server: xxxx-xxxxxxxxxxxxxxx.cn-hangzhou.personal.cr.aliyuncs.com username: uqsme password: - KAMAL_REGISTRY_PASSWORD
ssh 使用非 22 端口在
config/deploy.yml添加:ssh: port: 12345
ssh 使用非 root 用户Kamal 无法自动在目标上安装 Docker。你需要手动安装 Docker 与 Git:
#curl -fsSL https://get.docker.com -o- | bash -然后,在
config/deploy.yml添加:ssh: user: your_username
一切配置完成后,运行 kamal setup。
这将:
- 通过 SSH 连接到服务器(默认使用 root,通过 SSH 密钥认证)。
- 在任何尚未安装 Docker 的服务器上安装 Docker(使用 get.docker.com):这需要通过 SSH 获取 root 权限。
- 在本地和远程登录镜像仓库。
- 使用应用根目录下的标准 Dockerfile 构建镜像。
- 将镜像推送到镜像仓库。
- 从镜像仓库拉取镜像到服务器。
- 确保 kamal-proxy 正在运行,并在 80 和 443 端口接收流量。
- 启动一个与当前 Git 版本哈希对应的应用版本的新容器。
- 告诉 kamal-proxy,一旦新容器对
GET /up返回200 OK,就将流量路由到该容器。- 停止运行旧版本应用的上一个容器。
- 清理未使用的镜像和已停止的容器,防止服务器磁盘被占满。
稍后访问你的域名,或者 lastname.uqs.me,你应该就能看到一张图表,证明我们的 SPA 启动成功了。
接下来,假设你又进行了开发,请转到 app/src/index.html,找到这两行注释:
<!-- <h1>Here it is!</h1>
<p>I just changed the content.<p> --> 选中,用 Ctrl+/ 组合键取消注释。
Kamal 对 Git 有很好的集成,所以如果你有什么更改,可以在 git 提交后就立马上线。下面请 git add --all .、git commit -m "changed something" 完成提交,然后使用 kamal deploy 命令。
$kamal deploy 完成后,刷新你的网站,你会发现标题与内容如期呈现了。
PHP 应用
下面以异次元发卡系统为例。首先克隆源码:
$git clone https://github.com/lizhipay/acg-faka 然后初始化 Kamal
$cd acg-faka $kamal init 为了用 Docker 部署,编写 Dockerfile。由于有.htaccess,可以考虑用 fpm+apache 类型的,这里用 shinsenter 的:
FROM shinsenter/php:8.2-fpm-apache
COPY . /var/www/html/
WORKDIR /var/www/html
这里我还是直接提供完善的脚本 config/deploy.yml:
service: acg-faka # 把kamal改成你Docker Hub的用户名,或你自定义registry的命名空间 kamal/acg-faka # 部署的目标服务器,即你ssh的IP或域名 servers: web: - 192.168.0.1 # 自动启用Let's Encrypt proxy: ssl: true # 换成你要部署的域名 host: demo.uqs.me # fpm-apache脚本默认开80 app_port: 80 healthcheck: path: / builder: arch: amd64 registry: password: KAMAL_REGISTRY_PASSWORD accessories: db: mysql:8 # 假设是同一机器 host: 192.168.0.1 # 避免暴露公网 port: "127.0.0.1:3307:3306" # 要引入数据库容器的环境变量 # 都是mysql容器特有的设置 # 详见:https://hub.docker.com/_/mysql#environment-variables env: # 明文 clear: # 允许远程访问 MYSQL_ROOT_HOST: '%' # 库名 MYSQL_DATABASE: production # 需要保密的 secret: - MYSQL_ROOT_PASSWORD 首先要注意的是健康检查,上面说了 Kamal 要求 GET /up 返回 200 OK,但这只是 Rails 的惯例,往往其他应用就会 404,所以这里先把检查端点改成首页。
然后,发卡系统是一个数据库驱动的应用。一般情况下将网站与数据库同时部署,我们会考虑 Docker Compose,现在 Kamal 有 Accessories 机制,事实上就与前者等效了。
而且,Accessories 与主服务都在相同的 docker 网络下,可以直接用 <镜像名>-<accessory名> 代替 IP,来实现容器间的互通了。
在部署前,你仍要提供密码,不仅有 registry 的密码,还有你想让数据库使用的密码。.kamal/secret:
KAMAL_REGISTRY_PASSWORD=<你的registry密码>
MYSQL_ROOT_PASSWORD=mysql112233
配置完成后,提交一次 git commit,运行:
$git add --all . $git commit -m "setup kamal" $kamal setup 稍后,访问 demo.uqs.me,可以看到安装界面。在连接数据库时,可以这样写:
第一个字段是数据库所在主机,可以填 acg-faka-db,Docker 的域名解析就会转到数据库所在容器。第二个字段数据库名、第三个账户、第四个密码都是通过环境变量定义的。最后一个是表名前缀,但是 docker 部署的一个特点就是自带隔离,所以这个字段在这种部署模式下意义不大了。
最后看一下安装好的发卡网:
使用 kamal details 命令查看容器情况:
更多资料
官网
kamal 加速 - Ruby China
Kamal 的讨论与问题汇总 - Ruby China





评论区(暂无评论)