🗺️

MIERUNEのQGIS LABを支える技術

2024/10/23に公開

https://qgis.mierune.co.jp/

MIERUNEは2024年10月に、QGIS情報サイト「QGIS LAB」をオープンしました。
本記事はこのWebサイトの実装やインフラなどを紹介します。

SvelteKit

MIERUNEはSvelteKitを社内標準として採用しています

https://svelte.dev/

  • 本WebサイトはSvelteKitによるサーバーサイドレンダリング(SSR)で配信されています。リクエスト時にCMSから記事データを取得し、レンダリングされたHTMLを返します。
    • ビルドのコスト・拡張性の観点から、SSGは採用しませんでした。
  • SvelteKitのStreamingを最大限利用し、TTFBの短縮を図っています。SvelteKitのStreamingは簡潔に書けて大変よいです。
  • SvelteKitはNode.jsのコンテナイメージの上で動作させています。
    • Cloudflare WorkersやVercelなどのエッジランタイムは採用しませんでした。これはランタイムが「薄い」ゆえの拡張性の低下を避けるためです。CDNでキャッシュを頑張ればデータはエッジから配信されるため、デメリットは大したことないだろうと判断しました。

拡張性 is...?

たとえば動的にOGP画像を生成する際に定番ライブラリであるlovell/sharpを利用していますが、これはエッジで動きません。

https://qgis.mierune.co.jp/posts/usecase_g-data_fa_forest-resource

まあエッジでもOGP画像を作る手段はありますが、何にせよ選択肢は絞られてしまいます。こういった「拡張」の可能性を考慮して、Node.jsが動くランタイムを前提とました。そんなわけで別のマシンを用意するだとか複雑なことをする必要なく、Node.js上で動くSvelteKitのエンドポイントとしてogp画像をレスポンス出来て、非常にシンプルな構成となりました。

その他、SvelteKitでOGPを生成する際のtipsを以下にまとめています

https://qiita.com/Kanahiro/items/1d10b976401ce6a47815

インフラ構成

CMS以外は全てAWS上に構築されています。

AWS Lambda

  • アプリケーションサーバーにはAWS Lambdaを採用しており、SvelteKitアプリケーションのコンテナイメージを作成し、Lambda Web Adapterで動作させています。
  • Lambda Function URLsを利用し、CloudFrontのオリジンとしています。
  • コールドスタートは(それほど長くないにせよ)採用の障壁となりますが、後述のキャッシュ戦略により、その影響を緩和しています。
  • 静的ファイルもLambdaから配信されるため、キャッシュがない場合は大規模なスパイクアクセスには耐えられないかもしれません。今回のWebサイトではこの点は障壁になりませんでしたが、考慮しておくべき事項です。

CloudFrontとキャッシュ戦略

  • CloudFrontの共有キャッシュを利用してオリジンアクセスを減らすのは当然実施すべきことですが、AWS Lambda上でSSRしているため、オリジンアクセス時のコールドスタートを含むレスポンスタイムの増加はなるべく軽減したいところです。
  • SvelteKitは各ページでレスポンスヘッダを設定することが出来ます。ここに適切なCache-Controlを記述することで、共有キャッシュの生存期間(TTL)を制御することができます。
  • CloudFrontはオリジンレスポンスのstale-while-revalidateを読み取ることが出来ます。

以上を踏まえて、オリジンからは下記のCache-Controlをレスポンスしています。

max-age=0, s-maxage=30, stale-while-revalidate=86400, stale-if-error=86400
# これがベストな値かは定かではない

これにより:

  • Age<30では、共有キャッシュをそのままレスポンス
  • 30<age<86400では、共有キャッシュをそのままレスポンスし、裏側でオリジンアクセスしキャッシュ更新
    • もしオリジンがエラーレスポンスを吐いても、ひとまずキャッシュを返す

という挙動になります。アプリケーション側で複雑なことをしなくても、CDNを活用するだけで、ちょっとしたISR(Incremental Static Regeneration)みたいなものを構築出来ていると思います。これにより、CDNからの高速な配信とコンテンツの鮮度を両立しています。

開発環境

インフラは全てCDKで記述して自動デプロイを実施していますが、チーム開発のレビュー効率を高めるため、次のようなツールを導入しました。

AWS AmplifyによるPull Requestプレビュー

AWS Amplifyは、SvelteKitをデプロイしてSSRすることが出来ます。

https://qiita.com/Kanahiro/items/dd4afc0d0871b97d4f33

GitHubリポジトリでPull Requestを作成する際に、Amplifyでプレビューを作成するよう設定しました。実行環境はNode.jsを前提としていたことにより、同一のコードでAmplifyにも容易にデプロイすることが出来ます(Adapterは差し替える必要があります)。コードをpullしてサーバーを起動する必要がなくなり、レビュー効率が向上しました。

その他、以下のような知見が得られました。

  • Streamingには非対応
    • なのでAmplifyをProductionとすることは出来ませんでした。
  • Sharpのようなライブラリも問題なく動作する(!)
    • 特に特殊対応も必要なく普通に依存解決出来ます。ただのLambdaの上で動いていると思っていたので意外でした。

reg-suitとPlaywrightによるビジュアルリグレッションテスト

以下のページを参考にVRTを導入しました。

https://zenn.dev/koheii/articles/8214467454a46c

最小構成では、GitHub ActionsとAWS S3だけで自動VRTを導入出来ます。これにより、予期せぬデザイン崩れを検知しやすくなりました。ただし全てのコードのpush時にスナップショットの作成を走らせる必要があるため(多分)、Workflowのマシン代は気をつけましょう。

reg-suitによる差分比較画面の例

Playwrightは複数のデバイスや画面サイズをエミュレートすることが出来ますが、ChromiumのPCビューと、Safariのスマホ幅の2パターンで検証しています。これは、レスポンシブデザインをチェックしたいことと、Safariで発生するスタイル周りのバグがいくつか発生していたためです。パターンを増やすほどに実行時間が伸びて体験が悪くなりコストもかかるので、なるべく少ない数であるべきです。

まとめ

  • SvelteKitは素晴らしいフレームワークです。今後も社内で知見を深めていきます。
  • Lambda Web AdapterとFunction URLs、そしてCloudFrontにより大変シンプルで安いインフラを構築できます。ポータビリティにも優れています。
  • AWS Amplifyは思った以上にしっかりしたランタイムでした。Streamingが必要ないシチュエーションでは十分選択肢に入るでしょう。
MIERUNEのZennブログ

Discussion