Amazon Web Services ブログ
AWS CloudFormation のデプロイを楽観的な安定化で高速化した方法
はじめに
AWS CloudFormation を利用するお客様から、リソースプロビジョニングの内部処理や、AWS マネジメントコンソール や AWS Command Line Interface(AWS CLI) と比べてリソースまたはスタックのプロビジョニングに時間がかかる理由について質問をいただくことがあります。 そこで、この記事ではCloudFormation におけるリソースのプロビジョニングに影響する様々な要因について述べます。記事では特に、 CloudFormation やその他のInfrastructure as Code (IaC) ツールが信頼性の高いデプロイを確実に行うためのリソースの安定化について詳しく説明します。 また、CloudFormation スタックのデプロイ時間を最大 40 % 短縮し、新しい CONFIGURATION_COMPLETE ステータスを通じてリソースのプロビジョニングの可視性を高める、新しい楽観的な安定化方式についても紹介します。
AWS CloudFormation は、テンプレートファイルで AWS リソースやサードパーティリソースをモデル化できる IaC サービスです。CloudFormation スタックを作成することで、AWS CLI、マネジメントコンソール、AWS SAMを介して手動で、またはAWS CodePipelineを介して自動的にテンプレートで定義したリソースをプロビジョニングし、ライフサイクルを管理できます (CLI と SAM も活用可能)。あるいはGit syncを介して自動化することも可能です。 また、AWS Cloud Development Kit (AWS CDK) を使用して、使い慣れたプログラミング言語でクラウドインフラストラクチャを定義し、CloudFormation 経由でプロビジョニングすることも可能です。または、AWS Application Composerを活用して、アプリケーションアーキテクチャを設計、依存関係を可視化し、CloudFormation スタックを作成するためのテンプレートを生成することもできます。
CloudFormation スタックのデプロイ
AWS CloudFormation を使用したコンテナ化されたアプリケーションのデプロイを確認して、CloudFormation のリソースプロビジョニングを理解しましょう。
コンテナ化されたアプリケーションをデプロイするには、Amazon ECS サービスを作成する必要があります。 ECS サービスを設定するには、ECS クラスター、Amazon ECR リポジトリ、タスク定義、セキュリティグループやサブネットなどの関連する Amazon VPC インフラストラクチャが最初に存在する必要があります。 インフラストラクチャとアプリケーションのデプロイの両方を AWS CloudFormation で管理したいため、最初に以下を含む CloudFormation テンプレートを定義します。 ECS クラスターリソース (AWS::ECS::Cluster)、タスク定義 (AWS::ECS::TaskDefinition)、ECR リポジトリ (AWS::ECR::Repository)、サブネット (AWS::EC2::Subnet) やセキュリティグループ (AWS::EC2::SecurityGroup) などの必要な VPC リソース。 そして最終的に ECS サービス (AWS::ECS::Service) 自体です。 このテンプレートを使用して CloudFormation スタックを作成すると、ECS サービス (AWS::ECS::Service) は、他のリソースの作成が完了するのを待つため最後に作成されます。このように、リソースには依存関係があります。
リソースの依存関係:
CloudFormation では、リソースが先に作成される他のリソースと依存関係を持つことがあります。リソースの依存関係には 2 種類あります。
- 暗黙的: CloudFormation は、リソースが別のリソースを参照するために組み込み関数を使用する場合、依存関係を自動的に推測します。これらの暗黙の依存関係により、リソースが適切な順序で作成されます。
- 明示的: 依存関係は、DependsOn 属性を使用してテンプレートに明示的に定義することができます。これにより、リソースの作成順序を調整できます。
次のテンプレートスニペットでは、ECS サービスの依存関係を依存関係グラフで可視化しています:
テンプレートスニペット:
ECSService:
DependsOn: [PublicRoute] #明示的な依存関係
Type: 'AWS::ECS::Service'
Properties:
ServiceName: cfn-service
Cluster: ! Ref ECSCluster #暗黙的な依存関係
DesiredCount: 2
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- ! Ref SecurityGroup #暗黙的な依存関係
Subnets:
- ! Ref PublicSubnet #暗黙的な依存関係
TaskDefinition: ! Ref TaskDefinition #暗黙的な依存関係
依存関係グラフ:
注: 上図の VPC リソースには、PublicSubnet (AWS::EC2::Subnet)、SecurityGroup (AWS::EC2::SecurityGroup)、PublicRoute (AWS::EC2::Route) が含まれています。
上のテンプレートスニペットでは、ECS サービス (AWS::ECS::Service) リソースは、DependsOn 属性を使用して PublicRoute リソースに対する明示的な依存関係を持っています。 ECS サービスには、ECSCluster、SecurityGroup、PublicSubnet、TaskDefinition リソースに対する暗黙的な依存関係もあります。 明示的な DependsOn がなくても、CloudFormation はこれらのリソースが ECS サービスから参照される前に作成される必要があるとわかります。これはサービスが Ref 組み込み関数を使用してこれらのリソースを参照しているためです。 テンプレートファイルの定義に基づいて CloudFormation がリソースを特定の順序で作成する方法を理解したので、これらのリソースをプロビジョニングするのにかかる時間を見てみましょう。
リソースプロビジョニング時間:
CloudFormation がスタックをプロビジョニングするための総時間は、テンプレートで定義された各リソースを作成するのにかかる時間に依存します。リソースごとのプロビジョニング時間は、次のいくつかの時間要素によって決まります。
- エンジン時間: CloudFormation エンジン時間とは、リソースに関連するデータの読み取りと保存にサービスが費やした時間を指します。これには、CloudFormation テンプレートの解析と解釈の時間、Fn::GetAtt や Ref などの組み込み関数の解決にかかる時間が含まれます。
- リソース作成時間: リソースを作成し構成するために AWS サービスが実際に必要とする時間です。これは、サービスがプロビジョニングするリソースの種類によって異なります。
- リソース安定化時間: リソースが作成された後、利用可能な状態に達するまでに必要な時間です。
リソース安定化とは
AWS リソースをプロビジョニングする際、CloudFormation は基盤となるサービスに必要な API 呼び出しを行い、リソースを作成します。作成後、CloudFormation はリソース安定化プロセスとして、リソースがトラフィックを処理する準備ができていることを確認するための最終的なチェックを実行します。例えば、アプリケーションで ECS サービスを作成した場合、作成完了直後 (作成時) にサービスをすぐに利用できるわけではありません。CloudFormation は、ECS サービスのリソースに特化した追加の検証チェックを実行することで、ECS サービスが利用可能であることを確認します。リソース安定化は CloudFormation に限った話ではなく、あらゆる IaC ツールで、ある程度の対応が必要になります。
安定化基準と安定化タイムアウト
CloudFormation がリソースを CREATE_COMPLETE とマークするためには、そのリソースが 特定の安定化基準を満たす必要があります。この確認により、リソースが作成されただけでなく、使用可能な状態であることが検証されます。 リソースが許容される安定化タイムアウト期間内に、安定化基準を満たせない場合、CloudFormation はリソースの状態を CREATE_FAILED としてマークし、オペレーションをロールバックします。 安定化の基準とタイムアウトは、CloudFormation でサポートされている各 AWS リソースごとに、サービスによって独自に定義されており、リソースの作成と更新のワークフローの両方に適用されます。
AWS CloudFormation と AWS CLI によるリソースのプロビジョニング
次に、AWS CLI を使って同様の ECS サービスを作成します。 以前 CloudFormation で作成したタスク定義、ECS クラスター、VPC リソースを使って、次の AWS CLI コマンドを実行すると ECS サービスをデプロイできます。
コマンド:
aws ecs create-service \
--cluster CFNCluster \
--service-name service-cli \
--task-definition task-definition-cfn:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration ={ subnets = [subnet-xxx],securityGroups = [sg-yyy],assignPublicIp = ENABLED }" \
--region us-east-1
以下の出力結果は、上記のコマンドによってECS サービスが正常に作成され、ステータスが ACTIVE になっていることを示しています。
しかし、ECS コンソールに移動しサービスを確認すると、タスクは Pending 状態のままで、アプリケーションにアクセスできません。
サービスが安定した状態に至るのを待ってから、アプリケーションにアクセスする必要があります。
AWS CloudFormation を使用して同じ ECS サービスを作成する場合、リソースがスタックの CREATE_COMPLETE ステータスに達すると、すぐにサービスにアクセスできます。この確実な可用性は、CloudFormation のリソース安定化プロセスによるものです。 ECS サービスを初期作成した後、CloudFormation は待機し、サービスが安定するまでECS の DescribeServices API アクションを継続的に呼び出します。 ECS サービスがチェックに合格して使用準備が整ったら、そのときはじめて CloudFormation がスタックのリソース状態を CREATE_COMPLETE とマークします。 このような作成と安定化のオーケストレーションにより、遅延なくサービスにアクセスできるようになります。
次の出力は、 CloudFormation が安定化の間に DescribeServices API 呼び出しを行っていることを示すAWS CloudTrailのイベント履歴です。
CloudFormationは、リソース安定化の処理を行うことで、リソース作成後のカスタムステータスチェックや可用性のポーリングロジックの実装に伴う余分なコーディング作業と複雑さを避けることが出来ます。この機能が無いと、AWS CLIやAPIなどのツールを使って、すべてのインフラストラクチャとアプリケーションリソースに対して追加のロジックを開発する必要があります。CloudFormationに組み込まれた安定化オーケストレーションを使えば、テンプレートを一度デプロイするだけで、作成後にサービスが完全に準備できていることを信頼できるため、アプリケーション機能の開発に集中できます。
安定化戦略の進化
CloudFormation の安定化戦略では、リソース作成と安定化プロセスが連動しているため、リソースのプロビジョニングが完了したと見なされるのは、安定化が完了した場合のみです。
従来の安定化戦略
相互に依存関係のないリソースについては、CloudFormation は同時並行でプロビジョニングを開始します。しかし、あるリソースが他のリソースに依存している場合、CloudFormation は依存先リソースのプロビジョニング操作が完全に終了するまで待ってから、依存リソースのプロビジョニングを開始します。
上の図は、AWS CloudFormation を使用してデプロイする一部の ECS アプリケーションリソースのデプロイを示しています。Task Definition (AWS::ECS::TaskDefinition) リソースは ECR Repository (AWS::ECR::Repository) リソースに依存しています。また、ECS Service (AWS::ECS:Service) リソースは Task Definition と ECS Cluster (AWS::ECS::Cluster) の両方のリソースに依存しています。ECS Cluster リソースには依存関係は定義されていません。CloudFormation は ECR Repository と ECS Cluster リソースの作成を並列で開始します。その後、Task Definition リソースのプロビジョニングを開始する前に、ECR Repository が整合性チェックを完了するのを待ちます。同様に、ECS Service リソースの作成は、Task Definition と ECS Cluster リソースが作成され、準備ができた後にのみ開始されます。この順次的なアプローチは安全性と安定性を確保しますが、遅延が発生します。CloudFormation は厳密に依存するリソースを順番に 1 つずつデプロイするため、全体のスタックのデプロイが遅くなります。相互に依存するリソースの数が増えると、全体のスタックデプロイ時間が長くなり、ボトルネックが発生し、スタック全体の操作が遅くなります。
新しい楽観的な安定化戦略
スタックのプロビジョニング時間とデプロイのパフォーマンスを改善するため、AWS CloudFormation は最近、新しい楽観的な安定化戦略を導入しました。 この戦略により、お客様のスタックデプロイ時間を最大40%短縮できます。依存リソースを並列に作成できるようになり、これによりデプロイ速度が大幅に向上します。
上の図は、過去の戦略で説明した同じ 4 つのリソースのデプロイを示しています。Task Definition (AWS::ECS::TaskDefinition) リソースは ECR Repository (AWS::ECR::Repository) リソースに依存し、ECS Service (AWS::ECS::Service) リソースは Task Definition と ECS Cluster (AWS::ECS::Cluster) の両方のリソースに依存しています。 ECS Cluster リソースには依存関係の定義がありません。 CloudFormation は、ECR Repository と ECS Cluster リソースの作成を並列で開始します。 その後、ECR Repository が安定化するのを待たずに、ECR Repository の作成が完了したら Task Definition の作成を開始します。 同様にECS Service リソースの作成は、Task Definition と ECS Cluster の作成が完了した後に開始されます。 この変更は、すべてのリソースが依存リソースの安定化を待つ必要がないためです。 もしTask Definition または ECS Cluster リソースの安定化が完了していないことで ECS Service のプロビジョニングが失敗した場合は、CloudFormation がその依存関係の安定化を待ってから、ECS Service の作成を再試行します。
暗黙的な依存関係を持つリソースの作成ワークフローでは、この依存リソースの並列作成と自動再試行機能により、従来の直列リソースプロビジョニング戦略に比べてデプロイ時間が短縮されます。 なお明示的な依存関係を持つリソースについては、CloudFormation が従来の戦略を利用してリソースをデプロイします。
リソースプロビジョニングの可視性向上
CloudFormation スタックを作成するとき、リソースのプロビジョニングに時間がかかり、IN_PROGRESS 状態が継続しているように見える場合があります。これは、CloudFormation がリソースの安定化を待機しているためです。 リソースのプロビジョニング状況の可視性を改善するために、CloudFormation は新しい CONFIGURATION_COMPLETE イベントを導入しました。 このイベントは、作成ワークフローの中でリソースの作成または設定が完了したが、安定化プロセスがまだ進行中のときに、個々のリソースレベルと全体のスタックレベルの両方で発行されます。
上の図は、ECSApplication という名前の ECS アプリケーションの CloudFormation スタックについて、スタックイベントの出力結果を示しています。 イベントを下から上へ確認してみましょう。
- 10:46:08 UTC-0600 – ECSService (AWS::ECS::Service) リソースの作成が開始されました。
10:46:09 UTC-0600 – ECSService はステータスタブで CREATE_IN_PROGRESS ステータス、詳細ステータスタブで CONFIGURATION_COMPLETE ステータスとなり、リソースが正常に作成され、整合性チェックが開始されました。
10:46:09 UTC-0600 – スタック ECSApplication はステータスタブで CREATE_IN_PROGRESS ステータス、詳細ステータスタブで CONFIGURATION_COMPLETE ステータスとなり、ECSApplication スタック内のすべてのリソースが正常に作成され、安定化処理が行われていることを意味します。 このスタックレベルの CONFIGURATION_COMPLETE ステータスは、スタックの概要タブでも表示できます。
- 10:47:09 UTC-0600 – ECSService のステータスタブに CREATE_COMPLETE ステータスが表示されており、サービスが作成され、整合性チェックが完了したことを意味します。
10:47:10 UTC-0600 – ECSApplication のステータスタブに CREATE_COMPLETE ステータスが表示されており、すべてのリソースが正常に作成され、整合性チェックが完了したことを意味します。
結論:
この記事では、CloudFormationがリソースをデプロイする方法と、スタックおよびそのリソースの作成に影響する様々な時間要因について理解を深めていただけたと思います。また、リソース安定化のために CloudFormation が内部で行っていることと、可用性の高い本番インフラストラクチャのデプロイメントで、リソースを安全かつ確実にプロビジョニングする方法について詳しく見てきました。最後に、スタックのデプロイ時間を短縮し、リソースのプロビジョニングの可視性を向上させる新しい楽観的な安定化戦略についても解説しました。
本記事は、Bhavani Kanneganti と Idriss Laouali Abdou による How we sped up AWS CloudFormation deployments with optimistic stabilization を翻訳したものです。翻訳はソリューションアーキテクトの木村友則 (@tkimurz) が担当しました。