re:Invent 2024: AWSのAmazon ECSを用いた信頼性の高いデプロイメント手法
はじめに
海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!
📖 AWS re:Invent 2024 - Deployment best practices for reliable rollouts using Amazon ECS (SVS340)
この動画では、Amazon ECSとAWS Fargateを使用したコンテナデプロイメントの最適化について、詳しい解説が行われています。RollingデプロイメントとBlue/Greenデプロイメントの違いや、MinimumHealthyPercentとMaximumPercentの設定による可用性とスピードのバランス調整、SOCIとCheckpoint Restoreを組み合わせたタスク起動の高速化など、具体的な手法が紹介されています。また、Circuit BreakerやCloudWatch Alarmsを活用したデプロイメントの監視と自動ロールバック、Container Insightsによるデプロイメント頻度の追跡など、安全性と可視性を確保するための機能についても詳しく説明されています。
※ 画像をクリックすると、動画中の該当シーンに遷移します。
re:Invent 2024関連の書き起こし記事については、こちらのSpreadsheet に情報をまとめています。合わせてご確認ください!
本編
Amazon ECSデプロイメントの概要と本セッションの目的
みなさん、こんにちは。本番環境へのデプロイに CI/CD パイプラインを使用している方は何人いらっしゃいますか?手を挙げていただけますでしょうか。素晴らしいですね。では、その中で Amazon ECS を使用している方は手を挙げたままでお願いします。さらに、より安全に、より速く、より可視性の高い方法でデプロイを行う方法を学びたいと考えている方は、そのまま手を挙げていてください。私は Robert Northard です。コンテナスペシャリストのソリューションアーキテクトとして、日々お客様のコンテナソリューションの設計とアーキテクチャを支援しています。
みなさん、こんにちは。私は Vibhav Agarwal です。Amazon ECS と AWS Fargate のプリンシパルプロダクトマネージャーを務めています。これまで4年以上にわたって ECS と Fargate に携わり、その間、ECS と Fargate のカスタマーエクスペリエンスのほとんどの部分に関わる機会と特権を得てきました。お客様からよく伺うのは、コンテナ化の主な理由として、ソフトウェア開発チームが更新をより迅速に提供し、より多くのアップデートをお客様に届け、より多くのビジネス価値を生み出せるようになることだと言われています。
本題に入る前に、今日の議題について簡単におさらいさせていただきたいと思います。まず最初に、CDパターンとECSのデプロイメント構成について、そしてそれらが一般的なCDとどのように関連しているかについて簡単に復習します。そして、本日のセッションの大部分は、ECSデプロイメントのベストプラクティスとその導入方法についてお話しします。 セッションの最後には質疑応答の時間を設けたいと思います。いずれにしても、私とRobertは、セッション後も、そしてre:Inventの4日間を通してここにいますので、お話ししたい方はお気軽にお声がけください。
デプロイメントの速度と安全性:AWSの視点
さらに進める前に、AWSにおけるソフトウェアデプロイメントの主な目標、そして私が見てきた中でお客様の共感を得ている点について、少しお話ししたいと思います。AWS内外のサービスオーナーの方々が、ソフトウェアデプロイメントに関して最も重視しているのは、圧倒的にVelocity(速度)です。ここでVelocityという言葉の意味を明確にしておきたいと思います。というのも、この言葉にはいくつかの意味が含まれているからです。私がここで具体的に意味するデプロイメントVelocityとは、デプロイを行う頻度のことです。一定期間内により多くのデプロイを行うほど、そのデプロイメントVelocityが高いと考えています。
頻繁なデプロイには多くの利点があります。高いデプロイメントVelocityは、更新がエンドユーザーにより早く届くことを保証するだけでなく、各変更が小規模であるため、影響範囲が比較的限定されることを意味します。頻繁なデプロイがもたらす二次的な効果として、チームがデプロイプロセスの手動ステップを減らすきっかけとなることが多く、開発者がデプロイの手動管理に費やす時間を減らし、ビジネスのための革新により多くの時間を割けるようになります。
しかし、デプロイメントの速度は単独で実現できるものではありません。私の経験では、高いデプロイメント速度を達成するには3つの重要な側面に焦点を当てる必要があります。まず最も重要なのは安全性です。デプロイメントは、本番環境で稼働中のソフトウェアを変更し、リスクを伴う新しいソフトウェアを導入するタイミングです。トラフィックを処理している最中に、デプロイメントが原因でエンドユーザーのリクエストにエラーが表示されることは避けたいものです。また、新しいバージョンのソフトウェアにバグが含まれている可能性があることも明らかです。このプロセスを慎重に扱わないと、Service Level Objectivesが完全に台無しになる可能性があります。
本日のセッションで頻繁に使用する「デプロイメント速度」という用語について説明しますと、これは実装した変更を本番環境に展開するスピードを指します。
デプロイメント速度は非常に重要です。特に何か問題が発生した際のロールバックやロールフォワードの場合には重要となります。これは開発環境、ステージング環境、本番環境のいずれにも当てはまります。重要なCVEが発見され、ソフトウェアを迅速に更新する必要がある場合など、迅速なデプロイ能力が重要になります。速度とは、頻度、スピード、そして変更が実際に伝播するレートを意味します。
安全かつ頻繁なデプロイが重要である一方で、 デプロイメントのモニタリングも同様に重要です。デプロイメントが失敗することは避けられません。そのため、失敗したデプロイメントを素早く効果的に検出して対処し、同じ問題が再発することを防ぐことが極めて重要です。これらすべてを組み合わせることで、 お客様のソフトウェアデプロイメントにおいて最高の速度を達成できることを私たちは確認しています。
Amazon ECSの基本概念とService Revisionの導入
さて、 デプロイメントの概念とAmazon ECSのデプロイメントに関連する概念について、簡単な入門から始めましょう。これは一般的な CI/CDパイプラインです。ほとんどの方がCI/CDパイプラインの仕組みをご存知だと思いますので、詳しい説明は省略します。基本的にソースからビルドとユニットテストを経て、テストまたはステージング環境にデプロイし、最終的に本番環境に移行します。ここで私が強調したいのは、 このパイプラインにおけるAmazon ECSとAWS Fargateの役割です。Amazon ECSとAWS Fargateの役割は、主にコンテナイメージのビルド後から始まります。これは組織の構造によって異なる場合があり、デプロイメントを担当するインフラストラクチャチームがある場合もあれば、開発と運用の両方を行うDevOpsチームがある場合もあります。いずれにせよ、私たちの主な目標は、お客様のコンテナイメージを本番環境や開発環境など、どの環境であっても非常に迅速にデプロイできるようサポートすることです。
これは300レベルのセッションですので、Amazon ECSとAWS Fargateについて詳しく説明することは控えめにしたいと思います。ただし、Amazon ECSの構成要素における3つの重要な概念について少しお話ししたいと思います。Task Definitionは、基本的にデプロイするアプリケーションの仕様で、アプリケーションの単位を構成するコンテナのセットを含んでいます。これには、コンテナイメージの設定やコンテナイメージの名前、ネットワーク設定などが含まれます。Taskは、Task Definitionの実際のインスタンス化されたものです。そして3つ目のServiceは、アプリケーションを構成する同一のTaskのグループを表す論理的なリソースです。Serviceプリミティブを使用すると、Amazon ECSは一定数のTaskが実行状態を維持するようにします。このServiceの特性は、長時間実行されるアプリケーションに特に価値があり、そのため、このセッションの大部分では、お客様が長時間実行されるアプリケーションをデプロイする方法として、Serviceのコンテキストでデプロイメントについて説明します。Serviceを使用すれば、モノリスであれマイクロサービスであれ、あらゆるタイプのアプリケーションをデプロイできます。非常に拡張性の高いリソースであり、そのためお客様から好評をいただいています。
先月まさに導入した新しい概念についてお話ししたいと思います。Amazon ECSで2つのリソースと概念を導入しました:Service RevisionとService Deploymentです。Service Revisionは、ServiceをAmazon ECSにデプロイする際の完全な不変の設定を表します。これには、Task Definition、ネットワーク設定、ロードバランサー設定など、特定のアプリケーションバージョンの完全なインスタンス化を表すすべてが含まれます。次に導入した構成要素は、Service Deploymentと呼ばれるもので、これは1つのService Revisionから別のRevisionへ移行するプロセスです。この違いは微妙なので、例を挙げて説明させていただきます。Amazon ECSで最初からServiceを作成する場合、基本的にTask Definitionを提供し、ロードバランサーを使用する場合はそれも提供し、ネットワーク設定やService Connectなどの設定も行います。
Amazon ECSはこれらすべてをまとめて平坦化し、Service Revisionと呼ばれる新しいリソースとして保存します。新しいデプロイを行うとき、Serviceを立ち上げるプロセスが最初のデプロイメントとなります。次にServiceを更新する際、例えばコンテナイメージのバージョン1からバージョン2に移行し、新しいTask Definitionを作成する場合、そのTask DefinitionをAmazon ECSにデプロイすると、新しいService Revisionが作成され、Service Revision 1からService Revision 2への移行に新しいDeploymentが使用されます。
Service Deploymentは非常に強力なリソースです。デプロイメントが正常に成功したかどうかを追跡できるだけでなく、デプロイメントの履歴も提供します。Service RevisionとService Deploymentは、デフォルトで90日間保存されます。これは、古いデプロイメントが有用である期間として適切だと考えているためです。3週間前に失敗したデプロイメントを振り返って、なぜ失敗したのか、デプロイしようとしていたアプリケーション設定は何だったのか、以前の設定は何だったのかを確認することができます。これはデバッグに役立ちます。この機能は、Amazon ECSコンソールとAPIの両方でデフォルトで自動的に利用できます。
今年初めに導入したもう1つの概念は、ソフトウェアバージョンの一貫性という考え方です。バージョンの一貫性とは、デフォルトでAmazon ECSがService Revision内の各Taskが同一であることを保証するということです。コンテナイメージタグを使用してデプロイする場合でも、Task Definitionでコンテナイメージタグを使用してアプリケーションを設定している場合でも、Serviceをデプロイする際に自動的にイメージダイジェストに変換されます。これにより、Service内の後続のすべてのTaskが同じコンテナイメージで起動されることが保証されます。同一Service内の異なるTaskのバージョンの違いによってお客様が異なる動作を経験することがないため、これは非常に強力な機能だと考えています。
私が特に強調したいのは、お客様から、バージョンの一貫性を強制しないことが望ましいシナリオがあるというフィードバックを受けたことです。特に、ロギングや監視用のサイドカーを使用していて、そのコードベースを所有していない場合などです。そのコードベースは必ずしもエンドユーザーに影響を与えるものではありません。そこで最近、Task Definitionの特定のコンテナに対してバージョンの一貫性を無効にできる機能を導入しました。例えば、CloudWatchエージェントや、Datadogエージェント、Splunkエージェントを使用している場合、このコンテナに対してバージョンの一貫性を設定したくないと指定できます。Amazon ECSはその設定を尊重します。これはTask Definitionで設定可能です。
Amazon ECSのデプロイメントパターンと最適化戦略
では、Amazon ECSのデプロイメントパターンについて詳しく説明するため、Robertに引き継ぎたいと思います。 現在、お客様がAmazon ECSで使用している主要なパターンは2つあります。1つ目はRollingデプロイメント、 2つ目はBlue/Greenデプロイメントです。Rollingデプロイメントは、アプリケーションの以前のバージョンを新しいバージョンに少しずつ置き換えていき、最終的に100%完了させるデプロイメント戦略です。一方、Blue/Greenデプロイメント戦略は、アプリケーションの新バージョンを、古いバージョンと並行して同一環境にデプロイするというものです。新バージョンをテストして、エンドユーザーへのトラフィックをルーティングする準備が整ったと判断したら、新バージョンにトラフィックを切り替え、古いバージョンを終了することができます。
Canaryデプロイメントのような他の戦略もあります。これはBlue/Green戦略の一種で、最初は20%のユーザー、その後40%というように段階的にトラフィックを新バージョンにシフトさせ、最終的に100%に到達させます。今日は主にRollingデプロイメントと、Amazon ECSがそのプロセスをどのように簡素化するかに焦点を当てていきます。Amazon ECSのServiceを作成する際、 Rollingアップデートデプロイメント戦略を使用すると、Amazon ECSは現在実行中のTaskを新しいバージョンのTaskに置き換えます。Amazon ECSのデプロイメントコントローラーは、高可用性のためにTaskを複数のアベイラビリティーゾーンに分散させるなど、Taskの配置戦略や制約を考慮します。これは特にEC2デプロイメントタイプを使用する場合に有用です。
Rollingデプロイメント中にAmazon ECSがServiceに追加または削除するTaskの数は、Serviceのデプロイメント設定によって制御され、今日はこれについて詳しく見ていきます。Rollingデプロイメントには、慎重に検討すべき2つの重要なパラメータがあります。 MinimumHealthyPercentとMaximumPercentは、アプリケーションの可用性とスピードのトレードオフを調整するために使用でき、これについては後ほど詳しく説明します。
MinimumHealthyPercentは下限値で、デプロイメント中にServiceで実行状態を維持する必要があるTaskの最小数をパーセンテージで表したもので、デフォルトは100%です。MaximumPercentは上限値で、希望するTask数に対する割合として、任意の時点でServiceで実行中または保留状態になることが許可されるTaskの最大数を表し、デフォルトは200%です。MinimumHealthyPercentの値を設定する際は、エンドユーザーやトラフィックのニーズに対応できる総リクエスト処理能力を維持することが非常に重要です。
具体例をお見せしましょう。この例では、デプロイメントの速度を優先して可用性を少し犠牲にしています。minimum healthy percentを50%に設定しているため、デプロイメント中にタスクの50%を停止することができます。また、maximum healthy percentは200%に設定されており、デプロイメント中に元のタスク数の200%まで増やすことができます。最初に6つのタスクがデプロイされており、maximum healthy percentが200%なので、デプロイメントプロセス全体で最大12タスクまで増やすことができます。次に、Task Definitionを更新してAmazon ECSサービスの更新をトリガーすることで、デプロイメントを開始します。
最初に行われるのは、ソフトウェアバージョンの一貫性を保つため、Amazon ECSが1つのタスクを停止することです。その後、新しいタスクを開始し、コンテナイメージのダイジェストを解決して、そのデプロイメントの一部として今後作成される全てのタスクが同じコンテナイメージを参照するようにします。次に、ECSは2つの新しいタスクを停止し、5つの新しいタスクを起動します。この時点で、アプリケーションの新しいタスクが6つ実行されており、最後にECSは残りの実行中のタスクを削除します。これがRolling Deploymentの基本的な流れです - 古いバージョンのタスクを新しいバージョンに置き換えていくのです。この2つのパラメータについては、後ほど詳しく見ていきます。
では、Rolling Deployment中のワークロードのトラフィックとキャパシティがどのように推移するか見てみましょう。X軸は時間、Y軸はRolling Deployment中のワークロードへのトラフィックを示しています。Amazon ECSサービスで新しいバージョンのアプリケーションが展開されるにつれて、古いバージョンのアプリケーションからトラフィックが徐々に減少していくのが分かります。新しいバージョンのアプリケーションへのトラフィックは時間とともに徐々に増加します。Request Capacityについて考えると、これはタスクやワークロードの実行に必要な総リソース(vCPUやメモリ)を指します。Y軸にRequest Capacityをとると、minimum healthy percentとmaximum percentの設定によって、総Request Capacityはほぼ一定を保つか、デプロイメント中に短時間のピークを示すことがあります。
以上がAmazon ECS Rolling Deploymentの概要ですが、Blue/Green Deploymentについてはどうでしょうか?Amazon ECSでのBlue/Green Deploymentでは、2つのAmazon ECSサービスを用意することをお勧めします。1つは古いバージョンのアプリケーション用、もう1つは新しいバージョン用です。これらはAWS Elastic Load Balancerでフロントに配置され、Layer 7ではALBを使用できます。新しいバージョンのアプリケーションが正常で、トラフィックを受け入れる準備ができているかを判断するパラメータを決める必要があります。テスト中の500エラー、機能テスト、レイテンシーなどを確認して、アプリケーションに新しいエラーが発生していないことを確認することができます。
Blue/Green Deploymentを使用することで、ロールバックプロセスを簡素化し、アプリケーションの可用性を向上させ、デプロイメントのリスクを軽減することができます。全てのトラフィックが新しいバージョンのアプリケーションに移行された後、古いバージョンは廃止され、次のデプロイメントに備えてスケールダウンされます。Blue/Green Deploymentのリクエストキャパシティがどのように推移するか見てみましょう。Blue/Green Deploymentでは、短時間ですが追加の計算能力が必要になることが分かります。これは古いバージョンと新しいバージョンが並行して存在する必要があるため、Rolling DeploymentとBlue/Green Deploymentのトレードオフとなります。
これには追加のコスト考慮が必要になります。満足のいく結果が得られたら、トラフィックの100%を新しいバージョンのアプリケーションに切り替え、古い環境の削除を開始することができます。
デプロイメントの速度向上:タスク起動の最適化テクニック
これは比較的わかりやすい例で、デプロイメント中の基盤となるコンピュートの観点からは、新しい環境をスケールアウトし、古い環境をスケールダウンすることになります。 トラフィックの観点では、Blue/Greenデプロイメント中のある期間は、Rolling Deploymentと同様の状況になります。 Blue/Greenバージョンのアプリケーションは、古いバージョンがドレインされるまでの間、同時にトラフィックを受け取ります。 設定可能な時間が経過し、古いワークロードから古い接続がドレインされると、新バージョンのアプリケーションが全てのトラフィックを処理するようになり、古いバージョンはトラフィックを受け取らなくなります。
ここで、Blue/GreenデプロイメントとRolling Deploymentのトレードオフについて疑問に思われるかもしれません。デプロイメントタイプは、スピード、可用性、コストの間のトレードオフと言えます。先ほど述べたように、Rolling Deploymentは、先ほど説明した最小値と最大値の設定に応じて、必要な総コンピュート容量が少なくなるため、潜在的にコストが低くなりますが、デプロイメント中に問題が検出された場合のロールバックは遅くなる可能性があります。 一方、Blue/Greenデプロイメントは、古いタスクが並行して実行され続けるため、トラフィックを単純に切り戻すことができ、より迅速なロールバックが可能です。 ただし、古いバージョンと新しいバージョンのアプリケーションが同時に実行される期間は、追加のコンピュート容量が必要になります。とはいえ、デプロイメント中に新しいバージョンをスケールアップし、古いバージョンをスケールダウンしていくことも可能です。
それでは、スピーディで確実性が高く、可視性のあるリリースを実現するためのデプロイメントのベストプラクティスについて詳しく見ていきましょう。 Amazon ECSとAWS Fargateは、ワークロードのデプロイメントを大幅に簡素化してくれますが、これは依然として共有責任モデルの一部であり、これから私たちが言及してきた機能を実現するための設定可能なプロパティについて詳しく見ていきます。ここでまた挙手をお願いしたいのですが、会場の中でJavaを使用してWebサービスを開発している方は何人いらっしゃいますか? 手を挙げてください。素晴らしいですね。では、キューからの作業を処理するイベント駆動型アプリケーションを構築している方は何人いらっしゃいますか?なるほど。
ここで、開発者の方をご紹介したいと思います。開発者のNikkiさんは、何かを構築したいと考えています。NikkiさんはAmazon ECSとAWS Fargateを使用することにしました。というのも、インフラストラクチャの管理ではなく、アプリケーション開発に集中できるからです。あなたが最近フルスタック開発者として仕事を始め、最初のプロジェクトとして、JavaのWeb APIとキュープロセッサーの両方について、確実で安全なRolling Deploymentを実装する必要があったとします。これから、このような目標を達成するためにアプリケーションをどのように準備すべきかを説明していきます。先ほどの3つの次元を振り返ると、 安全性、スピード、可視性があり、これらの次元に基づいてベストプラクティスを分類していきます。
まず、Amazon ECSのワークロードを安全にデプロイするためのアーキテクチャについて見ていきましょう。Nikkiには以下のような目標があります:継続的なデプロイメントを可能にしてダウンタイムを最小限に抑えることで、より迅速に機能をリリースしてエンドユーザーに提供したいと考えています。また、問題のある変更がエンドユーザーに影響を与えることを防ぎ、エンドユーザーに一貫した体験を提供したいと考えています。デプロイメントが行われていることをエンドユーザーに気付かれたくないのです。
安全なデプロイメントのために、アプリケーションの正常性を定義する必要があります。アプリケーションがトラフィックを受け入れられる状態になったタイミングを把握する必要があります。これはアプリケーションによって異なる意味を持ちます。アプリケーションごとに異なるヘルスチェックの仕組みを実装する可能性がありますが、Webアプリケーションやキュープロセッサーの場合、通常は200 OKステータスコードを返す HTTPエンドポイントとなるでしょう。これは非常に重要です - 開発者がこれらのヘルスチェックの仕組みとエンドポイントを実装する責任を持ちます。また、これらは一般的にソフトヘルスチェックであるべきです。Just-in-timeコンパイレーションを使用するJavaのような言語では、Just-in-timeレイテンシーを減らすために、ヘルスチェックでシステムの他の部分も読み込む場合があります。タスクが正常とマークされた後は、そのマイクロサービスの上流サービスの正常性も確認するデフォルトチェックの設定には注意が必要です。これは連鎖的な障害を引き起こす可能性があるためです。
例えば、あるマイクロサービスが上流サービスへの接続を確認する際に、それらが異常な状態であった場合、自身のヘルスチェックが失敗し、さらに自分を呼び出している上流サービスにも影響を与える可能性があります。これは非常に重要なポイントで、これらのヘルスチェックは、独立したマイクロサービスアプリケーションがトラフィックを受け入れる準備ができており、正常であることを確認するソフトヘルスチェックであるべきです。
そのため、まず最初にアプリケーションの正常性が何を意味するのかを定義する必要があります。Amazon ECSでは、Task Definitionの一部としてコンテナのヘルスチェックを定義することができます。Task Definitionでヘルスチェックが定義されると、コンテナはその内部でヘルスチェックプロセスを実行し、終了コードを評価してアプリケーションが正常かどうかを判断します。例えば、curlを使用してAPIが200 OKを返すことを確認するといった方法があります。Amazon ECSのサービススケジューラーがタスクの正常性を待機できる時間は、インターバル(デフォルトは30秒)、タイムアウト期間、開始期間、異常と判断するまでの再試行回数などのコンテナヘルスチェック設定によって決定されます。
WebやAPIワークロードなどのロードバランサーを使用するアプリケーションでは、問題のある変更が本番環境に到達するのを防ぐため、Load Balancerにもヘルスチェックを設定できます。これらのヘルスチェックは到達可能性をテストします - つまり、ECS上で実行されているそのサービスやタスクに接続できるかどうかを確認します。ヘルスチェック設定の一部として考慮すべき重要な要素は、初期猶予期間(秒単位)、ヘルスチェックの間隔、そして正常性の閾値です。これらの設定をどのように調整してデプロイメントの速度を上げたり下げたりするかについては、後ほど説明します。通常は、到達可能性を確認するために、コンテナヘルスチェックとLoad Balancerヘルスチェックの組み合わせを実装することになるでしょう。
私がアドバイスする最も重要なポイントの1つは、多くの場合見落とされがちなのですが、サービスのデプロイ中にアプリケーションが計画的な中断を適切に処理できる必要があるということです。アプリケーションはSIGTERMシグナル(シャットダウンを開始すべきことを示すLinuxシグナル)を受信した際に、新規トラフィックの受け入れを停止する方法を理解している必要があります。アプリケーションが適切なタイミングでシャットダウンを開始しない場合、コンテナに対してSIGKILLが送信され、そのコンテナが強制終了される可能性があります。これはStop TimeoutやECS Container Stop Timeoutといったパラメータで、ECSエージェントの一部として調整可能です。計画的な中断は想定されたデプロイメントであるため、ワークロードがこれらの計画的な中断に対応できることは非常に重要です。エンドユーザーへの影響を避けるため、アプリケーションが正常にシャットダウンできることを確認する必要があります。
では、インフラストラクチャについてはどうでしょうか?これまでTaskやServiceのデプロイメント設定について多く話してきましたが、キャパシティ全体にわたるTaskの配置についてはどうでしょうか?高可用性のためにTaskをアベイラビリティーゾーン全体に分散させるにはどうすればよいのでしょうか?AWS Fargateデプロイメントタイプを使用している場合、Amazon ECSがこれを管理してくれます。Amazon EC2の場合は、Task配置の制約や戦略を活用することで、新しいバージョンのTaskをデプロイする際に、高可用性のためにアベイラビリティーゾーン全体に分散させることができます。最近発表されたAmazon ECSのアベイラビリティーゾーンリバランシング機能をご存知かもしれません。例えば、あるアベイラビリティーゾーンで障害が発生しているようなインフラストラクチャーイベント中に、Amazon ECSはゾーン間でTaskのバランスが崩れていることを検知し、自動的にリバランスを試みます。
これまでWebやAPIのワークロードについて多く話してきましたが、実装が必要なキュープロセッサについてはどうでしょうか?キュープロセッサは非常に興味深い例です。なぜなら、デプロイ中にワークロードの中断を防ぎたい場合があるからです。処理を再開するためのコストが大きく、エンドユーザーやプロセスに影響を与える可能性があるためです。これに対処するため、Amazon ECSはワーカーベースのノード向けにTask Scale-in Protectionという機能を提供しています。
アプリケーションの一部として、Amazon SQSからのポーリングなど、新しい作業を処理する必要がある場合、Taskの保護を有効にします。これにより、保護期間が終了するか無効化されるまで、そのTaskは終了できなくなります。デプロイ中は、作業が完了するまで終了がブロックされます。作業が完了すると、そのTaskの保護は無効化されます。
次の考慮事項は可視性です。アプリケーションは、新しい作業を処理または受け入れる前に、デプロイすべき新しいバージョンがあるかどうかを確認する必要があります。Amazon ECSサービスの新しいバージョンをデプロイする必要がある場合は、新しい作業を受け入れずに終了するようにします。安全性について話してきましたが、デプロイメントのスピードと、安全性および可用性のバランスをどのように取ればよいのでしょうか?
AWSでは、安全性とスピードの両方を非常に重視しています。そこで、可用性を損なうことなくECSのデプロイメントを高速化する方法について説明しましょう。大まかに言えば、ローリングデプロイメントの速度に影響を与える3つの重要な要素があります。それは、ECSが新しいTaskをスケジュールし、古いTaskをシャットダウンする速度、新しいTaskの起動速度、そして古いTaskのシャットダウン速度です。
デプロイメントの高速化:スケジューリングとシャットダウンの最適化
まずは最も簡単な要素であるスケジューリング速度の調整から見ていきましょう。ここには2つのパラメータがあります:maximumPercentとminimumHealthyPercentです。maximumPercentは、ほとんどの場合200%に設定されます。この設定により、ECSは常に成功するデプロイメントに必要な十分なTaskを起動できる余裕を持つことができます。ただし、データベースの同時接続数制限など、上流や下流の依存関係に制限がある場合には、200%より低い値を設定する必要があるケースもあります。
2つ目のパラメータであるminimumHealthyPercentは、より複雑で、スピードと可用性のトレードオフに関わります。これは、デプロイメント中に維持しなければならない最小限のTaskの数を指定します。この値を100%未満に設定すると、ECSは新しいTaskを起動する前に積極的に古いTaskを終了できますが、これには可用性面でのトレードオフが伴う可能性があります。開発環境では、デプロイメントをより迅速に完了させるために50%を使用することもあります。本番環境では、minimumHealthyPercentを100%に設定することがほぼ常に推奨されます。
次に、残りの2つの要素である起動とTaskのシャットダウンについて見ていきましょう。これらはどちらもECSにおける個々のTaskのライフサイクルと密接に関連しています。Taskがスケジュールされると、まずProvisioningステートに入り、ECSがTaskを起動するためのインフラストラクチャを見つけます。その後、ホスト上のECS Agentが作業を開始し、コンテナイメージのプル、展開、アプリケーションの起動、そして設定されている場合はロードバランサーへの接続を行います。Taskをシャットダウンする必要がある場合、ECSはロードバランサーの接続をドレインし、グレースフルシャットダウンのためのシグナルを送信します。
ここからは、多くの作業が行われるPendingステートに焦点を当てて、Task起動の最適化方法について詳しく見ていきましょう。
Pending状態の最適化を始める前に、ボトルネックがどこにあるのかを測定することで、何を最適化すべきかを理解することが重要です。 そのために最も有効な方法は、タスクを記述し、特定の時間帯を確認することです:Image Poolの開始時刻、Image Poolの完了時刻、そしてこれら3つの間のタスクの時間です。これらのタイムスタンプから、コンテナがイメージをPullするのにかかった時間、解凍にかかった時間、そしてアプリケーションを実行状態にするまでの時間を解読できます。これがアプリケーションがPending状態にとどまる時間を表しています。
Amazon ECSがコンテナを起動しようとする際に実際に何が起こるのか、詳しく見ていきましょう。コンテナの実行が開始されると、 まず最初に、イメージマニフェストと、特定のコンテナのすべての依存関係を表す様々なイメージレイヤーがPullされます。 すべてのレイヤーがPullされると、圧縮形式でPullされているため、アプリケーションを実際に実行できるように、各レイヤーを展開し、解凍し、解凍する必要があります。 その後、アプリケーションを起動する必要があります。これらの各ステップが、タスク起動の遅延を引き起こします。
これは特にAWS Fargateの場合に重要です。なぜなら、各タスクは新しいVMで起動されるからです。その結果、AWS Fargateでタスクが起動されるたびに、新しい一連のコンテナイメージをソースリポジトリからPullする必要があります。Nikkiがタスクの起動を最適化するために行える明確な方法がいくつかあります。 まず、不要な依存関係を減らし、重要なパッケージのみの最小セットを使用することで、より良いコンテナイメージを構築することです。これによりイメージのダウンロードにかかる時間を削減できます。また、ZSTDコンプレッションを使用することもできます。これは可逆圧縮形式で、tarballのサイズを削減することでイメージのダウンロード時間を大幅に節約できます。ただし、トレードオフがあります:イメージの圧縮率が高いほど、解凍にかかる時間が長くなります。
これは、Javaのような起動の遅い言語にとって特に重要です:最近、お客様が採用し始めているCheckpoint Restoreを使用することができます。Checkpoint Restoreとは、JVMを最初から起動するのではなく、アプリケーションが既に起動された後のスナップショットを取得したイメージを使用することを意味します。これにより、コンテナ内でJVMを起動する際にかかる時間が大幅に削減されます。最近まで、Checkpoint Restoreを使用するにはLinuxカーネルの特権、特にsys admin特権が必要でした。これはAWS Fargate上で実行されるタスクでは利用できませんでしたが、現在はその制限がなくなりました。Process ID名の制御がAWS Fargateで可能になったため、Checkpoint Restoreを使用できるようになりました。AWS FargateタスクでCP trace機能を設定することで、通常は起動の遅いJavaアプリケーションでもCheckpoint Restoreを有効にできます。
コンテナイメージの起動プロセスに話を戻しましょう。 研究によると、コンテナイメージのダウンロードと解凍が、コンテナの起動にかかる時間の大部分を占めることが示されています。もし起動のためにすべてのコンテンツをダウンロードする必要がなかったらどうでしょうか?なぜなら、アプリケーションの起動に実際に必要なのは、コンテナイメージのごく一部だけだからです。 最近導入されたLazy Loadingという概念により、コンテナの起動中にコンテナイメージのダウンロードを並行して行うことが可能になりました。
コンテナの起動中に、最近オープンソース化した技術についてお話ししたいと思います。SOCI(Seekable OCI)というもので、コンテナの遅延ロードをサポートし、シンプルにする技術です。非常にパワフルで優れた技術で、リリース以来多くのお客様にご利用いただいています。コードの変更は一切必要ありません。SOCIがタスクの起動を高速化するために必要なのは、アプリケーションの起動に必要なイメージの特定の部分を取得するためのマニフェストまたはインデックスだけです。このマニフェストやインデックスの追加に、コンテナイメージの再起動や再構築は必要ありません。SOCIを導入するためのツール群を用意しており、関連リソースへのリンクも提供しています。
SOCIはAWS Fargateですぐに使えます。タスク定義を変更する必要はありません。これはLinuxプラットフォームバージョン1.4で実行されるすべてのFargateタスクで利用可能です。簡単にロールバックすることもできます。一点お伝えしたいのは、遅延ロードで最も効果が高いのは、コンテナイメージのサイズが250メガバイト以上の場合だということです。これはJavaアプリケーションやその他のタイプのアプリケーションでは一般的なサイズです。コンテナイメージがそれ以上のサイズを必要とする場合は、SOCIの導入を検討する価値があるでしょう。Fargateで超高速なタスク起動を実現したい場合は、SOCIとCheckpoint Restoreを組み合わせることができます。これにより、データの読み込みが速くなるだけでなく、アプリケーションの起動時間も短縮されます。これらの2つのソリューションを組み合わせることで、タスク起動のパフォーマンスが大幅に向上します。
ここまでPendingについて多く話してきましたが、次はタスク起動プロセスの次の段階について簡単に説明しましょう。この段階では、Amazon ECSが実行中のコンテナをロードバランサーに接続します。これはキュープロセッサーには関係ありませんが、APIやWebワークロードには重要です。Robertが先ほど説明したように、ロードバランサーはヘルスチェックに合格した後でのみコンテナにトラフィックを送信し始めます。ロードバランサーからのヘルスチェックの実行頻度と速度を決定する2つの重要な設定があります:ヘルスチェックの間隔と閾値カウントです。これらの2つの値を掛け合わせると、デフォルトでは約2.5分となり、かなり長い時間となります。
アプリケーションが適切に設計されている場合、このような長い時間は必要ないはずです。この時間を大幅に短縮し、タスクの起動を高速化することができます。ただし、少し注意が必要です。タスクを迅速に起動させたいのは当然ですが、ヘルスチェックを軽視してはいけません。先ほどRobertが言及したように、遅延ロードやJavaのJust-in-Time機能を使用する場合は、適切なアプリケーションの依存関係がヘルスチェックに含まれていることを確認してください。これにより、アプリケーションが単に実行中と表示されるだけでなく、実際にエンドユーザーのリクエストに対応できる状態であることを確認できます。
次に進みましょう。デプロイメントを加速するもう一つのレバーセットであるタスクのシャットダウンの高速化について説明します。ここでも注意が必要です。これらの処理を高速化する際に可用性を犠牲にしないことが非常に重要です。アプリケーションのシャットダウン時間を最小限に抑えたい一方で、グレースフルシャットダウンやコンテナ内でのシグナルハンドリングの実装は非常に重要です。そのバランスを取ることが大切です。もう一つ指摘しておきたい設定は、再びロードバランサーに関連するもので、ロードバランサーを使用するアプリケーションに関係します。Deactivating状態では、Amazon ECSはロードバランサーに対して、シャットダウン予定のこのタスクへの新規トラフィックの送信を停止するよう指示します。
ECS が新しいトラフィックの送信を停止しても、タスクがシャットダウンする前に処理が必要な以前のリクエストが残っている可能性があります。ロードバランサーは、未処理のリクエストの処理のために最大300秒の期間を設けています。ほとんどのwell-architectedなコンテナアプリケーションでは、この数値をそれほど高く設定する必要はなく、アプリケーションの性質に応じて30秒以下といった、かなり低い値に設定することが望ましいでしょう。ただし、WebSocketのような長時間接続を使用する長時間実行アプリケーションを使用している場合は、特定のニーズに応じて比較的長いタイムアウトを設定することもあるでしょう。その他のほとんどのアプリケーションでは、保留中のリクエストの処理にそれほど時間はかからないはずなので、これは Amazon ECS でタスクのシャットダウンを迅速化するための、比較的シンプルで分かりやすい最適化方法と言えます。
デプロイメントの監視と保護:可観測性の重要性
これまで、迅速かつ安全なデプロイについて多く話してきました。しかし、 迅速で安全なデプロイも重要ですが、デプロイを監視し、障害を検出するための適切な可観測性を確保することも同様に重要です。まず最初に、デプロイ自体への可視性 についてです。Amazon ECS は現在、デフォルトでデプロイの可視性を提供しています。新しい API セットとコンソールエクスペリエンスを導入し、デプロイ履歴を確認できるようになりました。サービスの過去90日間のデプロイ履歴を確認でき、デプロイが成功したかどうか、開始時刻、終了時刻なども確認できます。また、デプロイ開始時のサービスバージョンや、デプロイ終了後のサービスバージョンなども確認できます。これらはすべて、Rolling Deploymentを使用するサービスでデフォルトで利用可能であり、特にデプロイ障害のトラブルシューティング時に非常に役立つ機能です。
デプロイの監視に関連してもう一つお話ししたいのは、 デプロイが失敗することは避けられないということです。失敗が発生した際には、デプロイがエンドユーザーに影響を与えないよう、適切な監視と保護措置を確実に整えておく必要があります。Amazon ECS には、ECS Circuit Breaker と Amazon CloudWatch Alarms という2つの重要な保護機能があります。まず Circuit Breaker について説明しましょう。これは、お客様とお話しする際に最も紹介したい機能の一つです - Circuit Breaker を有効にするだけでいいのです。サービスのデプロイ設定でフラグを1つ設定するだけの簡単な操作です。有効にするだけで、Amazon ECS が自動的にタスクの起動失敗や、タスクが正常な状態に到達できない状況を監視します。これらが閾値を超えると、Amazon ECS は自動的にデプロイを失敗と判断します。オプションとして、デプロイが失敗した場合にアプリケーションをロールバックするよう ECS に指示することもでき、ECS が自動的にそれを実行します。
特に言及したいのは、今年初めに行った改善で、Circuit Breaker の応答性が大幅に向上し、特に10タスク未満のサービスで効果を発揮するようになったことです。以前に Circuit Breaker を試して応答が遅いと感じた方は、ぜひもう一度試してみてください。現在では10タスク未満のサービスでもより効果的に機能します。 もう一つの、やや複雑な保護機能が Amazon CloudWatch Alarms です。Circuit Breaker が単に起動に失敗したタスクを監視するのに対し、CloudWatch Alarm を使用してデプロイを監視し、アプリケーションの性能低下を検出することができます。例えば、メモリリークによってメモリ使用率が急上昇する新しいアプリケーションバージョンをロールアウトしたり、CPU使用率の急上昇を引き起こしたり、500エラーの数が増加したり、APIレイテンシーが増加したりするようなシナリオを考えてみてください。これらはすべて、デプロイに問題があり、対応が必要であることを示すシグナルとなります。アプリケーションで重要な指標を監視するアラームを設定すると、Amazon ECS がそのアラームを自動的に監視します。アラームが警告状態になった場合、ECS はデプロイを失敗とし、設定に応じてオプションでデプロイをロールバックします。
CloudWatch Alarm をデプロイ用に設定する際の課題は、CPU指標、ALB指標、その他の指標に関連する遅延です。これは、通常のデプロイ終了時までに、アラームが警告状態に移行する機会がない可能性があることを意味します。これを防ぐため、アラームでデプロイを監視するように設定すると、新しいタスクが実行中で古いタスクがシャットダウンされた時点でデプロイが終了したとは見なされません。その後、アラームが警告状態にならないことを確認するために一定期間待機します。このBake Timeは、アラームの設定に基づいて自動的に計算されます。特別な設定は必要ありませんが、 指標の異常が遅れて表示された場合でも、機能が適切に応答し、正しく動作することを確実にするために実装された仕組みです。
CloudWatchアラームの設定時には、デプロイメントに適切なメトリクスを選択することが非常に重要です。APIやWebアプリケーションの場合、一般的にはロードバランサーのHTTP 500カウントや、ターゲットグループで測定されるレイテンシーを使用することをお勧めします。これらは、デプロイメントが正常な状態ではなくロールバックが必要であることを示す良い指標となります。CPUやメモリなどのインフラストラクチャの使用率メトリクスも重要です。キュー処理サービスの場合は、SQSメトリクス、特にデッドレターキューの深さを使用することをお勧めします。正常に処理できないメッセージがキューで急増している場合は、それが良い指標となるでしょう。使用率メトリクスも 一般的に使用できます。
デプロイメントにはCloudWatchアラームとCircuit Breakerの両方を使用することを、ほぼ常にお勧めします。CloudWatchアラームを設定しない場合でも、Circuit Breakerは必ず設定してください。多くのお客様にとって、非常にシンプルながら多くの手間を省くことができる安全装置です。観測性の観点からもう一つ強調したいのは、デプロイメントの速度と頻度です。Container Insightsを使用すると、Amazon ECSはECSでのデプロイメント数を追跡するメトリクスを発行します。これはクラスターレベルとサービスレベルの両方で確認できます。チームや特定のチームの速度を測定・追跡するために、このメトリクスを使用して新しいデプロイメントをどれだけ迅速に提供しているかを確認し、時間とともに比較して、デプロイメント速度が適切なレベルで変化しているかどうかを確認できます。
まとめと今後の学習リソース
では、主要なポイントについて説明するためにRobertに戻したいと思います。ありがとう、Viv。安全性、速度、スピードについては消化すべき内容が多いですが、今日取れる主要なポイントとアクションについてまとめたいと思います。 Amazon ECSには活用できる機能が多くあります。今日できることの一つは、バランスとスピードの観点からRolling Deploymentの設定を見直すことです。非本番環境では最小パーセンテージを下げ、本番環境ではデフォルトの100%と200%に上げることを検討してください。不適切な変更が本番環境に到達するのを防ぐためにヘルスチェックの設定を見直してください。設定は適切ですか?それらのヘルスチェックに合格した時点でアプリケーションは本当に正常な状態ですか?トラフィックの受信を開始する準備はできていますか?自発的な中断時にアプリケーションが正常にシャットダウンできることを確認してください。シグナルが来た時、アプリケーションは実際に正常にシャットダウンできますか?それとも期限切れ後に強制終了されてしまいますか?自動デプロイメントロールバックがない場合は、Circuit Breakerの導入を検討してください。
これは私とVivの写真の機会 ですが、実際にはAmazon ECSについてさらに学習を続けていただくための機会です。左側には、このセッションの資料とすべてのリンクがあります。これには、ワークショップや今日共有したプレゼンテーション、また参照したブログやWhat's Newへのリンクが含まれています。多くのお客様との協力を通じて構築されたAmazon ECSベストプラクティスガイド、そして実践的に学べるAmazon ECSワークショップもあります。このセッションの資料をチェックして、今日から学習の旅を続けることを強くお勧めします。ありがとうございました。このセッションを楽しんでいただき、より回復力のあるデプロイメントの実現に役立つことを願っています。お時間をいただき、ありがとうございました。
※ こちらの記事は Amazon Bedrock を利用することで全て自動で作成しています。
※ 生成AI記事によるインターネット汚染の懸念を踏まえ、本記事ではセッション動画を情報量をほぼ変化させずに文字と画像に変換することで、できるだけオリジナルコンテンツそのものの価値を維持しつつ、多言語でのAccessibilityやGooglabilityを高められればと考えています。
Discussion