AWS Workshop「Building CI/CD pipelines for lambda canary deployments using AWS CDK 」をTypeScriptで試してみる(前編)

AWS CDK を試してみたいと思っていたところ、以下のワークショップ資料を見つけたので、 今回はそちらに沿って、AWS CDK (TypeScript)で Lambda カナリアデプロイを試してみた記事になります。 catalog.us-east-1.prod.workshops.aws

ワークショップでは Python を使用していましたが、今回は TypeScript で実装してみました! 前編となる今回の記事では、CDK のインストールから Lambda カナリアデプロイまでを試してみます。

こんな人向け

  • AWS CDK (TypeScript)を触ってみたい
  • Lambda カナリアデプロイを試してみたい

AWS Workshops」とは

AWSおよびAWSパートナーによって作成されたワークショップで、実践的なスキルやテクニックを紹介しており、現在100ものコンテンツが公開されています。

workshops.aws

AWS Cloud Development Kit(CDK)」 とは

AWS Cloud Development Kit (CDK) は、AWSインフラを Python, TypeSctipt, Java といったアプリケーション言語で定義し、AWS CloudFormation を通じてプロビジョニングできるツール。 同様なツールとして Terraform があげられますが、Terraform は独自言語を利用することから、使い慣れた言語で記述する AWS CDK と大きく違うところですね。

カナリアデプロイ とは

アプリケーションの新しいバージョンをデプロイする際に、最初に一部のユーザーでテストを行い、段階的に新しいバージョンのアプリケーションへ切り替えていく方式。 Lambda カナリアデプロイは AWS CodeDeploy を利用して実現します。



概要がわかったところで、さっそくワークショップの資料に沿って進めていきます!

事前準備

こちらのワークショップのリンクに沿って準備します↓
prerequisitesprerequisites

必要なもの
・An AWS account
AWS CLI
・Node.js
AWS CDK Toolkit
・Git
・An IDE for your programming language

AWS CDK Toolkit のインストールもこれだけ

$ npm install -g aws-cdk

無事インストールできたようです!

$ cdk --verion
2.37.1 (build f15dee0)

Create your AWS CDK project

create-the-project

$ mkdir cdk-ts-lambda-canary-deploy && cd cdk-ts-lambda-canary-deploy

ワークショップ用のディレクトリを作成した後、TypeScript の CDK プロジェクトを作成します。

$ cdk init cdk-ts-lambda-canary-deploy --language typescript

Project structure

project-structure

ファイル構造

├── README.md
├── bin
│   └── cdk-ts-lambda-canary-deploy.ts
├── cdk.json
├── jest.config.js
├── lib
│   └── cdk-ts-lambda-canary-deploy-stack.ts
├── package.json
├── test
│   └── cdk-ts-lambda-canary-deploy.test.ts
└── tsconfig.json

Entry point

bin/cdk-ts-lambda-canary-deploy.ts

スタックの生成処理を行うためのエントリーポイントを記載するファイル

Main stack

lib/cdk-ts-lambda-canary-deploy-stack.ts

リソース定義を記載するファイル

Adding our app configuration

adding-app-configuration

Adding stack code

メインスタックの lib/cdk-ts-lambda-canary-deploy-stack.ts ファイルにリソース定義を記載していきます。 いくつかポイントを絞って解説していきます!(コード全体は最後に記載の github をご確認ください)

`const environmentType = this.node.tryGetContext('environmentType')`

this.node.tryGetContext で cdk.json に定義した context を取得できます。

const myLambda = new NodejsFunction(this, 'MyFunction', {
    functionName: context['lambda']['name'],
    handler: 'main',
    runtime: Runtime.NODEJS_16_X,
    entry: path.join(__dirname, '../lambda/handler.ts'),
    currentVersionOptions: {
      description: `Version deployed on ${currentDate}`,
      removalPolicy: RemovalPolicy.RETAIN
    }
})

Lambda 関数を TypeScript で作成する場合は aws-cdk-lib/aws-lambda-nodejs の NodejsFunction を使います。 NodejsFunction は自動的に JS ファイルにコンパイルしたものを Lambda にデプロイしてくれます!

Adding lambda code

次に Lambda 関数の実装を行いましょう。

$ mkdir lambda && touch lambda/handler.ts

ワークショップと同様に簡単なメッセージを返す function を作成します。

$ npm install -D @types/aws-lambda
import { Handler } from 'aws-lambda';

