はじめに
WINTICKET のWeb版(以降、WINTICKET Web)の開発を担当している鍛冶(@kj455)です。
WINTICKET Webは2022年10月頃からCloud Runを用いたアーキテクチャで稼働しており、CanaryリリースやDark Canaryリリースの仕組みを持っています。ここで、Dark Canary環境とは、通常のリクエストは到達せず、主に社内の開発者の検証目的として運用されている環境のことを指します。
既存アーキテクチャの詳細については以下の記事をご覧ください。
今回、Dark Canaryリリースの仕組みを拡張し、Pull RequestごとにDark Canary(以後PR Dark Canary)環境を作成する仕組みを整備しました。これにより、本番と同じURLでPull Requestごとの環境に接続して動作確認ができるようになりました。本記事ではその技術構成や運用について紹介します。
モチベーション
開発プロセス上の課題
WINTICKET Webの開発プロセスは、フィーチャーフラグを用いたトランクベース開発を中心に行われています。しかし、複雑なリファクタリングや大規模な機能追加の場合など、フィーチャーフラグでは管理しきれないケースも存在します。
このような場合、Pull Requestに連動したプレビュー環境での品質管理(QC)を試みますが、ドメインの違いやCDNの非配置など、開発環境との差異によって動作検証が困難なケースがあります。この場合、開発環境(mainブランチ)でQCを行うことになるため、動作確認が取れていないコードをmainブランチにマージすることになります。
もしこの期間中に他機能のリリースを行う場合、QC中の機能を一時的にmainブランチから退避させるためのリバート作業が必要になります。この一連の流れが開発者のリリース体験を悪化させ、機能リリースなどの必要に迫られたタイミング以外でのリリースが行われづらくなるという事態に繋がっていました。
PR Dark Canary による解決
これらの課題に対応するため、WINTICKET Webでは、開発環境と同一のドメイン・CDNを持つPull RequestごとのDark Canary環境(PR Dark Canary)を導入しました。これにより、フィーチャーフラグで管理できないような大規模な変更もmainブランチにマージせずとも開発環境と同等の条件で検証が可能になりました。mainブランチが常時リリース可能な状態を維持できるようになったため、WINTICKET Webのリリースプロセスがスムーズに進行するようになり、開発チームの生産性が大きく向上しました。具体的な成果については、本記事の最後に紹介します。
次章では、PR Dark Canary環境の技術的な紹介を行います。
アーキテクチャ
まず、PR Dark Canaryの全体アーキテクチャと処理の概要を以下の図に示します。
PR Dark Canaryを実現するために各コンポーネントで行っている処理について、図に示した番号の順に解説します。
1. URLマスクによるPRごとのリビジョン選択
Google Cloudのサーバーレスネットワークエンドポイントグループ(サーバーレスNEG)には、「URLマスク」という機能があります。この機能を使用すると、リクエストされたURLに基づいてトラフィックを適切なCloud Runサービスやリビジョンに自動的にルーティングできます。たとえば、example.com/<service>/<tag>
のようなURLマスクを設定した場合、example.com/service-1/tag-1
へのリクエストが来ると、service-1
というCloud Runサービスのtag-1
タグが付いたリビジョンに自動的にトラフィックが向けられます。
PR Dark CanaryではこのURLマスク機能を利用します。つまり、特定のPRに対応したCloud Runサービス名とリビジョンタグをURLに含めることで、選択的にそのリビジョンへアクセス可能にします。しかし、これだけでは開発環境の /page
ページにアクセスする際、開発者は/pr-candidate/pr-123/page
のように本来不要な情報をURLに追加する必要があり、使い勝手が良いとは言えません。次章ではFastlyを用いてどのようにこの問題に対処するかを説明します。
2. Fastly上でDark Canary用の情報をURLへ埋め込む
WINTICKET Webでは、CDNとしてFastlyを使用しています。Fastlyはキャッシュ機能だけでなく、VCLを記述することでさまざまな処理を行うことができます。今回はHTTPヘッダーから受け取ったPR Dark Canaryに関する情報をURLに動的に挿入する処理を行います。これにより、開発者はそれらの情報を手動でURLに入力する必要が無くなります。その代わりに特定のHTTPヘッダーを全てのリクエストに付与する必要がありますが、これはModHeader等のChrome拡張機能によって簡単に行えます。
これにより、リクエストのURLをPR Dark Canary用の情報を含んだものへと適切に変更できました。次に、このリクエストが、先述したPR Dark Canary用のサーバーレスNEGに適切にルーティングされるように設定します。
3. LoadBalancerによるヘッダーベースルーティング
FastlyによるURL書き換え処理の後、リクエストはGoogle Cloudの外部HTTP(S)ロードバランサーに転送されます。このロードバランサーは、HTTPヘッダーの値に基づいてトラフィックのルーティングを行う機能を持っています。従来からこの機能を用いてDark Canary環境を実現していましたが、今回新たに URL マスク機能を有効化したサーバーレスNEGを増やす必要があったため、ルーティングルールを以下のように修正しました。
この設定により、Fastlyで加工したURLを含むリクエストが、PR Dark Canary用のサーバーレスNEGへと適切にルーティングされます。しかし、以上の実装だけでは、Cloud Run上のWebサーバーがFastlyにより付与されたPR Dark Canary用の情報を含むURLをそのまま受け取ってしまいます。これを解決するため、次章ではCloud Run上でサイドカー構成を採用し、リクエストURLから不要な情報を除去する方法について説明します。
4. サイドカー構成によるURL復元
Cloud Runへと送られるリクエストには、本来必要のないCloud Runサービス名とリビジョン名が含まれています。この問題を解決するために、Cloud Runのサイドカー機能を活用し、アプリケーションサーバーの前段にEnvoyを配置します。EnvoyはURLから不要な情報を削除する役割を担います。サイドカー構成を採用することで、アプリケーションサーバーはユーザーの元のリクエストURLをそのまま受け取れるようになり、アプリケーションコードにPR Dark Canaryの知識を含める必要が無くなります。
まとめ
先述した各コンポーネントを組み合わせることにより、下記の流れでPR Dark Canaryを実現できました。
- 開発者がPR Dark Canary用のHTTPヘッダーを付与して開発環境にアクセスする
- FastlyがHTTPヘッダーの情報を読み取り、URLにそれらを埋め込む
- Load BalancerがHTTPヘッダーを元にPR Dark Canary用のサーバーレスNEGへとリクエストをルーティング
- サーバーレスNEGのURLマスクによって、URL情報を元に適切なCloud Runリビジョンへとリクエストをルーティング
- Cloud Run上ではWebサーバーの前段にEnvoyを配置し、 URLから不要な情報を取り除いたうえでwebサーバーへとリクエストをルーティング
アーキテクチャ図も再掲します。
運用
環境作成
開発者がPull Requestに特定のラベルを付与すると、PR Dark Canaryリリースが行われます。具体的には、Pull Requestへのラベル付与をトリガーにして以下の処理をGitHub Actionsで実行します。
- Dockerイメージのビルドとプッシュ:PRのソースコードを使用してDockerイメージをビルドし、プッシュします。
- Cloud Runサービス上にリビジョンを作成:PR Dark Canary用のCloud Runサービスに、新しいリビジョンを作成します。
- タグ付け:作成したリビジョンにPR番号を含んだタグを付与します。
このGitHub Actionが終了後、開発者が特定のHTTPヘッダーを付与した状態で本番環境へ訪れると、PRの内容が反映されたPR Dark Canary環境へとアクセスできます。
環境区別
Dark Canary環境は通常環境と同一URL上で動作するため、開発者はどの環境を閲覧しているのかが判別しづらいという課題があります。これに対し、本番環境以外では閲覧環境を画面上に常に表示することで解決しています。
本機能はwebサーバーでリクエストを受け取った際に、先述したHTTPヘッダの値を参照し、環境情報をアプリケーションのグローバルステートへと反映することで実現しています。
おわりに
本記事では、WINTICKET Webで導入したPR Dark Canary環境を紹介しました。
PR Dark Canary環境はPull Requestごとに作成され、開発環境と同一のドメイン・URLで動作します。これにより、従来は困難だった外部サービスとの連携テストや、特定の機能の動作検証がPull Request作成段階で容易に行えるようになりました。
また、開発プロセスにおける最も顕著な改善は、mainブランチを常にリリース可能な状態に維持できるようになったことです。これは、開発環境でのQCのために品質が担保されていない機能を一時的にmainブランチに反映する必要がなくなったことによります。結果として、WINTICKET Webのリリースプロセスがよりスムーズに進行するようになりました。
以下にPR Dark Canary導入前後での定量比較を示します。
Before | After | |
リリース回数(/月) | 6.7 | 11 |
リリース前 Revert 回数(/月) | 1.5 | 0 |
PR Dark Canary環境の導入がWINTICKET Webの生産性向上に大きく寄与していることが分かりました。今後もチームの開発生産性を高めるための改善を継続的に行い、高品質かつ高速でプロダクトをアップデートできる仕組みを整えていきたいです。