🚀

aws cdk&cfnを利用したIaCの取り組みを振り返る

に公開

取り組み前の状況

稼働して4年ほどのwebアプリケーションがあり、構成管理は部分的にterraformで管理していました。
クラウドサービスとしてはほとんどのAWSであったため、cdkとcfn(CloudFromation)を利用した構成管理ができるように載せ替えを進めていきました。

規模感としては以下のようなイメージです。

  • フロントエンド*3(CloudFront、ALB×ECS等)
  • バックエンドAPI*3(ALB×ECS等)

cdk&cfnへの移行の進め方

1. IaCジェネレータを利用して既存リソースのスキャン、およびスタック管理の適用

cfnには既存リソースをスキャンし、cfnテンプレートの生成、CDKコンストラクトライブラリを利用したソースコードに変換することのできるIaCジェネレータという機能があります。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/generate-IaC.html

cfnテンプレートからCDKに載せる形になるのでL1コンストラクトを利用したコードになるのですが、構成管理のできていなかった部分も多くブラックボックスとなってしまっていたのでこちらを利用してみました。
いくつか課題を感じたので記載します。

CDK deployが通る状態でCDKソースコードが生成されない

CfnLoadBalancerのloadBalancerAttributes、CfnTargetGroupのtargetGroupAttributesなどは生成された状態から不要な属性を削除したりする必要がありました。

再現性のある環境にならない

環境を複製しようと思った際に、addDependsOnで明示的に依存関係を示してあげないとdeployできないことは多々ありました。
このへんの再現性をもてない問題に関してはIaCのメリットを大きく損なうことにもなるのできついなと感じており、リファクタリングを兼ねてL2への移行を考え始めました。

2. スタック定義の統一・L2への移行

IaCジェネレータからのCDKコード生成は、スキャンしたAWSリソースをサービス単位で一つのスタックとして管理するようにしていました。
つまりALBとECSで構成されるフロントエンドサービス、ECS単独で構築されるバックエンドAPIなど、アーキテクチャは一緒でもサービス単位で個別のスタックの定義がなされることになります。

L2コンストラクトへの移行をするにあたり、サービス単位のスタックではなく、構成に応じたスタック定義を共通のものとして用意して各サービスで利用することとしました。

IaCジェネレータでスキャンされたリソースをスタックに入れ漏れていて、スタック移行をしたら意図せず構成が変わってしまったということにならないように注意は必要かと思いますが、その場合はarnで参照するようになっているので、そこまで追えれば意図しない変更も発生させずスタックの移行ができるかなと思います。

3. ネットワークリソースの構成管理

これまではアプリケーション層のリソースをスタックとして管理していましたが、VPC、Subnet、VPCEndpointといったネットワーク層のリソースもスタックで管理していきました。

振り返り

共通化されたL2コンストラクトのスタックに載せるまで、上記の通りいくつかの段階を踏んで進めた結果となったので、何度かスタックの再作成が発生しました。
細かく変更を繰り返したことによってそれぞれの動きも一定スピード感を持って実施することができた部分もありますが、IaCジェネレータで生成されたテンプレートやL1コンストラクトスタックの運用負荷を鑑みて、次回やることがあった際は上流のネットワークリソースから一度でこの状態まで載せ替えてしまう進め方をするかなと思います。

ECSだとamazon-ecs-deploy-task-definitionを利用してデプロイする際にクラスタ名、サービス名を指定するため一時的にワークフローの管理が煩雑になったりしてしまいますが、DNSレコードだけ付け替える形にしておけば本稼働中のアプリケーションと並列で稼働させて検証を回すこともできますし、リリース作業もルーティングの切り替えのみで済むので一括でやってしまった方が楽だったなと感じています。

躓きポイント:インフラとアプリケーションの構成管理棲み分け

アプリケーションリポジトリとIaCリポジトリを別々で持つ中で、ECSのタスク定義はアプリケーションリポジトリで保有し、クラスタやサービスに関してはIaCリポジトリで管理するというのがスタンダードな構成かと思います。
その中で、一部デプロイメント戦略をCODEDEPLOYにし、AWS CodeDeploy側でBlue/Greenデプロイをしているサービスがありました。

プロイメント戦略をCODEDEPLOYにしていると、一部の変更はcdkを通してdeployができません。
この取り組みの中でCodeDeploy側でBlue/GreenデプロイをしているサービスのSubnetを移行する必要が発生し、その際にcdk側でのSubnetの変更ができずに躓きました。