export const main: Handler = async () => {
  return {
    statusCode: 200,
    body: JSON.stringify({
        message: 'hello world',
    }),
  };
};

Modifying cdk.json file

cdk.json ファイルを以下のように修正します。

{
  "app": "npx ts-node --prefer-ts-exts bin/cdk-ts-lambda-canary-deploy.ts",
    :
  "context": {
 :
    "prefix": "cdk-workshop-stack",
    "qa": {
      "region": "ap-northeast-1",
      "lambda": {
        "name": "cdk-workshop-function-qa",
        "alias": "live",
        "stage": "qa"
      },
      "tags": {
        "App":"cdk-workshop",
        "Environment": "QA",
        "IaC": "CDK"
      }
    },
    "prod": {
    }
  }
}

Modifying cdk-ts-lambda-canary-deploy.ts file

エントリーポイントの bin/cdk-ts-lambda-canary-deploy.ts ファイルを修正します。

#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { CdkTsLambdaCanaryDeployStack } from '../lib/cdk-ts-lambda-canary-deploy-stack';

const app = new cdk.App();

const environmentType = app.node.tryGetContext('environmentType');
const environmentContext = app.node.tryGetContext(environmentType);
const region = environmentContext['region'];
const account = app.node.tryGetContext('account');
const stackName = `${app.node.tryGetContext("prefix")}-${environmentType}`

new CdkTsLambdaCanaryDeployStack(app, stackName, {
  env: {
    account,
    region,
  }
});

上記の定義により、environmentType=qa の context が指定された場合は以下のプロパティが渡されます。

"region": "ap-northeast-1",
"lambda": {
  "name": "cdk-workshop-function-qa",
  "alias": "live",
  "stage": "qa"
},
"tags": {
  "App":"cdk-workshop",
  "Environment": "QA",
  "IaC": "CDK"
}

Deploy the app

deploy-app

Running cdk bootstrap command

スタック以外に Lambda 関数など外部ファイルを含んでおり、CDK はこれらを S3 にアップロードしますが、そのS3 バケットを作成します。 リージョンごとに必要となります!

$ cdk bootstrap aws://AWS_ACCOUNT/REGION

Running cdk deploy command

$ cdk deploy -c environmentType=qa

Lambda からレスポンスが返ってくることを確認できました↓

$ curl https://2uxvuqmdf4.execute-api.ap-northeast-1.amazonaws.com/qa/
{"message":"hello world"}%

次に Lambda のメッセージを変更して、カナリアデプロイを試してみます!

lambda/handler.ts ファイルを少し書き換えてみます。

import { Handler } from 'aws-lambda';

export const main: Handler = async () => {
  return {
    statusCode: 200,
    body: JSON.stringify({
        message: 'hello world ver2', -> ここを変更
    }),
  };
};

再度デプロイ!

$ cdk deploy -c environmentType=qa

AWS コンソールから確認して、トラフィックの10%が新しいバージョンに割り当てられてることが確認できます。

実際にリクエストを送ってみると、新しいバージョンにもリクエストが送られていることが確認できました!

$ for i in {1..10}; do curl -w '\n' https://2uxvuqmdf4.execute-api.ap-northeast-1.amazonaws.com/qa/; done
{"message":"hello world"}
{"message":"hello world"}
{"message":"hello world"}
{"message":"hello world ver2"}
{"message":"hello world"}
{"message":"hello world"}
{"message":"hello world"}
{"message":"hello world"}
{"message":"hello world"}
{"message":"hello world"}

5分後・・デプロイが完了!

再度リクエストを送ってみると、今後は全てのリクエストが新しいバージョンに送られていることが確認できたのでカナリアデプロイがうまくいってそうです🎉

$ for i in {1..10}; do curl -w '\n' https://2uxvuqmdf4.execute-api.ap-northeast-1.amazonaws.com/qa/; done
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}
{"message":"hello world ver2"}

Cleanup

$ cdk destroy -c environmentType=qa
Are you sure you want to delete: cdk-workshop-stack-qa (y/n)? y
cdk-workshop-stack-qa: destroying...

 ✅  cdk-workshop-stack-qa: destroyed

最後に

上記のワークショップで実装したコードは github にも載せているのでよければ参考にしてください!

github.com

AWS workshops のサイトは他にもいろいろなワークショップが公開されていたので、試してみたいと思います!