CDKのことを良く知らなかったので,作りたいstackごとにcdk init
してしまったWindymeltです。おっちょこちょい。仕事でCDKをいじっています。
さて,package.json
がstackごとに生成されるのは困るので,1つのディレクトリにまとめる作業をするかたわら,CDKのディレクトリがどういう構造になっているのか勉強しました。
CDKにおいて,具体的にどのファイルがどういう過程を経てデプロイに至るかについて,日本語で解説された資料があまりないと感じ,初学者である自分には大変だったので,メモを整理して残すことにしました。サンプルは動かせるようになったけれどより詳しく仕組みを知りたい人の参考になれば幸いです。
構成
cdk init --typescript
を実行すると,以下のようなディレクトリ構成になります。
. ├── README.md ├── bin // ここにappを記述する ├── cdk.context.json // VPCのidなどの環境依存の情報(context)が入る ├── cdk.json // デフォルトのapp(後述)などが入る ├── cdk.out // cfnなどの出力先。コンパイルされたjsを動かすと吐く ├── jest.config.js // テストの設定ファイル ├── lib // stackの構成などを記述する ├── node_modules ├── package-lock.json ├── package.json ├── test └── tsconfig.json
この記事ではこのデフォルト構成を前提にして話を進めることにします。
binとlib: AppとStackの記述場所
われわれがCDKアプリケーションを書く上ではbin/
とlib/
が重要になってきます。
lib/
ではわれわれが構築したいstackの記述が行われます。すなわち,cdk.Stack
を継承したなにがしかのクラスが定義され,exportされます。
bin/
ではappを定義します。lib/
で定義してexportしたStack
クラスにappを渡してインスタンス化することで,コンテキストと呼ばれる種々のデータや,対象となるアカウントのIDやリージョンが渡り,CFnテンプレートに変換できるようになります。考え方としては,抽象的な設計図がインスタンス化されて具体的な存在になるという感じです。
appには複数のstackを含めることができます。しかしCFnにおけるデプロイ(プロビジョニング)の単位はstackなので,CFnテンプレートはstackごとに分離されます。appとstackとの関係については以下の記事が詳しいです。
Appについては以下のdeveloper guideを参照しました。
キホンの処理フロー
CDKアプリケーションの最終的なゴールを,aws-cdkがCFnテンプレートをデプロイすること,だと考えます。
CFnテンプレートはcdkライブラリによって記述されたappを実行することで生成されますが,appはTSで記述されているので事前にJSへとビルドし,nodeで実行可能にする必要があります。
TSからJSへのビルドはtsc
が,JSからCFnを生成してデプロイを行なう処理はcdk
が担っています。
また現在主流である,ビルドとデプロイを一気に行なう方法については,後述します。まずは基本的な動作を見ていきましょう。
基本的な流れは,TSのビルド・JSの実行・cdk
によるデプロイです。
npmによるタスクランナー
標準ではタスクランナーとしてnpm scriptsが用意されており,npm run ほげほげ
で大抵の動作を完結できます。後述するcdk
コマンドも,npm run cdk
の形式で実行可能です。
TSのビルド
デフォルトでは,npm run build
を使うことでTSをJSにビルドできます。
npm run build
するとtsファイルがビルドされ,tsファイルと同じ場所にjsファイルが出力されます。package.json
を見ればわかるとおり,npm script build
の正体はtsc
が実行されるだけです。
// (snip) "scripts": { "build": "tsc", "watch": "tsc -w", "test": "jest", "cdk": "cdk" }, // (snip)
ここでtsc
にはオプションが指定されていないのでtsc
はtsconfig.json
に従ってコンパイルしようとします。
tsconfig.json
にはコンパイル対象を指定するfiles
が指定されていないので,tsc
はデフォルトの挙動に従って全ての.ts
ファイルをコンパイルします。
If the "files" and "include" are both left unspecified, the compiler defaults to including all TypeScript (.ts, .d.ts and .tsx) files in the containing directory and subdirectories tsconfig.json · TypeScript
bin/
以下に記述されたbin/app.ts
といったappはコンパイルされ,この場合はbin/app.js
が生成されます。lib/
以下もまた然りです。
生成されるJSの役割
このJSは実行可能です。 実行すると指定したディレクトリにCFnテンプレートなどを出力してくれます。いわば実行可能なCFnのDSLみたいなもの。
手で実行することもできます。
$ CDK_CONTEXT_JSON='{"コンテキストがあれば": "ここに入れる"}' CDK_OUTDIR=cdk.out node bin/app.js
ここの環境変数は以下のリンク先で発見しました。
これを実行するとcdk.out/
以下にCFnテンプレートが出力されますが,実用上は手で実行せずにcdk
コマンドを使います。
cdk
コマンドは内部でこれと同様の処理を行い,生成されたCFnテンプレート使ってappをデプロイしたり,差分を表示したりしています。
ここで「同様の処理を行な」うために指定するのが,次のセクションで解説する--app
です。
cdkコマンドによるappのデプロイ
cdk deploy --app "node bin/app.js"
を実行すると,前項で行われたJSファイルの実行が内部で行われ,CFnテンプレートが生成されます。
これをもとにappがデプロイされます。
また--app "node bin/app.js"
は必須オプションですが,cdk.json
を書くことで省略できます。cdk.json
には以下のように記述します:
{ app: "node bin/app.js" }
app
オプションでは,CFnテンプレートを生成するために必要なコマンドを指定しています。前項で示したコマンドと違って環境変数を指定していませんが,これはcdk
コマンドが自動で指定してくれるため指定する必要がないのです。
より気の効いた処理フロー(主流)
前項の方式を発展させた,現在主流の方法をここで説明します。
ts-nodeによるビルドと実行の同時化
毎回tsファイルをビルドするのは大変なので,自動でtsc
を呼び,実行まで行なうようにします。cdk.json
には次のように指定します:
{ app: "npx ts-node bin/app.ts" }
ts-node
はTSをコンパイルしつつnode
を動かしてくれる便利なツールなので,cdk
コマンドを叩くたびに自動的にbin/app.ts
がコンパイルされ,これが実行されてCFnテンプレートが出力されるという寸法です。それ以降は手でビルドした場合と同様です。
npxについては拙エントリを参照ください。
まとめ
CDKで自分たちが書いたTSファイルがデプロイされるまでのデータの流れを中心にまとめました。お役に立ったら幸いです。
この記事を書くことでCDKのおおまかな動作原理がわかり,ようやくCDKを活用する第一歩が踏み出せたような感じです。
最初はbin/
とlib/
をどう使い分ければよいのかまったく分からず,勘で記述するような状態でしたが,調べ物を続けるうちに勘所が分かってきて,この記事を書くことができました。
良いCDKライフを!