AWS CDK (AWS Cloud Development Kit) がとても便利そうなので触ってみた

これ触ってみました。

github.com

インストール

上の公式にもありますが、npmでモジュール追加するだけです。これでcdkコマンドが使えるようになります。

$ npm install -g aws-cdk

使ってみる

架空の要件

何も無いと進めにくいので、架空の要件として、DynamoDBのMyUserテーブルをDev,Prodの2環境作ろうと思います。その際にキャパシティを別々に設定したいです。

開発

プロジェクト作成

まずはプロジェクト作ってみます。後は空のプロジェクトディレクトリに移動して、cdk init をします。この際に言語を選択しますが、現時点ではTypescriptとJavaが選択可能です。Useful Commandsとしていくつか上がっていますが、それは後で書きます。

ここで作成したディレクトリ名を利用して、テンプレートが初期化されます。

$ mkdir projdynamo
$ cd projdynamo/
  • cdkプロジェクトの初期化
$ cdk init app --language typescript
Initializing a new git repository...
Applying project template app for typescript
Executing npm install...
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN projdynamo@0.1.0 No repository field.
npm WARN projdynamo@0.1.0 No license field.


# Useful commands

 * `npm run build`   compile typescript to js
 * `npm run watch`   watch for changes and compile
 * `cdk deploy`      deploy this stack to your default AWS account/region
 * `cdk diff`        compare deployed stack with current state
 * `cdk synth`       emits the synthesized CloudFormation template

cdk init appapp は初期化するテンプレートの名前です。現状ではappとlibが選べるようです。libはライブラリそのものを作るものなので、普通の利用者はappを使うことが多いと思われます。

$ cdk init --list
Available templates:
* app: Template for a CDK Application
   └─ cdk init app --language=[java|typescript]
* lib: Template for a CDK Construct Library
   └─ cdk init lib --language=typescript
初期のディレクトリ構造

以下のようなディレクトリ構造になります。 projdynamo.ts に定義のコードを書いていきます。

├── README.md
├── bin/               
│   └── projdynamo.ts     
├── cdk.json 
├── bin
├── cdk.json
├── node_modules/
├── package-lock.json
├── package.json
└── tsconfig.json
初期状態のスクリプトファイル

公式リポジトリのデモで使われているコードが初期に書かれています。今回はSNSやSQSは必要なくて、DynamoDBを使いたいです。

#!/usr/bin/env node
import sns = require('@aws-cdk/aws-sns');
import sqs = require('@aws-cdk/aws-sqs');
import cdk = require('@aws-cdk/cdk');

class ProjdynamoStack extends cdk.Stack {
    constructor(parent: cdk.App, name: string, props?: cdk.StackProps) {
        super(parent, name, props);

        const queue = new sqs.Queue(this, 'ProjdynamoQueue', {
            visibilityTimeoutSec: 300
        });

        const topic = new sns.Topic(this, 'ProjdynamoTopic');

        topic.subscribeQueue(queue);
    }
}

const app = new cdk.App(process.argv);

new ProjdynamoStack(app, 'ProjdynamoStack');

process.stdout.write(app.run());
ドキュメント

DynamoDBの使い方を見るためにドキュメントを探します。cdkコマンドは該当のバージョンのドキュメントのURLまで生成してくれます。

$ cdk docs
https://awslabs.github.io/aws-cdk/versions/0.8.1/

サービス毎の仕様はここに纏まっています。

https://awslabs.github.io/aws-cdk/versions/0.8.1/reference.html

例えば後で書くDynamoDBならここ。

https://awslabs.github.io/aws-cdk/versions/0.8.1/refs/_aws-cdk_aws-dynamodb.html

aws-cdk使って定義を書いてみる

先述のドキュメントを見ながら開発してみます。

上記のドキュメントにある通り、DynamoDBのモジュールをインポートします。

$ npm i @aws-cdk/aws-dynamodb@0.8.1

これ使って、先述の要件に沿って、上記のコードを少し修正してみました。ツッコミどころはありますが、気にしないでください。

