概要
最近個人的に Azure App Service で Ruby on Rails アプリである Redmine を稼働させる DevOps プロジェクトに取り組んでおり、いくらか形になってきたので紹介します。
このプロジェクトで使用する Redmine は、ある程度新しければどのバージョンでも対応できます。先日 v2.1.0 が出たばかりの RedMica でも大丈夫です。
この記事では、今回 App Service で稼働させた Redmine アプリのコンテナ仕様とデプロイ手順について説明します。上記の GitHub プロジェクトの README.md にも英語で書かれている内容ですが、この記事には Rails アプリの DevOps の設計や実装に関連する内容も含まれていますので、参考にしてください。
Redmine アプリコンテナ仕様
DevOps の方針
App Service on Linux で web アプリをホスティングしようとするとき、App Service が提供する各処理系のランタイムスタックを利用するか、コンテナ化したアプリを動かすかという選択肢があります。
App Service で現在利用可能な Linux のランタイムスタックには一応 Ruby も含まれているのですが、2022年6月の時点でバージョンが 2.7.3 と古く、今後継続的にサポートされるのか怪しい状況なので、今回は Redmine アプリをコンテナ化して動かすことにしました。
App Service 向けに web アプリをコンテナ化するにあたり、押さえておきたい App Service の特徴は次の3つです。
- アプリ設定(app settings)がコンテナ内プロセスに環境変数として引き渡される → データベースの接続先などのアプリインスタンスごとの設定のほか、メンテナンスモードのようなアプリ稼働モードを変えるために利用します。
- 読み書き可能な共有ストレージが
/home/site/wwwroot
にマウントされる → 永続化すべきファイルの保存や、テーマやプラグインなどカスタマイズ要素の配置場所として使用します。 - コンテナ内のシェルに SSH 接続でアクセスできる → アプリの初期設定やデータベースのマイグレーションなど、メンテナンス作業に活用します。
特に最後の SSH 接続は非常に便利な機能で、web ブラウザから稼働中のコンテナに接続し、コンテナ内のツールを使ってアプリのメンテナンスやトラブルシューティング作業を行うといったことが手軽にできます。
今回コンテナ化した Redmine アプリではこれを最大限に利用することに決め、 Redmine 運用チームが行う設定やメンテナンスは web ブラウザだけで完結するように構成しています。
なお、利用を見送ったランタイムスタックも実際にはコンテナとして実装されており、そのソースコードは次のリポジトリにあります。
ですので、アプリのコンテナ化の作業ではこれを参考にするとよいでしょう。 Ruby の場合はランタイムスタックコンテナ作成の Dockerfile のテンプレートが GenerateDockerFiles/ruby フォルダにあります。
コンテナは GitHub Actions により自動ビルドされます。最新場の Redmine と RedMica を使ったコンテナイメージが ghcr.io で公開されていますので、すぐに試すことができます。
App Settings
Redmine アプリでは次のような app settings (アプリ設定) を使用します。
Variable | Description |
---|---|
WEBSITES_ENABLE_APP_SERVICE_STORAGE |
App Service 共有ストレージを /home にマウントするため常に true にします。 |
RAILS_ENV |
Rails アプリの動作環境。production development など。 |
RAILS_IN_SERVICE |
Rails アプリ動作モード。true の場合は正常に起動します。 false の場合はメンテナンスモードに入ります。 |
SECRET_KEY_BASE |
Rails アプリのセッションクッキー暗号化に使用するランダムな文字列を指定します。 |
DATABASE_URL |
Rails アプリのデータベース接続設定。参照: the guide for configuring a database |
コンテナ内のパス
Redmine アプリコンテナ内で重要なパスの一覧です。
Path | Description |
---|---|
/redmine/ |
Redmine アプリの展開場所 |
/docker/ |
様々なコンテナアプリ運用スクリプト |
/home/site/wwwroot/ |
永続化ストレージの場所 |
/home/site/wwwroot/redmine.sqlite3 |
Default sqlite3 database in development |
/home/site/wwwroot/staticsite/ |
メンテナンスモード時の静的サイトのドキュメントルート |
/home/site/wwwroot/backups/ |
バックアップ |
/home/site/wwwroot/files/ |
/redmine/files からのシンボリックリンク |
/home/site/wwwroot/config/* |
/redmine/config/* からのシンボリックリンク |
/home/site/wwwroot/plugins/* |
/redmine/plugins/* からのシンボリックリンク |
/home/site/wwwroot/public/themes/* |
/redmine/public/themes/* からのシンボリックリンク |
/home/site/wwwroot/public/plugin_assets |
/redmine/public/plugin_assets からのシンボリックリンク |
メンテナンスモード
app settings が RAILS_IN_SERVICE=false
の状態でアプリコンテナが起動すると、メンテナンスモードに入ります。
メンテナンスモードでは Redmine は起動せず、代わりに /home/site/wwwroot/staticsite/
をドキュメントルートとする静的サイトのサーバが起動しますので、コンテナ内で Redmine のメンテナンス作業を安全に行うことができます。
メンテナンス作業は web ブラウザないし Azure CLI によるコンテナ内シェルの SSH 接続により行います。
作業が終了したら app settings で RAILS_IN_SERVICE=true
に設定することでアプリコンテナは自動的に再起動し Redmine が通常通り起動します。
rmops
コマンド
rmops
はアプリコンテナ内で利用可能なメンテナンスコマンドです。
データベースの初期化、Redmine アプリのセットアップ、ユーザーパスワードのリセットといったメンテナンス作業を行う様々なサブコマンドが用意されています。
root@6228b40cf3a8:~# rmops
Commands:
rmops entrypoint # Container entrypoint
rmops help [COMMAND] # Describe available commands or one specific command
rmops passwd # Reset user password
rmops setup # Set up Redmine instance
rmops sql # Generate SQL to initialize database
また rmops entrypoint
はアプリコンテナのエントリポイントとして動作します。
ファイルの永続化
App Service において共有ストレージは /home/site/wwwroot
にマウントされますので、永続化したいファイルはこの場所に保存する必要があります。/home/site/wwwroot
の外にあるファイルの変更は、コンテナが再起動すると失われます。
そのため /redmine
内の永続化すべきファイルはその実体が /home/site/wwwroot
にあるようなシンボリックリンクがアプリコンテナのエントリポイント (rmops entrypoint
) で作られます。
Redmine サイトのカスタマイズを行うときはこの仕組みを理解しておく必要があります。たとえば Redmine のプラグインは /home/site/wwwroot/plugins
に、テーマは /home/site/wwwroot/public/themes
に置いておけば有効化されます。詳細は「コンテナ内のパス」の表を参照してください。
App Service へのデプロイ
手動デプロイの手順
ghcr.io の公開コンテナレジストリから利用可能なコンテナイメージを使って Redmine サイトを App Service にデプロイする手順を示します。
- まず Redmine アプリのためのデータベースを用意します。 Azure のサービスの場合 Azure Database for MySQL/MariaDB/PostgreSQL がサポートされています。データベース作成権限がある管理者のユーザー名とパスワードをメモしておいてください。
- App Service のプランおよびアプリを作成してください。種類は Linux のシングルコンテナアプリで、次のコンテナイメージのいずれかを指定してください。
-
ghcr.io/yaegashi/dx2devops-redmine/redmine
(Redmine) -
ghcr.io/yaegashi/dx2devops-redmine/redmica
(RedMica)
-
- 次の app settings を設定してください。
WEBSITES_ENABLE_APP_SERVICE_STORAGE=true RAILS_ENV=production RAILS_IN_SERVICE=false SECRET_KEY_BASE=<とても長いランダムな文字列> DATABASE_URL=<データベース接続設定(後述)>
- アプリが起動し、メンテナンスモードに入ります。
- Azure Portal などを使ってアプリのコンテナに SSH 接続してください。
- (データベース接続の準備が完了している場合はこの手順は不要です)
rmops sql
を実行すると、データベースとそのユーザーを作成するための SQL が出力されます。データベースの種類 (MySQL/PostgreSQL) はDATABASE_URL
設定から自動的に判断されます。この SQL の内容を確認した上で、コメントに書かれているroot@6228b40cf3a8:~# rmops sql -- Database URL: mysql2://test1_7ee9e7cf%40test1-7ee9e7cf:CI8zeEf5r2%24%26%28r%249zQvNSA2%3EYh3lq7%7B7@test1-7ee9e7cf.mariadb.database.azure.com/test1_7ee9e7cf?encoding=utf8mb4&sslverify=true -- Run the following command to connect to the database host as an admin user: -- mysql -vv -u <admin-user> -p -h test1-7ee9e7cf.mariadb.database.azure.com --ssl DROP DATABASE IF EXISTS `test1_7ee9e7cf`; DROP USER IF EXISTS 'test1_7ee9e7cf'@'%'; CREATE USER 'test1_7ee9e7cf'@'%' IDENTIFIED BY 'CI8zeEf5r2$&(r$9zQvNSA2>Yh3lq7{7'; CREATE DATABASE IF NOT EXISTS `test1_7ee9e7cf` DEFAULT CHARACTER SET `utf8mb4` COLLATE `utf8mb4_unicode_520_ci`; GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, REFERENCES ON `test1_7ee9e7cf`.* TO 'test1_7ee9e7cf'@'%';
mysql
psql
コマンドによりデータベースに接続して実行してください。<admin-user>
はデータベース管理者の名前で置き換えてください。root@6228b40cf3a8:~# rmops sql | mysql -vv -u db_admin@test1-7ee9e7cf -p -h test1-7ee9e7cf.mariadb.database.azure.com --ssl Password: ← データベース管理者 (db_admin@test1-7ee9e7cf) のパスワードを入力
-
rmops setup
を実行してデータベースのマイグレーションと Redmine の初期セットアップを行ってください。最後に admin ユーザーのパスワードが表示されますので、メモしておいてください。root@6228b40cf3a8:~# rmops setup ... I, [2022-06-05T18:52:11.206518 #72] INFO -- : Enter Redmine at /redmine I, [2022-06-05T18:52:31.645480 #72] INFO -- : Reset password for user "admin" I, [2022-06-05T18:52:31.647095 #72] INFO -- : New password: "tLtLGWFK7VqzZ8TS"
- ユーザーのパスワードを忘れてしまったときはいつでも
rmops passwd
でリセットができます。root@fc06fdbecede:~# rmops passwd admin I, [2022-06-14T22:58:39.292157 #306] INFO -- : Enter Redmine at /redmine I, [2022-06-14T22:58:58.152780 #306] INFO -- : Reset password for user "admin" I, [2022-06-14T22:58:58.161336 #306] INFO -- : New password: "QNFjQkawpuyoBi9F"
- (データベース接続の準備が完了している場合はこの手順は不要です)
- app settings で
RAILS_IN_SERVICE=true
を設定してください。 - アプリが再起動し、Redmine が起動します。
- Web ブラウザでアプリの web サイトを開き admin ユーザーでログインしてください。
データベース接続設定 (DATABASE_URL
)
Redmine のデータベースの接続先は app settings の DATABASE_URL
で指定します。その記述方法については Rails ガイドを参照してください。
前節で示したように、Redmineサイトごとのデータベース作成・接続ユーザー作成を行う SQL の生成機能 (rmops sql
) がありますので、 DATABASE_URL
にはデータベース管理者のユーザーは使用しないことをおすすめします。
Azure Database for MySQL/MariaDB (mysql2 adapter) の例:
mysql2://username%40servername:password@servername.mariadb.database.azure.com/dbname?encoding=utf8mb4&sslverify=true
Azure Database for PostgreSQL (postgresql adapter) の例:
postgresql://username%40servername:password@servername.postgres.database.azure.com/dbname?sslmode=require
Azure Database 製品を使用する場合は次の点に注意してください。
- ユーザー名に
username@servername
を使用する (@
は%40
にエンコードする) -
sslverify=true
やsslmode=require
のような SSL 接続指定を追加する
Terraform によるデプロイ
App Service での開発・テストを簡単にするために、必要な Azure リソースのデプロイと設定を自動化する Terraform プロジェクトが terraform/test1 にありますので参考にしてください。
サービス運用とメンテナンス
Redmine のサービス運用開始後も随時 SSH 接続を使ってアプリコンテナに接続し、稼働中の Redmine アプリに対して軽微なメンテナンスやトラブルシュートを行うことができます。コンテナではないサーバでサービスを動かす場合とほぼ同じ感覚で作業できると思います。
Redmine アプリからのログは標準出力に出ていますので、Azure Portal のログストリームや az webapp log tail
コマンドでそれを見ることができます。 アプリケーションのログ記録を設定をしないとログストリームには何も出てきませんので注意してください。
大規模なメンテナンスのために Redmine アプリを止める必要がある場合は、 app settings で RAILS_IN_SERVICE=false
を設定します。アプリコンテナが再起動してメンテナンスモードに入りますので、SSH 接続して作業してください。作業が完了したら app settings をRAILS_IN_SERVICE=true
に戻すと、アプリコンテナの再起動の後 Redmine アプリが起動してサービスが再開します。
ロードマップ
これまでの取り組みの結果 Azure App Service のカスタムコンテナのアプリとしては形になってきましたが、実際の Redmine サービスとして本番運用するにはまだまだ足りない機能があります。今後は次のような機能強化を予定しています。
- Redmineサイトのバックアップ・リストア
- EasyAuth によるサイトアクセス制御と Redmine ユーザー認証との統合
- メール送信サポート: SMTPサーバをコンテナ内で動かし SendGrid などにリレーまたは ACS email で送信
- ARM テンプレートと Deploy to Azure ボタンの作成
- 運用向け Terraform プロジェクトの作成
- App Service 可用性ゾーン冗長化の利用
おわりに
Redmine を Azure App Service で、単に動かすだけでなく運用に耐えうる DevOps プロジェクトの構築について、まだ道半ばではありますがこれまでの取り組みを紹介しました。不明な点の質問や、問題点や改善できる点などのご意見がありましたら、お気軽にご連絡ください。
今後もプロジェクトにアップデートがありしだい紹介記事を書いていきたいと思います。また今回触れなかったアプリコンテナと rmops
コマンドの実装の詳細やローカル開発環境などのトピックについても順次記事にしますのでよろしくお願いします。