アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

CDKv2 を触って既存のCDKv1プロジェクトをマイグレーションしてみた

morishitaです。

アクトインディのインフラの多くは AWS CDK を利用して構築しています。
それについていくつかのエントリで紹介してきました。

tech.actindi.net

そして 2021 年 12 月に初のメジャーアップデートとなる v2.0.0 がリリースされました🎉。

github.com

ちなみに 2022/02/04 現在の最新バージョンは v2.10.0 です。

今回は CDKv2 を触ってみて、既存プロジェクトをマイグレーションしてみたので紹介します。
なお、本エントリは Typescript 前提です。

まずは触ってみる

とりあえず、CDKv2 を触る最初の一歩として、CDK プロジェクトを初期化してみます。

❯ cd aws-cdk-init-sample
❯ npx cdk@2 init --language=typescript
npx: 213個のパッケージを7.157秒でインストールしました。
Applying project template app for typescript
# Welcome to your CDK TypeScript project!

This is a blank project for TypeScript development with CDK.

The `cdk.json` file tells the CDK Toolkit how to execute your app.

## Useful commands

 * `npm run build`   compile typescript to js
 * `npm run watch`   watch for changes and compile
 * `npm run test`    perform the jest unit tests
 * `cdk deploy`      deploy this stack to your default AWS account/region
 * `cdk diff`        compare deployed stack with current state
 * `cdk synth`       emits the synthesized CloudFormation template

Initializing a new git repository...
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:   git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint:   git branch -m <name>
Executing npm install...
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated uuid@3.3.2: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated sane@4.1.0: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
npm WARN deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN aws-cdk-init-sample@0.1.0 No repository field.
npm WARN aws-cdk-init-sample@0.1.0 No license field.

✅ All done!

CDKv1 のときと同様、実行したディレクトリに package.json とサンプルコードなどが作られたのち、npm install が実行されます。
すでに Git リポジトリも初期化された状態です。
デフォルトではメインブランチ名は master となっています。v2 になっても main にはなってないんですね。

出来上がるファイル群は次の通りです。これも CDKv1 と変わりません。

.
├── bin
│   └── aws-cdk-init-sample.ts
├── lib
│   └── aws-cdk-init-sample-stack.ts
├── node_modules
├── test
│   └── aws-cdk-init-sample.test.ts
├── README.md
├── cdk.json
├── jest.config.js
├── package-lock.json
├── package.json
└── tsconfig.json

ちなみに、上記の例ではわかりやすく npx cdk@2 init@2 でバージョンを指定していますが、バージョン指定なしでも構いません。
ちょっと前まではバージョン指定なしの npx cdk init では CDKv1 で初期化されたんですが、CDKv2 を使うようにアップデートされたようです。

cdk init を比べてみる

ざっと見た感じ、CDKv1 で init したものと大きくは変わらないようです。
もう少し詳しく見るために CDKv1 、CDKv2 それぞれで初期化した結果できたファイルと比較してみます。
わかりやすく比較するために npx cdk@1 init を使って作ったリポジトリに npx cdk@2 init で作ったファイルを上書きコピーしてプルリクエストを作りました。

github.com

違いがあるのは次のファイルでした。

  • package.json
  • package-lock.json
  • cdk.json
  • bin/aws-cdk-init-sample.ts
  • lib/aws-cdk-init-sample-stack.ts
  • test/aws-cdk-init-sample.test.ts

package.json

まずは package.json を見てみます。

f:id:HeRo:20220204184556p:plain
package.json

