宇宙最速!?CDK Refactor(Preview版)を試してみた in JAWS-UG CDK支部#22 #jawsug_cdk
はじめに
JAWS-UG CDK支部にて、「宇宙最速!? CDK Refactor(Preview版)試してみた」という内容で発表します。その際の資料として、本ブログを使用します。CDK Refactorは以下のブログで公開された機能です。
詳細な動作については、本ブログまたは以下の公式ドキュメントが参考になります
CDK Refactorとは
簡単に言うと、「AWS CDKのスタック/コンストラクトの構造をリファクタリングする機能」です。リファクタリングという名前の通り、外部から見た振る舞いは変えずに、内部構造を意図した形に再構成する機能です。例えば、DBの定義をAスタックで実装したあと、Bスタックに移動したいという操作を簡単に実行できます。
今まではステートフルなリソースを扱う場合、一度リソースをRetainで保持し、その後新しいスタック側で取り込むなど複数の操作手順が必要でした。従来はそれをすべて手動で行うか、CloudFormationのリファクタリング機能を使いました。後者の手順を以下のブログから抜粋します
- リファクタリングをする際には以下の2つの情報が必要
- リファクタリング後のStackのテンプレートファイル
- リファクタリング後に変更する論理IDをマッピングした定義ファイル
- AWS CDKでもデプロイしたStackに対しても使用できる
- AWS CDKでデプロイしているリソースをリファクタリングをする際のステップは以下
- AWS CDKのコードの修正
- npx cdk diffで差分と修正後の論理IDの確認
- npx cdk synthでテンプレートファイルを出力
- 出力されたテンプレートファイル内で、リファクタリング対象のリソースのメタデータを修正し、現在の値と一致させる
- リファクタリングの定義と実行
- npx cdk diffでメタデータのみ差分が出力されることを確認
- npx cdk deployでメタデータを反映
上記の手順はかなり多くのステップがあるため、時間を要する作業でした。これがAWS CDK Refactorだと以下の手順になり作業を簡略化できます。
- スタックは移行元/移行先両方デプロイしておく
- AWS CDKのコードの修正
- 以下のコマンドを実行
- npx cdk refactor --unstable=refactor
7~10ステップ程度必要だった作業が2~3ステップほどに短縮され、非常に簡単にスタック/コンストラクト構造のリファクタリングが可能になります!これならリファクタリングがより効率的に進められそうです!
CDK Refactor やってみた
実際の動作を確認していただく方が分かりやすいと思うので、以下の操作を実施します。できるだけ分かりやすい例として、LambdaとDynamoDBを定義しているスタックから、DynamoDBだけをほかのスタックに移動しリファクタリングしてみます。
まず以下のようにStackAとなるCdkRefactorTestStack
を作成します。サンプルなのでLambdaのソースはDynamoDBにアクセスするものではなく仮の内容です。
import * as cdk from 'aws-cdk-lib';
import { CdkRefactorTestStack } from '../lib/cdk-refactor-test-stack';
const app = new cdk.App();
const cdkRefactorTestStack = new CdkRefactorTestStack(app, 'CdkRefactorTestStack', {});
import * as cdk from 'aws-cdk-lib';
import { InlineCode, Runtime, Function } from 'aws-cdk-lib/aws-lambda';
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
export class CdkRefactorTestStack extends cdk.Stack {
readonly testLambda: Function; // 後ほど別スタックに渡すため設定
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const sampleCode = new InlineCode(`
exports.handler = async () => ({
statusCode: 200,
body: JSON.stringify('Success')
});
`);
const testLambda = new Function(this, 'TestLambda', {
runtime: Runtime.NODEJS_22_X,
handler: 'index.handler',
code: sampleCode,
memorySize: 128,
});
this.testLambda = testLambda
// 以下を移動
const table = new Table(this, 'SimpleTable', {
partitionKey: { name: 'id', type: AttributeType.STRING },
billingMode: BillingMode.PAY_PER_REQUEST,
});
table.grantReadWriteData(testLambda);
}
}
次にもう1つのStackBとなるCdkRefactorTest2Stack
を作成します。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Queue } from "aws-cdk-lib/aws-sqs";
export interface CdkRefactorTestStackProps extends cdk.StackProps {
lambda: Function;
}
export class CdkRefactorTest2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props: CdkRefactorTestStackProps
) {
super(scope, id, props);
const queue = new Queue(this, 'MyQueue')
}
}
DynamoDBを定義する前にスタックをデプロイしておく必要があるので仮のSQSを作成します。もしスタックがない状態でRefactorコマンドを実行すると以下のようなエラーになります。
上記2つのスタックをデプロイしたあと、スタックを以下のように修正しDynamoDBをスタックAのコード上から削除します。
import * as cdk from 'aws-cdk-lib';
import { InlineCode, Runtime, Function } from 'aws-cdk-lib/aws-lambda';
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
export class CdkRefactorTestStack extends cdk.Stack {
readonly testLambda: Function; // 後ほど別スタックに渡すため設定
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const sampleCode = new InlineCode(`
exports.handler = async () => ({
statusCode: 200,
body: JSON.stringify('Success')
});
`);
const testLambda = new Function(this, 'TestLambda', {
runtime: Runtime.NODEJS_22_X,
handler: 'index.handler',
code: sampleCode,
memorySize: 128,
});
this.testLambda = testLambda
}
}
そしてスタックBにDynamoDBを追加します。Lambdaの参照はコンストラクト間で渡す必要があるので、StackBのコンストラクタにStackAのLambdaを渡すようにします。
import * as cdk from 'aws-cdk-lib';
import { CdkRefactorTestStack } from '../lib/cdk-refactor-test-stack';
import { CdkRefactorTest2Stack } from '../lib/cdk-refactor-test-2-stack';
const app = new cdk.App();
const cdkRefactorTestStack = new CdkRefactorTestStack(app, 'CdkRefactorTestStack', {});
new CdkRefactorTest2Stack(app, 'CdkRefactorTest2Stack', { lambda: cdkRefactorTestStack.testLambda });
import * as cdk from 'aws-cdk-lib';
import { Function } from 'aws-cdk-lib/aws-lambda';
import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
import { Queue } from "aws-cdk-lib/aws-sqs";
export interface CdkRefactorTestStackProps extends cdk.StackProps {
lambda: Function;
}
export class CdkRefactorTest2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props: CdkRefactorTestStackProps
) {
super(scope, id, props);
const queue = new Queue(this, 'MyQueue')
const testLambda = props.lambda;
// 以下を移植
const table = new Table(this, 'SimpleTable', {
partitionKey: { name: 'id', type: AttributeType.STRING },
billingMode: BillingMode.PAY_PER_REQUEST,
});
table.grantReadWriteData(testLambda);
}
}
上記でソースコードの構造上はリファクタリングの準備が整いました。リファクタを実行する前に、--dry-run
という事前確認のオプションが使えます。CloudFormatinテンプレートの修正やリファクタリングされるリソースの確認などができるので、こちらで動作を確認します。
npx cdk refactor --unstable=refactor --dry-run
実行すると、新旧のコンストラクトのパスが表示されどのスタックに移動すると認識されているか事前に確認できます。cdk.out
に出力されるCloudFormaitonのテンプレートも修正後の内容になっているので、細かくはそちらでも確認できます。
問題なさそうなので、--dry-run
オプションを外して実行します。
npx cdk refactor --unstable=refactor
以下のように事前確認が入るのでY
を押下します。
実行前はStackAとなるCdkRefactorTestStack
にDynamoDBがあります。
これがリファクタリング実行後で以下のようにスタックのリソースが移動されます。
実行後に、DynamoDBを確認すると最初に作成したときの時間と変わりないため、問題なくスタック間移行ができたことを確認できます。
以下CDK支部での会話
このあと書きます。
宣伝
弊社でもCDKのイベントやります!来てね!
所感
今までの作業だと構造リファクタリングの負荷はかなり高かったので、後回しになりがちな印象でした。この機能によって構造の変更がより簡単になり、色々な選択肢を試しやすくなったのでとても良い機能です!これで物理名をリソースにつけるなという公式のプラクティスも変わっていくかもしれないですね。