KamalはVPS上では動きません。自分のマシンやCIランナーにインストールするデプロイツールで、作業のためにSSHでサーバーへ接続します。BasecampがKubernetesなしでRailsアプリをリリースするために作ったものですが、Dockerイメージにビルドできるものなら何でもデプロイできます。
この違いは、探すべきものを変えます。VPSはKamalのデプロイ先なので、アプリケーション、データベース、そして数日分のイメージのバージョンを載せられる大きさが必要です。Kamal自身がサーバー上で占めるリソースはゼロです。
Kamalが実際にサーバーへ置くもの
kamal setupはSSHで接続し(既定ではrootとして、SSH鍵で認証します)、3つの手順を実行します。Dockerがなければ導入し、宣言したアクセサリを起動し、それからアプリをデプロイします。完了後、ホストでは次のものが動いています。
- kamal-proxy:80番と443番ポートを担い、Let’s Encryptの証明書を取得し、デプロイ中は入れ替え先のコンテナが準備できるまでリクエストを保持する小さなHTTPプロキシ
- アプリのコンテナ:
config/deploy.ymlで定義したロールごとに1組 - アクセサリ:設定に列挙したPostgres、Redisなどのサービス
Kamal自体はgem(gem install kamal、執筆時点で2.12.0)であり、常駐プロセスを残しません。
前提条件
始める前に次のものを用意してください。
- クリーンなLinuxとroot SSH鍵アクセスのあるVPS(Ubuntu 24.04が無難な既定です)
- サーバーが取得できるコンテナレジストリ——Docker Hub、GitHub Container Registry、またはプロバイダー提供のもの
gem install kamalのためのローカルのRuby。Rubyがなければコンテナイメージからも実行できますが、制約があります- アプリの動作する
Dockerfile
VPSプロバイダーの選び方
サーバーはコンテナを動かすだけなので、ネットワークの安定性、ディスクの余裕、ホストをどれだけ速く作り直せるかで判断します。
| プロバイダー | 価格 | 特徴 | アフィリエイトリンク |
|---|---|---|---|
| Contabo VPS | 5.99 EUR/月 | 400 GBのディスクが大きなイメージと長いロールバック履歴を吸収 | Contabo VPS |
| Hetzner Cloud | 5.49 EUR/月 | 2 vCPU / 4 GB / 40 GB NVMe。スナップショットでホストを素早く再構築 | Hetzner Cloud |
| DigitalOcean | 6 USD/月 | ホスト自体をスクリプト化するための最良のドキュメントとAPI | DigitalOcean |
| Vultr | 5 USD/月 | アプリを利用者の近くに置くための豊富なリージョン | Vultr |
| Linode | 5 USD/月 | 読みやすい価格設定と素直なネットワーク構成 | Linode |
詳しい比較はVPS比較のページをご覧ください。
ここで最も始めやすいのはDigitalOceanです。ドキュメントが、Kamalが代わりにやってくれないホスト側の作業——ファイアウォールの設定、スワップ、自動セキュリティ更新——を押さえているからです。
サーバーの準備
- VPSを作成します。最小構成のUbuntu 24.04イメージにSSH鍵を紐づけてください。
- root SSHが通ることを確認します。Kamalは既定でこの方法で接続します。
ssh root@your-vps-ip
- 必要なものだけ開けます。 TLSはサーバー上でkamal-proxyが終端するので、22番、80番、443番で十分です。
ufw allow 22,80,443/tcp && ufw enable
Dockerを自分で入れる必要はありません。なければ最初のkamal setupでKamalが導入します。
Kamalの導入と設定ファイルの記述
自分のマシンで、アプリケーションのディレクトリ内にて実行します。
gem install kamal
kamal init
kamal initはconfig/deploy.ymlを作成します。動作する最小限の設定には、サービス名、イメージ、少なくとも1つのホストが必要です。
service: myapp
image: your-registry-user/myapp
servers:
web:
hosts:
- 203.0.113.10
registry:
username: your-registry-user
password:
- KAMAL_REGISTRY_PASSWORD
proxy:
ssl: true
host: app.example.com
シークレットはKamalがデプロイ時に読む.kamal/secretsに置きます。先に.kamal/secrets-common、次に.kamal/secretsの順で参照されます。
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
このファイルはバージョン管理の外に置き、値はシェルの環境変数かパスワードマネージャーから渡してください。
最初のデプロイ
kamal setup
このコマンド1つで、Dockerの導入、アクセサリの起動、イメージのビルドとプッシュ、アプリの起動までが行われます。以降のデプロイは次のとおりです。
kamal deploy
ホストで何が動いているかを確認するには次を実行します。
kamal app containers
イメージがビルドされる場所
Kamalは既定で、実行したマシン上のローカルでビルドし、その後レジストリへプッシュします。サーバーは取得するだけです。手元のノートPCがarm64でVPSがamd64なら、ターゲットのアーキテクチャを固定するか、Kamalをリモートビルダーに向けてください。
builder:
arch: amd64
remote: ssh://[email protected]
エミュレーション経由のクロスビルドは、毎回のデプロイで体感できるほど遅くなります。VPSが手持ちで唯一のamd64マシンなら、ビルダーを兼ねることもできますが、その場合はアプリを動かすためだけでなくDockerビルドに合わせて見積もる必要があり、たいていはRAMのために1つ上のプランへ移ることになります。
ディスクの見積もり
Kamalはkamal rollbackが以前のバージョンをレジストリから取得し直さずに起動できるよう、古いコンテナとイメージをサーバーに残します。既定では3日後に整理されます。アプリケーション自身のデータに加えて、およそ3日分のイメージのバージョンと、その共有ベースレイヤーを見込んでください。
1 GBのイメージを1日数回デプロイする程度なら、Hetznerの40 GB NVMeに余裕をもって収まります。25 GBのディスクに3 GBのイメージなら1週間で埋まり、しかも埋まったディスクが壊すのは実行中のデプロイではなく次のデプロイです。そのため、切迫するまで見逃しやすい問題になります。
無停止での切り替え
kamal-proxyがトラフィックを新しいコンテナへ移すのは、そのコンテナがGET /upに200を返したあとだけです。ここから2つのことが導かれます。
- アプリに
/upエンドポイントが必要です。Railsは標準で用意しており、ほかのフレームワークでも数行で足ります。 - そのエンドポイントは、アプリが実際にリクエストを処理できるようになってはじめて200を返すべきです。データベース接続の準備前に応答してしまうと、利用者が早すぎるタイミングで新コンテナに当たります。
ヘルスチェックが通らないままだとデプロイはタイムアウトし、Kamalは旧コンテナにトラフィックを流したままにします。
デプロイのセキュリティ
- KamalはSSHにrootで接続するため、パスワード認証は完全に無効化し、鍵に頼ってください。
- TLSはkamal-proxyに任せましょう。その前段にもう1つリバースプロキシを置くと、証明書の経路が複雑になるだけです。
- 4 GBのホストにはスワップを追加してください。デプロイでは短時間だけ旧コンテナと新コンテナが同時に動き、途中でスワップが始まったホストはヘルスチェックのタイムアウトを引き起こすほど遅くなります。
- レジストリのパスワードは、バージョン管理に入る
config/deploy.ymlではなく.kamal/secretsに置いてください。
最後に
- 手動での手順が固まったら、
kamal deployをCIから実行しましょう。設定は同じで、変わるのはシークレットの取得元だけです。 - 古いイメージがディスクに残っている間、
kamal rollback [バージョン]は即座に完了します。ロールバック先があると決めてかかる前にkamal app containersを確認してください。 - 同じホストへの2つ目のアプリのデプロイはKamal 2でサポートされています。kamal-proxyがホスト名で振り分けるので、各アプリに固有の
service名とproxy.hostがあれば足ります。
土台となるサーバー選びについては、VPS比較のページもあわせてご覧ください。
Frequently asked questions
KamalはVPS上で動くのですか、それとも自分のマシンで動くのですか?
Kamalは自分のマシンかCIランナーで動きます。SSHでサーバーに接続し、Dockerがなければ導入してコンテナを起動するRuby gemです。VPSに残るのはkamal-proxyとアプリおよびアクセサリのコンテナであり、Kamal自体が残ることはありません。
Kamalのデプロイ先に使うVPSにはどれくらいのディスクが必要ですか?
アプリのデータに加えて、およそ3日分のイメージのバージョンを見込んでください。Kamalはロールバック用に古いコンテナとイメージを保持し、既定では3日後に整理します。1 GBのイメージを1日数回デプロイする程度なら40 GBに十分収まりますが、25 GBのディスクに3 GBのイメージでは収まりません。
Kamalのデプロイがタイムアウトしてロールバックされるのはなぜですか?
kamal-proxyは、新しいコンテナがGET /upに200を返してはじめてトラフィックを移します。アプリにそうしたエンドポイントがない場合や、起動途中にエラーを返す場合はヘルスチェックが通らず、Kamalは以前のコンテナへ戻します。