15
4

More than 3 years have passed since last update.

AWS CDKでAWS SAMのテンプレートを出力してデプロイする

Last updated at Posted at 2020-12-07

はじめに

これまではSAMのテンプレートをYMLでがりがり書いていましたけど、定義したいリソースが増えてくるとYMLファイルが長くなり、作業効率がよくありません。作業効率を向上させるために、AWS CDKを使ってテンプレートを書こうと思い、CDKからSAMテンプレートを出力し、デプロイできることを確認してみました。

環境について

本記事は下記環境で実行しています。
- AWS SAM CLI version 0.39.0
- AWS CDK 1.76.0 (build c207717)
- typescript Version 4.1.2
- VSCode バージョン 1.51.1
- Docker Desktop 2.1.0.5
- macOS Catalina バージョン 10.15.7

手順

それでは、CDKからSAMテンプレートを作成する手順です

CDK

CDKのプロジェクトを作成します

% mkdir adventcalander2020; cd adventcalander2020
% cdk init --language=typescript
  • cdk initを実行する時にディレクトリが空じゃないと怒られます

CDKのバージョンが古い時は最新版にしておきます

これはやってもやらなくてもよいですが、CDKは頻繁にバージョンがアップしますので、新しいプロジェクトの時は最新を使ったほうがよいと思います。最新にする時にはnpm-check-updatesを使っています。

% ncu -u
(出力は割愛)
% npm install
(出力は割愛)
% which cdk
./node_modules/.bin/cdk  <-- ローカルの.binが先になるようにパスをきっています
% cdk --version
1.76.0 (build c207717)  <-- この記事を書いている時の最新版

Lambda 関数を用意します

今回はAPIGatewayからのリクエストに対してレスポンスを返すLambda関数を用意しました。なので、超シンプルです

app.py
import json

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'isBase64Encoded': False,
        'headers': {
            'Content-Type': 'application/json'
        },
        'body': json.dumps({
            'message': 'Hello World.',
        }),
    }

CDKを使ってSAMのテンプレートを記述します

コーディング中に必要となるモジュールはインストールします

npm install -s @aws-cdk/aws-sam

CDKのstackはこちらです。CDKのコードを書き終えたらcdk lsで認識されていることを確認して、cdk synthで出力します.

lib/adventcalander2020-stack.ts
import * as cdk from '@aws-cdk/core';
import * as sam from '@aws-cdk/aws-sam';
import { Role, ServicePrincipal, ManagedPolicy } from '@aws-cdk/aws-iam';

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

    const environment = this.node.tryGetContext('env')

    // The code that defines your stack goes here
    const lambdaRole = new Role(
      this,
      'lambdaRole',{
        assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
        managedPolicies: [
          ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')
        ],
        path: "/lambda/",
      }
    );
    const lambdaFunctionHelloWorld = new sam.CfnFunction(
      this,
      'lambdaFunctionHelloWorld',{
        codeUri: "lambda/HelloWorld/src",
        functionName: `${environment}-HelloWorld`,
        handler: 'app.lambda_handler',
        runtime: 'python3.8',
        role: lambdaRole.roleArn,
        autoPublishAlias:`${environment}`,
        events:{
          GetMethod:{
            type: "Api",
            properties:{
              path: "/helloworld",
              method: "GET",
            }
          }
        }
      }
    );

  }
}

% cdk ls
Adventcalander2020Stack
% cdk synth > template.yml 
(出力は省略)

これでSAMテンプレートが出力されます。

SAM

テンプレートが用意できたらsamの出番です。
Lambdaから応答があるかどうかの確認をします。事前にDockerを起動しておきます。

% sam local start-api
Mounting lambdaFunctionHelloWorld at http://127.0.0.1:3000/helloworld [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
2020-12-05 06:15:03  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

これで起動できたので、curlPostmanでAPIをコールします。

% curl http://127.0.0.1:3000/helloworld 

{"message": "Hello World."}%               

Lambdaが動作していることが確認できたのでdeployします。

sam deploy --guided

