🐏

AWS LambdaをSAMで deployしたときにハマったエラーをひたすら書いておくぜ!

2024/12/04に公開

はじめに

こんにちは、まさき。です。
この記事はNEアドベントカレンダー4日目の記事になります。

今年に入って業務でAWSのLambdaを触る機会がちょくちょくあり、その中にはコードだけではなくLambdaをSAMなどで作ってデプロイする機会もありました。
今回は、インフラにも興味があるアプリケーションエンジニアが、Lambdaをデプロイしたときにハマったエラーを紹介しようと思います。
また、LambdaのDeploy周りの知識の整理もします!

本記事では、AWS LambdaをAWS SAMを使ってデプロイする際に遭遇したエラーとその解決方法について解説します。
LambdaやSAMを業務で使い始めた方や、エラーに直面して対応方法を知りたい方を対象としています。

LambdaをDeployするとは?

Lambdaってそもそもなんだっけ?

AWS Lambda はAWSが提供しているサーバーレスコンピューティングサービスです。
AWS側が実行サーバを用意してくれているので、サーバのことを考えることなく、コード(関数)を実行することができます。
たとえば、EventBridgeでスケジュールをトリガーにすることで、毎日決まった時間にXに投稿するボットを構築できますし、APIGatewayと組み合わせて簡易のAPIサーバとして使うといったことも可能です。

AWS Lambda

Lambdaをdeployするってどういうこと?

Lambda関数を作成(デプロイ)する方法は大きく分けると2通りあります。

  • コンソールでのデプロイ

    • 以下のようにAWSのコンソール画面にあるエディタでコードを記述し実行する方法
    • その場でコードを実行することができて便利
    • 複数のAWSリソースを組み合わせた複雑なアーキテクチャには向かない
      コンソールでのデプロイの例
  • SAMでのデプロイ

    • コードとインフラを同時に管理できる
    • 複数環境やCICDパイプラインの構築が容易になる
    • SAM(CloudFormation)の知識が必要
       SAMでのデプロイの例

AWS SAM ってなんだ?

AWS Serverless Application Model (AWS SAM) は、Infrastructure as Code (IaC) を使用した、サーバーレスアプリケーション構築のためのオープンソースのフレームワークです。AWS SAM の省略構文を使用して、デベロッパーは、デプロイ中にインフラストラクチャに変換される AWS CloudFormation リソースおよび特殊なサーバーレスリソースを宣言します。
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/what-is-sam.html

AWS SAM を使用する場合、アプリケーション内で AWS SAM プロジェクトとテンプレートで構成されている関連リソースを管理します。アプリケーション内のすべてのリソースは、AWS SAM テンプレートで定義または参照されます。AWS SAM がテンプレートを処理すると、AWS CloudFormation リソースも作成されます。AWS CloudFormation では、リソースはスタックと呼ばれる単一のユニットで管理され、スタック内のすべてのリソースは、そのスタックの AWS CloudFormation テンプレートによって定義されます。
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/what-is-concepts.html

AWS SAMはAWS CloudFormationの省略構文を利用してインフラとアプリケーションを簡単に管理するためのフレームワークです。
たとえば、APIGatewayとLambdaを統合する設定も数行で記述できます。
AWS CloudFormation とは

AWS SAMでのLambda Deployってどうやるの?

SAMでのDeployの流れを大雑把に書くと以下のようになります。

  1. 実行したいLambda関数を書く
  2. AWS SAM CLIをインストールする
  3. sam initして、AWS SAMテンプレートを生成する
  4. チュートリアルAWS SAM テンプレートの構造分析などを参考にSAMテンプレートに構築したいインフラを記述していく
  5. ローカルで動くことを sam local invokeで確かめる
  6. sam deploy --guided を実行して、デプロイ設定ファイルを生成しつつ、deployする

Lambda Deploy時に遭遇したエラー

どんなLambdaか?

外部システムからのWebhookイベントを受け取り、そのイベントに含まれているデータを加工したものを別システムにAPIで連携するといったものを作りました。
Lambdaでつくったもの

SAMでインフラを構築しており、mainブランチにマージするとGitHubActionsで sam deployコマンドが実行され、
Lambda, APIGatewayなどがデプロイされるというものです。

エラーの内容

権限系のエラー

事象

GitHubActionsでいざsam deploy!というタイミングで以下のようなエラーが発生しました。

