AWS Serverless Application Model (SAM) のすゝめ

# はじめに
内部の業務改善の一環として、ちょっとしたWEBアプリを作りました。
その際にSAMを利用したのですが、バックエンド構築を爆速を実施でき、感動したので、この記事ではSAMやらIaCの事を書こうかと思います。

# システム構成とモチベーション
超簡略のシステム図はこのような感じ。
![Screen Shot 2021-06-21 at 17.58.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290494/19f2b604-daf8-ac6f-360b-11d3134ae392.png)

#### フロント
> Next.jsのSSG (Static Site Generator ) によるStaticコンテンツ生成 + S3ホスト 
> （※）社内ホストなのでCloudFrontといったCDN連携は無し

#### バックエンド
> SAM（API Gateway & Lambda）

#### DB
> Aurora Serverless v1
> （※） v2 はまだPreview。。


本システムのモチベーションは**ALLサーバレスで構築！**です

何故サーバレスにこだわるのかって？ インフラのメンテしなくていい！サーバレスだと従量課金なので安い！ビジネスロジック開発に集中できる！ これに尽きるかと思います。
（ 深夜にメンテするなんて嫌なのです。夜にわちゃわちゃ作業なんてしたくないのです。。用意されているものは使い倒していきましょう）

ところで、AWSでサーバレス！と言ったら先ず思い浮かぶのはLambdaではないでしょうか？
しかし、マネジメントコンソールでLambda開発していては諸々不便です。
マネジメントコンソール上からLambda叩いて問題なさそうならAPI Gateway を接続して〜〜なんてやっていたらめちゃ時間がかかります。（そして事故る匂いがプンプンします）
そして何より、フロント・バックエンド・DBでコーディング期間は3週間。。納期は何としてでも間に合わせなければなりません
そんな時に私は思いました「せや、SAM使ってローカル開発したらええやんけ。デプロイも楽やし」

# そもそもSAMって？
SAMは Serverless Application Model の略称で、その名の如く**サーバレス（Lambda、API Gateway、DynamoDB...etc）**に特化した開発フレームワークです。
ガリガリ開発している中で、個人的にSAMの導入でうれしかったこと3点を上げてみました。

**SAM導入で嬉しいことTop3**
> 1. ローカルで実環境相当のAPI Gateway ⇔ Lambda の挙動がエミュレートができる
> 2. テンプレート化できる（CloudFormationの拡張、という位置付け）
> 3. デプロイが楽 （sam deploy コマンドで一撃）

#### 1.ローカルで実環境相当のAPI Gateway ⇔ Lambda の挙動がエミュレートができる
もうこれだけでも導入の価値はありますよね。
SAMが裏でDockerコンテナを立ち上げて環境をエミュレートしてくれるので、バグやらをある程度手元でシューティングしてからAWS環境へリフトできます。
マネジメントコンソール上で0からポチポチデバッグは辛すぎます。トレースも辛い。。
ローカルで動かしてみるとこんな感じ。
![Screen Shot 2021-06-21 at 19.59.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290494/34fa7f92-5e51-0d38-734d-28d325e3da02.png)
きちんとエミュレートされてますね！良き

#### 2.テンプレート化できる
いわゆるIaC ( Infrastructure as Code ) というやつですね。
テンプレートファイルにLambda関数の設定やランタイムの設定、実行トリガー など諸々の情報を記載することができます。
SAMの裏でCloudFormationスタックが立ち上がるので、あれ？今どのバージョンがデプロイされているんだっけ？ みたいな事が減ります。
また、別スタックとしてデプロイしてあげることで、同じ環境をポカンと立ち上げることができます。間違えてバルスしてしまっても安心ですね。素敵
![Screen Shot 2021-06-21 at 19.43.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290494/d3febf57-963a-eb90-eabc-e4a5fa0e8ed1.png)
sam init してもろもろ入力してあげると右のようなテンプレートyamlが出来上がる。
よくよく眺めてみると ネイティブのCloudFormationと若干違うけど、直感的で見やすいですね
あとはこのテンプレートに関数を追加したり認証を追加したりして拡張していきましょう。
（samビルドすると、samテンプレートからCFnテンプレートへのコンバート処理が走るらしい）

#### 3.デプロイが楽
手動だと、sam deploy コマンド一発で現行リソースとの差分検知、失敗したらロールバックまでできます。
ちなみにCodeCommit、CodePipelineと連携してあげると、mainブランチにコミットが入ったら自動ビルド&自動デプロイ なんて事もできます。
sam deploy、mainブランチコミットしたら、コーヒーでも飲んでボーッとしている間にデプロイが終わっています。いい時代になったもんです。。
***2.テンプレート化できる***で作った SAMテンプレートをデプロイしてあげると、、
![Screen Shot 2021-06-21 at 20.13.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290494/0c8c5908-8b7f-7030-b491-7a4f33a9b1a2.png)
CloudFormationスタックが立ち上がり、、
![Screen Shot 2021-06-23 at 13.32.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290494/f8e4ef7c-7024-e196-2eb0-99c545eb477a.png)
API Gateway ⇔ Lambda の部分もちゃんとデプロイされていますね！いやぁ、これは楽だ。。


（※）SAMの更に深い情報はAWSサービスカットの資料をご覧ください
https://d1.awsstatic.com/webinars/jp/pdf/services/20190814_AWS-Blackbelt_SAM_rev.pdf


# まとめ
SAMでのIaC導入は少し学習コストがあるのですが、yamlテンプレートのお作法さえ理解してしまえば、
爆速で開発ができる、環境デプロイ時に動かない、バージョン管理が破綻した   のような本質的ではない苦行から開放されるので、とても良いなあと思いました。
そして、どれだけ打鍵しようがローカル環境なので無料です。お財布に優しい！
AWSでサーバレス構成でシステム作る！という時は選択肢の一つとして検討しても良いかもしれません
（CloudFormationでもLambdaやらAPI Gatewayはデプロイできるっちゃあできるのですが、S3にソースコードのZipをアップしないといけないので、若干手間。。）

# おまけ
SAMテンプレートはCFnテンプレートの拡張なので、IAM Roleといったリソースを定義しても sam deploy でのデプロイ対象となります。
今回作ったシステムではXrayトレースをONにして環境構築していたので、RoleやらLayerをまとめてビルドしていました。
yamlファイルを少し書き換えるだけで様々なことが自動化できるなんて、IaC素晴らしいですね、目指せヤムラー
そしてX-ray、かなり細かくトレースできていて見ていて楽しい。。
![Screen Shot 2021-06-21 at 21.19.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290494/40877438-89d6-590c-2185-834d504cbd26.png)

yamlの抜粋版はこんな感じ
``Tracing: Active``の部分でX-Rayトレースを有効化しています。

```yaml
Resources:
 LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      RoleName: 'LambdaSAMRole'
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole'
        - 'arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess'
 
  FunctionLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      Description: Layer description
      ContentUri: 'FunctionLayer/'
      CompatibleRuntimes:
        - python3.8
    Metadata:
      BuildMethod: python3.8
 
  LoginFunction:
    Type: AWS::Serverless::Function
    Properties:
      Role: !GetAtt LambdaRole.Arn
      CodeUri: LoginFunction/
      Handler: app.lambda_handler
      Runtime: python3.8
      Tracing: Active
      Layers:
        - !Ref FunctionLayer
      Events:
        login:
          Type: Api
          Properties:
            RestApiId: !Ref RestApi
            Path: /api/login
            Method: POST
```