aws-cdk2.10.0 (2022/02/04 時点の最新バージョン) になっています。
そして @aws-cdk/** が廃止され aws-cdk-lib に集約されていますね。
さらに
constructs* が追加されています。
AWS Cloud Development Kit v2 開発者プレビューのお知らせ | Amazon Web Services ブログ に記述されているとおりです。

cdk.json

f:id:HeRo:20220204184953p:plain
cdk.json

cdk.json は CDK プロジェクトの動作設定をするファイルですが、あんまり自分で意識して編集しているファイルではないかもしれません。
context には各種機能の動作を変更する Feature Flag が設定されています。

CDKv2 では context 以下に追加はなく削除されているものがあります。 AWS Cloud Development Kit v2 開発者プレビューのお知らせ | Amazon Web Services ブログによると「v1 で作成された Feature flags はすべてデフォルトで有効になっているためです」とのことなので気にしなくても良さそうです。
CDKv2 に残っているものもデフォルトは有効だけど、無効にできるものが残されているようです。

Typescript ファイル

Typescript のコードは import 部分が変更されているだけです。
package.json で見たようにモジュール構成が整理されたためですね。

主な違いは次の通りです。

CDKv1 CDKv2
@aws-cdk/core aws-cdk-lib
@aws-cdk/XX aws-cdk-lib/XX

基本的には importfrom@aws-cdk が含まれているところを直していけば良さそうです。

CDKv1 から CDKv2 へのマイグレーション

さて、ざっと CDKv2 について確認したところで、既存の CDKv1 のプロジェクトを CDKv2 にマイグレーションしてみました。

マイグレーションの手順

マイグレーションは次の手順で行いました。

  1. CDK を v1.x.x の最新バージョンにアップデートする
  2. 非推奨なメソッド等を利用している部分を修正する
  3. cdk diff でデプロイ環境と差がないことを確認する
  4. cdk.jsoncontext をすべて削除する
  5. package.json を変更する。
  6. コードを修正
  7. cdk diff で差分の有無の確認

以下にやってみて感じたポイントを記します。

1. CDK を v1.x.x の最新バージョンにアップデートする

どうせ CDKv2 にアップするなら最新版にしたいところ。
古い CDKv1 から一気に CDKv2 の最新版にマイグレーションした場合、不具合が出ても CDKv2 に移行したためかどうか判別しづらくなります。
なので先に CDKv1 の最新にアップデートしておきます。

また、CDK のテストに利用される @aws-cdk/assert は deprecated になっており、@aws-cdk/assertionsへの移行が促されています1

例えば、CDK のアップデート時の変更を検出しやすいようにスナップショットテストを実装することがあるかと思います。 @aws-cdk/assertions に移行すると SynthUtils クラスが使えなくなるため、代わりに Template クラスを使って次の様に変更します。

- import { SynthUtils } from '@aws-cdk/assert';
+ import { Template } from '@aws-cdk/assertions';
import { App } from '@aws-cdk/core';
import { MyStack } from '../../lib/stacks/MyStack';

describe('MyStack', () => {
  test('Snapshot test', () => {
    const app = new App();
    const stack = new MyStack(app);
-   expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot();
+   expect(Template.fromStack(stack)).toMatchSnapshot();
  });
});

この変更前後で保存されているスナップショットファイルは変わりませんでした。
@aws-cdk/assert には CDKv2 に対応したバージョンも一応提供されていますが、これを機に @aws-cdk/assertions 移行しておくほうがいいと思います。

2. 非推奨なメソッド等を利用している部分を修正する

古いバージョンからアップデートすると利用していたクラスやメソッド、プロパティが非推奨になってしまっているかもしれません。
VSCode だと非推奨の利用箇所にワーニングが表示されるかと思います。
CDKv1 の API リファレンスに代わりに使うクラスやメソッドなどの記述があるかもしれません。それを参考に修正しておきましょう。
というのも、CDKv1 の非推奨は CDKv2 すべて削除されているとのことだからです。

3. cdk diff でデプロイ環境と差がないことを確認する

CDKv1 で最新にバージョンアップデートしたら、それをデプロイ環境にも反映しておきましょう。

デプロイしたら、念の為 cdk diff で差分が発生しないことを確認しておきます。
ここで差分をなくしておくと CDKv2 への移行後に差分が発生した場合に移行が原因だとわかります。
仮に差分の解消が難しいとしてももともとある差を知っておくと差分発生時の切り分けに役立つと思います。

ここまでが CDKv1 でやっておく作業です。

4. cdk.jsoncontext をすべて削除する

ここからが CDKv2 への移行のための変更です。

まずは cdk.jsoncontext をすべて削除します。
もし、今まで自分で設定していたものがあるなら、それらの値が CDKv2 で引き続き設定可能か?
あるいは別のフラグに変わっていないか?
あるとして意味が変わっていないかを確認して再設定してください。

5. package.json を変更する。

次のポイントを変更します。

  • *@aws-cdk/** モジュールをすべて削除する
  • devDependenciesaws-cdk のバージョンを "^2.10.0" にする
  • dependencies に次のモジュールを追加する
    • aws-cdk-lib ("^2.10.0")
    • constructs ("^10.0.0")

変更したら npm install or yarn install を実行してモジュールをインストールします。

7. コードを修正

コードの修正は主にモジュール構成の変更に伴う import の修正です。
基本は次の 2 つの置換です。

  • @aws-cdk/coreaws-cdk-lib に置換
  • @aws-cdk/aws-cdk-lib/ に置換

上記の置換でほとんど解決しますが、Construct クラスは aws-cdk-lib ではなく constructs モジュールからインポートすることに注意です。 例えば、CDKv1 で Construct, Stack をインポートしていた場合、CDKv2では次の様に 2 つの import に分かれます。

- import { Construct, Stack } from '@aws-cdk/core'; 
+ import { Construct } from 'constructs';
+ import { Stack } from 'aws-cdk-lib';

一通り修正したら、 cdk list やテストを実行して抜け漏れがないかを確認しましょう。
要修正箇所が残っていればエラーが発生すると思います。

import を修正してもエラーが発生する場合、次の原因が考えられます。

  • CDKv1 で非推奨なメソッド等を利用している
  • experimental なモジュール(@aws-cdk/aws-appsync など) を利用している

上記以外が原因で発生するエラーもあるかもしれません。CDKv2 の API リファレンスなどを参考に修正しましょう。

7. cdk diff で差分の有無の確認

ここまで修正してコンパイルエラーも出なくなったら、cdk diff でデプロイされているものとの差分を確認しましょう。

差がないのが理想ですが、どうしても発生する差分があるようです。 Migrating to AWS CDK v2 - AWS Cloud Development Kit (CDK) v2Troubleshooting には次の様に記載されています。

Expected changes include but are not limited to:

  • Changes to the CDKMetadata resource
  • Updated asset hashes
  • Changes related to the new-style stack synthesis, if your app used the legacy stack synthesizer in v1 (CDK v2 does not support the legacy stack synthesizer)
  • The addition of a CheckBootstrapVersion rule

「Changes to the CDKMetadata resource」というのは CDKv1 でもアップデートすると時々変わっていたやつだと思います。
「Updated asset hashes」は S3 に一旦置かれるもの(Lambda 関数のコード)などのハッシュ値が変わるということで、これまでの CDK のアップデートでも時々発生していたと思います。
「The addition of a CheckBootstrapVersion rule」は cdk diff の結果で次の様に出力される部分のことだと思います。

[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}

この他に ParametersAssetParameters から始まるキーの定義とそれを参照している箇所がなくなっています。
おそらく Lambda 関数などをデプロイするときに一旦 S3 に置くのですが、そのファイルの情報を Parameters に定義して、それを参照する方法をやめた様です。

これらは仕方がないようなので、これら以外で差分が発生しているかを確認します。
ひょっとすると cdk.json でデフォルト有効となった次の Feature Flag に関係あるかもしれないので次の様に無効に設定すると解決するかもしれません。

{
  "context": {
    "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": false,
    "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": false,
    "@aws-cdk/aws-rds:lowercaseDbIdentifier": false,
    "@aws-cdk/core:stackRelativeExports": false
  }
}

理由不明の差分がなくなるまで調べますが、構築リソースの動作を変えるものでなければ無視してもいいかもしれません。

こうして原因不明の差分がなくなればマイグレーション完了です。cdk deploy でデプロイして動作確認しましょう。

まとめ

AWS CDK v2 がリリースされたので、CDKv1 からの変更点を確認しました。
そして既存の CDKv1 プロジェクトを CDKv2 にマイグレーションする手順とポイントを説明しました。

CDKv1 では利用する AWS のサービスに応じて @aws-cdk/** のモジュールを追加する必要がありました。
一方、CDKv2 では aws-cdk-libに集約されてモジュールの管理が格段に簡単になりました。
./node_modules/aws-cdk-lib/* 以下に利用してないサービスのモジュールもインストールされてしまうのが気になりますが、致し方なしか…。

さて、今は CDKv1 も更新が続いていますが、そのうち CDKv1 の更新は終了すると思われます。
マイグレーションによる差分発生も少ないうちに CDKv2 に移行しておくのがいいのかなと思います。 このエントリがその一助になれば幸いです。

参考

最後に

アクトインディではエンジニアを募集しています。

actindi.net