CodeDeployで更新するECS ServiceをCDK管理して詰んだ話

はじめに

皆様こんにちは、ソーシャル経済メディア「NewsPicks」(Media Infrastructureチーム)エンジニアの北見です。

先日、↓ の記事を書かせて頂きました。

tech.uzabase.com

前回では 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のバージョンアップができない、詰み状態です

  1. CodeDeploy でしか更新できない ECS ServiceCDK 管理する
  2. CDK のバージョンを上げる
  3. CDK のバージョンアップに伴い、意図せず ECS Service に変更が出る
  4. cdk deploy しようとすると、「この ECS ServiceCodeDeploy でしか更新しちゃだめだよ」と怒られてエラーになる

結局どうしたのか?

新基盤に作り直す ことになりました。

CDK 管理しない方式で ECS Service をデプロイ・更新するためのフローを作成し、新たな Service へリクエストを徐々に切り替え、最終的に↑の CDK コードを削除することができました 。

このためにかなり多くの時間を費やしてしまいましたが、結果的にデプロイフロー等もすっきりしたものになりました。

(新基盤の作成については、詳細はまた後日記事にさせて頂きます。)

何がよくなかったのか?

根本原因としては、

インフラとして管理すべきでないものを CDK 管理した

のが原因でした。

更新頻度が高い、すなわち揮発性が高いものはインフラではなく、インフラ上で動作する「アプリケーション」です。

一方で更新頻度が低い、すなわち揮発性が低いものは、アプリケーションを動かすための「インフラ」です。

API のタスク定義は、(一般的に)コード変更の度に新しく入れ替える必要があるので、高頻度で書き換わりますね?

今回の場合、確かに ECSCluster であればインフラです。

ですが、アプリケーションのコード変更の度に更新が走る Service は、インフラではなかったのです。

結論:「インフラ」でないものを CDK 管理してはいけない

「インフラ」でないものを CDK 管理してはいけません

逆に言うと、ECS Service だろうがなんだろうが、更新頻度が低いものはインフラとして捉える方が良いと思います。

さいごに

このようにドはまりしてしまった CDK ですが、正しく使えば、生の CloudFormation を扱うよりはるかにたやすくインフラを構築できますので、私はやはりメリットの方が大きいと判断しています。

どの技術にも言えることですが、誤った使い方をすると保守やアップデートが大変ですし、それは CDK でも変わりません。

今回の話を聞くと「CDKって怖い!」と思ってしまうかも知れませんが、

  • 「インフラ」はアプリケーションを動かすための基盤であり、更新頻度は低いはずである(頻繁に変更するものをインフラとは呼ばない)
  • CDK は「インフラ」をコード管理するものであって、インフラでないものはCDK で管理しないという強い意志を持つ。

というポイントを押さえておけば、同様の失敗はしにくいんじゃないかと思います。

CDK アンチパターンの一例を紹介させて頂きましたが、皆様のお役に立てれば幸いです。

Page top