改めて「AWS CDK」を深掘りしてみた

TopPage

こんにちは! 緊急自体宣言でお家時間が増える中、私はAWS CDKへのコミットに挑戦しました!

今回は改めてAWS CDKに関して、少し掘り下げてみましたので共有いたします。

はじめに

みなさんはインフラのプロビジョニングに関してどのようなイメージをお持ちでしょうか? これは結構意見が二分するところだと思っていて、すでに取り組んでいる方からすると常にコード化しておきたいくらい素晴らしいと感じますし、 そうでない方は非常に面倒な取り組みと感じると思います。 この原因として、私個人が感じるのは プロビジョニングの世界観が独特だから です。

TerraformはHCLなる謎の言語?で記述し環境変数の管理やApexの理解と管理運用設計が求められます。 また、SLSやSAMなどのAWSのプロビジョニングツールは設定を記述しておくため、Infrastructure as Code(IasC)として中途半端だし、 モジュール化や、環境ごとの設定の切り変えを検討するのが面倒です。 とはいえ、一度ブロビジョニング方法が確率すればインフラの管理が楽になりますし、ドキュメントとしても役立ち、様々な箇所に同じパターンを応用することができるため、 IasCはうまく活用すると非常にメリットが大きい取り組みになります。

「IasCはいいと思うけど、もっと普通にプロビジョニングしたい」という要望をまさに満たしてくれるのが、AWS CDKだと思います。

AWS CDK とは

AWS CDKは2019/07にGAしたばかりの新米のAWSインフラのプロビジョニングツールで、AWSより公式に開発されているOSSです。 特徴として、TypeScriptが標準言語になっており構造的型付けの恩恵をフルに享受することができます。 一応、PythonやJavaなどの他の言語からも使用できますが、CDKそのものはTypeScriptで実装されており、 各言語のパッケージはjsiiというAWS独自のコンパイラを使用して変換されています。 (jsiiによってTypeScriptの記述も完全に自由ではなく、一部制約されています)。

ただし、AWSのSAさんがDevDayに発表されているのを見た記憶では、「顧客の要望によりPythonなども使えるようにしたが、TypeScriptがあくまで推奨」と言っていました。

AWS CDKの実行すると、最初にCloudFormationテンプレートが排出され(デフォルトでcdk.out)、そのテンプレートをCloudFormationで実行する形になるため、 あくまでAWS標準のIasC機能を使用する互換性の安心感があります。

一方、CDKの懸念点としては2021年現在でissue数が1.5kを超えていおり、AWSのアップデートに対してCDKが遅れるのではと思われる方がいるかもしれません。 しかし、この点は実はうまくカバーされていますので、説明いたします。

以外と重要なLevel1(L1)コンストラクト

CDKは各種AWSリソースをモデル化したクラス(コンストラクトと呼びます)を初期化することでリソースの生成を表現し、CloudFormationテンプレートに追加します。

(イメージ)

import * as s3 from '@aws-cdk/aws-s3';
// S3が生成されます
const bucket = new s3.Bucket(this, 'test-bucket', { bucketName: 'test-bucket' })

このコンストラクトはLevel1(L1), Level2(L2), higher-levelの3種類が存在します

L1のコンストラクトはCfnXXXという名称のクラスになっており、CloudFormationでのリソースの定義と1vs1で対応しています。 そのため、L1コンストラクトを使えば、CloudFormationのテンプレートを抽象化しながらTypeScriptで記述できることになります。 (実際は、CfnリソースのGet::Attr属性などはL1クラスの中に抽象化されていますので、単なるCloudFormationテンプレートより便利に管理できます。)

そして、特筆すべき点として、このL1コンストラクトはなんと cfn2ts という独自ツールでビルド時に自動生成されています! 現状のAWS CDKは概ね1週間以内に複数のリリースが発生するため、CloudFormation側に変更があった場合は少なくとも1週間以内にはL1コンストラクトに追加・反映されます。 そのため、CloudFormationでできることがCDKで実現できないということは基本的に発生しないことになります。 また、L1のコンストラクトの全プロパティは実装箇所に対応するCloudFormation定義のURLが記載されているなど、開発時のリント設定が徹底されており、AWS側の仕様を確認するのも容易になっているため、 ドキュメンテーションとしての価値も高まっています。 まとめると、L1コンストラクトを使用することでCloudFormationテンプレート(のもうちょい便利版)が一般的なオブジェクト思考(っぽい)言語で抽象化して管理できる様になります。 単にモジュールに切り出せるだけでも、環境によって構成を切り替えたり、同じパターンを使いまわしたり、制約をコード化したりと夢が広がりますね。

便利なL2コンストラクト

さらに、AWS CDKを使用するさらなる旨味としてL2以上のコンストラクトが使用できます。 L2のコンストラクトは(基本的に)単体のAWSリソースを抽象化しますが、暗黙のデフォルト値設定や、周辺リソースの生成などを隠蔽してくれるものです。 たとえば、'@aws-cdk/aws-lambda'.Function(Lambda関数)を生成した場合はポリシー無しのデフォルトのロールが生成されます。

このL2コンストラクト中身ですが、L2コンストラクトクラスのconstructorでL1コンストラクトをnewする形で実装されています。 つまり、'@aws-cdk/aws-lambda'.Functionの場合は、'@aws-cdk/aws-lambda'.CfnFunction@aws-cdk/aws-iam/'.CfnRolenewされます。 そのため、最終的にはCloudFormationの仕様まで遡ってデバッグが可能です。

