Developoers.IO 2019 Tokyo 登壇資料「AWS CDKの基本と実例」 #cmdevio

Developers.IO 2019 Tokyoで、AWS CDKについて登壇したので、その内容を公開します。
2019.11.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Developers.IO 2019 Tokyoで、AWS CDKについて登壇したので、その内容を公開します。

目次

  • AWS構築の歴史
  • AWS CDKを使うメリット
  • AWS CDKでテストを行うべき理由
  • AWS CDKを学ぶ際に見るべきもの(参考リンク紹介)

AWS構築の歴史

概要

このセクションでは、AWS構築の歴史を紹介していきます。CDKは構築手段の一つなので、他と比べてどう違うかという事を理解してもらう目的です。

AWS構築の世代

私はAWS構築の歴史を4世代で捉えています。世代という表現ですが、ブログを書く際はマネジメントコンソールからリソース作成(1世代)を行うなど、ユースケースに応じて選択すべきです。
それぞれの、構築方法を説明します。

手順書

手順書を用意して、それに従ってAWSマネジメントコンソールから作成を行う方法です。手順書というのは、Markdownで生成されたPDF or HTMLやExcelに大量にスクリーンショットが貼られた例のアレなどです。

Infrastructure as Codeが存在しなかった、オンプレミス時代から脈々と続く伝統的な方法ですが、この方法は、サービス仕様の確認や、必要なリソースが少ない場合以外では利用すべきないでしょう。

なぜなら、人が操作するので時間がかかるしミスも起こります。たとえ二重三重にチェックを行うとしても、ミスは0にはなりません。さらに、サービス更新に伴ってマネジメントコンソールは頻繁に画面が更新されます。なので、手順書とズレが発生してしまい、作業者が判断して手順を置き換える必要があります。この際に判断を謝るとリソースの作成失敗が起これば良い方で、最悪の場合は意図していない設定でリソースが作成されます。リリース後に気づいて修正の為にメンテナンスタイムが必要になったりします。

利点はマネジメントコンソールから操作をすると、必要な関連リソースの作成をアシストしてくれたり、裏でリソースを作ってくれる事です。

例えばLambdaを作成する際にはユースケースに応じて、IAMロールを簡単に作成・設定する事ができます。これ以降の方法は、マネジメントコンソールを使わないので、当然このアシストを受けることができません。

私はブログを書く際にマネジメントコンソールからリソースを作成するという方法を選択する事が多いです。

スクリプト

AWS CLIやSDKを利用してBashやRuby、Pythonなどでスクリプトを作成し、そのスクリプトを実行する事でAWSリソース作成を行う方法です。この方法ならコード通りにリソースが作成されるので作業者によるミスを防ぐことができます。また、コードで管理されるので、VCSを使って世代管理やレビューも行いやすいです。
しかし、冪等性やロールバックの処理の実装が必要となります。例えば、S3バケット→EC2インスタンスの順番に作成するとしましょう、S3バケットの作成は成功したが、EC2インスタンスが上限で作成できなかった場合は、S3バケットのみが作成されます。
この後、上限緩和を行い、スクリプトを実行するとEC2インスタンスは作成されますが、S3バケットが初回の実行時と合わせて2個できてしまうか、名称重複で作成が失敗します。
この問題に対処するには、AWSリソースの作成に一部失敗した際に、作成済みの他のリソースを削除する様な処理を実装する必要があります。 つまり、自分が作ったのは何かステートを保持する必要があります。これを自前で実装するのはかなりめんどくさそうです。

プロビジョニングツール

AWS CloudFormationやHashiCorp Terraformなどのプロビジョニングツールを使った宣言的なAWSリソースの構築です。それぞれYAML、HCL(HashiCorp Configuration Language)といったデータ構造を宣言する事でAWSリソースを作成します。

複雑な条件分岐やループを使う事は難しく、できたとしても黒魔法チックになり他の人から読みづらくなります。

Document Object Models

プログラム言語を使ってCloudFormationを生成する方法です。GoFormation(Go)やTroposphere(Python)などがあります。最終的にできあがるのは、CloudFormationですが、プログラム言語なので、複雑な条件やループを扱うこともできます。 また、AWS SDKを使ってCloudFormationのデプロイまで行うこともできます。ただし、CloudFormationを経由するので、基本的にメリットもデメリットも引き継ぎます。

AWSの使い方の変化

時代の流れと共に、AWSの利用の仕方が変わっていった結果、AWSマネージドサービスを活用される機会が増えました。ALB・EC2・RDSだけを使っていた時代から、これらに加えてLambdaやAPI Gateway、SQS、SNSなどを使うようになりました。

これによって、Infrastructure as Codeする際に必要な記述量がどんどん増えていきます。
今までは、これに対してCloudFormationを分割する事やTerraform Moduleを使う事で対処して来ました。しかし、これらの方法はCloudFormation、Terraformを普通に使うのに対してプラスアルファの知識や考慮が必要で、それなりに大変でした。

今日の発表はこういった状態を前提として、CDKの良さをお話します。なので、このプロダクトでは、ALB-EC2-RDSしか使わないし他のプロダクトもそうだという場合は、恐らくCDKはオーバースペックで導入コストと比較して見合わないです。ご注意をお願いします。

AWS CDKを使うメリット

概要

AWS CDKは、プログラミング言語でAWSリソースの定義を書き、CloudFormationテンプレートを生成し、デプロイを行えるツールです。

型がある

AWS CDKはTypeScript、Python、およびプレビューですがJava、C#(.NET)でインフラを定義できます。
これによって、型がある言語ならIDEの支援を受ける事ができます。Infrastructure as Codeをやっていると、LambdaのランタイムをPythonにしたいけど、実際に何という文字列を書けば良いかわからない(小文字なのか大文字なのか、スペースは入るのか)と言った場合や、同様にRDSのエンジン指定でいちいち調べるのが面倒だと感じる事がありました。型によってこれらの問題から解放されます。

