Apollo Server + Prisma を AWS CDK で AWS Lambda にデプロイする
Apollo Server + Prisma は割りと鉄板なんじゃなかろうか。
これを AWS 上でコスト低く使いたい場合は Lambda に載せるという選択肢になるだろう。それぞれで Lambda への載せ方は書いてくれているが、微妙にほしい情報とズレているので、ここにまとめる。
構成
開発や事業に集中したいため、 API Gateway + Lambda の基本構成に加え Aurora Serverless を使って可用性を追求していく。 Aurora につなぐため Lambda は VPC 内に作っている。特に外に出ていく必要がないので VPC サブネットはプライベートのみ。 Aurora のパスワードは Secrets Manager に保存し、一定期間でローテーションさせる。 Lambda は Secrets Manager からパスワードを取り出し Aurora へつなぎにいく。
データベースのメンテナンスには EC2 をたてて SSM Session Manager 経由で SSH Port Forwarding し、直接つなぎにいく。
できているもの
次にコードをおいている。
解説とか
大枠
大枠の構成として参考にしたのは次。
ソースコードを公開してくれているので、非常にわかりやすかった。ここから要らないものを削っていった。
Prisma
Prisma を Lambda に載せる際はコツがいる。
公式のサンプルでは Serverless Framework でのやり方が載っているものの、 CDK で同様のことをやる場合は上の記事が参考になる。
また、 Prisma のドキュメントには環境変数経由でデータベースへの接続情報を指定する方法しか載っておらず、動的に接続先が変わる場合にしばらく悩んでいた。次の記事でクライアント生成時に接続情報を渡せることがわかったので非常に助かった。
Apollo Server
GraphQL schema ファイルをバンドルするために、 Prisma と同様、 NodejsFunction
の bundling.commandHooks
を使う。
本処理を開始する前に Secrets Manager からデータベースのパスワードを取得する必要があるが、 Apollo Server 提供の handler 作成関数は自前処理を差し挟む隙間がない。
仕方ないので、作ってもらった handler を自分で呼び出す handler を定義している。
export const handler = async (event: any, context: any, callback: any) => {
const secret = await getSecret();
const databaseUrl = `postgresql://${secret.username}:${secret.password}@${secret.host}:${secret.port}/${DATABASE_NAME}?schema=${DATABASE_SCHEMA}`;
const server = serverFactory(databaseUrl);
const serverHandler = startServerAndCreateLambdaHandler(
server,
handlers.createAPIGatewayProxyEventRequestHandler()
);
return serverHandler(event, context, callback);
};
もうひとつ注意点。公式で例にあがっている createAPIGatewayProxyEventRequestHandlerV2
は API Gateway で HTTP API を選択した際に使うもののようだ。 REST API を使う場合は V2
がないものを指定する。
踏み台
踏み台構築は別のスタックとして分けたほうが必要なときのみ踏み台をたてて、終わったら即破棄ができる。この場合、 CDK のクロススタック間参照が必要になる。
このとき厄介なのが、参照されているためリソースを消せなくなる事象で、次で対処法とともに詳しく説明されている。
また、 SSH でつなぐために Session Manager へのアクセス権限を EC2 インスタンスに与えてやる必要があるが、 CDK ではパラメーターの設定ひとつでできるようになっているようだ。
Discussion