細かい話になりますが、L2コンストラクトクラス(Hoge)には基本的に同オブジェクトを表すインターフェイス(IHoge)をimplementしています。

これは例えば、Lambdaのイベントソースとして既存のS3 Bucketを指定したい場合は
const externalBucket = '@aws-cdk/aws-s3.Bucket'.fromBucketArn
で、既存のBucketをインスタンス化することができますが、外部から引き込んだリソースのインスタンスはもちろんimmutableなので、単にIHoge型になります。 一方、mutableな関数をクラス側のみに定義することによって、外部リソースにできる操作と内部リソースにできる操作を分離しています。

さらに、higher-levelコンストラクトは複数のリソースを一つのパターンとして抽象化したもので、ベストプラクティスを簡単に取り入れることが出来るよう設計されています。

レポジトリについて

AWS CDKは巨大なモノレポ構成を取っております。将来的に正規化も検討しているらしいのですが、アクティブに開発が続く現状だと複数のパッケージに 関わる変更も少ないないので、この構成がリーズナブルな選択だと思います。

イシューが1.5kとまだまだ多く、star数も7kと伸びきっていないせいか、コアコントリビュータもほぼAWSの内部の方のようなので、L2以降の開発スケジュールなどは少し気になるところですが、 もし本当に期待したい機能が使えないとか、バグが発生したという場合は自分でコントリビュートするのもさほど難しくないと思います。

そもそも最終的にはCloudFormationテンプレートを吐き出す(だけ?)の仕組みですし、リソースの仕様は当然AWSのドキュメントと一致します。 全体として、jsiiやcfn2tsなどの変換ツールや、複数のリンター、ビルドツールなどがしっかり設定されており、 コードの形式を保ちながら例えばコンパイルエラーを手がかりに実装が進められるようになっています。

考察 - IasCとしてのプロビジョニングツール

プロビジョニングツールで比較する場合、既存の代表的なツールとしてはSLS、SAM、terraformなどが挙げられます。
冒頭でも触れた部分で、そもそもIasCはインフラをコードで管理し、アプリケーションの開発プロセスの中に取り込んでいくことなどが主なの目的として挙げられますが、 CloudFormation、SLS、SAMはyamlで記述するためプログラミング言語ではないし、terraformもHCLという特殊な言語(?)で記述する必用があります。 そのため、環境変数の管理や、環境ごとの条件分離に一手間かかることは少なくありません。 これに対してAWS CDKは完全に抽象化された一般的なプログラミング言語(TypeScript)で書けるという点が優れており、JSのエコシステムの恩恵をそのまま享受することができます。 アプリケーション開発者と相性がよいですし、構造的型システムを持っているため開発時にはインテリセンスが効き、ドキュメンテーションとしての質も向上させることができます。 クラウドがサーバレス化し、フロントのエコシステムが強化されている昨今では、「フロントエンド」自体の境界が曖昧になってきている部分がありますので、 一人の開発者がアプリケーション全体を通貫して意識ながら開発を進める重要性が増してきていますが、 フロントエンドのエンジニアに取ってインフラはなんとなく目に見えない部分で苦手意識を持つことが少なくありません。 しかしながらリソースの内容や繋がりがアプリケーション開発者が慣れ親しんだ言語でコード化されている場合、インフラが見える化され、理解大きく前進すると考えられます!

他に一般的な言語で記述できるプロビジョニングツールとしてはsparkle_formationpulumiなども挙げられますが、 長らくterraformが大きくシェアと取っているためなかなか注目が集まらなかったり、互換性の懸念から特別な事情がなければあえて使用に踏み切ること理由が薄かったと認識しています。 その点、AWSCDKはAWSオフィシャルで開発されており、前述の通りCloudFormationに対して常に同等の状態が保たれることが実装として証明されているため、 使用に踏みきる際の阻害要因が大幅に減り、メリットを想定しやすくなってきていると考えられます。

余談ですが、cdkの内容をterraformで実行するterraform-cdkとうツールもterraformのhasicorp社のレポジトリに上がっており、 まだ実験段階ですが、マルチクラウドでの運用も期待出来るかもしれません、、、ザワ

実践に当たって

全てをAWS CDKでプロビジョニングしきる必要はない(かも)

実際に開発で使用する際にはどのプロビジョニングツールを使用しても共通することですが、 全てをプロビジョニングツール上に完璧に記述するということは時には諦めながら進めるべきと考えています。 AWS CDKの場合、どうしてもCloudFormationで出来ないことはCDKでもできないので、CDKで書ききれないケースが存在します。 その場合は手作業に逃すことも検討できますし、AWS SDKなどを使用したnodeスクリプトやCLIでshell scriptを作成しておくのもオススメです。

ちなみにLambdaなどを構築する際は、CDK, Lambad, AWS SDKのNode.js Scriptまで、全て言語をTypeScriptで統一できますので、管理コストが抑えられます。 小規模のチームでは個々人がフルスタックに開発することがありますので、コンテクストスイッチは最小にするに越したことはありません。
フロントとインフラ分業する場合もお互いの領域を意識しながら実装を進めて行きたいところですが、 往々にして言語や技術スタックが二者を分断するため、言語や環境の統一はコミュニケーションに意義があると考えられます。

まとめ

以上、AWS CDKを少し深掘りしてみました。
ご意見ご感想などはtwitterまで頂けると嬉しいです。