Error: Failed to create/update the stack: XXXXX, An error occurred (AccessDenied) when calling the DescribeStacks operation: User: arn:aws:sts::***:assumed-role/sam_cli/GitHubActions is not authorized to perform: cloudformation:DescribeStacks on resource: arn:aws:cloudformation:ap-northeast-1:***:stack/XXXXX/* because no identity-based policy allows the cloudformation:DescribeStacks action
Error: Failed to create changeset for the stack: XXXXX, An error occurred (AccessDenied) when calling the CreateChangeSet operation: User: arn:aws:sts::***:assumed-role/sam_cli/GitHubActions is not authorized to perform: iam:PassRole on resource: arn:aws:iam::***:role/sam_deploy_role_for_cicd because no identity-based policy allows the iam:PassRole action
Error: Process completed with exit code 1.

sam_cliというSAMでdeployするために作られたIAMロールがもともとあり、今回はそのロールでSAM deployをしようとしてました。
が、そのロールに新たにデプロイしようとしたCloudFormationのStackが指定されていなかったことにより、deployを続行できなかったというものでした。

対応方法

sam_cliにアタッチされているIAMポリシーに、今回作成するStackのArnを追加しました。
また、PassRoleにもdeployに使用しているロール(role/sam_deploy_role_for_cicd)を追加しました。

deployコマンドの引数に関するエラー

事象

sam deployコマンド実行時に capabilities CAPABILITY_IAMの指定がないとのエラーが出ました。

Error: Failed to create changeset for the stack: XXXXX, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Requires capabilities : [CAPABILITY_IAM]
Error: Process completed with exit code 1.

対応方法

sam deployコマンドの引数に以下の値を設定して対処しました。
--capabilities CAPABILITY_NAMED_IAM

他にも不足している引数があったらしく、最終的に以下のようなコマンドに落ち着いたのですが、

- run: sam build
      - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset --parameter-overrides Stage="prod" --role-arn=arn:aws:iam::xxxxxx:role/sam_deploy_role_for_cicd --stack-name=XXXXX --s3-bucket=XXXXX --capabilities CAPABILITY_NAMED_IAM

同様に以前に作られたLambda DeployのGitHubActionsのコマンドを見てみると以下のようにだいぶシンプルでした。

      - run: sam build
      - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset --parameter-overrides Stage="prod" --role-arn=arn:aws:iam::xxxxxx:role/sam_deploy_role_for_cicd

この差の原因は、以前に作られたコマンドはsamconfig.tomlで引数を設定していたので、引数をコマンドに指定する必要がなかったようです。samconfig.tomlを適切に記述してあげることで同じようなコマンドで実行できるようになりました。

version = 0.1
[default.deploy.parameters]
stack_name = "XXXXX"
s3_bucket = "XXXXX"
capabilities = "CAPABILITY_NAMED_IAM"
parameter_overrides = "Stage=prod"
role_arn = "arn:aws:iam::xxxxxx:role/sam_deploy_role_for_cicd"

CloudFormation側でのエラー

事象

deployコマンドが実行されたなーと思っていた矢先、以下のようにCloudFormation側でエラーが発生しました。

Error: Failed to create changeset for the stack:XXXXX , An error occurred (ValidationError) when calling the CreateChangeSet operation: Stack:arn:aws:cloudformation:ap-northeast-1:***:stack/XXXXX/xxxxx is in CREATE_IN_PROGRESS state and can not be updated.
Error: Process completed with exit code 1.

いろいろと試行錯誤していた結果、Stackが中途半端に作られてしまい、CloudFormationの処理としてはまだ途中というステータスになってしまったようです。

対応方法

AWS CloudFormationの管理画面から当該Stackを削除して再度実行することで無事デプロイできました!

実運用で注意すべきポイント

リクエスト先にもIP制限があった

APIGatewayのエンドポイントを叩いても、システムBにデータが作られないということが後日わかりました。
ログを確認したり、過去の対応を見直したところ、システムBには特定のIPアドレスからのリクエストしか受け付けないようになっていることが判明しました。

結局、過去にシステムBと連携するLambdaが使っているVPCやSubnetなどを共用することで、特定のIPアドレスからのリクエストを実現できました。

この問題は、GitHubActionsでAPIGatewayおよびLambdaのデプロイに成功した!あとに、実際にAPIリクエストを投げていれば良かったものの、問題を解消できた、デプロイできたことに満足して検証を後回しにしたことで発覚が遅れたというものでした。

正しくリクエストテストを行うこと、マージしたら自動でデプロイしてくれる仕組みになっていても、ちゃんとデプロイが完了しているかを確認することの大切さを身を以て改めて実感しました。

リソースの無駄遣いにご用心

この対応でLambdaを作ったときにNATやVPCも作成していました。
NATなどのAWSリソースをむやみやたらに増やすとそれはインフラの料金の増加に直結するということを
この対応で相談にのっていただいたインフラの方にご指摘いただきました。(当たり前ではありますが、、、)
既存のものと違うIP制限をしたいなど特別な要件がないのであれば、既にあるリソースを利用するように意識するのも大切だなと思いました。

まとめ

AWS LambdaをSAM(Serverless Application Model)を用いて、IaaSでデプロイしました。
今までなんとなく使っていたSAMとその周辺知識を整理しました。
システム連携でLambdaをデプロイしたときの実際のエラー例とその対応内容を説明しました。
適切なIAMポリシーの管理、samconfig.tomlの活用、エラー発生時のデバッグ方法を意識することで、効率的なデプロイが可能になります。
アプリケーションエンジニアだけどインフラにも詳しくなりたい人や自分もSAMでLambdaをデプロイしてみたい!って人の一助になれたら嬉しいです!

NE株式会社の開発ブログ

Discussion