このページは原文を AI が翻訳したものです。
最近、Kamal を使って初めて Rails アプリケーションをデプロイしました。これまで Docker Compose と Caddy でブログを管理してきた私は、Rails のデプロイも似たような流れになるだろうと最初は考えていました。しかし実際には、リバースプロキシの設定や Docker のストレージ構成を中心に、いくつか摩擦ポイントがありました。
以下が、そのセットアップで得たメモと解決策です。
アーキテクチャの決定:プロキシ競合の回避
当初の計画は、ブログがすでに Caddy の背後で動いている既存サーバーにすべてをデプロイすることでした。
しかし、Kamal はデフォルトで kamal-proxy を使用します。同じサーバー上で 2 つのリバースプロキシ(kamal-proxy と Caddy)が競合すると、すべてのトラフィックを Caddy 経由にするか、すべてを kamal-proxy に移行しない限り、すぐに複雑になってしまいます。
設定の複雑さを増やしたり、複数のアプリを処理するためにその単一サーバーをアップグレードする代わりに、私はシンプルな方法を取りました:Rails アプリ専用に同等スペックの別 VPS を立ち上げるのです。これにより、両方の環境をクリーンで分離された状態に保てました。
動作する deploy.yml
以下が最終的な config/deploy.yml 設定です:
service: <your-application-name>
image: <your-name>/<your-application-name>
ssh:
user: <your-ssh-user-name>
servers:
web:
- <server ip address>
job:
hosts:
- <server ip address>
cmd: bin/jobs
# Critical if behind Cloudflare
proxy:
ssl: true
host: <your-host-name>
forward_headers: true
registry:
username: <docker-hub-username>
password:
- KAMAL_REGISTRY_PASSWORD
env:
secret:
- RAILS_MASTER_KEY
clear:
HOST: <your-host-name>
RAILS_SERVE_STATIC_FILES: true
RAILS_LOG_TO_STDOUT: true
DB_HOST: <same-as-server-name>-db
aliases:
console: app exec --interactive --reuse "bin/rails console"
shell: app exec --interactive --reuse "bash"
logs: app logs -f
dbc: app exec --interactive --reuse "bin/rails dbconsole"
asset_path: /rails/public/assets
volumes:
- "pulse_storage:/rails/storage"
builder:
arch: amd64
accessories:
db:
image: postgres:18
host: <your-server-ip>
env:
clear:
POSTGRES_USER: <application-name>
POSTGRES_DB: <application-name>_production
secret:
- POSTGRES_PASSWORD
files:
- config/init.sql:/docker-entrypoint-initdb.d/setup.sql
volumes:
- <application>_db_data:/var/lib/postgresql/data主要な設定の詳細
1. Cloudflare と forward_headers
Cloudflare でプロキシを有効(オレンジ色の雲)にしている場合、proxy ブロック内に forward_headers: true を設定する必要があります:
proxy:
ssl: true
host: example.com
forward_headers: trueこの設定がないと、kamal-proxy はクライアントの実際の IP を Rails に渡さず、request.remote_ip が Cloudflare のエッジ IP を報告することになります。
2. Docker エラー:directories と volumes の違い
最初の kamal deploy の際、データベースコンテナが以下のエラーで起動に失敗しました:
docker: Error response from daemon: failed to create task for container:
failed to create shim task: OCI runtime create failed: runc create failed:
unable to start container process: error during container init:
error mounting "/home/<username>/<application>-db/data" to rootfs at
"/var/lib/postgresql/data": change mount propagation through procfd:
open o_path procfd: open /var/lib/docker/overlay2/[...]/merged/var/lib/postgresql/data:
no such file or directory: unknownKamal のアクセサリ設定では:
directoriesはホストパスを直接バインドします(ホストファイルシステム上に存在する必要があります)。volumesは名前付き Docker ボリュームを作成・管理します。
多くのチュートリアルではアクセサリを directories で設定していますが、名前付き volumes に切り替えることで、ホストディレクトリの権限やパス作成の問題を完全に回避できました:
accessories:
db:
volumes:
- <application>_db_data:/var/lib/postgresql/data3. init.sql による複数データベースの初期化
Rails 8 で Solid Queue、Solid Cache、Solid Cable を使用する場合、本番環境でデータベースを分離するのが標準です。SSH で手動でデータベースを作成する代わりに、PostgreSQL では初期化スクリプトを /docker-entrypoint-initdb.d/ にマウントできます:
-- config/init.sql
CREATE DATABASE <application>_production;
CREATE DATABASE <application>_production_cable;
CREATE DATABASE <application>_production_cache;
CREATE DATABASE <application>_production_queue;このファイルを deploy.yml でマッピングします:
accessories:
db:
files:
- config/init.sql:/docker-entrypoint-initdb.d/setup.sqlデータベースコンテナが初めて起動する際、必要なすべてのデータベースが自動的に作成されます。
まとめ
適切に設定すれば、Kamalは非常にスムーズなデプロイワークフローを提供してくれます。主な摩擦ポイントは、kamal-proxyがCloudflareのヘッダーとどう連携するかを理解することと、アクセサリー用のDockerボリュームマウントが未初期化のホストディレクトリではなく名前付きボリュームを使用するようにすることでした。


コメントを読み込み中…