NestJsで作成したプロジェクトをAWSのLambdaにあげて動かすまでの作業ログを残しておく
個人開発プロジェクトのバックエンドをNestJSで作成した。 公開したいので、awsのLambdaに乗せて動かしてみることにした。
初めてのaws、いつかの自分のために記録として一挙手一投足のログを残しておくことにする。
前提
バージョンは上記の通り。
で、Dockerの設定ファイルを諸々作ったが、使っていないので、ない前提でいく。
さて、それでは進めていく。
AWSアカウントの作成
AWSの公式ウェブサイトでアカウントを作成する必要がある。
で新規アカウント作成ができる。
その後、認証コードを経て、
AWS アカウント作成の流れを参考にすればいけるけど、意外と時間画かかったし、アカウント作成時にクレジットカードを登録することはいい気がしない。
とりあえず、これでアカウント作成が完了した。
必要なパッケージのインストール
ここからはNestJS-サーバーレスの公式を参考にしていく。
説明のために、Nest (@nestjs/platform-express完全に機能する HTTP ルーター全体を使用して起動) をサーバーレスフレームワーク (この場合は AWS Lambda をターゲットとしています) と統合します。
公式もawsで説明してくれているので、ありがたい。
まずは上記の通り、必要なパッケージをインストールする。
上記のようなバージョンがインストールされた。
次に、serverless.ymlファイルを作成する。
とりあえず公式をコピペしたやつ。
という風にpackage.jsonに合わせた。
node.jsのバージョンも揃えたほうがいいのかもだけど、いったんこのままで進める。
次にmain.tsを編集する。
のように変更した。
最後にtsconfig.jsonのesModuleInteropを有効にする。
で、下記コマンドを打つと
アプリケーションが実行されたら、ブラウザを開いてhttp://localhost:3000/dev/ANY_ROUTE に移動します。
ということなので、やってみる。
が、「このサイトにアクセスできません」になる。
そもそもサーバーを立ち上げてないので動くわけがない。
じゃあどうするのか。
いらいらググると、
というコマンドを叩く必要がありそう。てかドキュメントにも書いてあった。
そういうことなので実行してみる。
なんか出たけど、y。
入っていないパッケージを参照するよ、ということらしい。
なんかタイムアウトしてるけど、動いているっぽい。
タイムアウトの原因をデータベースとの接続と考え、データベースを介さない、ただ文字列を返却するだけのエンドポイントを作成し、テストする。
変わらず、タイムアウトした。
仕方ないので、main.tsをまるっとドキュメントのコピペにした。
これで動けばmain.tsに問題がある。
動かない。
次にserverless.ymlのservice: serverless-exampleを戻したら、データベースを介さないやつは動いた。
そしてまた何回か繰り返すとタイムアウトした。
再度起動しなおしたら、データベースの値を取得出来た。
そういうわけで、main.tsをもとに戻してみる。
タイムアウトした。
main.tsの原因を探る。
これにしたら動いた。
の実行場所が問題だったっぽい。
というわけでservice: serverless-exampleをservice: backendに戻した。
これでサーバーレスのテストをローカルでできた。
追記:2023年6月30日
ローカルでテストする際に、timeoutが頻発するので調べたところ、
プロバイダーの下にタイムアウトを追加します。ラムダのタイムアウトの最大値は 900 秒です。実行時間に応じて 30 秒などに設定し、何が起こるかを確認してください。
という記事を見つけた。
実際に
にしたところ、タイムアウトしなくなった。
AWSにデプロイ
次にawsにデプロイする必要があると思っている。
その為に何をすればいいのか調べて実行していく。
もしかして、コンソールに入り関数を作成し、zipファイルをアップロードすればいいだけ?
そんな気がするのでそれでやってみる。
Lambdaの関数作成
AWS Lambda ダッシュボードの 関数を作成をクリックする。
一から作成を選択し、関数名を決める。
次に、関数の記述に使用する言語を選択します。ということでNode.js 14.xを選択した。理由してはローカルでサーバレスのテストをした時のランタイムにNode.js 14.xを使用したので。
よく考えたら18で作ってるんだけど、今回は気にしないことにする。
(※このログを書いた後にNode.js 18.xにした。)
アーキテクチャっていうのは
のふたつがあってよくわからないので、x86_64を選択した。
一応調べてみると下記に詳しい。
https://docs.aws.amazon.com/ja\_jp/lambda/latest/dg/foundation-arch.html
どうやら値段が違うようで、arm64の方が安そうな気がする。
ということで、arm64を選択することにした。
(※このログを書いた後にx86_64にした。)
で、関数の作成を終了する。
Zip のアップロード
作成後遷移したページでコードタブをクリックし、npm run buildで作成されたdistフォルダをzip化し、アップロードする。
API Gatewayの構築
次に API Gatewayのダッシュボードにいき、HTTP APIの構築を行う。
統合で先ほど作成した関数を選択する。API名は特に決まりないよう。
次にルート設定でリソースパスを/{proxy+}に変更する。
最後に「ステージを設定」は変更せず、そのまま 作成をする。
どうやら次に権限の設定があるらしい。
API Gateway コンソールで、先ほど作成した API の詳細ページを開く。左のタブから、「統合」を選択し、ルートの統合の詳細を見る。
アクセス許可を呼び出すのポリシーステートメント例の、source-arn内の文字列をコピーしておく。
またLambdaに戻り、設定→アクセス権限、リソースベースポリシー内の アクセス権限を追加をクリック。
AWSのサービス を選択し、下の画像のように入力して保存します。ソースARNに先ほどの文字列を追加。
アクションはlambda:InvokeFunctionを選択する。
URLにアクセスするが、{"message":"Internal Server Error"}になる。謎が深い。
serverless.ymlにならい、dist/main.handlerにハンドラを変更したが変わらない。
検索するとHTTP API Lambda 統合に関する問題のトラブルシューティングのページにたどり着いたので、見ていく。
最近気が付いたけど、結局公式見るのが一番早い。
急がば回れ、誰が考えたか知らないけど、先人の知恵ほど偉大なものはない。
内部サーバーエラーのトラブルシューティングを行うには、ログ形式に $context.integrationErrorMessage ログ記録変数を追加し、HTTP API のログを表示します。これを達成するには、次の操作を行います。
という操作をするらしいけど、何言ってるのか全くわからない。
とりあえず、下記公式の通り進める。
やろうと思ったけど、既にグループがあったので、「ロググループの Amazon リソースネーム (ARN) を書き留めます。」だけやった。
次に下記の公式通りの手順を行う。
Network Failureでできない(※ 今思えば、タイムアウトしていた?)。謎が深い。
諦めて、ロググループにあった既存のグループのログを見てみると、@nestjs/coreモジュールを見つけることができない的なことが書いてある。
それをchat GPTに聞くとnode_modulesもアップロードしろという返答が返ってきたので試してみる。
重すぎてあげられない。
s3を使えとのこと。
そういうことであればと思うのだが、お金発生しているのか不安になる。
調べるとAWS 無料利用枠があるっぽいので行ってみる。
アップロードしたs3のデータをLambdaにアップロードする。
リレージョンが異なるので無理っぽい ??
よくよく見ると、Lambdaのリレージョンシドニーでやってた。
東京に作り直す。
東京に作り直したけど、s3のファイルサイズが大きすぎると怒られた。
たぶん、node_modulesにdevDependenciesも含まれているからなので、Dependencies のみのパッケージをインストールするnpm install --productionコマンドでnode_modulesを作成する。
ついでにzipファイルにpackage.jsonとpackage.look.jsonも入れてみる(結果的に正しかった)。
でも無理で、色々試した結果、distフォルダがダメだということに気が付いた。
つまり、
- package.json
- node_modules
- main.ts etc
という構成にしなければならないことに気が付いた。
かつ、bcryptが使用できないいっぽいのでbcryptjsを使用するこにした。
で、あげたら今度はタイムアウトになった。 すこしづつ進んでいるが、まだできない。
基本設定でタイムアウトを30秒にした。
次に秘密鍵のエラーが出た。 JWTの秘密鍵をenvで管理しているのだからそりゃそうだ。
環境変数を設定すると、ログからエラーが消えたが、{"message":"Service Unavailable"}という結果になる。
新しい。
ログを見るとUnable to connect to the databaseということなのでデータベースに接続てきていないらしい。
ローカルのMySQLに接続できないだろし、これを機にRDSの構築をすることにした。
RDSの作成
やり方は公式に詳しかったのでそのようにした。
同時にLambdaの環境変数を編集した。
でエンドポイントを叩いたが、
うまくいかない。
色々と調べてみると、IMAロールとVPCの設定が必要とのこと。
https://qiita.com/kobayashi\_0226/items/d8f97c652873d80b1367
上記記事を見つつ、ぽちぽちやっているとできた。
ただ、どうも接続できないエンドポイントがある。
調べてみるとNestJSのsynchronizeオプションをTrueにしたままだったのが原因っぽい。
次は踏み台サーバーを作成し、RDSの中身を見てみる。
Discussion in the ATmosphere