SAMデプロイで-tオプションを使う時の注意点

Pocket

こんにちは、DX推進室の安齋です。

今回はAWSサーバーレスリソース構築でハマった・工夫した箇所の紹介の最後となります。

第6回はSAMデプロイ時に -t オプションを付ける際の注意点をご紹介します。SAMを使用すると、同一テンプレートでローカル環境とAWS上にAPI GatewayやLambdaを構築することができます。ローカル環境の場合は模造されたAWSリソースが構築され、ローカル環境のみで動作します。

今回、-t オプションを付けてデプロイをした時に、AWS上のLambdaが使用するソースコードのバイナリファイルが存在しなくなる事象が発生したため、同じような事象が発生して困った人がいた時のために注意点をまとめます。

今回のテーマ

serverless-6-1

上記構成図の赤枠内の管理者用API GatewayとLambda、一般用API GatewayとLambda、バッチ用LambdaをSAMビルドとデプロイをしてAWS上に構築します。ビルドとデプロイはCodeBuildにて行います。

ファイルの階層構造

SAMテンプレートを含むファイル構造は以下の通りです。

管理者用と一般用のAPI GatewayとLambdaはVPC上に構築するため、同一のSAMテンプレート(template_sam_vpc.yaml)に記述し、バッチ用LambdaはVPC外に構築するため、別のSAMテンプレート(template_sam_nonvpc.yaml)に記述します。

両方のSAMテンプレートはソースコードやビルドとデプロイ内容を定義する buildspec.yml と一緒に格納します。src/ 配下はLambdaのソースコードを配置します。

.
├─buildspec.yml
├─template_sam_vpc.yaml
├─template_sam_nonvpc.yaml
└─src
    └─get_tech4all # 管理者用APIのLambdaソースコード
        └─main.go
    └─post_tech4all # 一般用APIのLambdaソースコード
        └─main.go
    └─batch_tech4all # バッチ用のLambdaソースコード
        └─main.go

当初のビルドとデプロイの内容

ビルドコマンドは sam build で、-t オプションでテンプレートファイルを指定することができます。デプロイコマンドは sam deploy で、同様に -t オプションでテンプレートファイルを指定することができます。

今回はテンプレートが2つあるため、-t オプションを付けてビルドとデプロイをしていました。

以下、buildspec.yml です。

version: 0.2

phases:
  install:
    runtime-versions:
      golang: 1.14
  pre_build:
    commands:      
      - s3_bucket=$BUCKET_NAME
  build:
    commands:
      - echo "=====sam on vpc build & deploy start====="
      - app_name="${SERVICE_NAME}-${ENV}-sam-vpc"
      - sam build -t template_sam_vpc.yaml
      - sam deploy --stack-name $app_name --s3-bucket $s3_bucket --region $REGION --no-fail-on-empty-changeset --parameter-overrides Env=$ENV -t template_sam_vpc.yaml
      - echo "=====sam on vpc build & deploy end====="
      - echo "=====sam on nonvpc build & deploy start====="
      - app_name="${SERVICE_NAME}-${ENV}-sam-nonvpc"
      - sam build -t template_sam_nonvpc.yaml
      - sam deploy --stack-name $app_name --s3-bucket $s3_bucket --region $REGION --no-fail-on-empty-changeset --parameter-overrides Env=$ENV -t template_sam_nonvpc.yaml
      - echo "=====sam on nonvpc build & deploy end====="

何が起こったか

ビルドもデプロイもエラーが出ず、成功しているように見えましたが、それぞれのLambdaを呼ぶと以下のエラーが出ました。例として get_tech4all を上げます。

fork/exec /var/task/get_tech4all: no such file or directory

調べてみると、Go言語のビルドしたバイナリがLambdaの使用するディレクトリに存在しないことが判明しました。当初はビルドに失敗しているのかと思いましたが、ローカル環境においてビルド後の sam local コマンドでAPI GatewayとLambdaを起動することができたため、ビルドには成功していると確信が持てました。また、AWS上のLambdaも構築されており、デプロイも成功しているように思えました。ただ、AWS Lambdaのコンソール上ではGo言語のテキストエディターが提供されていないため、テキストでデプロイされているソースコードの実体は見れない状況でした。

