AWS CDKとは何か、IaCの変遷を踏まえての紹介

概要

これまで業務・プライベートの双方でAWS CDK (Cloud Development Kit)というInfrastructure as a CodeツールをDeveloper Previewのころから着目し利用してきたので、その特徴を競合と比較しながら説明します。

概観を掴んでもらうことを目的とするので詳細は外部の公式・非公式のドキュメントへ適宜リンクするかたちをとります。

想定読者

  • AWS CDKが何かよくわかっていない人
    • CloudFormationとの違いがわかっていない人
  • IaCに興味がある人

前提・背景

AWS CDKの特徴を説明するためにIaCの変遷について簡単に言及したく、そのためにはWeb開発におけるインフラの変遷に触れる必要があります。

結論から言うと 現代においてインフラとアプリケーションの境界は非常に曖昧 で、比較的はっきり別れていた時代に定義・設計された IaCの概念や実装から進化した存在が必要 であるという考えに応えて登場したのがAWS CDKです。

以下に書く話はデプロイ今昔 - Hatena Developer Blogがよくまとまっているのでそちらを読むのが早いかもしれません。 もう少しインフラにフォーカスして書きます。

ペットと家畜

インフラとアプリケーションがはっきり別れていた時代では、インフラのライフサイクルとアプリケーションのライフサイクルが異なっており、インフラリソースの制御がほとんどすべてユーザーの手にありました。

アプリケーションの生まれ変わり = デプロイよりインフラの方が大抵長生きするので、インフラが必然と状態を持つ、そんな時代です。 サーバーがペットや家畜として扱われていた時代という比喩もあります。

また、インフラの制御がユーザーの手にほぼあるということは、プロバイダーは何もしてくれずユーザーが管理しなければ壊れたり動かないといった管理コストの裏返しでもあります。 まず仮想マシンの調達と在庫管理からユーザーを開放したのがAWS EC2のようなIaaSサービスです。

マネージド化

長じて、コンテナオーケストレーションサービスやサーバレスサービスが登場します。これらをまとめてクラウドプロバイダーによる進んだ資源管理が行われることをうけてマネージドサービスと称することにします。

これらのサービスはコンテナ技術や進化した仮想化技術を利用しアプリケーションに設計上の制約を与える一方で意欲的なインフラの隠蔽を行うことで、クラウドプロバイダー側が最適化を行う余地を広げました。 ここでいう最適化とは、アプリケーションが利用されていないあいだは計算機資源を開放してコストを抑えることであったり、突発的な利用の増加に複雑な事前準備なしに対応できるよう計算機資源を用意することであったり、あるいはクラウドプロバイダーがより簡素・柔軟なデプロイメントを設計しDX (Developer Experience) を向上させることであったりします。

コンテナオーケストレーションサービスの代表例はAWS ECS (Elastic Container Service) やGKE (Google Kubernetes Engine) などが、サーバレスサービスの代表例にはAWS LambdaGoogle App Engine (GAE)などが挙げられるでしょう。

アプリケーションとインフラの漸近

マネージドサービスがアプリケーションに与えられる制約は様々で、コンテナ技術のようにシステムコールや計算機資源へのアクセスがスーパーバイザに隠蔽・管理されることであったり、AWS Lambdaのように利用できるランタイムに限りがあることであったり、様々です。

インフラがユーザーの管理下にあるケースでは、たとえばCPUやメモリをよりヘビーに使いたければサーバーを増強したり、たとえばアプリケーションのランタイムにPythonを使いたければサーバーにインストールしたりすることで、インフラがアプリケーション側に寄り添わせる柔軟性がありました。 設計の主導権はアプリケーションに多くあったといえそうです。

一方で、インフラ = 計算機資源にはいくつかのハードリミットがあり制約条件たりえました。 用意できるCPUやメモリのスペックに現実的な上限があったり、単位時間あたりに用意できる数が限られていたり、様々です。

また、必要なライブラリを揃えるプロビジョングやアプリケーションを実行可能な配置を行うデプロイメントは、インフラ (OS, ミドルウェア, etc.) による制約や要求が生じますが、アプリケーションと異なるレイヤと規定され分断されがちでもありました。

あるインフラ上で動かすのが非常に手間であったり非効率なアプリケーションを生むケースもありえたでしょう。

サーバレスサービスなどマネージドサービスではアプリケーションに制約をいくつか与える代わりにそれら分断を埋め開発からデプロイメントまで包括的なツールや体験が整備されていることが期待できます。

ここまでのまとめ

  • EC2などIaaS時代までは、インフラとアプリケーションは分断され、ツールもそれぞれの世界で独自に設計されていた
  • マネージドサービスではインフラとアプリケーション双方に制約を与えるかわりにスケーラビリティなど包括的な体験が提供されるようになった

AWS CDKとは

