はじめに
本記事はANGEL Dojo参加者によるアドベントカレンダー23日目の記事です。
よかったら他の記事もチェックしてみてください!
今回はAWS SAM(以下SAM)初心者である私が、SAMを使ってみて便利だと感じた点を挙げたいと思います。私と同じようにSAMを使ったことがない、使ってみたい方を対象としており、初心者向けの内容となっています。
そもそもAWS SAMって?
SAMとは、Serverless Application Modelの略であり、AWS上でサーバーレスアーキテクチャを構築するためのIaCフレームワークです。
AWSインフラをコード化するには、AWS CloudFormationや、AWS CDK、Terraformあたりがよく使われています。SAMは前述のものとは異なり、サーバーレスアーキテクチャに特化したIaCフレームワークとなります。
AWSでサーバーレスアーキテクチャを構築する際は、このSAMを使うことで開発スピードが格段に上がります。
今回はANGEL Dojoで実際にSAMを使って見て便利だと思った点を5点紹介します。
SAMの使い方
まず簡単にですがSAMの使い方です。
インフラのデプロイやLambda関数のテストなどはSAM CLIというコマンドラインで行います。そのSAM CLIをインストールする必要があります。またAWSリソースをデプロイするので、デプロイのための権限も準備してあげる必要があります。
詳しくは以下を参考にしてみてください。
SAM CLIの一つにsam init
コマンドがあります。このコマンドはAWS側が用意してくれているSAMテンプレートを呼び出して、ディレクトリの雛形を作成することができます。
今回はpython3.12で、パッケージマネージャーとしてpipを指定して、hello-worldテンプレートを使います。
sam init \
--app-template hello-world \
--dependency-manager pip \
--name my-sam-app \
--runtime python3.12 \
--no-interactive
このコマンドを実行すると以下のようなディレクトリとファイルが作成されます。
.
└── my-sam-app
├── README.md
├── __init__.py
├── events
│ └── event.json
├── hello_world
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── samconfig.toml
├── template.yaml
└── tests
├── __init__.py
├── integration
│ ├── __init__.py
│ └── test_api_gateway.py
├── requirements.txt
└── unit
├── __init__.py
└── test_handler.py
AWSインフラを定義しているSAMテンプレートは、template.yaml
で、以下が作成されます
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
my-sam-app
Sample SAM Template for my-sam-app
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.12
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: get
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
SAMはCloudFormationの拡張となっており
Transform: AWS::Serverless-2016-10-31
を記述することで使うことができます。
このテンプレートでは以下の構成図のインフラを作成しています。
API Gatewayに/hello
へGETリクエストを送信し、Lambdaからhello-worldが返されるAPIを構築しています。
SAMテンプレートのファイル以外にも、テストで使用するイベントが入っているevents
ディレクトリやテスト用のtest
ディレクトリ、SAMの設定を入れておくsamconfig.toml
、Python特有の__init__.py
などが作成されています。
Lambdaのコードはhello_world
ディレクトリに存在し、テンプレートでは以下のようにディレクトリを指定します。
CodeUri: hello_world/
コードの詳細は適宜以下のGitHubを参照してください。
SAMを使ってみて便利だと感じた点
1. 簡略化された記述
少ない記述量
SAMを使うことで少ない記述量でインフラを定義することができます。
SAMはサーバーレスに特化しているので、サーバーレスアーキテクチャでよく使われるAWSサービスは簡単に書くことができます。
例えば、Lambda関数をCloudFormationで定義するには、
Type: AWS::Lambda::Function
を使用しますが、SAMでは
Type: AWS::Serverless::Function
を使用して、Lambdaを定義します。
これを使うことで、Properties
の一つのEvents
を使うことができます。
今回のようなAPI GatewayへのGETリクエストをトリガーとしてLambdaをキックしたい場合、CloudFormationではリソースを一対一で記述する必要があるので、API GatewayもAWS::ApiGateway::RestApi
で定義してあげる必要があります。
ですが、SAMのEvents
を使うことで、以下のようにAPI Gatewayを明示的に定義してあげる必要がありません。
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Lambdaを定義する箇所でトリガーとなるパスとメソッドを指定するだけでよいので、API Gatewayも記述する必要があるCloudFormationに比べて記述量が格段に少なくなります。
SAMテンプレートで使用できる
Type: AWS::Serverless::
は現在API GatewayやStep Functionsといった9サービスに対応しています。
Globalセクション
複数のLambda関数でタイムアウト値やランタイムバージョンなどを統一させたい場合は、Globalセクションを使うことができます。
Globals:
Function:
Timeout: 3
CloudFormationであればParameterを使用して、変数化する必要がありましたが、
SAMではGlobalセクションに設定値を指定するだけで、テンプレートに設定値を反映させることができます。
権限周り
Lambdaが使用する権限も簡単に書くことができます。
先ほど伝えた通りCloudFormationは一対一で記述する必要があるので、Lambda用のIAM Roleも定義する必要がありますが、SAMではRoleも自動で作成してくれます。
よく使う権限がポリシーテンプレートとしてまとまっているので、例えば、「DynamoDBの特定のテーブルに対するCRUD処理に必要な権限」も以下のように書くことができ、AWS APIを一つ一つ指定する必要がないのでとても便利です。
Policies:
DynamoDBCrudPolicy:
TableName: !Ref TableName
以上のように、SAMを使うことで記述量が格段に少なくなります。
2. ローカルでのテストのしやすさ
一般的な開発の流れは、開発→テスト→リリースだと思います。
少ない記述量で開発スピードを素早くするだけでなく、ローカルでのテストも簡単に実施することができます。
ローカルでのLambdaのテストしたい場合は、
sam local invoke
コマンドを使用します。イメージとしては、aws lambda invoke
コマンドのローカル版でしょうか。
このコマンドを実行するにはdockerが必要です。
Lambdaが実行時に受け取るeventは、API Gatewayからjsonで渡されるので、テスト用としてそのjsonを用意してあげるだけでローカルでも実行することができます。
sam init
で作成したテンプレートには、テスト用イベントがあるので、それを指定してテストしてみます。
sam local invoke HelloWorldFunction -e events/event.json
xxx@xxx my-sam-app % sam local invoke HelloWorldFunction -e events/event.json
Invoking app.lambda_handler (python3.12)
Local image is up-to-date
Using local image: public.ecr.aws/lambda/python:3.12-rapid-x86_64.
Mounting /Users/xxx/Desktop/blog/my-sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container
START RequestId: c67408da-3498-4cfd-98de-30730e4a2fdd Version: $LATEST
END RequestId: 31216613-8c6b-4a00-9170-d6d33dbe91a4
REPORT RequestId: 31216613-8c6b-4a00-9170-d6d33dbe91a4 Init Duration: 0.41 ms Duration: 284.04 ms Billed Duration: 285 ms Memory Size: 128 MB Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
dockerコンテナが起動され、無事hello world
が返されています。
またテスト用のeventを作成するためのコマンドもあるので、対象のアーキテクチャにあったイベントも簡単に作成することができます。
sam local generate-event
Lambda内でDynamoDBの処理を実行してる場合は、DynamoDB localを使うことで、ローカル環境でもDynamoDBを使うことができます。
3. デプロイも容易
テストが完了したあとはAWSへデプロイを行います。このデプロイ作業もSAMを使うことで簡単に行うことができます。
SAMを使わない場合は、まず外部ライブラリをpipでインストールして、同じZipパッケージに固めてからデプロイします。SAMを使うことで外部ライブラリのインストールからデプロイまで行ってくれます。
Lambdaの起動方法として、dockerを指定している場合はイメージのビルドを行ってくれます。
Lambdaで使用するコードが3MBを超えるとコンソールに表示されないので、不都合がある場合は適宜Lambda Layerの利用を検討してください
まずはデプロイ前の準備としてライブラリのインストールなどを行ってくれるコマンドを実行します。
sam build
実行が完了すると.aws-sam
ディレクトリが作成され、.aws-sam/build/HelloWorldFunction
に作成したLambdaコードと合わせて外部パッケージがインストールされます。
このあと、以下のコマンドでデプロイします。
sam deploy
Successfully created/updated stack - my-sam-app in us-east-1
上のようにコマンドが成功すると、正常にCloudFormation Stackが作成されており、Lambda関数やAPI Gatewayが作成されています。
このようにAWS環境への簡単にデプロイすることができます。
AWS環境にデプロイされたLambdaでのテストも以下のコマンドで可能です。
sam remote invoke HelloWorldFunction --stack-name my-sam-app
テンプレートのアウトプットからURLを参照してリクエストでもできます。
curl -X GET $(sam list endpoints --output json | jq -r '.[1].CloudEndpoint.[0]')/hello
4. サクッとAWSへデプロイ
開発しているときに、ちょっとコードを修正して、サクッとAWS環境で確認したいといったこともよくあると思います。
そのときには、sam sync
コマンドが素晴らしいです。
コマンドの通り、ローカルのコードとAWS環境を同期してくれるコマンドです。
このコマンド実行中はコードの変更を監視して、コードの修正ならコードをアップロードしてくれたり、インフラの変更ならインフラデプロイしてくれます。
sam sync
コマンドを実行後、Lambdaコードを修正すると、AWS環境にすぐにコードを反映してくれます。
Syncing Lambda Function HelloWorldFunction...
Manifest is not changed for (HelloWorldFunction), running incremental build
Building codeuri: /Users/xxx/Desktop/blog/my-sam-app/hello_world runtime: python3.12 architecture: x86_64
functions: HelloWorldFunction
Running PythonPipBuilder:CopySource
23/Sep/2024:14:46:07: Finished syncing Lambda Function HelloWorldFunction.
Lambdaのコード修正くらいであれば、体感10秒ほどで同期してくれます。
常にAWS環境にデプロイしながら開発を進めることができるので、サクッと確認したいときや開発初期段階での開発スピード爆上がり間違いなしです。
5. sam pipeline
個人だけの開発であればsam sync
でだけでもいいですが、複数人でのチーム開発になるとちゃんとCI/CDパイプラインを構築する方がよいです。
パイプラインファーストという考え方もあるので初期段階から構築しておきましょう。SAMにはCI/CDパイプラインを構築するためのコマンドもあります。
sam pipeline
長くなるのでここでは詳細は書きませんが、AWS Code系サービスやGHA、Jenkisなど主要なパイプライン環境に対応しています。
コマンド一つだけでCode系サービスをデプロイしてくれたり、buildspec.yaml
や/.github/workflows/pipeline.yaml
といったファイルも作成してくれるので、非常に便利です。
また本番環境と開発環境などをマルチアカウントで分割している場合でも、SAMの設定ファイルであるsamconfig.toml
の中でパラメータを出し分けることで対応可能です。
最後に
いかがでしたでしょうか。
ここに記載した内容は基本的な内容なので、ぜひ習得してほしいです。sam sync
などは実際に使ってみないとその良さがわからないので、ぜひテンプレートで遊んでみてください。