NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    AWS SAM 入門!! SAMテンプレートを作成できるようになろう!!

    本記事は  IaCウィーク  11日目の記事です。
    ⚙️  10日目  â–¶â–¶ æœ¬è¨˜äº‹ â–¶â–¶ ðŸ’»

    はじめに

    こんにちは。入社2年目のインフラエンジニアの渡部です!

    本記事は、初学者の方が中級者になるための架け橋として、
    AWS SAM(※以下 SAM)とはそもそも何なのかという概要からSAMの基本的なコマンド、
    そして本記事の最終目的であるSAMテンプレートの作成方法についてお話したいと思います。

    ※本記事ではテンプレートの書き方にフォーカスしており、デプロイ手順などの内容は含まれていません。

    SAMの概要

    SAMというのは、イベント駆動の性質を持つサーバーレスアプリケーションのデプロイに特化した、AWS CloudFormationの拡張機能です。

    イベント駆動? AWS CloudFormation?

    よくわかりませんね。順番に説明していきます。

    イベント駆動とは、イベント(出来事)をきっかけに処理が開始される仕組みや設計思想のことをいいます。
    たとえば、アプリでユーザーがボタンをクリックすると、クリックというイベントを受けて処理が実行されます。 AWSでは、午前8時になると、その時刻をイベントとして検知し、自動的に関数を起動する仕組みもあります。これもイベント駆動型の一例です。

    AWS CloudFormationとは、テンプレートを使用して、AWSのリソースをプロビジョニングできるサービスです。
    こちらに関しては、私の同期である中村さんが詳しく解説していますので、以下のブログをご覧ください。

    tech.nri-net.com

    中村さんのブログでは、CloudFormationは、AWSのリソース構成をYAMLやJSON形式で記述することによって、テンプレートの定義を行い、それをもとにAWSが自動的にリソースを構築してくれる仕組みであると説明されています。

    AWS SAMとCloudFormationの違い

    中村さんのブログを見ていただくと、「CloudFormationもSAMもテンプレートで書くのならば、違いは何なのか。」という感想を抱く方もいらっしゃると思います。
    ここで、CloudFormationとSAMの違いを整理します。

    大きな違いは、CloudFormationはAWS全体のリソースをコードで管理するためのサービスですが、SAMはサーバーレスアプリケーション開発を簡単にするためのフレームワークだということです。
    コードで比較してみましょう。 例として、Lambda関数 + API Gateway + S3バケットを含む構成を書いたときのCloudFormationのテンプレートがこちらです。

    Resources:
      MyBucket:
        Type: AWS::S3::Bucket
        Properties:
          BucketName: my-app-bucket
    
      MyLambdaFunction:
        Type: AWS::Lambda::Function
        Properties:
          FunctionName: MyFunction
          Handler: index.handler
          Runtime: nodejs18.x
          Code:
            S3Bucket: my-code-bucket
            S3Key: lambda-code.zip
          Role: arn:aws:iam::xxxxxxxxxxxx:role/lambda-role
    
      MyApi:
        Type: AWS::ApiGateway::RestApi
        Properties:
          Name: MyApi
    
      MyApiResource:
        Type: AWS::ApiGateway::Resource
        Properties:
          ParentId: !GetAtt MyApi.RootResourceId
          PathPart: hello
          RestApiId: !Ref MyApi
    
      MyApiMethod:
        Type: AWS::ApiGateway::Method
        Properties:
          HttpMethod: GET
          ResourceId: !Ref MyApiResource
          RestApiId: !Ref MyApi
          AuthorizationType: NONE
          Integration:
            IntegrationHttpMethod: POST
            Type: AWS_PROXY
            Uri: !Sub >-
    

    そして、SAMテンプレートのコードがこちらになります。

    Transform: AWS::Serverless-2016-10-31
    Resources:
      MyBucket:
        Type: AWS::S3::Bucket
        Properties:
          BucketName: my-app-bucket
    
      MyFunction:
        Type: AWS::Serverless::Function
        Properties:
          Handler: index.handler
          Runtime: nodejs18.x
          CodeUri: ./src/
          Role: arn:aws:iam::xxxxxxxxxxxx:role/lambda-role
          Events:
            HelloApi:
              Type: Api
              Properties:
                Path: /hello
                Method: get
    

    同じ構成を書いたにも関わらず、CloudFormationは約40行、SAMは約20行となり、SAMであれば半分の行数で簡潔に書くことができます。
    また、CloudFormationではローカルでテストすることはできませんが、SAMではSAM CLIと呼ばれるコマンドラインツールを使用することで可能になります。
    他にも異なる点がありますので、表でまとめてみたいと思います。

    é …ç›® CloudFormation AWS SAM
    対象範囲 AWS全リソース サーバーレス関連リソースに特化
    構文 標準CloudFormation構文(JSON/YAML) SAM専用の簡略構文(YAML)
    ローカルテスト × ○(SAM CLIで可能)
    デプロイ補助 基本なし(AWS CLIやコンソールを利用) 簡単(sam deployで簡単に自動化)


    SAMの概要の説明が終わったところで、AWSリソースをSAMで構築することによる開発者にとってのメリットを説明します。

    開発者にとってのメリット

    1. Lambda、API Gateway、DynamoDB、S3など、サーバーレスサービスを簡潔に定義できる。

    2. CodePipelineやCodeBuildなどのCI/CDサービスとスムーズに連携できるため、継続的なデプロイ環境の構築が容易である。
      例えば、下記のような構成だと、GitHubへのpushをトリガーに、ビルドからデプロイまで自動で実行することができ、さらにCodeBuild が SAM CLI に対応しているため、追加で複雑な設定をする必要はなくSAMのコマンドを実行することができます。


    こちらのメリットにより、最終的に、開発サイクルが短縮され、新機能やサービスのリリースが迅速に行えるようになります。

    コマンドの概要

    ここでは、SAMテンプレートの作成に必要なコマンドについてお話をしていきたいと思います。

    コマンド1:sam init

    SAMテンプレートと必要なディレクトリを自動生成するコマンドです。
    コマンドをうつと、以下のメッセージが表示されます。

    Which template source would you like to use?
    1 - AWS Quick Start Templates
    2 - Custom Template Location

    sam initコマンド実行後、AWSが公式に提供するテンプレートを利用するか、自分でテンプレートを指定するのかについての質問です。

    Which runtime would you like to use?
    1 - python3.13
    2 - python3.12
    3 - python3.11
    4 - python3.10
    5 - python3.9
    6 - python3.8
    7 - nodejs22.x
    8 - nodejs20.x
    9 - nodejs18.x
    10 - nodejs16.x
    11 - java21
    12 - java17
    13 - java11
    14 - dotnet8
    15 - dotnet6
    16 - ruby3.4
    17 - ruby3.3
    18 - ruby3.2

    こちらは、Lambda関数のランタイムを何に設定するかについての質問です。

    Which package type would you like to use?
    1 - Zip
    2 - Image

    ソースコードと依存関係をZIPファイルにまとめてアップロードするのか、コンテナイメージとしてデプロイするのか、Lambda関数のデプロイ方式を選択する質問です。

    Project name [sam-app]:

    プロジェクト名に関する質問です。このままEnterを押すと、デフォルトで設定されている「sam-app」になります。
    この質問に回答すると、AWS SAM プロジェクトが作成されます。
    このAWS SAMプロジェクトの中心となる設定ファイルがtemplate.ymlという名前のSAMテンプレートとなります。

    コマンド2:sam validate

    SAMテンプレートの構文や基本的な設定が正しいかを検証するためのコマンドです。
    私が初めてSAMテンプレートを作成したときは、YAMLファイルの細かい文法エラーをすることが多く、 コマンド実行時には、エラーメッセージが表示されることが何回もあり、大変貴重な経験をさせていただきました。(笑)
    template.ymlに問題なければ下記のメッセージが表示されます。

    Template provided at template.yaml is valid SAM Template

    コマンド3:sam build

    SAMテンプレートに基づいてLambda関数やLayerのコードを依存関係込みでビルドし、デプロイ可能な形に整えるコマンドです。
    デフォルトでカレントディレクトリにあるtemplate.yml を読み込み、別の場所にテンプレートがある場合は、--template オプションでパスを指定する必要があります。
    template.yml ファイルが存在するディレクトリで実行すると、オプションなしでコマンドを実行することができるので、便利です。

    コマンド4:sam deploy --guided

    サーバーレスアプリケーションを 対話形式でデプロイするコマンドです。
    初回デプロイするときは「--guided」オプションをつけます。
    コマンドを実行すると下記について質問されます。

    1.デプロイするCloudFormationのスタック名をどのようにするのか

    Stack Name [sam-app]:

    2.デプロイ先のリージョン(東京リージョンにデプロイしたい場合はap-northeast-1)

    AWS Region [us-east-1]:

    3.デプロイ前に変更内容を確認するかどうか。

    Confirm changes before deploy [Y/n]:

    4.Lambdaなどに必要なIAMロールをSAM CLIに作成させるかどうか。

    Allow SAM CLI IAM role creation [Y/n]:

    5.入力した設定をsamconfig.tomlに保存するかどうか。

    Save arguments to samconfig.toml [Y/n]:

    5つ目の質問に対して、「Y」を入力した結果、samconfig.tomlというファイルが生成されるようになります。
    2回目以降はsam deploy コマンドを実行するだけで、samconfig.toml の設定が自動的に使用されるので、--guidedオプションを使用する必要はありません。

    SAMテンプレートの説明

    では、SAMの基本的コマンドについて説明したところで、下記3つのリソースをSAMテンプレートで作成したいと思います。

    1. S3バケットに格納されているファイルの中身を出力するLambda関数

    2. 簡単な足し算を行うLambda関数

    3. S3バケット

    ※Lambda 関数はコンテナイメージとしてデプロイします

    下記がtemplate.ymlになります。

    AWSTemplateFormatVersion: '2010-09-09'
    Transform: 'AWS::Serverless-2016-10-31'
    Resources:
      MyLambdaFunction:
        Type: AWS::Serverless::Function
        Properties:
          FunctionName: !Sub "${AWS::StackName}_printlambda"
          Policies:
            - S3ReadPolicy:
                BucketName: !Ref MyS3Bucket
          PackageType: Image
    
        Metadata:
          Dockerfile: Dockerfile
          DockerContext: ./printlambda
    
      MyS3Bucket:
        Type: AWS::S3::Bucket
        Properties:
          BucketName: !Sub "s3-watanabe-${AWS::AccountId}-${AWS::Region}"
          VersioningConfiguration:
            Status: Enabled
    • ec2-user/printlambda/app.py
    import json
    import boto3
    
    
    BUCKET_NAME = 's3-watanabe'
    OBJECT_KEY_NAME = 'hello.json'
    
    s3 = boto3.resource('s3')
    
    def lambda_handler(event, context):
        bucket = s3.Bucket(BUCKET_NAME)
        obj = bucket.Object(OBJECT_KEY_NAME)
    
        response = obj.get()    
        body = response['Body'].read()
    
        return json.loads(body.decode('utf-8'))

    また、ディレクトリ構成は以下のようになります。
    この場合、ec2-user/template.ymlに上記のSAMテンプレートを記載することになります。

    ec2-user
    |
    | ---------printlambda
    |                      |
    |                      |  ------Dockerfile
    |                      |  ------requirements.txt
    |                      |  ------app.py
    |
    |
    |---------------template.yml
    |---------------docker-compose.yml

    SAMテンプレートの補足説明1.「!Sub」

    Properties:
          FunctionName: !Sub "${AWS::StackName}_printlambda"

    の!Subとは何をしているかについて説明いたします。
    !Subというのは、入力文字列の変数を、指定した値に置き換えるFn::Sub関数でYAMLファイルを記述するときの短縮形となります。
    例えば、スタック名を「s3-watanabe」に設定すると、Lambda関数の名前は「s3-watanabe_printlambda」になります。

    SAMテンプレートの補足説明2.「Policies」

    Policies:
            - S3ReadPolicy:

    今回作成したLambda関数はS3バケットの中のファイルを読み取るため、s3の読み取り権限をこの箇所で付与しています。
    S3ReadPolicy以外にも様々なポリシーがありますので、詳しくは、こちらの公式ドキュメントをご覧ください。
    こちらのページでは、SAMで利用可能なポリシーテンプレートの一覧や使い方、記述例が詳しく紹介されています。
    実際に私もSAMでIAMポリシーについてどう書いたらよいかわからなくなったときは、参考にしています。

    SAMテンプレートの補足説明3.「PackageType」

     PackageType: Image

    の箇所は、サーバーレスアプリケーションをコンテナイメージとして構築するために宣言しています。
    この場合、コードと依存関係を含むDockerイメージを自分で定義するため、

    Metadata:
          Dockerfile: Dockerfile
          DockerContext: ./printlambda

    のようにDockerfileの定義が必要になります。

    AWS SAMで PackageType: Image を使うとき、SAM CLIは Dockerfile を使ってイメージをビルドします。
    つまり、Dockerfileがないと、SAMは何をどうビルドすればいいか分からないということになります。
    逆に、以下のようにPackageTypeがZip の場合は、SAMが自動でビルドしてくれるため、Dockerfileは不要です。

     PackageType: Zip 

    SAMテンプレートの作成方法のコツ

    私が尊敬している先輩から教えてもらったSAMテンプレートを効率的に作成するためのコツを紹介します。

    1. 私が作成したtemplate.ymlのように基本的なリソースについてテンプレートが記載されているものをWebで検索して雛形を作成する。
      たとえばEventBridgeについてSAMで記載したい場合、「AWS SAM EventBridge」などで調べると、検索結果にtemplate.ymlを記載したブログや記事が表示されやすいです。

    2. Lambda関数に追加したい設定があればAWSの公式ドキュメントをもとに書いてみる。

    最後に

    AWS SAMはサーバーレス開発を効率化する強力なツールですが、IaCの世界にはTerraformやCloudFormationなど、さまざまな選択肢があります。
    重要なのは、プロジェクトの要件やチームのスキルセットに最適なツールを選ぶことです。
    SAMがすべてのケースに万能というわけではありません。ぜひ、複数の選択肢を比較しながら、最適なアーキテクチャを設計してください。

    最後までお読みいただきありがとうございました。

    執筆者:渡部暉也 クラウドエンジニア