12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SAM初心者がSAMを使ってみて便利だと思った点

Posted at

はじめに

本記事は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

を記述することで使うことができます。

このテンプレートでは以下の構成図のインフラを作成しています。
構成図.png
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が作成されています。

CFn.png

このように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などは実際に使ってみないとその良さがわからないので、ぜひテンプレートで遊んでみてください。

12
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?