🚑

sam-cli-beta-cdkを試してみた

2021/05/09に公開

はじめに

ある日、TLをぼんやり眺めているとこんな記事が流れて来ました。

https://twitter.com/_kensh/status/1387972711855525892?s=20

初めてみたときは、AWS SAMに目がいってあまり気にしていませんでした。
よくみてみると、これからAWSを触っていくにあたって気になることが書かれていました。

アツい内容

何がすごいのかは、元記事 のこの文に詰まっています。

Before today, you could use the AWS SAM CLI to build locally, test, and package serverless applications defined using AWS CloudFormation or AWS SAM templates. With this preview release, you can use AWS SAM CLI to build, test, and in the future, package applications defined using the AWS CDK.

ざっくりいうとAWS CDKで構築したAWSのサーバーレスサービス群を、AWS SAMを利用してローカルで色々できるみたいです!
あと将来的にパッケージ化できるみたいです。

私は今まで、AWS CDKを利用していたのですが、AWS側にデプロイする用途でしか利用できず、ローカルでのテストや実行ができていませんでした。
そのほかの有名なパッケージ、terraformならローカルでのテストも確かできたと思うのですが、yaml形式で記述しなければならず、私は苦手でした。
加えて公式ではないため、(公式に匹敵するくらい有名・利用されているのは知っていますが)あまり利用に気が進みませんでした。

そんな時に、CDKで作成したサービス群をSAMを利用してローカルで実行ができる!これはアツいです!

試してみる

上記のページにもサンプルはありますが、自力でもろもろやってみようかと思います。

LambdaとAPI GatewayをCDKで作成して、SAMを通してローカル実行してみます。

準備・インストール・環境設定

このページ を参考に必要なパッケージを入れていきます。

必要なツールは以下の3つです。

私の環境・バージョンは次の通りです。

  • OS: macOS Big Sur (11.3), intel
  • AWS CLI: aws-cli/2.2.3 Python/3.9.5 Darwin/20.4.0 source/x86_64 prompt/off
  • AWS CDK: 1.102.0 (build a75d52f)

AWS CLIとCDKは既に入っていたので、AWS SAM CLI - betaのみbrewにて入れていきます。

$ brew install aws-sam-cli-beta-cdk
######################################################################## 100.0%
==> Pouring aws-sam-cli-beta-cdk-202104291816.sierra.bottle.tar.gz
🍺  /usr/local/Cellar/aws-sam-cli-beta-cdk/202104291816: 3,951 files, 82.4MB

$ sam-beta-cdk --version
SAM CLI, version 1.22.0.dev202104291816

これで全ての準備が整いました。

CDK Stackの作成

sam-beta-cdkのインストールが終わったら、CDKのStackを作成します。

ファイル構成は以下の通りです。
全てのファイルはこの記事には載せないので、詳細は以下のリポジトリを参考にしてください。

.
├── sam-example  # コマンド実行場所
│   ├── README.md
│   ├── cdk.json
│   └── index.ts
└── stacks
    ├── SamExample.ts # CDKでLambdaを記述
    ├── index.ts
    └── lambda
        └── sam_example # lambda functionを保持
            ├── lambda_function.py
            ├── requirements.txt
            └── utils.py

https://github.com/gsy0911/aws-cdk-ts-small-examples

実行するStack

今回は、LambdaとAPI Gatewayのサービスを利用しました。

  • Lambda関数名:LambdaFunction
  • API Gateway: GET /sample

特に理由はないですが、Lambda関数にS3パスを環境変数で持たせるようにしています。

SamExample.ts
import * as cdk from "@aws-cdk/core";
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apigw from "@aws-cdk/aws-apigateway";
import { PythonFunction } from '@aws-cdk/aws-lambda-python';

export interface ISamExample {
  S3Path: string
}


export class SamExample extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, params: ISamExample, props?: cdk.StackProps) {
    super(scope, id, props);

    /** lambda role */
    const role = new iam.Role(this, 'lambdaRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com')
    })
    role.addManagedPolicy(iam.ManagedPolicy.fromManagedPolicyArn(this, 'CloudWatchLogsAccess', 'arn:aws:iam::aws:policy/CloudWatchFullAccess'))

    /** note: when you use the stack, configure the entry path */
    const lambdaSimpleResponse = new PythonFunction(this, 'LambdaFunction', {
      functionName: "simple_response",
      entry: '../stacks/lambda/sam_example',
      index: 'lambda_function.py',
      handler: 'handler',
      runtime: lambda.Runtime.PYTHON_3_8,
      role: role,
      environment: {
        S3_PATH: params.S3Path
      }
    })

    /** API Gateway*/
    const api = new apigw.LambdaRestApi(this, 'sample_api', {
      handler: lambdaSimpleResponse,
      proxy: false
    });

    /** add GET method*/
    const items = api.root.addResource('sample');
    items.addMethod('GET');
  }
}