前処理をコードで書くことができるので、Lambdaデプロイ前にビルドを行うようにMakefileを作るという手間から解放されます。結局処理を書くという事には変わりないですが、私は一度慣れるとMakefileよりコードで書いたほうがより楽に感じました。

また、コードなので条件分岐やループも簡単にできます。

そして、CDKに元々用意されているAWSリソースのコンストラクトを継承して拡張する事で、独自のデフォルト値を決めたり、AWSリソースを1つにまとめて簡単に扱う事もできます。

AWS構築のフレームワーク

Webアプリケーションを作る時に、フルスクラッチで書くのではなく、Webアプリケーションフレームワークを使う場合があります。この文脈と同じ意味で、AWS CDKはAWS構築のフレームワークです。 直接CloudFormationなどを書くより少ない記述量で構築が行えます。

CDKによる抽象化

CloudFomationでLambdaとS3・DyanmoDBを作成し、Lambdaからアクセスできるようにしたい。よくある要件ですが、必要なAWSリソースはとても多いです。

  • Lambda Function
  • S3 Bucket
  • DynamoDB Table
  • IAM Role
  • IAM Policy
  • IAM Role-Policy 関連付け
  • Lambda Function-IAM Role 関連付け

IAM Policyでどのリソースに割り当てるかを制限が可能で、セキュリティのベストプラクティスとしては権限を最小化すべきです。
しかし、全てのLambda毎にIAMロールを作成するのは管理が大変です。今までは、Lambdaを役割でグループ分けしてIAMロールを割り当てるなど妥協をしていました。(権限の最小化よりも管理できない事が危険だという判断)

CDKならたったこれだけの記述で、2つのLambdaとDynamoDBとS3バケットを作成し、最小化された権限を付与してくれます。

複数環境へのデプロイの考慮

cdk.jsonというjsonファイルにAWSアカウントにの番号やリージョン、リソースの個数やスペックを設定して置くことで、環境毎の差分の考慮が行いやすい作りになっています。 ここで設定した情報はコード内で簡単に取り出す事ができます。 コンテキストはファイルだけでなく、実行時のコマンドラインオプションでも指定が可能です。

これを使い、cdk.jsonに各環境毎のパラメータを定義しておき、コマンドラインオプションで環境を指定する事で、簡単に環境ごとのデプロイが行えます。

AWS CDKでテストを行うべき理由

概要

CDKでAWSを構築する=プログラミングです。つまりテストが必要になってきます。 このセクションでは、CDKではどのようなテストが、なぜ必要なのか説明して行きます。実際のテストコードは紹介しませんが、スライドの最後で、コードが乗っている参考サイトを紹介します。

CDKにテストが必要な理由

私が考える、CDKにテストが必要な理由は大きく2つあります。1つ目はCDKが頻繁にアップデートされる事への対応として必要だからです。 CDKは正式リリース後ハイペースでアップデートが行われており、少なくとも2週間に1回はアップデートが行われています。プロダクトでCDKを使い始めた時からリリースされるまでの間には十中八九バージョンが変わっています。 なので、CDKをバージョンアップした際に、生成されるCloudFormationに変化が無い事を確認する必要があります。試しに作ったCDKでRenovateというパッケージの自動アップデートツールを使って見た所、CDKのアップデート後に、後述するスナップショットテストが失敗する事はよくありました。

もう1つ目は意図した変更が行われている確認が必要だからです。例えばRDSを1つ作るという課題が自分に割り振られて、CDKで追加したとしましょう。RDSは作成にそれなりに時間がかかる為、開発環境があったとしても、とりあえずデプロイしてみて確認しようというのは面倒です。かといって生成されたCloudFormationを全部読むのも大変です。
そこで、今行ったCDKに対する変更でCloudFormationにどんな変更が行われたかという差分を確認する事で、意図した変更を確認するという方法が便利です。

テストの種類

CDKのテストには大きく分けて3つの粒度があります。

Snapshot tests

Snapshot testsは先程説明したケースに該当するもので、生成されるCloudFormation全体がどのように変化したかを確認するテストです。コードが変更されても生成されるCloudFormationが変化しない事を担保するテストです。

Fine-grained tests

Fine-grained testsはCDKが生成したCloudFoamtionに意図したリソースが含まれているかのテストです。先程紹介しように、LambdaとS3をアクセスさせる場合はCDK内では明示的に宣言していなくても、IAMリソースも作成されているはずで、それをテストします。 例えば、元々EC2だけが存在するCDKで作成したインフラ環境を想定してください。ここに、RDSを追加すると、Snapshotテストは必ず失敗します。 この際に、生成するCloudFormationを確認しなければ、既存のEC2に破壊的な変更が行われたかわかりません。Fine-grained testsが存在すればEC2のパラメータが正しい事はテストされており、RDSを追加しても、そのEC2に関するテストは失敗しないのでEC2に破壊的な変更が行われていない事がわかります。

Validations tests

Validations testsはStack作成時に呼び出すConstractsに正常・異常なパラメータを設定してみて、正常か・エラーを吐くかを確認するテストです。 例えば、RDSのバックアップ期間の様な上限が存在するリソースに対して上限値を超える値を設定しようとした時にエラーが発生し、そうではない場合は正常に動作する事をテストします。CDKに用意されているコンストラクトは予めテストされている(はず)なので、自分で作成したコンストラクトに対して作成します。

AWS CDKを使う実際の流れ

このセクションは、簡易的なチュートリアルを見せるセクションなので、ブログでは説明を省略します。

AWS CDKを学ぶ際に見るべきもの