こんにちは。BASE BANK株式会社 Dev Division にて、 Software Developer をしている永野(@glassmonekey)です。
弊社ではAWS Lambdaを活用する機会が増えまして、 最近メジャーアップデートのあった「AWS SAM CLI」を使ってリリースフローの改善にチャレンジしてみました。
そこで、samコマンドで作成したサンプルプロジェクトをローカルで実行しデプロイする方法を紹介します。それに加えて、現状BASE BANKチームで行っている代表的な運用設定をご紹介します。
今回記事作成に際して、サンプルプログラムを用意しているのでもしよければ手元でご確認ください。
なお、今回LambdaにはGoを採用しました。検証に使用した環境は以下の通りです。
macOS: 10.15.x (Catalina) SAM CLI: version 1.2.0 AWS CLI: aws-cli/2.0.50 Python/3.7.4 Darwin/19.6.0 exe/x86_64 Go: go1.15.2 darwin/amd64
SAM CLIとは
SAMとはServerless Application Model
の略称で、
AWS Lambda
のデプロイ管理に使われるツールで、ローカル上のデバッグ
、ビルド
、デプロイ
をサポートします。
公式のアナウンスによると7月にバージョン1
がリリースされました。
画像はAWS SAM Local(ベータ版) – サーバーレスアプリケーションをローカルに構築してテストするより引用しました。
またこの愛らしいリスのキャラクターは公式の説明によると
"SAM the Squirrel (リスの SAM)" について SAM the Squirrel は、サーバーレスアプリケーションで使用されるリソースを定義するモデルで、AWS Serverless Application Model (AWS SAM) から名付けられました。SAM は、AWS ユーザーがサーバーレスアプリケーションを効果的かつ簡単に構築できるよう、ツリー (木) の中で居心地のよい生活を後にしました。
とのことで、なんだか愛着が湧いてきますね。
環境構築
samコマンドを実行するために必要な設定をしておきます。
1. AWS SAM CLIのインストール
最初にAWS SAM
を動かすためのCLIをインストールします。
macOSの場合は公式に紹介されている通り、Homebrewを使って簡単にインストールすることができます。
$ brew tap aws/tap $ brew install aws-sam-cli
または、公式で提供されているpipパッケージを利用する方法もあります。 CI環境などでbrewを入れたくない場合はこちらがおすすめです。
$ pip install aws-sam-cli
sam --version
でバージョンが返ってきたらインストール成功です。
$ sam --version SAM CLI, version 1.2.0
2. AWS CLIのインストール
直接SAMの実行には関係ありませんが、動作確認などで必要になってくるのでこちらも公式の方法に従って入れておきます。
$ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" $ sudo installer -pkg AWSCLIV2.pkg -target /
こちらもaws --version
で結果が返ってきたらインストール完了です。
$ aws --version aws-cli/2.0.50 Python/3.7.4 Darwin/19.6.0 exe/x86_64
3. sam実行ユーザーの作成
AWSコンソールなどからsamコマンドを実行するユーザーを追加しておきます。
必要なポリシーなどは後ほど解説するので、この段階ではAdministratorAccess
を設定しておきます。
また、cliからも触れるように設定しておきます。
aws configure
4. dockerをインストールしておく
ローカル実行を行う場合のみ、docker daemonが動いてないといけないのでインストールをしておいてください。 docker for mac はこちらからインストールできます。
SAMを使った開発入門
まず最初に公式が提供してくれるHello, Worldなapiをsamを使ってデプロイすることをためしてみます。
1. プロジェクトを作成する
sam init
を行うとsam用のプロジェクトの初期化を行うことができます。今回はサンプルプログラムを流用するので対話型の設定を行いますが、既にディレクトリ構成などが完成している場合は不要なフローです。
sam init
今回は入門用のAPIをぱぱっと作るので1を選びます。
Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location
Goを使うので4を選びます。PHPも選択肢に来ないかなー
Which runtime would you like to use? 1 - nodejs12.x 2 - python3.8 3 - ruby2.7 4 - go1.x 5 - java11 6 - dotnetcore3.1 7 - nodejs10.x 8 - python3.7 9 - python3.6 10 - python2.7 11 - ruby2.5 12 - java8.al2 13 - java8
プロジェクト名はデフォルトのsam-app
のままで行います。
Project name [sam-app]:
今回は単純なapiを作成するので1を選びます。
AWS quick start application templates: 1 - Hello World Example 2 - Step Functions Sample App (Stock Trader)
すると
プロジェクトテンプレートもデフォルトのhttps://github.com/aws/aws-sam-cli-app-templates
を使用した形でプロジェクトが初期化されています。
├── Makefile <-- Make to automate build ├── README.md <-- This instructions file ├── hello-world <-- Source code for a lambda function │ ├── main.go <-- Lambda function code │ └── main_test.go <-- Unit tests └── template.yaml
AWS SAM
としてLambdaの構成管理しているのはtemplate.yml
になります。
これはCloud Formationのラッパーとなっており、基本的な文法はCloud Formation
に準拠します。
また、内容に関しての詳細はSAMのドキュメントやCloud Formationのテンプレートリファレンス を一読することをおすすめします。
2. ローカルで実行してみる。
2.1 ビルド実行
実行の前にビルドをしてみます。
$ sam build
実行後に下記のようにbundle含めてデプロイ用のファイル群が作成される。
├── .aws-sam │ └── build │ ├── HelloWorldFunction │ │ └── hello-world(バイナリ) │ └── template.yaml
生成内容の設定に関してはtemplate.ymlに記述されている
CodeUri: hello-world/ Handler: hello-world
と対応しているので、生成したいhandler名やソースディレクトリを変更したい場合はここを変更します。
2.2 ローカル実行
次にローカルで実行を行うsam local invoke
を実行します。
するとAWSリソースを介さずにローカルで完結して結果が返ってくるはずです。
$ sam local invoke {"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"Hello, $実行環境IPアドレス\n"}
また -eオプションをつけることでイベント情報のjsonを渡すことも可能です。
特にAPIコール以外(SQS経由など)で呼び出されるLambdaの場合だとデバッグ用のイベントを都度作成するのも手間だったりするので、イベント情報も極力Git管理しておくことをおすすめします。
副次的効果として、チーム内にどのようなイベントのやりとりが発生するかの共有にもつながります。
$ sam local invoke -e path/to/event.json`
なお、雛形はsam local generate-event $リソース名 $API名
で作成することができます。
例えば、SQSのreceiveMessageのイベントのJsonは下記のような形式となります。
$ sam local generate-event sqs receive-message { "Records": [ { "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", "receiptHandle": "MessageReceiptHandle", "body": "Hello from SQS!", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1523232000000", "SenderId": "123456789012", "ApproximateFirstReceiveTimestamp": "1523232000001" }, "messageAttributes": {}, "md5OfBody": "7b270e59b47ff90a553787216d55d91d", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", "awsRegion": "us-east-1" } ] }
3. デプロイしてみる。
3.1 デプロイファイルの作成
初回デプロイの場合は--guided
オプションを使用して対話形式でデプロイ設定ファイルsamconfig.toml
を作ります。
$ sam deploy --guided Stack Name [sam-app]: AWS Region [us-east-1]: ap-northeast-1 #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: n #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: Y HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y Save arguments to samconfig.toml [Y/n]: y
各項目に関してですが
- Stack Name … 内部的に使用するcloud formationのstack名。samを実行するユーザーはこのstack名に関しての権限が不足しているとデプロイに失敗するので、必要なので注意が必要です。最低限以下の権限が必要なことは確認しています。
"cloudformation:DescribeStackEvents", "cloudformation:CreateChangeSet", "cloudformation:DescribeChangeSet", "cloudformation:ExecuteChangeSet", "cloudformation:GetTemplateSummary", "cloudformation:DescribeStacks",
Confirm changes before deploy: deploy前に確認を必要とするか。CI上で動かす想定なら
n
にしておくと良いです。Allow SAM CLI IAM role creation: デプロイするlambdaに付与するロールを自動作成するか。自動作成する場合は余計な権限もつくことがあるので要注意です。
Save arguments to samconfig.toml:
y
ならこの対話型操作の設定内容を保存します。
すると以下のようなファイルが生成されます。次回以降のsam deploy
実行時はこの設定ファイルを見てくれるので、必要に応じて設定を書き換えると良いでしょう。
version = 0.1 [default] [default.deploy] [default.deploy.parameters] stack_name = "sam-app" s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-6qa86tkchc0g" s3_prefix = "sam-app" region = "ap-northeast-1" capabilities = "CAPABILITY_IAM"
なお、capabilities
で指定できる値は以下のいずれかを設定することができます。
- CAPABILITY_IAM
- CAPABILITY_NAMED_IAM
- CAPABILITY_RESOURCE_POLICY
- CAPABILITY_AUTO_EXPAND
詳細はAWS Serverless Application Repository開発者ガイドに記載されていますが、template.yml
管理下のリソースの複雑さで設定の切り替えが必要なようです。Iamロールをtemplate.ymlで管理する場合などのケースでは要注意です。
今回はシンプルにlambdaのみのリソース管理化なのでデフォルトのCAPABILITY_IAM
で良いようです。
3.2デプロイの実行
samconfig.toml
が存在していればsam deploy
でデプロイされるはずです。
無事にデプロイされました 👍
lambdaのコンソールを見に行くとAPI Gatewayの項目からエンドポイントが確認できるはずです。
確認できたエンドポイントを仮にhttps://hogehoge.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
とすると結果が返ってくれば成功です。
$ curl https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"Hello, $実行環境IPアドレス\n"}
エンドポイントにcurlを投げてみるとローカル実行と同じようなレスポンスが返ってくるはずです。
しかし、ローカル実行時とは内部的にはネットワーク構成が異なっているので返ってくるIPは違います。
その他設定項目について
次に現状BASE BANKチームで行っている運用設定の一部をご紹介します。
環境ごとの設定の切り替え
リソースのarnの切り替えなどを環境ごとに切り替えたい設定を外部から注入できるようにします。
今回はシンプルにAPP_NAME
とENV
を環境変数経由で受け取り表示するように書き換えてみます。
name := os.Getenv("APP_NAME") env := os.Getenv("ENV") return events.APIGatewayProxyResponse{ Body: fmt.Sprintf("%s@%s", name, env), StatusCode: 200, }, nil
この環境変数を受け取るために、以下のようにトップレベルにParameters
を追加し、Functionリソース配下にEnvironment.Variables
を追加します。
ここの意味としてはtemplateがパラメータとして受け取った値は環境変数としてFunctionリソースに受け渡すという形になります。!Ref
はCloud Forrmationの関数で、セクション間の参照を表します。
Parameters: ENV: Type: String AppName: Type: String Resources: HelloWorldFunction: ~~~ 省略 ~~~~ Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object Variables: ENV: !Ref ENV APP_NAME: !Ref AppName
このようにしておくと実行時にKey=Value
の形式でパラメータを渡すことができます。
$ sam local invoke \ --parameter-overrides 'ENV=local AppName=Hello'
$ sam deploy \ --parameter-overrides 'ENV=dev AppName=Hello'
値を渡す方法は他にもあったりはしますが、ローカル実行とdeploy時の対照が取れているのところがおすすめなポイントです。
若干ハマったポイントとしてパラメータキーには_
などの記号を含むパラメータは読み込んでくれないようでAPP_NAME
ではなくAppName
としています。
環境ごとのデプロイ設定
特にCI上ので実行を加味した場合、環境変数の注入用のparameter-overrides
の他にも設定しておきたいパラメータがあります。
$ sam deploy \ --s3-bucket dev-sam-stacks \ --parameter-overrides 'ENV=dev AppName=Hello' \ --force-upload \ --no-fail-on-empty-changeset
s3-bucket
: cloud formationのアップロード先のバケット。基本的に環境ごとで、切り替えることになる思うのでsamconfig.toml
から記述を消し、パラメータで受け取れるようにしておきます。- force-upload: ソースコードの変更を伴わないLambdaの設定値のみの変更の際に、正しくデプロイされないケースがあったのでフラグを追加しておきます。
- no-fail-on-empty-changeset: 内容の変更がなくてもエラーにならないフラグです。CIで動かす場合を考えると、変更がないときも正常系と考えて良いはずなのでこのフラグを立てておきます。
Makefileの用意
以上を踏まえて環境ごとの設定に切り替えはあらかじめMakefile内に記述しておきます。
Makefileの用意を用意しておくと、チーム内にコマンドの共有ができるのと、CI 上で何かあったときに手元での検証がしやすくなるので便利です。
.PHONY: build test local-run local-up dev-deploy build: sam build # テストの実行 test: @cd ./hello-world/ && \ go test -v ./... # ローカルでlambdaを実行する local-run: build sam local invoke \ --parameter-overrides 'ENV=local AppName=Hello' # apiとして起動する場合 local-up: build sam local start-api \ --parameter-overrides 'ENV=local AppName=Hello' # デプロイする dev-deploy: build sam deploy \ --s3-bucket sam-app-stack \ --parameter-overrides 'ENV=dev AppName=Hello' \ --force-upload \ --no-fail-on-empty-changeset
まとめ
AWS SAM
を使った入門と運用設定の一部を紹介しました。
もしこれからAWS SAM
を使用される方々の助けになれれば幸いです。
従来の都度deployして確かめる方法だとどうしても1回の検証に時間がかかってしまい、Lambdaに手を出しづらい状況下だったので、今後の量産体制に無事に繋げられそうです。バージョン1
になったことにより大分扱いやすくなった印象をうけました。
また、今回は時間の関係で導入まではできてないのですがlocal stackを使えばAWSリソース依存のテストもできそうなので、機会があればこちらも挑戦してみたいと思います。