AWS CDKはAWSが開発しているツールセットでCloud Development Kitの略です。 便宜上IaCと呼びますが、インフラに限らない開発キットを志向していることは明らかなので、以下に示す競合との比較も厳密には同じレイヤとはいえず比較として不十分・不適切とも捉えられますが、わかりやすさのためにあえて比較します。

AWS CDKは:

  • L1 layer: CloudFormationのリソース定義に一対一で対応するクラス群
  • L2 layer: CloudFormationのリソース定義を包みユーザーフレンドリーなAPI (intent-based API) を提供するクラス群
  • patterns: 1つのクラスが複数のサービスのリソースを包括的にプロビジョニングするクラス群

……の、3つの層から成り立っています。 refs. Constructs - AWS Cloud Development Kit (AWS CDK)

L1 layer

L1 layerはCloudFormationのリソース定義からコードが自動生成されます。 L2より上位の階層のクラス群はより下位のクラス群を利用しています。言い換えるとCDKが提供するCloudFormationに関わるクラス群は最終的にL1 layerを利用し、CloudFormationの変更適用に還元されます。 基本的にCloudFormationでできることはCDKでも実現できると捉えてよいです。一方でCloudFormationに存在するハードな制約を越えることはできません。

L2 layer

CloudFormationはAWSのリソース設計に強く根ざしリソースのあるべき状態を宣言すれば差分の適用を任せられるサービスですが、宣言的な設計のために生じる様々な要求・制約が絡み、ひとつのリソースをプロビジョニングするのにも慣れが必要な癖の強いサービスでもあります。

たとえばあるIAMユーザーにだけS3バケットへのアップロードを許可したいという目的を達成するために必要なCloudFormationのリソース定義は AWS::IAM::User, AWS::IAM::Policy, AWS::S3::Bucket などに渡るなど人間の思考から離れたリソース設計を見つけ適切な設定を与える必要があります。

また、各リソースの意味論レベルで実現が難しい制約は実行時に検証されることも多いです。 たとえばある設定Aはある設定Bがtrueであることを求めるが、それは実行してみるまでわからない、など。

こうしたCloudFormationの難点に対してまず設計されたのがL2 layerです。 たとえば上記で例にあげた「あるIAMユーザーにだけS3バケットへのアップロードを許可する」ケースでは以下のようなコードで済みます。

import { Stack, App } from '@aws-cdk/core'
import { Bucket } from '@aws-cdk/aws-s3'
import { User } from '@aws-cdk/aws-iam'

const app = new App()
const stack = new Stack(app, 'buckets')
const uploader = new User(stack, 'Uploader')
const bucket = new Bucket(stack, 'FilesBucket')
bucket.grantRead(uploader)

Bucket#grantRead を呼べばいい、非常に人間がやりたいと望んだことにほぼ対応するAPI設計ではないでしょうか。

patterns

最後にpatternsです。これはL1/L2 layerのクラス群を1つ以上包みAWSのサービスでよくあるパターンをカプセル化したクラス群です。

たとえばApplicationLoadBalancedFargateServiceというECS FargateのサービスがALBの後ろにいるというのは典型的なパターンですが、 ApplicationLoadBalancedFargateServiceインスタンス化するだけでALBやターゲットグループ、リスナー、クラスターやタスク定義、サービスなどなどにプロビジョニング・動作に必要十分なデフォルト設定を備えたリソース定義が与えられます。

慣れれば楽ですが、慣れるまでに数え切れないトライアンドエラーを要するCloudFormation. 特に複数サービスにまたがるベストプラクティスは情報ソースを見つけるのにも一苦労だったりします。 上記例だと情報がECSのドキュメントにあったり、ALBのドキュメントにあったり、などなど。

patternsレイヤはよくあるパターンに名前が付けられパッケージ化されているので、たとえばECSに関するよくあるパターンを手軽に使いたいと考えたらaws-ecs-patternsを見に行けば望むものが見つかるかもしれないというアフォーダンスを与えてくれます。

もちろん複数のリソースに良いかんじのデフォルトを与えてくれるということは、デフォルト設定が自分たちのユースケースにフィットするかきちんと読み解くことが求められます。 手放しにこれを使うべきというより、ベストプラクティスがコード化されていること・それを読んで必要な知識への参照を得られることが本質的に重要でしょう。

モジュール化と再配布

AWS CDKはコアがTypeScriptで書かれNPMパッケージとして配布されています。TypeScriptが第一級のサポート対象言語ですが、PythonJavaへも移植されています。 refs. Translating TypeScript AWS CDK code to other languages - AWS Cloud Development Kit (AWS CDK)

いずれにせよ既にパッケージシステムが確立したエコシステム上で提供されていますし、AWS CDK自体がそうであるように自分たちがCDKを利用して作ったクラスや関数をNPMやその他のパッケージとして配布するのに特別な労力は要りません。 NPMパッケージならTypeScriptで書いて、型定義とトランスパイルされたJavaScriptのコードを成果物に含めるだけです。

