search
LoginSignup
3

More than 1 year has passed since last update.

posted at

updated at

Organization

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

はじめに

これまでは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で、目に優しいオリジナルテトリス

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
What you can do with signing up
3