はじめに: 全体像を理解することが重要です

こんにちは!前回の1編では、GitHub Webhookを活用した自動デプロイシステムを実際に構築しようとする理由と必要な準備物についてお話ししました。今回の2編では、いよいよコードの実装に入る前に、私たちが作る自動デプロイシステムがどのように動作するのか全体的なアーキテクチャとプロセスを設計する時間を設けます。

それぞれの開発環境は少しずつ異なるかもしれません。ある方はラズベリーパイを、別の方はクラウドVPSを使用するかもしれませんし、デプロイ対象のプロジェクトの構造も様々です。このように多様な環境の中で柔軟にシステムを構築し、問題が発生した際には自分自身で解決策を見出せるようにするためには、細かいコードよりも全体的な流れやコンテキストを理解することが何よりも重要です。

今回の編を通じて、私たちが作成するシステムの大きな絵を頭の中に思い描き、各構成要素がどのような役割を果たすのかを明確に把握する時間を持ちましょう。

自動デプロイシステムのフローチャート

自動デプロイワークフロー: 全体フローの概要

私たちが実装する自動デプロイシステムは、以下の一連のプロセスを経て動作します。

  1. ローカル作業マシンでのコード変更とGitプッシュ: 開発者がローカル環境でコードを修正し、変更点をGitHubリポジトリの特定のブランチ(例: main または develop)にプッシュします。

  2. GitHubリポジトリでのWebhookイベント発生: GitHubリポジトリは、pushイベントが感知されると、予め設定されたWebhookを通じて指定されたURLにHTTP POSTリクエストを送信します。

  3. ステージングサーバーのWebhookエンドポイントへのリクエスト伝達: GitHubが送信するWebhookリクエストは、私たちが用意したステージングサーバーの特定のURL(例: https://deployer.example.com/webhook)を指すようになります。このリクエストには、プッシュされたコミットの情報、変更されたファイルリストなど、様々なペイロードが含まれます。

  4. ステージングサーバーのFastAPIサービスがWebhookを受信: ステージングサーバーで実行されている私たちのFastAPIアプリケーションがこのWebhookリクエストを受信します。ここからFastAPIサービスの役割が始まります。

FastAPI Webhookサービスの核心的な役割

私たちがFastAPIで実装するWebhookサービスは、単にリクエストを受け取るだけでなく、次のような重要な役割を果たします。

Webhook受信および初期処理

FastAPIアプリケーションはGitHubから送信されるHTTP POSTリクエストを受け取ることができるエンドポイント(例: /webhook)を提供します。このエンドポイントは、リクエストのHTTPヘッダとボディをパースして必要な情報を抽出します。

Secret検証

セキュリティは自動デプロイシステムで最も重要です。GitHub Webhookは、X-Hub-Signature-256というヘッダを通じてリクエストの整合性を検証するためのSecret値を提供します。私たちのFastAPIサービスはこのSecret値を用いて、GitHubから送られたリクエストが正しいか、また途中で改竄されていないかを検証するロジックを含める必要があります。この検証に失敗した場合、即座にリクエストを拒否して不正アクセスを防ぎます。

即時応答およびバックグラウンド作業

GitHub Webhookは、リクエストを送信した後、一定の時間(デフォルトで10秒)内に応答を受け取らない場合、タイムアウトと見なされ再試行または失敗として記録します。しかし、実際のデプロイプロセス(Git Pull、Dockerビルド/再起動など)は長くかかることがあります。

そのため、私たちのFastAPIサービスはWebhookリクエストを受け取った時点でSecret検証などの初期処理を終えた後即座に200 OK応答をGitHubに送信します。 そして、実際のデプロイロジックはFastAPIのBackgroundTasks機能を活用してバックグラウンドで非同期に実行されるように設計する予定です。これにより、GitHubのタイムアウト問題を回避しつつ、安定してデプロイ作業を行うことができます。

デプロイハンドラーのロジック詳細

バックグラウンドで実行されるデプロイハンドラーは、以下のような核心的な作業を行います。

マルチプロジェクト管理: リポジトリパスの読み取り

私たちは1つのFastAPIWebhookサービスで、複数のGitHubリポジトリ(プロジェクト)に対する自動デプロイを処理できるように設計します。そのために環境変数や設定ファイルから各プロジェクトのGitHubリポジトリのパスと、そのプロジェクトがデプロイされるサーバー内のローカルパスをマッピングして管理します。Webhookペイロードからどのリポジトリでイベントが発生したかを確認した後、該当プロジェクトのパスに移動してデプロイ作業を行います。

プロジェクト別カスタマイズ設定: .envファイルパース(オプション)

各プロジェクトはビルドまたはデプロイ時に必要な独自の環境変数や設定値を持つことがあります。例えば、特定のDockerイメージタグ、ビルドオプション、サービス再起動コマンドなどが異なることがあります。これを効率的に管理するために、各プロジェクトのローカルリポジトリパスにある.envファイルから必要な値を解析してデプロイロジックに活用できるように実装します。これは柔軟でカスタマイズされたデプロイロジックを実現する上で大きな助けとなります。

コードの更新: git pullの実行

最も基本的なステップです。subprocessモジュールを使用して、該当プロジェクトのローカルリポジトリでgit pull origin <branch_name>コマンドを実行してGitHubリポジトリの最新コードを取得します。

Dockerイメージ再ビルドの判断: git diffの活用

Dockerベースのプロジェクトの場合、コードが変更された場合はdocker compose up -dで十分ですが、Dockerfilerequirements.txt(Pythonプロジェクトの場合)など、Dockerイメージビルドに影響を与えるファイルが変更された場合は、イメージを新たにビルドする必要があります。

私たちはgit diffコマンドを活用して、最近プッシュされたコミットと以前のコミットの間でDockerfileまたはその他のビルド関連ファイルの変更があるかどうかを判断します。変更が検出されると、docker compose up -d --buildを実行し、そうでない場合はdocker compose up -dを実行して不要なイメージ再ビルドを避け、デプロイ時間を短縮します。

Docker Composeの実行: subprocessモジュールを利用

最新コードを取得し、イメージ再ビルドの要否を決定した後、subprocessモジュールを使用してdocker compose up -dまたはdocker compose up -d --buildコマンドを実行し、Dockerコンテナを最新の状態に更新し、サービスを再起動します。

ロギング: 全過程の記録

すべてのデプロイ過程(Webhook受信、検証、Git Pull結果、Dockerビルド/再起動ログなど)は詳細に記録されるべきです。これは問題が発生した際に原因を特定し、デバッグするために不可欠です。Pythonのloggingモジュールを活用してファイルにログを残すように実装します。

FastAPIサービスのデプロイおよび運用戦略

私たちが実装するFastAPI Webhookサービスは、ステージングサーバーで安定して常に実行される必要があります。

Systemdサービスを利用する重要性

FastAPIアプリケーションを直接Dockerコンテナとして起動するのではなく、Systemdサービスで実行することを強く推奨します。その理由は以下のとおりです。

  • リソース効率: すでにステージングサーバーにはgitdockerdocker composeなどデプロイに必要なツールがインストールされている可能性が高いです。FastAPI Webhookサービス自体をDockerコンテナとして作成し、そのコンテナ内に再度gitdockerをインストールしてシステムのdockerデーモンを制御するように設定するのは、不要にコンテナのサイズを大きくし、docker-in-dockerdocker-out-of-dockerといった複雑な設定やセキュリティ問題を引き起こす可能性があります。

  • 簡潔な管理: SystemdはLinuxシステムのサービス管理の標準です。FastAPIアプリをSystemdサービスとして登録すると、サーバーのブート時に自動起動したり、サービスの状態確認、簡便な再起動/停止などOSレベルで統合的且つ簡潔な管理が可能になります。

  • システムリソースの活用: Systemdを通じてFastAPIアプリを実行すると、アプリがシステムにインストールされているgitおよびdockerコマンドを直接呼び出してデプロイ作業を行うので、システムの既存リソースを最も効率的に活用できます。

次回はFastAPIアプリケーションをSystemdサービスとして登録し、管理する方法について詳しく説明します。

Nginx/Apache2によるリバースプロキシおよびHTTPS

前回の1編で強調した通り、FastAPIアプリケーションをインターネットに直接公開することはセキュリティ上非常に危険です。したがって、私たちはNginxまたはApache2のようなWebサーバーをリバースプロキシとして使用し、Webhookリクエストを安全にFastAPIアプリケーションに渡すことになります。

また、GitHub WebhookはHTTPS通信を強く推奨しているため、deployer.example.comのような専用サブドメインを準備し、Let's Encryptなどを利用してHTTPS証明書を発行しWebサーバーに適用する必要があります。これにより、外部からのすべての通信が暗号化され、セキュリティが強化されます。

監視およびデバッグ

自動デプロイシステムが正常に動作しているか確認し、問題が発生した場合には原因を特定するために、次の2つの方法を活用します。

  • FastAPIサービスのロギングファイル: 私たちが実装するFastAPIサービスは、デプロイ過程のすべてのステップを自身のロギングファイルに記録します。このファイルを確認することで、デプロイの成功の有無、発生したエラーメッセージなどを把握できます。

  • Systemd journalctl: SystemdでFastAPIサービスを管理しているため、journalctl -u your-fastapi-service.serviceコマンドを通じてサービスの標準出力およびエラーログをリアルタイムで確認および解析できます。

まとめとして: 次回の予告

今回の2編では、GitHub Webhookを活用した自動デプロイシステムの全体的なアーキテクチャとFastAPIサービスの核心的役割、さらにデプロイおよび運用戦略について詳細に見てきました。これで全体像が頭の中に浮かんできたことを願います。

次回の3編では、今日設計した内容を基に、実際のFastAPI Webhookサービスのコードを作成し、GitHub Webhookを設定し、Systemdサービスとして登録する具体的な実装過程を取り上げます。ご期待ください!


GitHub Webhookを活用した自分だけの自動デプロイシステム構築シリーズ

1編 - なぜ自分で実装するのか?