Kamal 2で さくらのVPS にRailsアプリをデプロイ
開催されたばかりのRailsWorld 2024で発表されたKamal 2を使って、さくらのVPSにRailsアプリをデプロイしましたので報告します。
Linuxのサーバデプロイの知識がある程度あることを前提に、私が引っかかったポイントを中心に説明します。入門的な内容は、いつか別途書きたいと思います。
TL;DR
- Kamalを使うと さくらのVPSなどに、Railsアプリを簡単にデプロイできます
- VPS、ドメイン、Dockerイメージレポジトリは事前に用意が必要です
- SSLもLet's Encyryptから自動的に取得され、追加の設定が不要です
- 多数のアプリをひとつのVPSに同時に載せることができますので、ポートフォリオや個人開発には最適です
- Railsに限らず、Docker化できるアプリならなんでもいける
- 自分の感触だと、AWSよりマジでずっと簡単 (例えばこの例など)
前提とする知識
以下のことを前提に話をします。難しい内容ではなく、かなり枯れた技術なので、LinuxのVPSサーバを使ったことがなくてもググれば学習できるはずです。
- さくらのVPSをセットアップできること
- OSはUbuntu
- ポート 22 (SSH)と 80 (http)、443 (https)を開ける
- SSHにパスワードなし、public keyを使ったアクセスを設定できること
- rootからpublic keyでアクセスできるように設定できること (通常passwordによるアクセスはさせない)
- ドメイン名を取得できること
- DNS設定をして、取得したドメイン名をセットアップしたVPSに向けられること
- Docker, Gitの基本的な使い方ができること
- DockerHubにアカウントを作成し、Docker imageのレポジトリが用意できること
- ローカルマシンにRubyがインストールされていること(必須ではないのですが、本記事ではこれを前提とします)。Rbenvでインストールされているという前提で進めます
Kamalとは何か
Kamalの公式ドキュメントの解説を下に引用します
Kamal offers zero-downtime deploys, rolling restarts, asset bridging, remote builds, accessory service management, and everything else you need to deploy and manage your web app in production with Docker. Originally built for Rails apps, Kamal will work with any type of web app that can be containerized.
- Docker化ができるweb appであればすべてデプロイできます
- ゼロダウンタイムのデプロイができます
- データベースなどの"accessory service"にも対応します(同じVPSでホストできます)
- 1台のVPSに複数のサービスを搭載できます
一言でいうと、自分で管理しているサーバ(例えばさくらのVPS)で、HerokuとかRenderとかFly.ioのような超簡単なデプロイが安価に、かつ定額でできるようになります
Kamalのデプロイ手順(超概略版:精度は微妙だけど感覚的にはこれ)
- ローカルマシンで
kamal setup
(あるいはkamal deploy
)コマンドを打ち込みます - Kamalは
config/deploy.yml
(GitHub)に登録されたサーバにSSHで接続し、必要ならDockerをインストールします - 次にKamalはgitにcommitされている内容を元に、Dockerfileに従ってDockerイメージを作成します
- DockerHubなどのレポジトリに接続して、作成されたDockerイメージをアップロードします
- 次にKamalは
config/deploy.yml
(GitHub)に登録されたサーバにSSHで接続します、上記のレポジトリからDockerイメージをダウンロードさせます - 各サーバではDockerイメージを使ってコンテナを起動します
- Health checkをして、コンテナが正常に起動していることを確認したら、ネットワークからのリクエストを新しいコンテナに振り向けます(古いコンテナから新しいコンテナに切り替えます)
- 古いコンテナを終了させます
- デプロイ完了!
やること
さくらのVPSを用意する
- 新規にUbuntuをインストールします (ゼロからやるのが一番早いです)
- ポート22, 80, 443を開けます
- ローカルマシンからrootユーザでSSHログインできるようにします(パスワードではなくpublic key認証にするのが良い)
ドメイン名の取得
DNS設定が伝播するのに時間がかかることがありますので、まず最初にこれをやっておきます。好きなドメイン名を取得して、これが上記のVPSに向かうように設定してください
DockerHubでレジストリを作成
Kamalでデプロイをするためには、DockerHub等でレポジトリを登録できる必要があります
-
DockerHubでレポジトリを作成します。公開レポジトリなら無数に作れるので、今回は公開のものを作ると良いでしょうKamalは自動的にレポジトリを登録してくれることがわかりました。このステップは不要でconfig/deploy.yml
に任意の名前を記載すれば十分です。もちろん事前に登録しても良いです - KamalからDockerHubにアクセスするためのアクセストークン(Personal access token)を作成し、記録しておきます。"Read & Write"の権限が必要です。あとで使います
Railsアプリを新規作成
- Railsがインストーラされていない場合は
gem install rails
でインストールします -
rails new kamal-rails
で新規プロジェクトを作ります。ファイルはkamal-rails
のフォルダに作成されます - 最新のRailsはDockerfileが自動的に作成されますので、この場所と内容を軽く確認します。変更は不要です
- Kamalは静的ファイルを配信するNGINXのようなウェブサーバがありませんので、
config/environments/production.rb
(GitHub)でconfig.public_file_server.enabled = true
とします。いまどきは静的ファイルはCDNを使いますので、静的ファイル配信用のウェブサーバを使わないことも多くなっています。なおGoで書かれたThrusterは静的ファイルの配信が得意なので、実際にはThrusterもインストールした方が良いしょう - RailsのデフォルトのデータベースはSqlite3です。
config/database.yml
(GitHub)の設定をみると、本番(production)環境だけdatabase
が設定されていません。本番データベースを下記のように設定してください
production:
<<: *default
database: storage/production.sqlite3
Kamalをインストール
- 新しく作った
kamal-rails
フォルダに入ります(cd kamal-rails
)。 -
bundle add kamal
でKamalをインストールします -
kamal init
でKamalの設定ファイルを作成します
Kamalを設定
config/deploy.yml
詳しくはGitHubに公開していますので、ご確認ください。
-
service:
のところをサービス名にする。今回はkamal-rails
-
image:
のところは、DockerHubで作ったレジストリの名前です(レジストリが存在しない場合はKamalが自動的に作成してくれます)。私はnaofumik/kamal-rails
を事前に登録したので、これを使います。ここは自分の情報を入力してください。 -
servers: web:
のところは、さくらのVPSのIPアドレスを入力します。私は133.167.104.211
です。ここは自分の情報を入力してください。 -
proxy: host:
のところは、自分が取得したドメイン名を入力してください。私はkamal-rails.castle104.com
です。ここは自分の情報を入力してください。 -
proxy: app_port: 3000
を追加してください。これはRailsコンテナに接続するポートになります。Thrusterを使うとportが80になるという情報もあり、その場合はこれが不要になると予想されます。 -
registry: username:
はDockerHubのユーザ名を入れてください。私はnaofumik
です。ここは自分の情報を入力してください。 -
env: secret: - RAILS_MASTER_KEY
を記載してください。ここではRailsが稼働しているコンテナにRAILS_MASTER_KEY
環境変数を渡します
また以下のところも必要に応じて変更してください。
/storage
の永続化
# Use a persistent storage volume.
#
volumes:
- "kamal_rails_storage:/rails/storage"
上記はRailsの/storage
フォルダの中を永続化します。Dockerを使ったデプロイの時は、デプロイのたびにすべてのファイルが入れ替わってしまいます。/storage
フォルダはsqlite3のデータベースやActiveStorageの画像ファイルを入れる場所になりますので、これでは都合が悪いです。そこでvolumes
を使って、別のところにこのフォルダの中身を保存しています。
なお/rails/storage
のところの"/rails"は、DockerfileのWORKDIR
に相当しますので注意してください。
デプロイ時のAssetがスムーズに切り替わるように
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
# hitting 404 on in-flight requests. Combines all files from new and old
# version inside the asset_path.
#
asset_path: /rails/public/assets
RailsはJS, CSSや画像ファイル等のassetにfingerprint(指紋)のハッシュをつけます。assetはCDNやブラウザなどにキャッシュさせたい一方、更新された時に確実に新しいものに切り替える必要があります。そのためにRailsはSprocketsやPropshaftが指紋ハッシュをファイル名につけます。しかし、ゼロダウンタイムのデプロイをする時に問題になることがあります。何かの理由でHTMLのバージョンが古い場合、そこから呼び出される古いassetが見つからなくケースがあるためです。
Kamalはこのようなケースでも問題が起きないための工夫をしています。これがその設定です。
なお/rails/public/assets
のところの"/rails"は、DockerfileのWORKDIR
に相当しますので注意してください。
.kamal/secrets
デプロイする際に必要なトークンやパスワードを読み込むところです。ここに登録された情報は上記のconfig/deploy.yml
で読み込まれます。また.kamal/secrets
に直接秘密情報を記載せず、別のところに記載します。
私は下記のようにしています。
-
KAMAL_REGISTRY_PASSWORD
は.kamal/registry_password.key
を読み込むようにします。 -
.kamal/registry_password.key
ファイルを作ります。.kamal/registry_password.key
は.gitignore
でGitに登録されないようにしてください!!! 。中身はDockerHubのアクセストークン(Personal access token)の内容をペーストします -
# RAILS_MASTER_KEY=$(cat config/master.key)
となっているところはコメントアウトして、RAILS_MASTER_KEY=$(cat config/master.key)
としています。config/master.key
から読み込むようにしています
Healthcheckの設定
config/environments/production.rb
(GitHub)を変更します。下記のところをコメントアウトしてください。ヘルスチェック時のリクエストに対してはSSLを使わないようにするものです。
# Skip http-to-https redirect for the default health check endpoint.
config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
Kamal実行
-
kamal setup
でセットアップが行われ、Railsアプリがデプロイされます - 次回以降は
kamal deploy
でデプロイできます
Rails以外
Docker化できるものは基本的にKamalでデプロイできますので、試しに色々作ってみました。
やらなくて済むこと
やらなきゃいけない事も大切ですが、やらなくて済む、いや考える必要すらないことも大切です。最後にそこを少しだけお話しします
- AWSと異なり、複雑な権限管理を考える必要はありません。AWSで出てくるVPCも考える必要がありません。内部では存在していますが
- SSL証明書は無料だし、それどころか一切考える必要がありません。Let's EncryptとKamal 2がその辺りを全部やってくれます。SSLって何のことか知らなくてもほぼいけます
- ゼロダウンタイムデプロイは勝手にやってくれます。それ以外のデプロイはやらせてくれません
今後
今回はsqlite3を使いましたのでRailsと同じコンテナの中で動かしましたが、別コンテナにデータベースを載せたり、あるいはそのバックアップを取ったりということをやりたいと思います
Discussion