マンガメディア第2チームでGigaViewerのWeb面のSREを担当している id:s-shiro です。
この記事は、はてなのSREが毎月交代で書いているSRE連載の10月号です。9月の記事はid:cohalzさんの新規事業「toitta」で導入したGoogle Cloudのセキュリティ機能の紹介でした。
リポジトリのブランチごとに、メインブランチにマージする前の動作確認に使える環境を起動することをPreview DeployやDeploy Previewと呼ぶらしいのですが(以下Deploy Preview)、マンガメディアチームではEC2で構築していたDeploy Preview環境を、メンテナンス性や使い勝手の観点からコンテナ化しようとしています。
はてな全体でみるとDeploy Preview環境の構成や要素技術は、歴史的な経緯もあり各サービスを開発するチームごとに異なっており、ちょうど私がコンテナ化を検討し始めるより半年ほど前から、これを共通化する仕組みとしてmirage-ecsの検証が部署横断で行われていたため、今回はそれに乗っかるかたちで移行することにしました。
mirage-ecsについては面白法人カヤックさんの紹介記事がありますので、そちらをご覧ください。
この記事ではDeploy Preview環境の移行のために行った設計について紹介します。
現行の構成と課題
マンガメディアチームで運用している現行のDeploy Preview環境では、1台のEC2インスタンス上で、各ブランチごとにプロセスが /tmp/<ブランチ名>.sock
というUNIXドメインソケットでリクエストを待ち受けており、NginxがHTTPのhostヘッダーをもとに、リクエストをどのプロセス(ソケット)にフォワードするかを判断しています。
Nginxのconfファイルにすると、こんな感じ。
server { listen 80; # ブランチ名を正規表現で取得する server_name ~^branch-([\w-]*\w)?\.example\.com$; # 取得したブランチ名のUNIXドメインソケット set $unix_domain_sock "http://unix:/tmp/$1.sock"; location / { ...... # フォワードする proxy_pass $unix_domain_sock; } }
この構成では次のような課題がありました。
EC2インスタンスのメンテナンスコスト
定期的にOSやNginxをはじめとするミドルウェアをアップグレードしたり、プロセスが起動しなくなったときのトラブルシュートでインスタンスにssh接続して原因を調査する作業コストが高くなってきていました。
複数言語、複数リポジトリでの開発
GigaViewerはもともとPerlのモノレポで開発されていたのですが、近年フロントエンドにはNext.jsが導入されたり、サービスの一部分だけリポジトリを分けてGoで開発されるようになったりと、ブランチとDeploy Preview環境を1対1で対応づけるのが難しくなってきていました。
特にNext.jsが導入されてからは、Perl環境とNext.js環境でDeploy Previewの起動手順が分かれており、開発体験が損なわれていました。
mirage-ecsでは1つのDeploy Previewに対して1つのECSタスクを起動してくれるのですが、そのECSタスクの中で起動するPerlやNext.js, Goのコンテナ、さらには将来増えるかもしれないコンテナは、それぞれ個別にリポジトリやブランチ名を指定してデプロイできるようにCI/CDを構築する必要があります。
リクエストの振り分け
現行環境は1台のインスタンス上で動作しているので、1台のNginxから、複数のDeploy Preview環境へUNIXドメインソケットでリクエストをフォワードできました。一方mirage-ecsでは、Deploy Previewごとに1つのECSタスクが起動され、リクエストのhostヘッダをもとに、どのECSタスクにフォワードすべきかまではmirage-ecsが決めてくれるのですが、フォワードされた先で、さらにPerlコンテナへフォワードすべきなのか、Next.jsコンテナなのかを判断するためには、引き続きNginxが必要になります。
そこでDeploy PreviewごとにデプロイされるECSタスクの中にNginxコンテナをデプロイすることで、mirage-ecsからのフォワードをNginxで受け、そこから先のPerlやNext.js, Goコンテナへは、ポート番号を分けてlocalhostに対してフォワードするという、若干複雑な多段構成をとることになりました。
設計する
ここまで上げてきた課題に対する解決方法を盛り込んだ結果、上記のような設計になりました。
各deploy Preview環境へのリクエストはALBで受け、ALBから一度mirage-ecsのproxyにフォワードされます。mirge-ecsはhostヘッダーとECSタスクの対応関係の情報を持っており、リクエストを適切なECSタスクにフォワードしてくれます。
各ECSタスクではNginxがリクエストを受け取り、localhostの別々のポートでlistenしているPerl, Next.js, Goなどの各コンテナに対してリクエストを振り分けます。
それぞれのコンテナはGitHub上の異なるリポジトリ、異なるブランチのビルドを指定してデプロイすることができます。
この図では新たにCognitoが登場していますが、これはmirage-ecsのWebUIに対して認証をかけています。
構築編につづく(と良いな)
今回はマンガメディアチームで取り組んでいるのDeploy Preview環境のリプレースについて、既存環境や移行時の課題をもとに設計した内容を紹介しました。
この設計をもとに、現在動作検証中ですので、リリースした際には環境構築のIaCやCI/CDの実際のコード例などを紹介できればと思います。