6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS Lambda と ServerlessAdvent Calendar 2024

Day 5

AWS LambdaでSSRするフロントエンドをデプロイする

Last updated at Posted at 2024-12-04

TL;DR

  • QGIS LABではLambda Web Adapter(LWA)を利用して、Node.jsコンテナ上でSvelteKitを動かしてサーバーサイドレンダリング(SSR)している
  • LWAはストリーミングをサポートしているので、SvelteKitのStreamingを問題なく利用できる
  • CloudFrontの共有キャッシュでstale-while-revalidateを利用してコールドスタートの影響を軽減
  • このような工夫をすれば、LWAでSSRするアプリケーションをデプロイすることは実用的

QGIS LAB

筆者所属のMIERUNEが運営する、QGISの情報サイトです。2024年10月にリリースしたこのWebサイトは、AWS Lambda上で動作しています。フロントエンドフレームワークにはSvelteKitを採用しており、ヘッドレスCMSと通信したうえで、SSRによりHTMLをレスポンスしています。

Lambdaでコンテナを動かすLambda Web Adapter

AWS Lambdaはコンテナイメージを動かすことも出来ます。ただしコンテナであっても通常のLambdaと同様、handler関数を実装する必要があります。これではLambda専用コンテナとなってしまいますが、ポータビリティを売りとするのがコンテナなので、外界とは普通のrequest/responseでコミュニケーションしたいところです。Lambda Web Adapterはそれを可能とします。

Lambda Wed Adapterを利用することで、任意のHTTPをしゃべるコンテナイメージをLambda上で動作させることができます。また、Lambdaは「関数URL(Function URLs)」でHTTPSリクエストを受け付けるエンドポイントを取得することが出来るため、最小構成ではLambda単独でWebサーバーをデプロイすることが出来ます。

SvelteKitをNode.jsのコンテナイメージ上で動作させる

カレントディレクトリにpackage.jsonbuildディレクトリ(SvelteKitのビルドアーティファクト)があるとして、下記のようなコンテナイメージ書くだけでよいです。

FROM public.ecr.aws/docker/library/node:22-bookworm-slim

# Lambda WebAdapter
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
ENV PORT=3000
ENV READINESS_CHECK_PATH=/
ENV AWS_LWA_INVOKE_MODE=response_stream

WORKDIR /app
COPY package.json package-lock.json ./
# ランタイムに必要な依存関係のみをインストール
RUN npm install --omit=dev 

COPY ./build ./build

CMD ["node", "/app/build/index.js"]

このイメージをECRにデプロイし、Lambdaをこのイメージを参照してデプロイすればOKです。気になるのはコールドスタートですが、 体感で2,3秒程度です(推測するな測定せよという声が聞こえてくる)。関数が実行されるにつれコンテナイメージのキャッシュが暖まってくるらしく(出典忘れた)、暖まった状態でこの体感速度でした。コンテナイメージが大きく変更されない限りは、イメージのバージョンが変わってもキャッシュは再利用されていそうです。

また、DockerfileのLWAのイディオムのうち:

ENV AWS_LWA_INVOKE_MODE=response_stream

この記述は、ストリーミングを有効にするために必要です。これによりSvelteKitのStreamingがLWAでも動作します。

参考:Lambdaの過去4週間のメトリクス

Screenshot 2024-12-04 at 14.03.10.png
maximumがコールドスタート時と理解できます

Screenshot 2024-12-04 at 14.04.28.png
ブレはあるものの同時実行数の平均値は5程度に収束しています。同時実行数の最大を1000としたとき、今の100倍アクセスがあっても大丈夫そう、しらんけど。

このあたりのメトリクスはもちろんアクセス数の多い・少ないの影響は受けるので、目安としてご理解ください。QGIS LABは現状、膨大なリクエストが発生するようなWebサイトではありません。発生するようになったら嬉しいですね。

CloudFrontでキャッシュを頑張る

Lambdaをそのまま晒すことはないので、以下のような構成になると思います。

なおCloudFront-Lambda間でOACを利用することでLambdaへの直接アクセスを防げます

SvelteKitは、load()関数内で以下のようにレスポンスヘッダを設定出来ます:

setHeaders({
    'Cache-Control': 'max-age=0, s-maxage=30, stale-while-revalidate=86400, stale-if-error=86400'
});

CloudFrontはstale-while-revalidateディレクティブを解釈してくれるので、キャッシュが完全に期限切れならオリジンリクエスト、SWRのTTL内ならキャッシュをレスポンス・キャッシュ更新、キャッシュTTL内ならキャッシュをレスポンス、という処理を、clientとCloudFrontの間で実施してくれます。

このようにSWRを利用してコンテンツの鮮度を保ちつつ共有キャッシュを活用しています。Lambdaの弱点であるコールドスタートの影響も一定程度緩和できていると思います。

参考:キャッシュヒット率

https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_73197_8d8fde35-8aaf-bc77-c812-99f3731bf1e3 copy.png
足して100%にならないのでその分がSWRのRefresh Hitだと勝手に思っている(情報求)。俺たちは雰囲気でCDNをやっている。

結論:LWAでSSRは可能!

  • QGIS LABのリリース後、現状までの1ヶ月強の期間で特に不都合なく、パフォーマンスもよく配信出来ているので、SSRしたいWebサイトのデプロイ方法としてAWS Lambda + Lambda Web Adapterは十分に実用的であると言えます。Lambdaは非常に安いのでインフラコストも低く抑えられています。
  • また弊社はSvelteKitを標準として採用していますが、コンテナイメージでNode.jsが動いているのでNext.jsでも同じことが出来るはずです。
  • 静的アセットもLambda関数を通じて配信される点については留意が必要です(関数の実行数が消費されることもあり)。たとえばsveltekit-adapter-awsは静的アセットはS3から配信するという工夫をしていたりします。複雑性は結構高まりそうですが、改善の余地はありそうです。

以下にもQGIS LABに関する技術情報が記載されています

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?