CDK利用時の注意点

皆さん、AWS CDKは利用していますか？
CDKは使い慣れた言語でAWSリソースをコード化し、再利用できるため、上手く用いれば爆速で環境構築が可能です。しかし、「プロジェクトの要件としてCDKの利用が推奨されたから」「AWSのセールスにCDKを推奨されたから」という理由で理解が浅いままＣＤＫを利用すると、かえってCloudFormationでの構築より工数が増えることもあり得ます。新規プロジェクトでCDKの採用が決まった際、留意すべきことをまとめたのでぜひ参考にしてください。

# 【原則】ＣＤＫはプログラミングだ　～プログラミング経験者をアサインせよ！～
以下にいろいろ留意点を書いていますが、まとめるとこの一言に尽きます。CDK構築はプログラミング要素が強いため、今までの基盤構築の経験が通用しないケースが多数発生します。アプリ開発経験がある方をチーム内に組み込み、アプリ開発の知見を活かしましょう。

## 1.　プログラミング経験者をアサインせよ

TypescriptやPythonでCDKを構築する場合、各リソースを順番に静的に宣言していくだけでも構築は可能です。しかし、それではプログラミングの利点を発揮できません。For文やIf文、クラス等を利用することで、コード量を減らし効率化できます。最低限クラスについて理解できるくらいのプログラミング力がメンバー全員にあると開発が捗ります。プログラミング初心者が躓きやすい要素は、他にもスタックやコンストラクタ間での値の渡し方、synthやエラーの解決法、gitの使い方などたくさんあります。基盤出身者だけでチームを組まず、アプリ出身者をチームに含め、教育体制を整えましょう。
CDK開発に必要なスキルは以下記事にまとめたので参考にしてください。

https://qiita.com/Figure_Skate_IT/items/761e626b4213b6321e6d


同じIaCでも、TerraformやCloudFormationは宣言型で覚えることが少ないため、プログラミング未経験者でも扱えます。また、構築作業も以下のようにマニュアル化できます。チームにプログラミング経験者が少ない場合、CDKの代わりにこれらの手段を検討しましょう。
1. 設計書の該当箇所を読む
2. 以下公式サイトで設計要素を確認する
3. 不明点があれば調査する
4. Json（YAML）で記述する
5. 有識者がレビューする

CloudFormationのリファレンス

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html


## 2. 型に注意せよ
TypesciptでCDKを利用した場合、型の違いに悩まされることがあります。たとえば、Undefined型をString型に渡すことはできません。このほかにも、interface ITaskDefinition　は、Ec2TaskDefinition, ExternalTaskDefinition, FargateTaskDefinition, TaskDefinitionなど様々な型で利用されますが、それぞれの型の違いを意識しないとエラーが発生します。

## 3. 循環参照に注意せよ
複数のスタックで同じリソースを変更すると、循環参照エラーが発生します。例えば、S3用スタックで作成したバケットを、EC2用スタックで利用する際、EC2スタックはS3スタックに依存します（S3スタック←EC2スタック）。この状態で、EC2スタック側でS3スタック内のS3のバケットポリシーを変更しようとすると、S3スタックにEC2スタックへの依存関係が発生します(EC2スタック←S3スタック)。その結果循環参照が発生し、CDK synthでエラーが発生します。解決方法は以下記事に詳しくまとめられているので、ご参照ください。

https://dev.classmethod.jp/articles/aws-cdk-props-cross-stack-reference-problem-and-handle/

## 4. デプロイエラーに注意せよ
CDKに関する経験が浅いと、デプロイエラーが何回も発生し、エラー解決にかなり時間がとられます。エラー発生中はデプロイがストップするため、できれば実験的にデプロイできる環境を複数用意し、開発がストップしないようにしましょう。

よくあるエラーの例と解決策をまとめたので、参考にしてください。
|エラー例|説明|解決方法|発生頻度|
|--|--|--|--|
|Already Exists|デプロイした資材がすでに存在した場合エラー。リソース名を明示的に指定した場合、作成済みと同じ名前のリソースを作成すると発生。論理IDを変えた際や、以前作成したリソースと同じ名前を付けた際などに遭遇。|解決策は３つある。①（ＣＤＫ推奨）リソース名を明示的に指定しない。<br>②（①ができない場合）リソース名を変える。<br>③既存のリソースを削除する。|リソース名を明示的に指定した場合、よく発生する。半分くらいこのエラーの印象|
|Cloudformation stuck in UPDATE_ROLLBACK_FAILED|何らかのデプロイエラーが発生し、ロールバックに失敗した場合に発生。|Cloudformationコンソールから手動で対象リソースをロールバックする。最悪全削除が必要なことも|たまに遭遇。DB関連のロールバックで発生しやすい。|

