はじめに
皆様こんにちは、ソーシャル経済メディア「NewsPicks」(Media Infrastructureチーム)エンジニアの北見です。
先日、↓ の記事を書かせて頂きました。
前回では CDK の良さをメインに紹介しましたが、今回は上手く使えずにドはまりしてしまった例をご紹介します。
ことの始まり
ある日、チームメンバーからこんな報告があがってきました。
「CDKのバージョン上げたのですが、cdk deployで失敗しちゃうんですよね。これ分かります?」
xxx-cluster-stack failed: Error: The stack named xxx-cluster-stack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Resource of type 'AWS::ECS::Service' with identifier 'xxx-app-service' already exists." (RequestToken: aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee, HandlerErrorCode: AlreadyExists) at FullCloudFormationDeployment.monitorDeployment (/Users/hoge/xxx/node_modules/aws-cdk/lib/index.js:412:10236) at runMicrotasks (<anonymous>) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Object.deployStack2 [as deployStack] (/Users/hoge/xxx/node_modules/aws-cdk/lib/xxx/node_modules/aws-cdk/lib/index.js:415:152469) at async /Users/hoge/xxx/node_modules/aws-cdk/libg/yyyy/node_modules/aws-cdk/lib/index.js:415:136265
原因を深掘りしていくと、ECS Service の管理・更新を↓のように CodeDeploy を使って行っていることが原因でした。
const appEcsService = new ecs.FargateService(this, "xxx-app-service", { serviceName: "xxx-app-service", cluster, taskDefinition: appTaskDefinition, desiredCount: 2, securityGroups: [appSecurityGroup], deploymentController: { type: ecs.DeploymentControllerType.CODE_DEPLOY, }, vpcSubnets: privateAppSubnets, });
このコードでは「この ECS Service の更新は CodeDeploy でしなきゃだめだよ」という設定をしています。
しかし CDK のバージョンアップによって ECS Service に変更が入ってしまい、CodeDeploy 以外の方法で更新しようとしていました。
これってつまり...?
CDKのバージョンアップができない、詰み状態です
CodeDeployでしか更新できないECS ServiceをCDK管理するCDKのバージョンを上げるCDKのバージョンアップに伴い、意図せずECS Serviceに変更が出るcdk deployしようとすると、「このECS ServiceはCodeDeployでしか更新しちゃだめだよ」と怒られてエラーになる
結局どうしたのか?
新基盤に作り直す ことになりました。
CDK 管理しない方式で ECS Service をデプロイ・更新するためのフローを作成し、新たな Service へリクエストを徐々に切り替え、最終的に↑の CDK コードを削除することができました 。
このためにかなり多くの時間を費やしてしまいましたが、結果的にデプロイフロー等もすっきりしたものになりました。
(新基盤の作成については、詳細はまた後日記事にさせて頂きます。)
何がよくなかったのか?
根本原因としては、
インフラとして管理すべきでないものを CDK 管理した
のが原因でした。
更新頻度が高い、すなわち揮発性が高いものはインフラではなく、インフラ上で動作する「アプリケーション」です。
一方で更新頻度が低い、すなわち揮発性が低いものは、アプリケーションを動かすための「インフラ」です。
API のタスク定義は、(一般的に)コード変更の度に新しく入れ替える必要があるので、高頻度で書き換わりますね?
今回の場合、確かに ECS の Cluster であればインフラです。
ですが、アプリケーションのコード変更の度に更新が走る Service は、インフラではなかったのです。
結論:「インフラ」でないものを CDK 管理してはいけない
「インフラ」でないものを CDK 管理してはいけません
逆に言うと、ECS Service だろうがなんだろうが、更新頻度が低いものはインフラとして捉える方が良いと思います。
さいごに
このようにドはまりしてしまった CDK ですが、正しく使えば、生の CloudFormation を扱うよりはるかにたやすくインフラを構築できますので、私はやはりメリットの方が大きいと判断しています。
どの技術にも言えることですが、誤った使い方をすると保守やアップデートが大変ですし、それは CDK でも変わりません。
今回の話を聞くと「CDKって怖い!」と思ってしまうかも知れませんが、
- 「インフラ」はアプリケーションを動かすための基盤であり、更新頻度は低いはずである(頻繁に変更するものをインフラとは呼ばない)
CDKは「インフラ」をコード管理するものであって、インフラでないものはCDKで管理しないという強い意志を持つ。
というポイントを押さえておけば、同様の失敗はしにくいんじゃないかと思います。
CDK アンチパターンの一例を紹介させて頂きましたが、皆様のお役に立てれば幸いです。