初回のdeployは--guidedを付けて実行すると設定がsamconfig.tomlというファイルに保存されるのでよいと思います。
無事にdeployできたらAWSコンソールでURLを調べて、呼び出してみます。

curl https://******.execute-api.ap-northeast-1.amazonaws.com/Prod/helloworld
{"message": "Hello World."}%

はい、無事に呼び出せませたね。

パラメータはCDKとSAMのどちらで吸収するのがいいのか?

実運用すると、環境は異なるけど1つのテンプレートで運用することが多いと思います。その場合、環境別のパラメーターをどうするのか?という問題が出てくると思います。

CDKの場合

いろんな方法があると思いますが、私の場合はcdk.jsonに環境別のパラメータを記述し、それを読み込む方法をよく使います。

cdk.json
{
  "app": "npx ts-node bin/cdk.ts",
  "context": {
   "stg":{
     "BucketName":"stgbucket"
    },
    "prod":{
     "BucketName":"prodbucket"
    }
  }
}

bin/adventcalander2020.ts
const app = new cdk.App();
const environment: string = app.node.tryGetContext('env')
const params = app.node.tryGetContext(environment)

このようにparamsで読み込んで、コンストラクターに渡してあげて、下のように使います。

lib/adventcalander2020.ts
const contentBucket = new s3.Bucket(this, "ContentBucket", {
     bucketName: `${props.BucketName}`,
})

cdkのコマンドを実行する時には、cdk synth -c env=prodとすれば、cdk.jsonの内容を読み込んでくれます。

SAMの場合

SAMでパラメータを渡したい場合は、CDKではパラメータは読み込まず、CloudFormationの組み込み関数である、Fn::RefFn::Subのままテンプレートを出力できるようにします。
CloudFormationパラメータをCDKで以下のように宣言します。

lib/adventcalander2020.ts
    const environment = new cdk.CfnParameter(this, 'environment',{
      type: 'String',
    });

宣言をされたパラメータは、以下のように使います。

lib/adventcalander2020.ts
        functionName: cdk.Fn.sub(`\${environment}-HelloWorld`),
        handler: 'app.lambda_handler',
        runtime: 'python3.8',
        role: lambdaRole.roleArn,
        autoPublishAlias: cdk.Fn.ref('environment'),

これで出力してみると、CloudFormationの組み込む関数のままテンプレートを出力できています。

template.yml
Parameters:
  environment:
    Type: String
(省略)
      AutoPublishAlias:
        Ref: environment
      FunctionName:
        Fn::Sub: ${environment}-HelloWorld

どっちがいいの?

どっちでも結果は同じなので、どっちを選択してもよいですが、実際の開発になると複数人で作業することになるし、CI/CDをどうするのかという話も絡んでくるので、チームの中でやりやすい方法でよいかと思います。

最後に

SAMのテンプレートもCDKで出力できることを確認できました。
SAMテンプレートの中で、Lambda関数を複数定義したい時には、TypeScriptのループ処理、関数やクラスを使えばとんでもなく長くなるCloudFormationのYAMLから開放されます。また、VSCodeのコード補完が使えるメリットがあります。
CDKのSAMには、High-level Constructsは無く、Low-level Constructsしかありませんが、メリットだけでデメリットは無いと思いますので、がんがんCDKを使ってSAMテンプレートを出力していきたいと思います。

参考にしたほうがよいサイト

CDKやSAMを使ったことがない場合は、以下のサイトを参考にするといいと思います。

  • AWS サービス別資料
    • このページにある、AWS Serverless Application ModelAWS Cloud Development Kit (CDK)はまず最初に読みましょう。
  • CDK workshop
    • はじめてCDKを触るときにはこのworkshopをやるとよいと思います。
  • aws-samples/aws-cdk-examples
    • CDKのサンプルがたくさんありますので参考になります。

:christmas_tree: FORK Advent Calendar 2020
PREV :arrow_backward: canvasとJSで、目に優しいオリジナルテトリス

15
4
0

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
15
4