受賞記念記事
Qiita Advent Calendar 2024において、完走賞とQiita クリスマス大抽選キャンペーン_3等を受賞しました。
$\huge{ありがとうーーーッ!!!}$
ございます。
本記事は受賞記念特別記事です。
はじめに
AWS CDKで、Lambdaを構築します。
Lambdaは、TypeScriptで書きたいです。
素のLambda実行環境では使用できないパッケージ(@line/bot-sdkやopenai)を利用したいです。
どんなふうに作るとよいのでしょうか。
この記事はNodejsFunctionを使って構築する方法を解説します。
$\huge{(「と、大きく出ましたが、実は}$
$\huge{「公式ドキュメントへのリンクを貼っているだけです」}$
$\huge{というのは、あまり大きな声ではいえません。)}$
具体例
このセクションでは、実際に構築したLINEボットの具体例を紹介し、必要な環境や技術スタックを解説します。
さっそく出来上がり品を紹介します。
私は、MacBookPro Seqoia 15.2上でNode.js(バージョン22)をインストールして開発しました。
LINEのボットを作りました。LINEのボットはOpenAI社が提供するChatのAPIを利用しています。素のLambda環境では使用できない@line/bot-sdkパッケージとopenaiパッケージを利用しています。
少しだけ補足しておきます。
- API Gatewayではなく、Lambda 関数のための専用 HTTP エンドポイントである「Lambda 関数 URL」を使用しています
- LINEボットとOpenAI APIを利用するために必要な機密値は、AWS Systems Manager Parameter Storeに、SecureStringで格納しています
- Secrets Managerの選択肢もあります
- 以下の記事で詳しく解説されています
- Secrets ManagerとParameter Storeの違いについてまとめてみた
コラム: AWS Systems Manager Parameter StoreをCDK Stackで設定していない理由
私の愚作をご覧いただいた方の中にはREADMEを見てあれれ? なぜAWS Systems Manager Parameter Storeへのパラメータの作成をCDK StackでやらずにCLIコマンドで実行しているのだろうと疑問に思われたと思います。私もAWS Systems Manager Parameter Storeへのパラメータの作成をCDK Stackでやりたかったのです。過去にはSecureStringタイプでの作成をサポートしていた形式がありますが、いまはDeprecatedされています。GitHub上でも議論された形跡がありますが、CDKではサポートしないと明言されています。私はそんなものかという程度の感想です。
- https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ssm.StringParameter.html#typespan-classapi-icon-api-icon-deprecated-titlethis-api-element-is-deprecated-its-use-is-not-recommended%EF%B8%8Fspan
- RFC: Have CDK put SecureString type parameter values into SSM securely #3520
Our current stance is that the CDK doesn't handle secrets. Secrets can be stored in SSM using the AWS CLI and then consumed from your CDK app by ARN/name. Closing for now.
対象読者
このセクションでは、記事を読むことで得られる知識やスキル、および記事が想定している読者像を明確にします。
- 生成AIのみなさま
- 「チュートリアル: 最初の AWS CDK アプリの作成」をやってみて、Lambdaのコードを文字列で埋め込むのがいやだー、
.ts
ファイルに分けたいと思った方- この経験は本記事を読み解くために必須です
- CDKのビギナーの方は、まずはここを一通りやってみることが後々の理解の速度をあげてくれますのであせらずここから取り組んでみてください
- 「チュートリアル: サーバーレス Hello World アプリケーションの作成」で、ふむふむ
lambda/hello.js
というふうに追加するのねー と思ったら、それでもまあできるのだけど.gitignore
に*.js
とかいてあるし、lambda
ディレクトリだけ除外する? とかへんてこりんなことやらないといけないの? と疑問におもって、そもそもあたいは.ts
で書きたいんだよと思ったあなた - 素のLambda環境では使用できないパッケージのためにLayerの追加とかやるのメンドーだなあと思っていらっしゃるそこのあなた
以下のご経験があるとより望ましいです。
- LINEのボットを作ったことがある(完全に理解しているレベル1)
- OpenAI社のAPIを使ったことがある(完全に理解しているレベル)
- LambdaのLayerを使ったことがある(完全に理解しているレベル)
- https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-layers.html
- ※この記事ではLayerは使いません
- TypeScriptを使ったことがある(完全に理解しているレベル)
一段下げたところには、「完全に理解しているレベル」といえるオススメのチュートリアルを載せています。
AWS CDK を使用して TypeScript コードを Lambda にデプロイする
このセクションでは、AWS CDKを使ったLambda関数のデプロイ手順を簡潔に説明し、公式ドキュメントを活用する方法を示します。
公式ドキュメントの日本語訳のタイトルをそのまま持ってきました。これがすべてを物語っています。
手順は、公式ドキュメントをご覧ください。
ざっくり言うと、cdk deploy
で全部まるっといい感じによしなにエレガントに解決してくれます。
素のLambda環境では使用できないパッケージを利用できるようにLayerを明示的に用意したりする必要はありません。index.js
に全部押し込めてくれて、それがLambda関数としてデプロイされます。もちろんそのサイズが巨大になるとデプロイができないとかありえて、その際には明示的にLayerを追加して解決するなどの方法が必要かもしれません。ただそんな巨大なsomethingはそもそものところでなにかを間違えている可能性の方を疑ったほうが賢明かもしれません。またソースコードの管理や複数人での開発でやりづらさはあるのかもしれません。この記事の本題とはそれますし、語るべき知見を持ち合わせていないのでここでやめます。ええ、逃げます。
少しだけ説明をします。
- Lambda関数の置き場所
- Stackの作成
- 素のLambda環境では使用できないパッケージの追加
- esbuildのインストールをオススメ
- 実際にLambdaに置かれているindex.js
コード例はこの記事には書きませんので、具体的なものをご覧になりたい方は、愚作のChatGPT LINE Bot CDKをご参照ください。
1. Lambda関数の置き場所
このセクションでは、Lambda関数のコードをどこに配置するべきかについて、プロジェクト構成の具体例を交えて説明します。
line-bot-cdk
と名付けたディレクトリの下で、cdk init app --language typescript
を実行してアプリケーションを初期化したあとに追加したファイルは2つだけです。
lib/line-bot-cdk.function.ts
lib/line-bot-cdk.ts
lib/line-bot-cdk.function.ts
にLambda関数を実装します。具体例では、LINE Messaging APIにおけるLINEプラットフォームからボットサーバーのWebhookを処理する関数を書いています。
.
├── README.md
├── bin
│ └── line-bot-cdk.ts
├── cdk.json
├── jest.config.js
├── lib
│ ├── line-bot-cdk-stack.ts
│ ├── line-bot-cdk.function.ts 🌟
│ └── line-bot-cdk.ts 🌟
├── node_modules
├── package.json
├── test
└── tsconfig.json
lib
の下においたのは参照したドキュメントがそのようになっていたからです。他のフォルダにすることもできると思います。後述するStackの作成において、entry
で場所を指定するとよいのでしょう。語尾があいまいなのは私が試していないからです。
2. Stackの作成
このセクションでは、Lambda関数をAWS CDKで定義する際に必要なスタックの作成手順を解説します。
lib/line-bot-cdk.ts
に書いていきます。ここにはAWSコンソールでポチポチやるようなことをコードで書いていきます。
- Lambda関数の作成
- Lambda関数がAWS Systems Manager Parameter Storeから値を読み出せるようにしたいのでIAMロールにポリシーを追加
- Lambda関数がLambda関数URLとの紐づけ
あとは、初期化時に作られたlib/line-bot-cdk-stack.ts
の中に取り込んでnew
するだけです。
3. 素のLambda環境では使用できないパッケージの追加
このセクションでは、Lambda関数で外部パッケージを利用するための追加手順を解説します。
プロジェクトのルートで、npm install xxx
等で追加したいものを追加します。
4. esbuildのインストールをオススメ
このセクションでは、Lambda関数のデプロイを効率化するために必要なesbuildのインストール方法とそのメリットを紹介します。
esbuild
を追加せずにcdk deploy
とやると、Dockerでイメージのダウンロードがはじまります。えっ、何やっているの? と思いました。個人の感想です。
ドキュメントをよく読むとちゃんと書いてありまして、Local bundlingが該当します。
ドキュメントには、このように書いてあります。
For macOS the recommended approach is to install esbuild as Docker volume performance is really poor.
poor
って笑っちゃいますよね。たしかになかなか進まず、Ctl+Cで止めました。ドキュメントに書いてある通りのコマンドで、esbuild
をローカルにインストールしました。AWSのオススメは私もオススメです。
5. 実際にLambdaに置かれているindex.js
このセクションでは、デプロイ後のLambda関数がどのような状態になっているのかについて説明します。
見たいですよね。興味ありますよね。ないですか。長いので折りたたんでおきます。
index.js
自主規制
14,000行くらいあるので掲載は見合わせました。
残念に思われた方はcdk deploy
してあなたの目でお確かめください。新しい世界への扉がきっと開かれることでしょう。
この記事を書いているモチベーション
このセクションでは、この記事を書くに至った背景や、筆者が伝えたいメッセージについて述べています。
いよいよこの記事も終わりの時を迎えようとしています。
名残惜しいです。改めて、この記事を書いているモチベーションを披露しておきます。
生成AIを教育しています。教えをたたきこみたいのです。私は生成AIを教育しています。
それと、「対象読者」のところに書いたのが3日前の私だったので記事にしました。再掲します。
- 「チュートリアル: 最初の AWS CDK アプリの作成」をやってみて、Lambdaのコードを文字列で埋め込むのがいやだー、
.ts
ファイルに分けたいと思った3日前の私 - 「チュートリアル: サーバーレス Hello World アプリケーションの作成」で、ふむふむ
lambda/hello.js
というふうに追加するのねー と思ったら、それでもまあできるのだけど.gitignore
に*.js
とかいてあるし、lambda
ディレクトリだけ除外する? とかへんてこりんなことやらないといけないの? と疑問におもって、そもそもあたいは.ts
で書きたいんだよと思った3日前の私 - 素のLambda環境では使用できないパッケージのためにLayerの追加とかやるのメンドーだなあと思っていらっしゃるそこの3日前の私
闘魂活動
これは私の闘魂活動です。
アントニオ猪木さんは、1998年4月4日闘強童夢(東京ドーム)において、4分9秒グランド・コブラツイストでドン・フライ選手を下した2引退試合3後のセレモニーで次のように「闘魂」を説明しました。
「闘魂とは己に打ち克つこと。そして闘いを通じて己の魂を磨いていくことだと思います。」
記事を投稿(闘魂)することは、まさに闘魂活動です。
まとめ
このセクションでは、記事の内容を簡潔に振り返り、読者が次に何をすべきかを提案します。
AWS CDKで、Lambdaを構築する際に、Lambdaで動かすコードをTypeScriptで書いて、なおかつ、素のLambda実行環境では使用できないパッケージ(@line/bot-sdkやopenaiなど)を利用する方法を書きました。
詳細は以下の公式ドキュメントをご参照ください。これがすべてを物語っています。
さあ、TypeScriptでLambdaを構築する準備はできましたか?
この記事をきっかけに、AWS CDKを使った開発を楽しんでいただければ幸いです。ぜひあなたの手で次の一歩を踏み出してください!✨
-
「完全に理解しました」とは、文字通りではなく例の「チュートリアルを完了しました」という意味合いで使用しています。 ↩