AWS

CDKで「エスケープハッチ」してみた

yoshi

はじめに

暑い日が続きますね。週末にサウナに入って水風呂に入るのが日課になっている「よっしー」です。

最近仕事でAWS CDKを使用していて、初めて「エスケープハッチ」する機会があったので、「エスケープハッチ」について書きたいと思います。

コンストラクトのレベル

まず前提として、CDKのコンストラクトには、L1、L2、L3という3つの種類のコンストラクトがあります。
L1コンストラクトは最も抽象化のレベルが低く、CloudFormationテンプレートのリソースセクションと1対1で対応しており、プロパティを細かく設定することが可能です。
L2コンストラクトはAWSリソースが使用しやすい形で抽象化されており、ベストプラクティスに沿ったデフォルト値があらかじめ設定されていたり、ヘルパーメソッドが用意されていたりと、CloudFormationの詳細なプロパティを設定することなく、手軽に利用できるようになっています。
また、L3コンストラクトは複数のL2コンストラクトをまとめたアーキテクチャパターンのレベルまで抽象化されており、最も抽象化のレベルが高く、特定のユースケースにおいて最小限の設定で迅速にリソースを展開できるようになっています。

通常は、L2、L3など抽象化レベルの高いコンストラクトを使用することで、開発の効率性や利便性を享受することができますが、全て完璧に設定ができるわけではありません。使用したいプロパティやメソッドが提供されていないことはしばしばあります。そのような細かなカスタマイズが必要な時は、L1のレイヤーに降りて作業する「エスケープハッチ」が必要になります。
「エスケープハッチ」は、文字通り、高レベルの抽象化から低レベルの抽象化へ脱出するための「脱出口」となります。

エスケープハッチ

今回は簡単な例として、Aurora PostgreSQLの「RDS 延長サポート」の設定を「エスケープハッチ」を使用して無効化してみたいと思います。
CDKでAuroraを作成するとデフォルトで「RDS 延長サポート」は有効になります。無効にするためのプロパティは、CloudFormationやL1コンストラクトである「CfnDBCluster」では「EngineLifecycleSupport」という形で提供されていますが、L2の「DatabaseCluster」では現在は提供されていません。

Amazon RDS 延長サポート
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/extended-support-creating-db-instance.html

簡単に、最低限起動するだけのプロパティを設定してcdk deployでデプロイしてみます。

    const aurora = new rds.DatabaseCluster(this, 'DatabaseCluster', {
      engine: rds.DatabaseClusterEngine.auroraPostgres({
        version: rds.AuroraPostgresEngineVersion.VER_16_1,
      }),
      vpc: props.vpc,
      writer: rds.ClusterInstance.serverlessV2("Writer", {}),
    })

この状態で、RDSのコンソールを見ると、「RDS 延長サポート」は有効になっています。

では、これを無効化するために「エスケープハッチ」してみましょう。

    const aurora = new rds.DatabaseCluster(this, 'DatabaseCluster', {
      engine: rds.DatabaseClusterEngine.auroraPostgres({
        version: rds.AuroraPostgresEngineVersion.VER_16_1,
      }),
      vpc: props.vpc,
      writer: rds.ClusterInstance.serverlessV2("Writer", {}),
    })

    // escape hatch
    const cfnDbCluster = aurora.node.defaultChild as cdk.aws_rds.CfnDBCluster;
    cfnDbCluster.engineLifecycleSupport = "open-source-rds-extended-support-disabled";

最後の2行が「エスケープハッチ」になります。L2コンストラクト「DatabaseCluster」のインスタンスを作成し、.node.defaultChildとすることで、L1コンストラクト「CfnDBCluster」を参照しています。

この.node.defaultChildという部分が自分はピンとこなかったのですが、コンストラクトツリーについての説明を見て理解できました。

コンストラクトツリー
https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/apps.html#apps-tree

各コンストラクトには1つのノードがあり、ノードを通してコンストラクトツリーという階層構造を表現しています。コンストラクトは、ノードを通して、親コンストラクトや子コンストラクトの情報を保持しています。
試しに以下のようなコードを書いて、L2コンストラクト「DatabaseCluster」がどのような子コンストラクトを持っているかを確認してみましょう。

const aurora = new rds.DatabaseCluster(this, 'DatabaseCluster', {
    ...
})
console.log(aurora.node.children)

---

<ref *1> SubnetGroup {
id: 'Subnets',
<ref *2> SecurityGroup {
id: 'SecurityGroup',
...
<ref *5> DatabaseSecret {
id: 'Secret',
<ref *6> CfnDBCluster {
id: 'Resource',
<ref *7> AuroraClusterInstance {
id: 'Writer',

aurora.node.childrenで子コンストラクトを見てみると、複数のコンストラクトを持っていることがわかります。特に、ResourceというIDを持つコンストラクトは特別なコンストラクトで、通常、そのL2コンストラクトにおいて最も重要なL1コンストラクトを指しており、.node.defaultChildで参照することが可能となっています。L2コンストラクト「DatabaseCluster」の場合、主要な子コンストラクトとして、L1コンストラクト「CfnDBCluster」を含んでいます。
言い換えると、L2コンストラクトである「DatabaseCluster」は、L1コンストラクトである「CfnDBCluster」をコンポジションしているとも言えます。

そのため今回は、.node.defaultChildを使用してL1コンストラクトを参照し、適切なL1のタイプにキャストし、L1コンストラクトのプロパティ「engineLifecycleSupport」を直接変更し、L2コンストラクトの動作をカスタマイズしています。

defaultChild
https://docs.aws.amazon.com/cdk/api/v2/docs/constructs.Node.html#defaultchild

cdk diffで差分をみてみると、「EngineLifecycleSupport」が追加されています。

$ cdk diff
...
Resources
[~] AWS::RDS::DBCluster DatabaseCluster DatabaseCluster68FC2945
 └─ [+] EngineLifecycleSupport
     └─ open-source-rds-extended-support-disabled

そのままcdk deployでデプロイし、RDSのコンソールで確認すると、「RDS 延長サポート」が無効になっていることを確認できました。

まとめ

今回は、「RDS 延長サポート」の無効化を例に、簡単にL2コンストラクトからL1コンストラクトに「エスケープハッチ」し、L1コンストラクトのプロパティを設定できることを確認しました。「エスケープハッチ」を知るまでは、いかなる時もCDKはL2かL3で書くべきと勝手に思い込んでいたところがあり、L1は使うことにかなり抵抗を感じていました。そのため、L1を使ってまで実装することはないと思い、実装自体を見送ったり、最悪手動で設定をしてしまったりしていたケースがありました。「エスケープハッチ」という方法を知ることで、L2やL3の利便性を受けながら、「必要なとき」はL1のレイヤーに降りて細かなカスタマイズができ、痒いところに手が届く良い手法だと実感しました。これからは今まで以上にL1とも仲良くしていけたらと思っています。

参考

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/cfn_layer.html
https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/constructs.html

AUTHOR
yoshi
yoshi
記事URLをコピーしました