面白法人グループアドベントカレンダー2024 2日目の記事です。SREの藤原です。
2024年も暮れようとしていますね。ところで今から5年前のこと、builderscon tokyo 2019 というイベントで「レガシーサーバーを現代の技術で再構築する」というタイトルで発表しました。
この発表は、当時 Amazon EC2 のシングル構成で動作していたサーバー(SVN, Redmineほか、社内の開発支援ためのサービスが動作していました)を、Amazon ECS をはじめとしたコンテナや AWS のマネージドサービスを用いてリプレイスした、という内容です。
2019年末にはこの発表で予定していた移行作業は全て完了し、以下の画像のように EC2 が空っぽになりました。
あれから5年
さて、早いもので2019年から5年が経過しました。このシステムで動いていたサービスもだんだん需要が少なくなり、いくつか廃止できそうなものも出てきました。具体的には次のような状況になったのが2024年秋のことです。
- SVN (Subversion) は利用するサービスが全て終了もしくは移行が完了 (廃止)
- Redmine は2025年4月に全ての利用が完了する予定 (その時点で廃止)
- Gyazo (画像投稿サービス)と nopaste (テキストスニペット共有サービス) は、過去のデータのみ参照できるようにする
- 新規の投稿は Slack などへ移行できるようになった
- IRC の過去ログは参照と全文検索だけしたい
ということで、この際廃止できるものは廃止してついでに運用コストも削減しようということになり、CloudFront + Lambda + S3 で構築し直したというのが本日のお話です。
最終目標: VPC内のリソースを全て削除する
2019年のリプレイスでは、Application Load Balancer (ALB) をエントリーポイントにして、そこに EC2 をぶら下げた状態から ECS に徐々に移行していきました。
今回のリプレイスでは、以下の構成に移行して、URLはそのまま最終的に ALB から後ろ(つまりVPC内のリソース全部) を廃止するのが目標です。
- CloudFront を導入し、静的に返せるコンテンツはそこで配信する
- nopaste, Gyazo のコンテンツ
- 動的に配信する必要があるものは AWS Lambda (Function URL)で動作させる
- IRC 過去ログ閲覧と全文検索
- 認証は ALB で導入していた Cognito をそのまま利用して CloudFront で認証する
- Google 認証に federation しています
では、やっていきましょう。
CloudFront を導入する
2019構成ではユーザーが ALB に直接アクセスする状態でした。その前段に CloudFront を配置します。
この時点では全てのリクエストを ALB に送ることで、全てのサービスが今までと変わらず利用できる状態です。特に難しいことはありませんね。今回は行っていませんが、IPアドレス制限などを行っている場合は CloudFront がリクエストを受けたアドレスを ALB 以下に伝える必要があることに注意してください。
CloudFront で Cognito 認証をする
ここが今回、一番手間の掛かったポイントです(といっても数日ですが…)。フルマネージドで CloudFront + Cognito の認証を行う方法は現時点ではありません。
検索すると世の中にいくつか実現している事例はあるのですが、だいたいどれも実現するために Lambda@Edge を使用しているようです。
しかし、Lambda@Edge のランタイムは Node.js と Python しか利用できない、という制限があります。Node.js ではランタイムの EoL が頻繁に通告されるため、メンテナンスの手間が掛かります。社内には Python を得意とするエンジニアが少ないため、Python で書くのもできれば避けたいところです。
以下のように社内的なメリットが多いため、できれば Go で書きたいのでした。
- 社内に扱えるエンジニアが多い
- Lambdaのランタイムはカスタムランタイム (今なら
provided.al2023
) を選べばよい - 言語自体の互換性が高いので、更新が必要になっても新しいGoでビルドし直すだけでほぼ問題がない
ということで今回は Lambda Function URL と CloudFront Functions (CFF) を併用するアーキテクチャを採用しました。詳細は省きますが、流れとしては下記のようになります。
- Go で書いたアプリケーションを Lambda Function URLs で動作させ、CloudFront の特定 path (
/__congnito
) はこの Lambda が処理する - CFF を viewer-request フェーズで動作させて認証が必要な全てのリクエストに適用し
- 自前の認証 Cookie (JWT) をみて valid なら通過
- Cookie がなかったり JWT の exp が切れている場合は Lambda (
/__cognito
) にリダイレクトする
- Lambda は Cognito にリダイレクトし、Cognito で認証した結果の callback (code) を取得して検証した上で、自前の認証 Cookie (JWT) を発行する
なお、CFF のテストとデプロイには拙作の OSS である cfft を使用しています。
これで ALB で利用していた Cognito による認証をそのまま、CloudFront で使うことができました。
IRC ログビューアを作り直す
2019年構成の IRC ログビューアは、Perl で書かれた Web アプリケーションと全文検索のための Groonga で構成されていました。これを ECS で動かしていたのですが、Perl はともかく Groonga を Lambda で動作させるのは困難です。
これはもう Go で書き直そう、全文検索は SQLite3 Full text index (fts) でやればいいでしょう、ということで一晩で書き直したのが以下の tiarraview です。需要は多分ないでしょうが OSS にしてあります。
tiarraview はスタンドアロンの Go の net/http.Server でも、Lambda Function URL でも動作するように ridge を使用しています。
Lambda で動作する場合は、初期化フェーズで全文検索index込みの .sqlite3
ファイルを S3 からダウンロードし、それを読んで動くようになっています。更新はしないのでそれで問題ありません。
Gyazo, nopaste の参照を CloudFront 経由にする
社内 Gyazo (画像共有), nopaste (テキストスニペット共有) については、2013年末の時点でデータを全て S3 に保存するようになっていました。11年前のブログエントリが以下のものです。懐かしいですね。
なので、これは CFF で認証を掛けた上で CloudFront から S3 を参照するように設定するだけです。
ただし、URL が例えば /gyazo/7b4a79c92.png
のようになっていた場合、S3 上には 7b/4a/79c92.png
のようにpathを区切ってデータを保存するようになっていました 1。そのため CFF で認証後に path を書き換えて、URL と S3 上の key を一致させる一手間が必要でした。
ALB 以下を爆破解体する (未完)
ということで、2024年12月時点の構成図は次のようになりました。
2024構成で必要なものはすべて CloudFront + Lambda + S3 になったので、あとは ALB 以下にある Redmine の利用が完了次第、ALB 以下の VPC 内リソースを全て掃除すればおしまいです。来年早々にはすっきりするでしょう。
歴史は大事です。歴史は引き継ぎつつも、レガシーになったリソースは適切に移行して行きたいものですね。
カヤックでは適切にシステムを解体できるエンジニアを募集しています
- これは ls したときに爆発するのを避ける定番手法です↩