1. はじめに: バックグラウンドでデプロイロジックを実行する
こんにちは!前回の3編では、ステージングサーバー環境を設定し、GitHub Webhookリクエストを受け取ってSecretを検証するFastAPI webhookサーバーの基本骨組みを一緒に作成しました。この過程で、main.py
ファイル内にhandle_deploy
およびshould_rebuild
関数のコードをあらかじめ含めることにより、実際のデプロイロジックの核心アイデアを垣間見ることができました。
今回は4編では、3編で簡単に取り上げたデプロイハンドラーロジックの動作方式を再度明確にして、このFastAPI webhookサーバーがサーバーの再起動時にも自動的に実行され、安定して運営されることができるように、Systemdサービスとして登録する方法に重点を置いて説明します。これで、皆さんの自動デプロイシステムはさらに堅牢になる準備が整いました!
前の投稿を見られなかった方は、まず前の投稿を読まれることをお勧めします。
③ ステージングサーバー環境設定とFastAPI webhookサーバーの基礎構築
2. デプロイハンドラー (handle_deploy
) ロジックの再確認
3編で作成したmain.py
のhandle_deploy
関数は、GitHub webhookリクエストが来た時にバックグラウンドで実行される実際のデプロイ作業を担当します。この関数は次のような核心的な役割を果たします。
3編の例コードをご覧になりながら、以下のガイドをご覧になってください。
2.1. 複数プロジェクト管理と環境変数設定
複数のGitHubリポジトリ(プロジェクト)を1つのWebhookサーバーで管理するには、各リポジトリがサーバー内のどこに位置しているかを知らせる必要があります。handle_deploy
関数はこれを実現するためにrepo_paths
辞書を使用し、これらの値はサーバーの環境変数から読み込むように設計されています。
例サンプルコードでSAMPLE_PROJECT_1_PATH
のような環境変数が登場しました。皆さんはステージングサーバーの.env
ファイル(このファイルはWebhookサーバーのmain.py
と同じディレクトリに配置する必要があります)やsystemd
サービスファイル内に次のように各プロジェクトの実際のパスを設定する必要があります。
コードスニペット
# ~/projects/webhook_server/.env
# GitHub Webhook Secret (3編で設定)
GITHUB_WEBHOOK_SECRET="your_github_webhook_secret_value"
# 各プロジェクトのサーバー内実際のパス
SAMPLE_PROJECT_1_PATH="/var/www/my-project1"
SAMPLE_PROJECT_2_PATH="/home/user/another-project"
SAMPLE_PROJECT_3_PATH="/opt/third-project"
handle_deploy
関数はWebhookペイロードから受け取ったリポジトリ名を利用してrepo_paths
辞書から該当プロジェクトのパスを探します。もしマッピングされたパスがない場合は、Unknown repository
という警告を残して終了します。
また、各プロジェクト別の.env
ファイル(例: /var/www/my-project1/.env
)からDEBUG
やCOLOR
といった追加設定値を読み込み、Docker Composeファイルの選択(docker-compose.dev.yml
またはdocker-compose.prod.yml
)やDockerプロジェクト名設定などに活用する柔軟なロジックも含まれています。これはプロジェクトの特性に応じたデプロイ方式をカスタマイズするのに非常に便利です。
2.2. Dockerイメージ再ビルドの決定 (should_rebuild
)
デプロイ時に毎回Dockerイメージを再ビルドすることは時間がかかり、非効率的です。should_rebuild
関数はGitのdiff
コマンドを活用して、Dockerfile
, requirements.txt
または.env
ファイルなど、Dockerイメージビルドに影響を与える核心ファイルが変更されたかを検出します。
この関数はgit diff --name-only HEAD~1
というコマンドを使用して前のコミットとの変更点を確認し、変更されたファイルリストにあらかじめ定義されたtrigger_files
(例: Dockerfile
, requirements.txt
, .env
, REBUILD_TRIGGER
など)が含まれていればTrue
を返してイメージの再ビルドを指示します。
特にREBUILD_TRIGGER
ファイルは、空のファイルでも存在すれば強制的に再ビルドを実行する便利なトリックです。このファイルをサーバーに手動で作成し、git pull
以外の理由でイメージの再ビルドが必要な場合に活用できます。should_rebuild
関数はこのファイルを検出すると自動的に削除し、次回のデプロイ時には不必要に再ビルドが行われないようにします。
2.3. GitとDocker Composeコマンドの実行
handle_deploy
関数はPythonのsubprocess
モジュールを使用してサーバーでのgit
およびdocker compose
コマンドを実行します。
-
subprocess.run(["git", "-C", repo_path, "pull"], check=True)
:check=True
オプションはGitコマンド実行中にエラーが発生した場合にsubprocess.CalledProcessError
を発生させ、Pythonコードでこれを検知して適切にエラーをログに記録することができます。-C
オプションは指定したディレクトリでGitコマンドを実行するようにします。 -
subprocess.run(["docker", "compose", "-p", project_name, "-f", compose_file, "up", "-d", "--build"], check=True)
:should_rebuild
関数の結果に応じてup -d
のみを実行するか--build
オプションを追加してイメージの再ビルドを実行します。-p
オプションでDocker Composeプロジェクト名を指定し、複数のプロジェクトが互いに衝突しないようにします。
このようなロジックを通じてWebhookリクエスト1回でプロジェクトの最新コードを取得し、必要な場合にのみイメージを再ビルドして効率的にサービスを更新できます。
3. FastAPI webhookサーバーをSystemdサービスとして運営する
これまでuvicorn main:app --reload
コマンドでWebhookサーバーを手動で実行してきました。しかし、サーバーが再起動するとこのプロセスは消えてしまい、ターミナルセッションが切断されるとサービスが中断されます。これを防ぎ、安定した運営のために、FastAPI webhookサーバーをSystemdサービスとして登録して管理する必要があります。
3.1. なぜSystemdを使用するべきか?
-
自動起動: サーバー再起動時にも自動的にWebhookサービスが起動します。
-
持続的な実行: ターミナルセッションに依存せず、バックグラウンドで常に実行されます。
-
簡単な管理:
systemctl
コマンドを使ってサービスの開始、停止、再起動、状態確認、ログ確認などを簡単に行えます。 -
リソース効率: 2編で説明したように、Webhookサーバー自体は軽量なPythonアプリとしてSystemdで実行し、GitやDockerなどの重いツールはシステムにインストールされたものを直接利用することで不必要なDocker in Docker設定やコンテナサイズの増加を避けることができます。
3.2. Systemdサービスファイル(*.service
)の作成
Systemdサービスは.service
ファイルを通じて定義されます。このファイルは/etc/systemd/system/
ディレクトリに配置する必要があります。私たちはgithub-webhook-deployer.service
という名前でファイルを作成してみましょう。私は説明しやすいように長い名前を付けましたが、皆さんは適度に簡潔でわかりやすい名前を選択してください。
# ファイル作成 (sudo権限が必要)
sudo nano /etc/systemd/system/github-webhook-deployer.service
ファイル内容:
# /etc/systemd/system/github-webhook-deployer.service
[Unit]
Description=GitHub Webhook Deployer Service
After=network.target # ネットワークがアクティブになった後にこのサービスを開始します。
[Service]
User=your_username # このサービスを実行するユーザーアカウント(例: ubuntu, your_user)
Group=your_username # このサービスを実行するグループ(例: ubuntu, your_user)
WorkingDirectory=/home/your_username/projects/webhook_server # FastAPIアプリがあるディレクトリ
# 環境変数設定:
# 1. FastAPIアプリ自体が.envファイルをロードするため、GITHUB_WEBHOOK_SECRETなどアプリ内で使用する変数は
# 必ずしもEnvironmentFileでロードする必要はありません。しかし、systemdレベルで全ての環境を一貫して
# 管理したい場合はEnvironmentFile=のパスを維持することができます。
# EnvironmentFile=/home/your_username/projects/webhook_server/.env
# 2. subprocess.run()でgit、docker、docker composeなどのシステム命令を呼び出すには
# PATH環境変数に該当命令があるパスを明示的に追加する必要があります。
# 以下の例示PATHは一般的なLinuxシステムのパスとPython仮想環境のbinパスを含みます。
# 'your_username'と'venv'のパスは実際の環境に合わせて修正してください。
# 正確なPATHはサーバーの.bashrcファイルや'echo $PATH'コマンドで確認し、必要な経路を含めてください。
Environment="PATH=/home/your_username/projects/webhook_server/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/home/your_username/projects/webhook_server/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 # 仮想環境のuvicornパスを使用
Restart=always # サービスが終了したら常に再起動します。
StandardOutput=journal # 標準出力をSystemdジャーナルに送信します。
StandardError=journal # 標準エラーをSystemdジャーナルに送信します。
[Install]
WantedBy=multi-user.target # マルチユーザーモード(一般的なサーバー起動状態)でサービスが開始されるようにします。
説明:
-
[Unit]
セクション: サービスに関する一般的な情報(説明、依存関係)を定義します。After=network.target
はネットワークサービスが開始された後にこのサービスを開始するようにします。 -
[Service]
セクション: サービスがどのように実行されるかを定義します。-
User
,Group
: サービスを実行するユーザーとグループです。 必ずDocker権限のあるユーザー(例:sudo usermod -aG docker your_username
で設定したユーザー)として設定してください。 -
WorkingDirectory
: FastAPIアプリのmain.py
ファイルがいるディレクトリです。 -
Environment="PATH=..."
: この部分が重要です。subprocess.run()
でgit
、docker
、docker compose
など外部命令を呼び出すとき、systemd
環境ではPATH
環境変数が.bashrc
などで設定されたものと異なる場合があります。したがって、仮想環境のuvicorn
実行パス(/home/your_username/projects/webhook_server/venv/bin
)とシステムの基本実行ファイルパス(/usr/local/bin
、/usr/bin
など)を明示的に指定する必要があります。 -
EnvironmentFile
(オプション): FastAPIアプリ自身がpython-dotenv
経由で.env
ファイルをロードするため、アプリ内で使用する環境変数(例:GITHUB_WEBHOOK_SECRET
,SAMPLE_PROJECT_N_PATH
など)はこの設定を通さなくても構いません。しかし、systemd
がサービスを開始する前にこれらの変数を明示的にロードさせたい場合(ExecStart
が実行するuvicorn
プロセス及びそのサブプロセスに一貫した環境を提供したい場合)には、EnvironmentFile
を使用することも有効な方法です。ユーザーの環境や好みに応じて、コメントアウトしたり維持したりできます。 -
ExecStart
: サービスを開始する実際の命令です。 仮想環境のuvicorn
実行ファイルパスを正確に指定しなければなりません。--host 0.0.0.0 --port 8000
はすべてのIPから8000番ポートへのリクエストを受け取るようにします。 -
Restart=always
: サービスが何らかの理由で終了した場合、Systemdが自動的に再起動を試みます。 -
StandardOutput=journal
,StandardError=journal
: サービスのすべての出力およびエラーをSystemdの統合ログシステムであるjournalctl
に送信します。
-
-
[Install]
セクション: サービスがどのターゲットによりアクティブ化されるかを定義します。WantedBy=multi-user.target
はサーバーが一般的なマルチユーザーモードで起動される際にこのサービスが自動的に開始されるようにします。
3.3. Systemdサービスの登録と開始
サービスファイルを作成したら、Systemdがそのファイルを認識し、サービスを開始します。
# Systemdに新しいサービスファイルを知らせます。
sudo systemctl daemon-reload
# サービスがブート時に自動的に起動されるように設定します。
sudo systemctl enable github-webhook-deployer.service
# サービスを開始します。
sudo systemctl start github-webhook-deployer.service
# サービスの状態を確認します。「active (running)」と表示される必要があります。
sudo systemctl status github-webhook-deployer.service
これで皆さんのFastAPI webhookサーバーはサーバーが再起動しても自動的に実行され、バックグラウンドで安定してWebhookリクエストを待つことができます。
4. FastAPI webhookサービスの監視とデバッグ
Systemdサービスを使用すると、ログを確認し、問題を診断するのがはるかに簡単になります。
- サービス状態の確認:
sudo systemctl status github-webhook-deployer.service
このコマンドはサービスの現在の状態、最後の実行時間、プロセスIDなどを示します。
- リアルタイムでのログ確認:
sudo journalctl -u github-webhook-deployer.service -f
-u
オプションは特定のサービスのログを、-f
オプションはリアルタイムで新しいログを継続的に出力します。Webhookリクエストが入るたびにmain.py
で設定したロギングメッセージがここに表示されます。
もしサービスが開始されなかったり、期待通りに動作しない場合は、journalctl
ログを最初に確認し、エラーメッセージを探す必要があります。
5. 終わりに: 次回予告
今回は4編では、3編で実装したデプロイハンドラーのロジックの動作方式を再度明確にし、最も重要なステップの1つであるFastAPI webhookサーバーをSystemdサービスとして登録して安定的に運営する方法を学びました。これで皆さんのWebhookサーバーはサーバーの再起動時にも動作することでしょう。
次回の5編では最後のパズルのピースを組み合わせてシステムを完成させます。Nginxをリバースプロキシとして設定してWebhookサーバーを外部に安全に公開し、HTTPSを適用してセキュリティを強化し、最終的にGitHubリポジトリにWebhookを連携させて実際の自動デプロイをテストするプロセスを扱います。お楽しみに!
コメントはありません。