ローカル環境とAWS上のLambdaの相違点

調べていくうちに、ビルド後のローカル環境のバイナリとAWS上のLambdaのサイズが大きく異なっていることが判明しました。ローカル環境でビルドをすると、.aws-sam ディレクトリが出来上がり、ローカル環境のファイル構造は以下の通りになります。

.
├─.aws-sam
    └─build
        └─BatchTech4all
            └─batch_tech4all
        └─template.yaml
    └─build.toml    
├─buildspec.yml
├─template_sam_vpc.yaml
├─template_sam_nonvpc.yaml
└─src
    └─get_tech4all # 管理者用APIのLambdaソースコード
        └─main.go
    └─post_tech4all # 一般用APIのLambdaソースコード
        └─main.go
    └─batch_tech4all # バッチ用のLambdaソースコード
        └─main.go

バイナリの .aws-sam/build/BatchTech4all/batch_tech4all のサイズがローカル環境だと1.2MB程あるのですが、AWS上のLambdaだと3KB程しかありませんでした。また、先述のAWS Lambdaのコンソール上からソースコードが見られるようなテキストでデプロイされていることも気になりました。

何が原因だったのか

原因はデプロイにあると確信し、sam deploy ドキュメントを読むと以下の通り -t オプションに関する記述がありました。

注意: このオプションを指定すると、AWS SAMは、テンプレートとそれが指すローカルリソースのみをデプロイします。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html

要するに、-t オプションを付けてデプロイをすると、所謂「側」だけがデプロイされ、ビルド後のバイナリはデプロイされない状態になります。

こうして改善した

デプロイに -t オプションを付けてはいけないことが判明したため、付随してビルドも -t オプションを付けずに実施しました。-t オプションが無い場合、両コマンドは template.yaml をそれぞれビルド、デプロイする挙動になります。

以下、改善後の buildspec.yml です。2つのテンプレートをリネームしてからそれぞれビルドとデプロイをしています。

version: 0.2

phases:
  install:
    runtime-versions:
      golang: 1.14
  pre_build:
    commands:      
      - s3_bucket=$BUCKET_NAME
  build:
    commands:
      - echo "=====sam on vpc build & deploy start====="
      - mv template_sam_vpc.yaml template.yaml # ★★★新規追加★★★
      - app_name="${SERVICE_NAME}-${ENV}-sam-vpc"
      - sam build # ★★★変更有★★★
      - sam deploy --stack-name $app_name --s3-bucket $s3_bucket --region $REGION --no-fail-on-empty-changeset --parameter-overrides Env=$ENV # ★★★変更有★★★
      - mv template.yaml template_sam_vpc.yaml # ★★★新規追加★★★
      - echo "=====sam on vpc build & deploy end====="
      - echo "=====sam on nonvpc build & deploy start====="
      - mv template_sam_nonvpc.yaml template.yaml # ★★★新規追加★★★
      - app_name="${SERVICE_NAME}-${ENV}-sam-nonvpc"
      - sam build # ★★★変更有★★★
      - sam deploy --stack-name $app_name --s3-bucket $s3_bucket --region $REGION --no-fail-on-empty-changeset --parameter-overrides Env=$ENV # ★★★変更有★★★
      - mv template.yaml template_sam_nonvpc.yaml # ★★★新規追加★★★
      - echo "=====sam on nonvpc build & deploy end====="

まとめ

エラーが出たときは、AWS上では別のビルド方法が必要になるのかと思い、Go言語のネイティブなビルド方法調べては失敗する試行錯誤をしていました。しかし、SAMは言語ごとのビルドをラップしている作りなっているはず、でないと同一テンプレートでローカル環境とAWS上のリソースを構築できる強みが無くなる、と思い返し、ドキュメントを良く読み返して解決に至りました。今回はビルドとデプロイで同じオプションでも効果が違う罠があり、大変勉強になりました。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です