この記事は、ギークフィード Advent Calendar 2023 21日目の記事です。
クリスマスがもうすぐやってきますね…!
目次
はじめに
改めまして、新人エンジニア冨田です。
私の所属するチームではAWS上でシステムを構築しており、EC2・RDS構築などのインフラ業務を行っています。
CDKを導入した理由としては、リストアやDRの容易さ・コード管理にあります。
具体的なCDKの利点に関しては弊社西山が書いた記事を参考にしていただけるとわかりやすいです。
初めてチームにAWS CDKでIaCを導入してみた
インフラチーム内でCDKスタックがイケてないと話し合い、イケてるCDKとは何かを追い求めています。
そんな時、RDSをL1コンストラクタからL2コンストラクタに変更してみたいと思ったのですが、意外とブログ記事にはまとまっておらず苦労した記憶があります。
本記事では、備忘録も込めてL2コンストラクタでイケてるスタック作成に挑戦します。
私も初学者の身ですのでアウトプットも兼ねて、わかりやすく説明いたします。是非参考にしてください。
※環境構築の手順は省いての説明になります。
AWS CDK レイヤーモデルについて
そもそもL1とかL2って何なんだよ…。って話ですよね。
AWS Cloud Development Kit (AWS CDK) v2 デベロッパーガイドによると、以下の内容になります。
AWS CDKには大体2つの層があり、レイヤー1(L1)が低、レイヤー2(L2)が高レイヤーと呼ばれます。
(L3もあります。)
L1レイヤーは、CloudFormationを書く際に使用するパラメータと同様の設定をすることが出来ます。
ただし、CloudFormationテンプレートに沿って書くのでその自由度や抽象度がほとんどなくなります。
誰が書いても同じものになりそうですよね。
対してL2レイヤーでは、プロパティやメソッドは自動生成ではなく、人間が開発者にとって使いやすいものを考えて設計されています。
ガイドラインに沿って作成さえすれば、同じことをやるとしても様々な手法を取れるということになります。
かといって、L1がダメ、L2がいい!なんてことはないです。
必要に応じて使い分けていく必要があります。
イケてるCDKスタック
以前のスタックを見て、何がイケてなかったのかを具体的に列挙していきたいと思います。
1.cdk.jsonにべた書きしていた
20個ほどあるスタックすべてが1つのcdk.jsonから値を参照しており、cdk.jsonがめちゃくちゃになっていました。
例えば、
1 2 3 4 5 6 7 8 9 10 11 12 13 |
"hogehogeSubnetInstanceName":[ "hogehoge-tyo-ec2-1", "hogehoge-tyo-ec2-2" ], "piyopiyoSubnetInstanceName": [ "piyopiyo-tyo-ec2-1", "piyopiyo-tyo-ec2-2", "piyopiyo-tyo-ec2-3", "piyopiyo-tyo-ec2-4", "piyopiyo-tyo-ec2-5", "piyopiyo-tyo-ec2-6" ]... |
インスタンス名1つ探すだけでも大変です。探すときはいつもマウスホイールをずっと回していました。
AWS System Managerのパラメータストアを使用することで、パラメータを複数に分け、可読性を向上させ誤った参照を起こさないようにできます。
2.ローカルでパスワード管理していた
先ほどのcdk.jsonにパスワードを書き込んでいたため、ローカルでパスワードが管理されていました。
また、cdk.jsonにべた書きするとCloudFormationのスタック情報にパスワードが出てしまうという脆弱性もあります。
パスワードはできる限りローカル管理せず、SecretManagerに任せていきましょう。
スタックの完成形
【ざっくり要件】
・VPC、SG等のリソースはすべて構築済み
・cdk.jsonからではなく、パラメータストアから呼び出してくる
・RDSのpasswordはsecret managerに登録
・L1コンストラクタとL2コンストラクタの併用を行い、CDK未対応のエンジンバージョンを採用する。
完成したスタック情報
具体的な説明は↓に続きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as ec2 from "aws-cdk-lib/aws-ec2"; import * as rds from "aws-cdk-lib/aws-rds"; import * as ssm from 'aws-cdk-lib/aws-ssm'; import * as secretmanager from 'aws-cdk-lib/aws-secretsmanager'; export class TommyPracticeRds extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const SsmcommonParameter = ssm.StringParameter.valueForStringParameter(this, '/My/tommy-hogehoge-pram'); const supernJson = JSON.stringify(SsmcommonParameter); const commonJson = JSON.parse(supernJson); const vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: commonJson.vpc }); const subnetGroup = rds.SubnetGroup.fromSubnetGroupName(this, 'SubnetGroup', commonJson.subnetGroup); const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(this, 'SecurityGroup', commonJson.securityGroup); const userName: string = 'admin'; const rdsName: string = 'testdb'; const rdsCredentials: rds.Credentials = rds.Credentials.fromGeneratedSecret(userName); const DBInstance = new rds.DatabaseInstance(this, "DBInstance", { availabilityZone: 'ap-northeast-1', vpc, instanceIdentifier: 'tommy-hogehoge-rds', engine: rds.DatabaseInstanceEngine.POSTGRES, securityGroups: [securityGroup], subnetGroup: subnetGroup, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO), credentials: rdsCredentials, caCertificate: rds.CaCertificate.RDS_CA_RDS2048_G1, }); const cfnDbInstance = DBInstance.node.defaultChild as cdk.aws_rds.CfnDBInstance; cfnDbInstance.engine = 'postgres'; cfnDbInstance.engineVersion = '15.3'; } } |
AWS Systems Managerパラメータストアから値を指定するように設定する
今回は可読性向上のためcdk.jsonからの参照を撤廃し、パラメータストアから値を参照します。
1 |
import * as ssm from 'aws-cdk-lib/aws-ssm'; |
をインポートし、AWS Systems Managerのライブラリを使用できるようにします。
次にパラメータストアの設定を行います。
パラメータの作成を行い、参照したい値を入れます。
最大の文字数が4096文字までなので、必要に応じてパラメータを分けて下さい。
また、CDK側で以下のように参照したいパラメータストアの名前を参照し、変数に格納します。
1 2 3 |
const SsmcommonParameter = ssm.StringParameter.valueForStringParameter(this, '/My/tommy-hogehoge-pram'); const supernJson = JSON.stringify(SsmcommonParameter); const commonJson = JSON.parse(supernJson); |
呼び出す設定はこれだけでOK。
この時点で各段にイケてる感じがします。
後は、下記のようにパラメータストアの値を変数に格納してあげれば自由に使うことが出来ます。
1 2 3 4 5 |
const vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: commonJson.vpc }); const subnetGroup = rds.SubnetGroup.fromSubnetGroupName(this, 'ExitingSubnet', commonJson.subnetGroup); const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(this, 'SecurityGroup', commonJson.securityGroup); |
AWS Secrets Managerで機密情報の管理を行う
1 |
import * as secretmanager from 'aws-cdk-lib/aws-secretsmanager'; |
をインポートし、AWS Secrets Managerのライブラリを使用できるようにします。
1 2 3 4 |
const dbUser: string = 'admin'; const dbName: string = 'testdb' const rdsCredentials: rds.Credentials = rds.Credentials.fromGeneratedSecret(dbUser); |
RDSで使用するCredentialsクラスを事前に定義してくことで
パスワードはDeploy時に自動作成され、AWS Secrets Managerにて管理されます。
ローカルで管理していた時よりも格段に安心感があります。
RDSをL2で構築する
以下のように構築することでL2コンストラクタのRDSを構築することが出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
const DBInstance = new rds.DatabaseInstance(this,"DBInstance",{ availabilityZone: 'ap-northeast-1', vpc, instanceIdentifier: 'tommy-hogehoge-rds', engine: rds.DatabaseInstanceEngine.POSTGRES, securityGroups: [securityGroup], subnetGroup: subnetGroup, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO), //上記で指定したCredentialsクラスを使用する。 credentials: rdsCredentials, //証明書はデフォルトだと、2024年に有効期限切れになるため注意 caCertificate: rds.CaCertificate.RDS_CA_RDS2048_G1 }); //エスケープハッチを使ってL2コンストラクタで指定不可なバージョンをL1コンストラクタで指定する const cfnDbInstance = DBInstance.node.defaultChild as cdk.aws_rds.CfnDBInstance; cfnDbInstance.engine = 'postgres'; cfnDbInstance.engineVersion = '15.3'; |
L2コンストラクタは抽象化されており非常に便利ですが、そのためのプロパティやメソッドが提供されていないことはしばしばあります。
そんな時に、エスケープハッチを利用して具体的な処理を指定しましょう。
ここではnode.defaultChildプロパティを利用してDefaultのノードを取得しています。
本来L1コンストラクタでのみ使用可能なバージョンもL2で呼び出すことが出来ます。
おわりに
いかがでしたでしょうか?
イケてるコードへはまだまだ長い道のりになりそうです。
精進していきます!
また次のブログでお会いしましょう!
- 【AWS CDK】RDS L2で指定できないエンジンバージョンを使用する方法を実装しました。 - 2023-12-21
- AWS ベストプラクティスに沿った作業を確実に! 知識の宝庫 AWS 規範的ガイダンスの使い方 - 2023-12-09