はじめに
NTTテクノクロスの渡邉 洋平です。
わたしはAWS CDKが好きなので個人的に数年コントリビュートしてますし、カンファレンスでも3年ほど登壇しています。そんなバックグラウンドからか、「CDKに入門するには?」という相談を受けることもあります。
以下のような良質なコンテンツは当然伝えるとして、毎回いろいろな内容を口頭で伝えており、今回はそれらの内容(偏見)を記事にまとめてみようと思います。
1. プログラミング言語の機能に頼るべからず
「TypeScriptでCDKを書くから、TypeScriptの高度な機能を習熟しなきゃ」と思い込むのはNG
TypeScriptやPythonなどで書かれたCDKのコードは、最終的にCloudFormationテンプレート(YAML/JSON)にトランスパイルされるだけであり、無理に高度なプログラミング機能を使い倒す必要はありません。具体例を挙げると、たとえば型パズルのような複雑なGenericsの多用は、インフラを定義するうえでほぼ必要ありません。
CDKのコア要素を見極めて宣言的に書きつつ、必要に応じて配列や条件分岐や繰り返し文が使えれば困らないでしょう。CDK固有の問題にぶつかって初めて、必要最小限のTypeScriptの知識を習得するのでも、実は問題ありません。
用意されたConstructに「.」を打つとIDEがプロパティ補完してくれる!くらいの喜びを軸にして、過度にプログラミングとして向き合い過ぎないことが大切です。
CDKのコア要素
ちなみにコア要素とは、以下の抽象化が理解できれば十分です。
- App(CDKアプリ)
- Stack(CloudFormation Stackに対応)
- Construct(部品の単位)
※出典:
2. プログラミング言語を選定するべからず
「PythonやGoもサポートしているし、どれを使っても一緒でしょ?」はNG
CDKは複数の言語をサポートしていますが、メインストリームはTypeScriptです。実際のコミュニティ事例も、8~9割がTypeScriptで書かれているように思えます。また公式ドキュメント・サンプルもTypeScript前提のコードが多く、疑問点が出たときにググるときも、英語・日本語いずれでもTypeScriptの情報が圧倒的にヒットするため、他の言語を採用した場合はトラブルシュートやベストプラクティスの事例を探す際に苦労しがちです。
恐らくTypeScript以外を選ぶモチベーションとして、”プロダクトのメイン言語とのスイッチングコスト”がありそうです。しかしTypeScriptを選択しなかった場合、世間のCDKナレッジとのスイッチングコストも馬鹿にならないのが現実です。チーム内で普段PythonやGoを使っている場合でも、「CDK部分だけTypeScriptにする」ことで学習コスト・保守コストともに低く保てるケースが多いです。
3. DRYを過度に期待するべからず
「Terraformと同じようにDRYに書けるだろう」と思い込むのはNG
CloudFormation(CFn)はスタック単位の分割や再利用がさほど得意ではありません。CDKを用いると、プログラム的な抽象化を組み込めるようにも見えますが、CDKの根本がCFnであり、そのCFnが制約を持つ以上、DRYの徹底が、かえって複雑さを生むことがあります。
以下のようにある程度のナレッジこそあれ、多くのハマりどころがこの”スタック単位の分割や再利用”で、CDK(CFn)ユーザーの頭を悩ませています。
大規模であれば複数スタックに分割する必要性は当然出てくるものの、それでも無闇にDRYを求めるより、モノリスに寄せたほうがトラブルを避けられるのが現実です。
4. CDKのCLIに削除を任せるべからず
「cdk destroyして掃除しよう!」はNG
CDKのcdk destroy
は実行時にCloudFormationのテンプレート整合性チェックが入るため、直観的に削除できないケースがあります。たとえば「Resourceがエラー状態で正常に更新できない→destroyも通らない」など、デプロイと削除が強く結びついているゆえの不便があるのです。
またマイナートラブルとしてcdk deploy
とcdk destroy
を誤って打ってしまう事例もあり、なるべくdestroy自体を封印した方がトラブルを少なくできるでしょう。
代替え案
rain
はAWSが開発しているCloudFormationスタックを扱うためのCLIツールです。cdk destroy
の代わりに使えば、テンプレートの整合性問題を無視してスタック単位で手軽に削除が可能です。
また削除が難しいスタックに関しては、AWS Heroの後藤氏が開発しているdelstack
を用いることで削除を簡易に進められるでしょう。
5. デフォルト値に依存するべからず
「CDKが用意してくれているデフォルト値なら安心」と思い込むのはNG
AWS CDKは後方互換性を重視しており、メジャーバージョンのv2は2021/12から3年以上メジャーバージョンが上がっていません。そのため、初期の頃に設定されたデフォルト値、つまり当時のベストプラクティスがそのまま残っていることがあります。
いまの所、aws-cdk-lib
モジュールの再分割やL1 Constructの構造の最新化が水面下で動いているようですが、これがv3更改へのトリガーになるのか、それがいつ実施されるかも分かりません。
ユーザにできることとしては、以下のFeature Flagsを理解したり、各種サービスの最新のベストプラクティスを理解したうえで、デフォルト値への過度な依存を減らすことが重要です。
具体例
せっかくなので具体例を挙げましょう。
私がコントリビュートした以下のRuntime対応ですが、デフォルト値はruntime: cloudfront.FunctionRuntime.JS_1_0
で未だに設定されています。
機能から見たときに、CloudFront FunctionのランタイムとしてJS 2.0
を用いるデメリットは考えにくいのですが、既存ユーザへの影響を優先し、JS 2.0
がデフォルト値のままです。
よって新規に使う場合はデフォルト値を使わずに、このように書くのが無難です。
const cfFunction = new cloudfront.Function(this, 'Function', {
code: cloudfront.FunctionCode.fromInline('function handler(event) { return event.request }'),
runtime: cloudfront.FunctionRuntime.JS_2_0,
});
まとめ:
ここまでの話をまとめると、こんなところでしょうか。
- プログラミング言語固有の機能を使いすぎない
- 特別な理由がない限り、TypeScriptを選定する
- 過度にDRYを考えず、愚直に書く
-
cdk destroy
を利用しない - デフォルト値に依存せず、必ず最新のReadme/仕様を確認して設定する
正直いって、一般的なベストプラクティスと直交するような著者独自の偏見もあるので、話半分に捉えていただくのが無難かもしれません。
それでも私が伝えたいのは、AWS CDKがもたらす自由度に惑わされず、あくまでシンプルに使い始めるのがポイントだということです。2025年以降もAWS CDKの進化は続くでしょうが、上記の“べからず”を頭の片隅に入れておくことで、大きく道を踏み外さずにAWS CDKによるIaCを実現できるでしょう。
Appendix. プロジェクト構成
AWS CDKのプロジェクト構成を設定するには別のツールであるprojen
が推奨されています。
ただ、ちょっとゴテゴテしているので、以下のように完成度の高いリポジトリの構成をそのまま参考にするのが個人的な好みです。