解決策としてはappspec.yml側でNetworkConfigurationを定義しSubnetを移行するという方法が取れるのですが、後追いでIaC側と整合性を取る必要があります。
タスクだけではなくサービスもアプリケーションリポジトリで管理することでネットワークの設定が重複してしまうことは防げるのですが、プロイメント戦略がECSの場合はローリングアップデートとなりCodeDeployを利用しないのでappspec.ymlを管理することにはなりません。
デプロイ戦略によってサービスをアプリケーションリポジトリで管理するか、IaCリポジトリで管理するか分かれるのも直感的に避けたいなという感覚があり悩ましさを感じています。
現状は、プロイメント戦略がCODEDEPLOYの場合はappspec.ymlとcdkコード両方でネットワークの設定を持つ形としています。

IaCの目標に対してメリットを感じられているか

以下はO'Reilly『Infrastructure asa code』に記されている「1.2.1 Infrastructure asa codeの目標」からの抜粋です。

  • ITインフラが変化の障害や制約になるのではなく、変化を支えて実現すること。
  • システム変更がユーザーやITスタッフのストレスや大事件ではなく、日常作業になること。
  • ITスタッフが、日常的反復的なタスクではなく、彼らの能力を活かした価値のある仕事のために時間を使うこと。
  • ユーザーがITスタッフに頼るのではなく、自分で必要なリソースを定義、プロビジョニング、管理できるようになること。
  • 失敗を完全に予防できるという前提に立つのではなく、失敗が発生しても簡単かつスピーディに回復できるようになること。
  • コストがかかりリスキーな「ビッグバン」プロジェクトではなく、継続的な作業により改善、改良を実現すること。
  • 会議やドキュメントでソリューションを論ずることではなく、実装、テスト、計測を通じてソリューションの効果が証明できるようになること。

いくつかピックアップして感じたことを紹介します。

失敗を完全に予防できるという前提に立つのではなく、失敗が発生しても簡単かつスピーディに回復できるようになること。

失敗というのはインフラ設計上の話だけではなく、 ビジネス上の事業判断における方針の変更など、システム単独で見るよりも大きなレイヤでの話も含む と解釈しています。
この点に関してはIaCを運用してきて大きなメリットを生んでいると感じています。

事業上の意思決定に対し、即座にシステム側が順応できるというのはスピード感が重視される現代においてはもはや必須の取り組みであると感じます。
サービスの新規立ち上げ時にはインフラ管理が追いついていないというのはあるあるかと思うのですが、立ち上げだからこそ大きな変化というのは発生しやすいはずですし、変化を実現しなければ生き残れない。
そんな中で IaCが行き届いているかどうかは変化の実現に対してハードルを大きく下げる取り組みの一つである と感じます。

そしてこれは、別の目標にも取り上げられている 「ITインフラが変化の障害や制約になるのではなく、変化を支えて実現すること。」 にもまさに直結するところとなります。

システム変更がユーザーやITスタッフのストレスや大事件ではなく、日常作業になること。

IaCは取り組みを始めてストレスが急激に解消されるというものはないと思います。
特に稼働中のシステムに適用するとなると、インフラを変更する分、ユーザー影響も大きくなりがちであるという性質が変わるわけではないです。
実際、最初にcdk deployなり、(今回の移行前の話になりますが)terraform applyをする際は緊張したものです。

とにかく、 IaCを継続させられるか? というのが大事で、最初に構成管理ができていても運用の中で手動変更なり、既存のリソースとソースコードで乖離が生まれていってしまっては意味がないと感じます。
継続し続けることによって、インフラの変更が手のひらに乗るようになり、少しずつストレスや大事件と感じることは少なくなっていった という感覚があります。

会議やドキュメントでソリューションを論ずることではなく、実装、テスト、計測を通じてソリューションの効果が証明できるようになること。

IaCがあることで、まずやってみよう、うまくいかなかったら考えようという流れでPoCを挟んでみるということがしやすくなったかなと感じます。
流れが早く、次々にあらゆるソリューションが世の中に登場してくる中で、その適用の判断においては一つひとつ試してみるというのが一番確実性が高いと感じる反面、実装が遅いとその分コストがかかってしまうのでしっかりとした調査が必要になり、二の足を踏んでしまったり、実装コストの部分で見送るという判断もあり得ます。
これは典型的な悪循環であると感じていて、特に今では生成AIの発達もあるので実装コストは低く試してみようというのがやりやすい中で、実装できる状態(IaCで構成管理されている状態)にあるかどうかは変化を組み込めるかの大きな分岐点であると思います。

まとめ

最後の部分で少し触れましたが、生成AIの技術発展により、これまで以上にIaCの取り組みが事業だったり変化をスピーディに支える大きなポイントになると感じる。
やり始めるのは大変だけど、今回みたいに少しずつアップデートを重ねて理想系に近づけていくでも良し、一気にやるでも良し、とにかく継続してインフラも自分も育てていくのが大事。

GitHubで編集を提案

Discussion