なお、コード書いている際に npm run watch を立ち上げておくとリアルタイムで静的チェックしてくれます。

#!/usr/bin/env node
import dynamodb = require('@aws-cdk/aws-dynamodb')
import cdk = require('@aws-cdk/cdk')

class ProjdynamoStack extends cdk.Stack {
  constructor(
    parent: cdk.App,
    name: string,
    env: string,
    dynamodbProps: dynamodb.TableProps,
    props?: cdk.StackProps
  ) {
    super(parent, name, props)

    const userTable = new dynamodb.Table(this, `MyUser${env}`, dynamodbProps)
    userTable.addPartitionKey('id', dynamodb.KeyAttributeType.String)
  }
}

const app = new cdk.App(process.argv)

new ProjdynamoStack(app, 'ProjdynamoStackDev', 'Dev', {
  readCapacity: 5,
  writeCapacity: 5
})

new ProjdynamoStack(app, 'ProjdynamoStackProd', 'Prod', {
  readCapacity: 10,
  writeCapacity: 10
})

process.stdout.write(app.run())

書き終わったらビルドします。エラー無く通れば完了。

$ npm run build

スタックのデプロイ

上記のビルドが終わった段階で、cdkコマンドでの作業に移ります。

スタックの一覧

listコマンドで一覧ができます。DevとProdの2スタックが定義されいます。複数スタックが定義されている場合はcdkコマンドの引数にスタック名を渡すことで任意のスタックを操作できるようになります。

$ cdk list -l
-
    name: ProjdynamoStackDev
    environment:
        name: 012345678912/ap-northeast-1
        account: '012345678912'
        region: ap-northeast-1
-
    name: ProjdynamoStackProd
    environment:
        name: 012345678912/ap-northeast-1
        account: '012345678912'
        region: ap-northeast-1
デプロイ実行

cdk deploy コマンドに上記から任意のスタック名を渡せばデプロイが実行されます。

$ cdk deploy ProjdynamoStackDev
 ⏳  Starting deployment of stack ProjdynamoStackDev...
[0/2] Wed Aug 15 2018 20:12:42 GMT+0900 (JST)  CREATE_IN_PROGRESS  [AWS::CloudFormation::WaitConditionHandle] WaitCondition 
[0/2] Wed Aug 15 2018 20:12:42 GMT+0900 (JST)  CREATE_IN_PROGRESS  [AWS::CloudFormation::WaitConditionHandle] WaitCondition Resource creation Initiated
[1/2] Wed Aug 15 2018 20:12:43 GMT+0900 (JST)  CREATE_COMPLETE     [AWS::CloudFormation::WaitConditionHandle] WaitCondition 
[2/2] Wed Aug 15 2018 20:12:45 GMT+0900 (JST)  CREATE_COMPLETE     [AWS::CloudFormation::Stack] ProjdynamoStackDev 
[0/4] Wed Aug 15 2018 20:12:58 GMT+0900 (JST)  CREATE_IN_PROGRESS  [AWS::CDK::Metadata] CDKMetadata 
[0/4] Wed Aug 15 2018 20:12:58 GMT+0900 (JST)  CREATE_IN_PROGRESS  [AWS::DynamoDB::Table] MyUserDev7ABD51C6 
[0/4] Wed Aug 15 2018 20:12:58 GMT+0900 (JST)  CREATE_IN_PROGRESS  [AWS::DynamoDB::Table] MyUserDev7ABD51C6 Resource creation Initiated
[0/4] Wed Aug 15 2018 20:13:00 GMT+0900 (JST)  CREATE_IN_PROGRESS  [AWS::CDK::Metadata] CDKMetadata Resource creation Initiated
[1/4] Wed Aug 15 2018 20:13:01 GMT+0900 (JST)  CREATE_COMPLETE     [AWS::CDK::Metadata] CDKMetadata 
[2/4] Wed Aug 15 2018 20:13:29 GMT+0900 (JST)  CREATE_COMPLETE     [AWS::DynamoDB::Table] MyUserDev7ABD51C6 
[2/4] Wed Aug 15 2018 20:13:32 GMT+0900 (JST)  UPDATE_COMPLETE_CLEANUP_IN_PROGRESS  [AWS::CloudFormation::Stack] ProjdynamoStackDev 
[2/4] Wed Aug 15 2018 20:13:34 GMT+0900 (JST)  DELETE_IN_PROGRESS  [AWS::CloudFormation::WaitConditionHandle] WaitCondition 
[3/4] Wed Aug 15 2018 20:13:35 GMT+0900 (JST)  DELETE_COMPLETE     [AWS::CloudFormation::WaitConditionHandle] WaitCondition 
[4/4] Wed Aug 15 2018 20:13:35 GMT+0900 (JST)  UPDATE_COMPLETE     [AWS::CloudFormation::Stack] ProjdynamoStackDev 
 ✅  Deployment of stack ProjdynamoStackDev completed successfully, it has ARN arn:aws:cloudformation:ap-northeast-1:123456789012:stack/ProjdynamoStackDev/1f2afd20-a07c-11e8-9539-50a68669984a