Lambda関数

S3のファイルを操作して何かしらの結果を返すLambdaを想定しています。
そのため、Stack作成時にLambda関数へ付与した環境変数(S3のパス)を返すようにしています。
response_wrapper()はAPI Gatewayで実行する際に、返り値を整形する用途で利用しています。

lambda_function.py
from utils import response_wrapper
import os


@response_wrapper()
def handler(event, _):
    return 200, {
        "status": "success", 
        "from": "SAM CLI", 
        "s3": f"{os.environ.get('S3_PATH')}/lambda/..."
    }

CDKのエントリポイント

上で作成したSamExample.tsindex.tsにてスタックとして作成します。
(同じディレクトリにある、cdk.jsonにてCDKのエントリポイントとして指定してあります)。

index.ts
import * as cdk from "@aws-cdk/core";
import {SamExample} from '../stacks';

const app = new cdk.App();
new SamExample(app, "SamExampleStack", {S3Path: "s3://sam-example"}, {description: "ts-example: for AWS SAM"});

app.synth();

これで、準備は整いました!実際に実行していきます!

ローカル実行(Lambda)

まずは、Lambdaのローカル実行です。
Lambdaを実行するコマンドは次のようになります。

# Invoke the function FUNCTION_IDENTIFIER declared in the stack STACK_NAME
$ sam-beta-cdk local invoke [OPTIONS] [STACK_NAME/FUNCTION_IDENTIFIER]

それでは、SamExampleStackに作成したLambdaFunctionを実行するコマンドを打ちます。
また、[OPTION]project-typeを指定します。

$ sam-beta-cdk local invoke --project-type CDK SamExampleStack/LambdaFunction
Synthesizing CDK App
Invoking lambda_function.handler (python3.8)
Failed to download a new amazon/aws-sam-cli-emulation-image-python3.8:rapid-1.22.0.dev202104291816 image. Invoking with the already downloaded image.
Mounting /Users/yoshiki/Development/Projects/aws-cdk-ts-small-examples/typescript/sam-example/.aws-sam/.cdk-out/asset.fd201469c9cd087ed2f5e86224bde9c4be5383f4ce718a425a0aa8b98db0f36a as /var/task:ro,delegated inside runtime container
END RequestId: 106b4af6-94a2-496a-906c-2632efe22f6d
REPORT RequestId: 106b4af6-94a2-496a-906c-2632efe22f6d  Init Duration: 0.14 ms	Duration: 96.61 ms	Billed Duration: 100 ms	Memory Size: 128 MB	Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"status\": \"success\", \"from\": \"SAM CLI\", \"s3\": \"s3://sam-example/lambda/...\"}"}

正しく実行できているみたいですね!

ローカル実行(API Gateway)

次に、API Gatewayのローカル実行です。
APIを実行するコマンド例は次です。

# Start all APIs declared in the AWS CDK application
$ sam-beta-cdk local start-api [OPTIONS]

[OPTION]--project-type CDKのみ加えて実行します。

$ sam-beta-cdk local start-api --project-type CDK
Mounting simple_response at http://127.0.0.1:3000/sample [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-05-09 14:32:02  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

3000番ポートで実行されるみたいです。
作成したAPIはGET /sampleなので別シェルからcurlを実行します。

$ curl localhost:3000/sample
{"status": "success", "from": "SAM CLI", "s3": "s3://sam-example/lambda/..."}

こちらも、問題なくいけました。

AWSへのデプロイ

最後にデプロイを試してみます。
今回は、CDKを利用しているので、わざわざSAMを介する必要はないのですが一応できるみたいなのでやってみました。
この機能は、おそらく今までSAMを利用していた人向けなのかなと思いました。

# SAMプロジェクトとしてビルド
$ sam-beta-cdk build --project-type CDK

# SAMでビルドしたものをデプロイ
$ cdk deploy -a .aws-sam/build

# SAMでビルドしたものをデストロイ
$ cdk destroy -a .aws-sam/build

おわりに

AWS CDK + AWS SAMの組み合わせで、サーバーレスの検証・デプロイは選択肢が増えましたね!
ここら付近は、terraformとか従来のSAMでも十分だったと思うのですが、何よりプログラミング言語でIaCできるのが強みかなと思います。
加えて、CDKとSAMはAWSが公式に提供しているツールなのでそこも安心できる点です。

将来的にアプリケーションをパッケージ化できるという点も面白そうです!
パッケージ化したサービスを買い切りで配布などもできるようになったら、Dockerとは別の選択肢にもなりえるのかなと思いました。

正式リリースが待ち遠しいですね!

脱線しますがAWS CDKは、そろそろv2.0系がリリースされそうなのでそちらも楽しみですね。

GitHubで編集を提案

Discussion