きちんと動くパッケージシステムを作るのは存外に難しく、無闇にパッケージシステムを独自開発しても成熟したものに比べてうまく動かず利用者に負担を強いたりdependabotやrenovateのようなSaaSのサポートが受けられず不便であったりします。 過去のバージョンのアーカイブを参照できたり、脆弱なバージョンを非公開にしたり、改竄されていないか整合性を確認する手段を提供したり、などなど、パッケージシステムに求められる要件は多岐に渡ります。

その点でCDKは独自開発せずに既存の成熟したシステムにうまく乗っており、この点でCDK固有のフラストレーションを抱えることはほとんどないでしょう。

実例

たとえば前職ではステージング環境や開発用途のアプリケーションなどを社内の人間のみに公開する用途にG Suiteのメンバー一覧から作られたCognitoのユーザープールとそれを使って認可するリスナールール類を社内モジュールとしてメンテナンス・提供しました。 用途によって社員のみ・アルバイトを含むユーザープールなどを選ぶオプションを提供しました。

これを実現するためには認可されたユーザーが特定のグループに所属しているかG SuiteのAPIに問い合わせる必要があり、このモジュールはそれを実現するLambdaのコードも含みます。

単にCloudFormationのテンプレートを配布するだけではなく付随するリソースも含められる点が、競合と比べてCDKが傑出している点であり、それをうまく活かしている例に挙げられるでしょう。

競合との比較

AWS CloudFormation

基本的にCDKはCloudFormationの上位互換と捉えてよいです。即ちCloudFormationでできることはすべてCDKで実現できると捉えて問題ありません。

CDKはchangesetを作成→実行というCloudFormationのベストプラクティスが cdk deploy というコマンドひとつで実現されているほか、 cdk diff でスタックとテンプレートの差分を表示できるだけではなく、 cdk synth でCloudFormationのテンプレートを出力できるなど、ワークフローにおいてもCloudFormationを包括しより使いやすくしてくれます。

CloudFormationがフィットするケースにおいては、CDKの検討を強く勧めます。

SAM (Serverless Application Model)

ベストプラクティスや典型的なケースをより使いやすくするという思想は似通っています。

SAMはCloudFormationのテンプレート変換として実装されており、SAMに精通しているメンバーがいない場合はCDKを勧めます。 CDKはL2 layerのコードを見たり cdk synth コマンドでどんなCloudFormationテンプレートが適用されようとしているか静的に確認できます。

SAM localなど周辺ツール群はCDKのスコープにないのでそれらは別途利用しても良いでしょう。

Terraform

Terraformはレイヤや設計としてはCloudFormationの直接的な競合ですが、IaCという括りで比較してみます。

抽象度の点ではTerraformよりCDKの方が高いです。一方でTerraformはAWSに限らずGCPなどのクラウドプロバイダーではなくGitHubなどのWebサービスのリソースも管理できます。

特定のクラウドプロバイダーと結びついていないことは柔軟な機能にも繋がっており、コード上で管理していないリソースをインポートしてさも最初からコードで管理されていたかのように扱える terraform import は早いうちから実装されており便利です。 ちなみにCloudFormationでは2019年11月に既存リソースのインポートがリリースされました: 新機能 – CloudFormation スタックへの既存リソースのインポート | Amazon Web Services ブログ

GCPなどAWS以外のリソースも扱う場合はTerraformを使うのがベストです。

一方でCDK for TerraformというバックエンドをTerraformとしたCDKも開発・リリースされています: CDK for Terraform: Enabling Python & TypeScript Support 日本語記事: AWS CDKでプロバイダーとしてTerraformが使える!!CDK for Terraformが発表されました!! #awscdk | Developers.IO

また同様にKubernetesのリソースを管理できるCDK for Kubernetesもリリースされています: Introducing CDK for Kubernetes | Containers

このようにCDKの基本的な概念は他のIaCプラットフォームにも拡張可能ということがDeveloper Previewとして示されており、比較的安定しつつあるCDKでこれらの体験に触れることは今後を見据えればポジティブではないでしょうか。

まとめ

  • CDKは、インフラとアプリの関係性の変遷に応じて生まれた新しいツールキットです
  • 単にインフラリソースを管理するだけではなく、クラウドプロバイダのサービスをあたかも外部モジュールのように扱う設計が特徴です
  • しかもTerraformやKubernetesへの展開も進みつつあり、既存のIaCから拡張された普遍的な概念にもなりえそう

付録: 筆者とAWS CDK

speakerdeck.com

AWS Cloud Development Kit -CDK- Meetupというイベントで新サービス開発にAWS CDKを採用した事例とその特徴について発表しました。上記スライドはその時の資料です。

またaws-cdk · GitHub Topicsにこれまで公開してきたAWS CDK関連のライブラリやリポジトリがあります。