スタックを差分を確認

readCapacityを変えてみました。git diffで確認します。

$ vim bin/projdynamo.ts
$ git diff -U0
diff --git a/bin/projdynamo.ts b/bin/projdynamo.ts
index 246cf4b..c2bc1e9 100644
--- a/bin/projdynamo.ts
+++ b/bin/projdynamo.ts
@@ -23 +23 @@ new ProjdynamoStack(app, 'ProjdynamoStackDev', 'Dev', {
-  readCapacity: 5,
+  readCapacity: 10,

ビルドします。

$ npm run build

これでcdk diffコマンド打つことで、現行のスタックとの差分を取ってくれます。

$ cdk diff ProjdynamoStackDev
[~] 🛠 Updating ProjdynamoStackDev7ABD51C6 (type: AWS::DynamoDB::Table)
 └─ [~] .ProvisionedThroughput:
     └─ [~] .ReadCapacityUnits:
         ├─ [-] Old value: 5
         └─ [+] New value: 10
CloudFormationの定義をダンプ

cdk synth コマンドで実際のCloudFormationの定義のダンプも可能です。

$ cdk synth ProjdynamoStackDev
Resources:
    MyUserDev7ABD51C6:
        Type: 'AWS::DynamoDB::Table'
        Properties:
            KeySchema:
                -
                    AttributeName: id
                    KeyType: HASH
            ProvisionedThroughput:
                ReadCapacityUnits: 5
                WriteCapacityUnits: 5
            AttributeDefinitions:
                -
                    AttributeName: id
                    AttributeType: S
    CDKMetadata:
        Type: 'AWS::CDK::Metadata'
        Properties:
            Modules: '@aws-cdk/aws-dynamodb=0.8.1,@aws-cdk/cdk=0.8.1,@aws-cdk/cx-api=0.8.1,js-base64=2.4.5,projdynamo=0.1.0'
スタックの削除

destroyでスタックの削除も可能です。

$ cdk destroy ProjdynamoStackDev

使ってみての所感

個人的感想も多分に含まれていますが。

良さそうな部分

  • 型付けで書けるのでIDEとの連携も含めて書くのが楽。覚えることも少ないと思う。(今回はVSCode使いました)
  • YAMLJSONより見通しが良く感じる
  • ライブラリのコード内にコメントがある(関連するCloudFormationの仕様へのリンクなど)のでIDEで定義にジャンプすると関連情報へたどり着きやすい
  • !Subや!Refや色々でゴニョゴニョやっていた部分をスマートに書ける

大変そうな部分

  • TypescriptかJavaの知識が必須(この分野を使う人にはプログラム苦手意識がある人も多いかも)
  • npm等のNode.jsの開発知識が無いと何かあった時に迷いそう
  • 良くも悪くも書き方の自由度が大幅に増す。この記事に載せたような適当なコードでもコンパイルしてしまえば動きは一緒になってしまうので、後々のメンテ考えれば、ある程度のルールは必要かも