## 5. コード規約を作成せよ
コード規約やクラスの作榮方法などを決めておかないと、次のような不都合が起こります。基盤経験者だけではどのようなコード規約を作成すべきか分からないので、アプリ経験者の知見を活かしてプロジェクトとしてのコード規約を作成しましょう。

①リソースの論理IDや変数の名前がバラバラで可読性が落ちる。
②CDK公式のコンストラクタを継承しないクラスを各メンバーが勝手に作成し、可読性が落ちる。また、再利用が困難になる。
③パラメータ値をどこまでコンストラクタ内に書いて、どこまで外だしするか（例　A：環境差分のみ、B：CPU数等今後変更があるかも知れないもの　C：リソース名も外だしする。D：基本すべて外だし可能なものは外だしする　等）がバラバラで、再利用が困難になる。

## 6. クラスの作成方針を明文化せよ

ただコード規約を作成するだけでなく、どのような方針でスタックやコンストラクト（を継承したクラス）を作成し、構造化していくか、プロジェクト内で統一しましょう。TerraformやCloudFormationと比較して自由度が高い分、各々がそれぞれの思想で勝手にクラスを作成すると、気が付いたらコードの構造が180度違うものになってしまうこともあります。


例として以下のように、EC2及び、CloudWatchによるログ収集の仕組みを作成するとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3482344/20489471-e8bc-a24f-8c9e-491cffd4d46d.png)

要件：EC2ごとにCloudWatch Logsのロググループを分け、これらのログをKinesis Data Firehose経由でS3バケットに送付する。
<br>

Ａさんは、「各クラスはリソース別に作成すべき」と考え、以下のようなクラス構成としました。

EC2用クラス：EC2①、②、③を作成
CloudWatch用クラス：CloudWatch Logs ロググループ①、②、③を作成
Kinesis用クラス：Kinesis Data Firehose①、②、③を作成
S3用クラス：ログ用バケット、業務データ用バケットを作成
<br>

Ｂさんは、「同じ構成をクラスにまとめて再利用しよう」と考え、以下のようなクラス構成にしました。

S3用クラス：ログ用バケット、業務データ用バケットを作成
EC2＆監視用クラス：EC2＋CloudWatchLogs+Kinesisを一つのクラス内に作成

EC2&監視用クラスを三回利用することで、EC2、CloudWatchLogs、Kinesis Data Firehoseのセットを３つ作成する。

<br>
どちらの考えもメリット、デメリットがあるため、プロジェクトの性質や規模間、メンバーのスキル、利用できるアセットに応じて使い分けることが大切です。しかし、一つのプロジェクト内で違う思想のクラスが作成されると、保守性や再利用性が落ちてしまいます。製造が始まる前にしっかり意識合わせを行いましょう。

https://qiita.com/Figure_Skate_IT/items/0e70f2d6b10cede6c0db

## 7. スタックの分け方に注意せよ
スタックはデプロイの頻度等に応じて適切に分割することが大切ですが、うまく分けないとスタック間循環参照エラーが発生します。1つのスタック内で全リソースを作成すると循環参照エラーも発生しませんが、スタック内のリソース上限500に引っかかってしまいます。「500もいかないでしょ」と思いますが、たくさんバケットを作成したり、監視項目を増やしたり、細かくサブネットを分けたりすると、すぐ上限に到達します。開発途中でのスタック構成変更には時間がかかるので、スタック間参照やリソース上限に注意しながら上手くスタックを分けてください。


## 8. すぐに専門家にエスカレーションできる体制を用意せよ
プログラミング未経験者がプロジェクト内にいる場合、Synthのエラーやデプロイエラーが自力では解決できないことが多々あります。また、L2コンストラクトが対応していない、実装にカスタムリソースの利用が必要、どうしても循環参照エラーが解決しないなど、経験者でも対応が難しい問題に遭遇することがあります。これらの問題の解決に時間をかけすぎると、必要な作業が進まず、遅延につながります。問題が発生したら即座にチーム内の有識者やAWSサポートに即座に質問できる体制を整え、目の前の開発に集中できるようにしましょう。

## 9. 利用できるアセットを集めよ
ゼロからCDKコードを作成する場合、コードの構成方法が分からず、とても時間がかかります。以下のリンクを参考にしてコード例を集め、真似できるようにしてください。

https://github.com/aws-samples/aws-cdk-examples




また、以下ブログでCDK学習方法や役立つリンク集をまとめたので参考にしてください。

https://qiita.com/Figure_Skate_IT/items/83a395d26567f01b3ae2


## 最後に
以上、ＣＤＫを利用する際の注意点でした。
今後も各項目の解決方法が載った記事のリンクをつけるなど、本記事を改善していこうと思います。
最後まで読んでくださり、ありがとうございました。