Amazon Web Services ブログ
Amazon EKS スケーラビリティテストへの Deep Dive
はじめに
Amazon Elastic Kubernetes Service (Amazon EKS) の「Elastic」とは、「必要なときにリソースを確保し、不要になったときにリソースを解放する」機能を指します。Amazon EKS はほとんどすべてのワークロードを処理できるように拡張できますが、Amazon EKS のお客様から「1 つの Amazon EKS クラスターでサポートされる Pod やノードの最大数はいくつですか」というような質問をよく耳にします。
Kubernetes は複雑なシステムであり、Kubernetes クラスターのパフォーマンス特性はワークロードの特性によって異なる場合があるため、これらの質問に対する答えはさまざまです。Kubernetes コミュニティは Kubernetes コンポーネントのサービスレベル指標 (SLI) とサービスレベル目標 (SLO) を定義しており、これらをスケーラビリティに関する議論の出発点として使用できます。この投稿では、これらの SLI と SLO について説明し、Amazon EKS チームがどのようにスケーラビリティテストを実施しているのか説明します。
SLI は私たちがシステムを測定する方法です。例えばリクエストのレイテンシーやカウントのように、システムの稼働状況を判断するために使用できる指標があります。SLO は、例えば、リクエストのレイテンシーが 3 秒未満というように、システムが正常に稼働しているときに期待される値を定義します。Kubernetes SLO と SLI は Kubernetes コンポーネントのパフォーマンスに重点を置いており、Amazon EKS クラスターの Kubernetes API エンドポイントの可用性に重点を置いた Amazon EKS サービス SLA とは無関係です。
Kubernetes アップストリームの SLO
Amazon EKS はアップストリームの Kubernetes リリースに準拠しており、Amazon EKS クラスターが Kubernetes コミュニティによって定義された SLO の範囲内で動作することを保証しています。Scalability Special Interest Group (SIG) は Kubernetes のスケーラビリティ目標を定義し、SLI と SLO を通じてパフォーマンスのボトルネックを調査しています。
Kubernetes には、Container Storage Interface (CSI) ドライバー、Admission Webhook、Autoscaler など、ユーザーがカスタムのアドオンやドライバーを使用してシステムを拡張できる機能が数多くあります。これらの拡張は、Kubernetes クラスターのパフォーマンスにさまざまな形で影響を与える可能性があります。例えば、failurePolicy=Ignore
の Admission Webhook は、Webhook ターゲットが利用できない場合、Kubernetes API リクエストのレイテンシーを増加させる可能性があります。Kubernetes Scalability SIG は、「you promise, we promise」フレームワークを使用してスケーラビリティを定義しています。
次のことを約束していただければ:
- クラスターを正しく構成する
- 拡張機能を「合理的に」使用する
- クラスターの負荷を推奨制限内に抑える
クラスターがスケールすることをお約束します:
- すべての SLO が満たされます
Kubernetes SLO は、ワーカーノードのスケーリングや Admission Webhook など、クラスターに影響を与える可能性のあるプラグインや外部要因をすべて考慮しているわけではありません。これらの SLO は Kubernetes コンポーネントに重点を置いており、Kubernetes のアクションとリソースが期待どおりに動作することを保証します。SLO は、Kubernetes 開発者が Kubernetes コードの変更によってシステム全体のパフォーマンスをデグレードさせない役割を担っています。
Kubernetes Scalability SIG では、以下の公式 SLO/SLI を定義しており、Amazon EKS チームはこれらの SLO や SLI について Amazon EKS クラスターで定期的にスケーラビリティテストを実施して、変更が行われたり新しいバージョンがリリースされたりしたときのパフォーマンスのデグレードを監視しています。
Objective | Definition | SLO |
API request latency (mutating) | Latency of processing mutating API calls for single objects for every (resource, verb) pair, measured as 99th percentile over last 5 minutes | In default Kubernetes installation, for every (resource, verb) pair, excluding virtual and aggregated resources and Custom Resource Definitions, 99th percentile per cluster-day <= 1 second |
API request latency (read-only) | Latency of processing non-streaming read-only API calls for every (resource, scope) pair, measured as 99th percentile over last 5 minutes | In default Kubernetes installation, for every (resource, scope) pair, excluding virtual and aggregated resources and Custom Resource Definitions, 99th percentile per cluster-day: (a) <= 1 second if scope=resource (b) <= 30 seconds otherwise (if scope=namespace or scope=cluster) |
Pod startup latency | Startup latency of schedulable stateless pods, excluding time to pull images and run init containers, measured from pod creation timestamp to when all its containers are reported as started and observed via watch, measured as 99th percentile over last 5 minutes | In default Kubernetes installation, 99th percentile per cluster-day <= 5 seconds |
API リクエストのレイテンシー
kube-apiserver では、--request-timeout
がデフォルトで 1m0s
と定義されています。つまり、リクエストがタイムアウトしてキャンセルされるまでに最大 1 分(60 秒)実行できます。Latency に定義された SLO は、送信されるリクエストのタイプによって分類されます。リクエストのタイプは、変更可能な場合もあれば、読み取り専用の場合もあります。
変更
Kubernetes のリソース変更リクエストは、作成、削除、更新などを行います。これらのリクエストは、変更されたオブジェクトが返される前に etcd バックエンドに書き込まれます。etcd はすべての Kubernetes クラスターデータに使用される分散型のキーバリューストアです。
このレイテンシーは、Kubernetes リソースの (resource, verb) ペアに対して 5 分間の 99 パーセンタイルとして測定されます。例えば、Pod 作成リクエストやノード更新リクエストのレイテンシーが測定されます。SLO を満たすには、リクエストのレイテンシーが 1 秒未満である必要があります。
読み取り専用
読み取り専用リクエストは、「Get Pod X」など単一のリソース、または「Get all Pod from Namespace X」などコレクションを取得します。kube-apiserver はオブジェクトのキャッシュを保持するので、要求されたリソースをキャッシュから返すこともあれば、etcd から取得する必要がある場合もあります。
また、これらのレイテンシーは 5 分間にわたって 99 パーセンタイルで測定されますが、読み取り専用リクエストではスコープが異なっていてもかまいません。SLO には 2 つの異なる目標が定義されています。
kubectl get pod -n mynamespace my-controller-xxx
など単一のリソースに対して行われるリクエストの場合、リクエストのレイテンシーは 1 秒未満にとどまる必要があります。kubectl get pods -A
など名前空間またはクラスター内の複数のリソースに対して行われるリクエストの場合、レイテンシーは 30 秒未満にとどまる必要があります。
Kubernetes リソースのリストに対するリクエストでは、リクエストに含まれるすべてのオブジェクトの詳細が SLO 内で返されることを前提としているため、SLO はリクエストスコープごとにターゲット値が異なります。クラスター上の Kubernetes オブジェクトなどリソースの大きな集合では、応答サイズが大きくなり返されるまでに時間がかかることがあります。例えば、数万の Pod を実行しているクラスターで、JSON でエンコードした各 Pod が約 1KiB の場合、クラスター内のすべての Pod を返そうとすると 10MB 以上になります。Kubernetes クライアントは、ApiListChunking を使用して大量のリソースコレクションを取得することで、このレスポンスサイズを減らしています。
Pod 起動時のレイテンシー
この SLO は主に、Pod の作成から Pod 内のコンテナが実際に実行を開始するまでにかかる時間に関係します。これを測定するために、Pod に記録された作成タイムスタンプと、その Pod の WATCH がコンテナの起動を報告した時刻の差が計算されます (コンテナイメージのプルとコンテナの初期化にかかる時間は除く)。この SLO を満たすには、Pod 起動レイテンシーの 1 クラスター稼働日あたりの 99 パーセンタイルを 5 秒未満にする必要があります。
Kubernetes SLI メトリクス
Kubernetes では、これらの SLI を経時的に追跡する Kubernetes コンポーネントに Prometheus メトリクスを追加することで、SLI に関するオブザーバビリティも向上させています。Prometheus Query Language (PromQL) を使って、Prometheus や Grafana ダッシュボードなどのツールで SLI のパフォーマンスを経時的に表示するクエリを作成できます。以下は先述の SLO の例です。
API サーバーのリクエストレイテンシー
メトリクス | 定義 |
apiserver_request_sli_duration_seconds |
verb、group、version、resource、subresource、scope、および component ごとの秒単位の応答レイテンシー分布 (Webhook の持続時間、優先度、公平性キューの待機時間はカウントされません)。 |
apiserver_request_duration_seconds |
verb、dry run value、group、version、resource、subresource、scope、および component ごとの秒単位の応答レイテンシー分布。 |
注 : apiserver_request_sli_duration_seconds
メトリクスは Kubernetes 1.27 から利用可能になりました。
これらのメトリクスを使用して API サーバーの応答時間を調査したり、Kubernetes コンポーネントや他のプラグイン/コンポーネントにボトルネックがないかを調べたりすることができます。これらのメトリクスを比較することで、リクエスト処理の遅延がどこで発生しているのかを把握できます。
API リクエストレイテンシー SLI — Kubernetes コンポーネントがリクエストを処理して応答するのにかかった時間です。SLI メトリクスは、リクエストが API 優先度や公平性のキューで待機している時間、および Admission Webhook やその他の Kubernetes 拡張機能での処理に費やされる時間を除外することで、Kubernetes コンポーネントのパフォーマンスに関するインサイトを提供します。
API リクエスト合計レイテンシー — 合計時間メトリクスは、アプリケーションが API サーバーからの応答を待つ時間を反映しているため、より包括的な見方ができます。リクエストが受信されてからレスポンスが送信されるまでの時間が計算されます。これには、すべての Webhook 実行時間と、優先度と公平性のキューに費やされた時間が含まれます。
Pod 起動のレイテンシー
メトリクス | 定義 |
kubelet_pod_start_sli_duration_seconds |
イメージのプルと init コンテナーを実行する時間を除く、Pod を起動するまでの秒数。 Pod の作成タイムスタンプから、すべてのコンテナーが起動済みとして報告され、監視されるまでの時間を測定します。 |
kubelet_pod_start_duration_seconds |
kubelet が初めて Pod を確認してから Pod の実行が開始されるまでの秒数。これには、Pod をスケジュールする時間やワーカーノードのキャパシティをスケールアウトする時間は含まれていません。 |
注 : kubelet_pod_start_sli_duration_seconds
メトリクスは Kubernetes 1.27 から利用可能になりました。
前のクエリと同様に、これらのメトリクスを使用すると、kubelet アクションと比較して、ノードのスケーリング、イメージのプル、および init コンテナが Pod の起動を遅延させている時間の長さを把握できます。
Pod 起動レイテンシー SLI – Pod が作成されてから、アプリケーションコンテナが実行中と報告されるまでの時間です。これには、ワーカーノードのキャパシティが利用可能になり、Pod がスケジュールされるまでにかかる時間が含まれますが、イメージをプルしたり、init コンテナを実行したりするのにかかる時間は含まれません。
Pod 起動合計レイテンシー – kubelet が初めて Pod を起動するまでにかかる時間です。これは、kubelet が WATCH 経由で Pod を受信した時点から測定したもので、ワーカーノードのスケーリングやスケジューリングにかかる時間は含まれていません。これには、イメージをプルし、init コンテナを起動して実行する時間も含まれます。
Amazon EKS がスケーラビリティにアプローチする方法
Amazon EKS は Kubernetes コントロールプレーンコンポーネントを管理し、そのセキュリティ、可用性、スケーラビリティを確保しますが、アプリケーション、拡張機能、およびデータプレーンインフラストラクチャ (AWS Fargate を使用していない場合) の可用性とスケーラビリティについてはお客様の責任となります。Amazon EKS チームは定期的に一連の内部負荷テストを実施して、変更や新しいリリースによって改善されるか、同じパフォーマンスレベルが維持されるかを検証しています。EKS ベストプラクティスガイドの「スケーラビリティ」セクションには、クラスターのスケーラビリティを向上させるために実装できる推奨事項とパターンが記載されています。
アップストリームの Kubernetes SLO および SLI 定義との一貫性を確保するために、Amazon EKS チームは SIG スケーラビリティで定義されているアップストリームのスケーラビリティテストに使用されているものと同じ基準を適用して Amazon EKS クラスターのスケーラビリティを測定しています。さまざまなユースケースや構成をすべてテストすることはできないため、これらのテストは、より高度なワークロードを評価または比較する際に使用できるスケーラビリティのベースラインとなります。
Amazon EKS でスケーラビリティテストを実行する方法
Amazon EKS チームは、Kubernetes の公式スケーラビリティおよびパフォーマンステストフレームワーク ClusterLoader2 を使用しています。Cluster Loader は宣言型スタイルのテストを使用して、指定された規模と速度で Kubernetes オブジェクトを作成します (例えば「5,000 ノードでノードあたり 30 Pod を実行し、1 秒あたり 50 Pod の速度でリソースを作成したい」など)。詳細については、ClusterLoader2 の GitHub リポジトリを参照してください。
Amazon EKS スケーラビリティテストは、kubernetes/perf-tests リポジトリで定義されている汎用負荷テスト設定に基づいています。Amazon EKS コントロールプレーンが大規模な場合でも SLO を維持できるように、5,000 ノードでテストを実行するように設定しています。Kubernetes コミュニティでは、これをクラスターあたり 5,000 ノードを超えると Kubernetes のパフォーマンスが低下する可能性があるというしきい値として定義しています。ノード数は、ClusterLoader2 でテストを実行する際の追加パラメータ (名前空間の合計数など) の計算に使用されます。負荷テストを開始する前に、クラスター内のノードを 5,000 にスケールアウトしておきます。
負荷テストでは、Pod、(ReplicaSet と Pod を作成する) Deployment、Service、Secret などのさまざまな Kubernetes リソースが 1 秒あたり 50 Pod で作成され、Kubernetes コントロールプレーンのコンポーネントに持続的な負荷がかかります。Prometheus メトリクスは、リソースが作成されても SLO が満たされていることを確認するために、追加の詳細とともにテスト中に収集されます。
AWS のサービスクォータと考慮事項
クラスターを 5,000 ノードにスケールアウトするには、AWS アカウントのサービスクォータを増やす必要がありました。テストに必要な制限項目を以下の表に示します。これらはテストクラスターのスケールとチャーンに合わせて増やす必要があったクォータです。EKS ベストプラクティスガイドには、ワークロードに影響する可能性のあるその他の AWS サービスクォータが掲載されています。
AWS サービスクォータコンソールまたは AWS コマンドラインインターフェイス (AWS CLI) から、名前またはクォータコードを使用して、これらの制限の引き上げをリクエストできます。
サービス | クォータ名 | クォータコード | デフォルト | 増加後の値 |
Amazon Elastic Compute Cloud (Amazon EC2) | Running On-Demand Standard (A, C, D, H, I, M, R, T, Z) instances (最大 vCPU 数) | L-1216C47A | 5 | 32,000 |
Amazon Elastic Kubernetes Service (Amazon EKS) | Nodes per managed node group | L-BD136A63 | 450 | 1,000 |
Amazon Virtual Private Cloud (Amazon VPC) | Security groups per network interface | L-2AFB9258 | 5 | 16 |
Amazon VPC | IPv4 CIDR blocks per VPC | L-83CA0A9D | 5 | 20 |
Amazon Elastic Block Store (Amazon EBS) | Storage for General Purpose SSD (gp3) volumes, in TiB | L-7A658B76 | 50 | 1,100 |
Amazon EBS | Storage for General Purpose SSD (gp2) volumes, in TiB | L-D18FCD1D | 50 | 1,100 |
また、次の表に示すアクションに対する Amazon Elastic Compute Cloud (Amazon EC2) へのリクエストに対応するため、AWS アカウントのレート制限を引き上げています。Amazon EC2 のレートスロットリングの計算方法、アカウントでのレートスロットリングのモニタリング方法、および引き上げのリクエスト方法の詳細は、EC2 ドキュメントに記載されています。
変更アクション | 読み取り専用アクション |
AssignPrivateIpAddresses | DescribeDhcpOptions |
AttachNetworkInterface | DescribeInstances |
CreateNetworkInterface | DescribeNetworkInterfaces |
DeleteNetworkInterface | DescribeSecurityGroups |
DeleteTags | DescribeTags |
DetachNetworkInterface | DescribeVpcs |
ModifyNetworkInterfaceAttribute | DescribeVolumes |
Amazon EKS クラスター
ClusterLoader2 テストを実行するには、事前に合計 5,000 のワーカーノードにスケールアップされたマネージド型ノードグループを含む Amazon EKS クラスターを使用します。Amazon EKS は、クラスターからの多数のシグナルに応じて Kubernetes コントロールプレーンを自動的にスケーリングします。そのスケーリングの一環として、Amazon EKS は 1 秒あたりのクエリ数 (QPS) や処理中リクエストの制限など、Kubernetes コントロールプレーンコンポーネントの一部のパラメータもスケーリングします。Amazon EKS クラスターは Kubernetes アップストリームのこれらのパラメータのデフォルト値を使用して作成され、Amazon EKS サービスはコントロールプレーンのスケールアップに合わせて自動的に値を増加させます。
Kubernetes コンポーネントは、起動時に設定された値をログに出力します。Kubernetes コンポーネントで Amazon EKS コントロールプレーンのログが有効になっている場合は、FLAG:
で始まるログメッセージを検索してこれらのメッセージを確認できます。Amazon EKS が特定のクラスタースケールに設定する正確な値は、Kubernetes が変更されたり、より適切な値が見つかったりすると変わる可能性があります。
テスト用の Amazon VPC Container Networking Interface (CNI) プラグインは、Pod の集約率と IP アドレス割り当てのパフォーマンスを向上させるために、IP アドレス割り当てにプレフィックス委任を使用するように設定されています。クラスターはマネージド型ノードグループを幅広いインスタンスファミリーで使用しています。インスタンスタイプ間でインスタンスを多様化することで、複数のキャパシティプールからキャパシティを調達しやすくなります。テスト構成では、c5.large、m5.large、r5.large、t3.large、t3a.large、c5a.large、m5a.large、r5a.large のインスタンスを使用できます。
クラスターから Prometheus メトリクスを収集し、Amazon Managed Service for Prometheus と Amazon Managed Grafana を使用して確認します。
テストの結果
負荷テスト中、ClusterLoader2 はクラスターのパフォーマンスを監視します。上記の SLO が満たされていない (つまり、1 つの Pod を取得する API リクエストのレイテンシーの 99 パーセンタイル [p99] が 1 秒以上かかっている) 場合、テストは失敗とみなされます。Amazon EKS チームはこれらの結果をレビューし、失敗したテストを調査して失敗の原因を把握し、リグレッションが対処されていることを確認します。
負荷テスト中に作成されるリソースの総数は ClusterLoader の設定によって決まります。負荷テストでは、ノードあたり 30 Pod 、名前空間あたり 100 ノードで 計 5,000 ノードを想定しています。次に、テスト構成として、Pod の総数 (ノードあたり 30Pod に 5,000 ノードを掛けたもの)、名前空間 (5,000 ノードを名前空間あたり 100 ノードで割ったもの)、および名前空間あたりの Pod 数を計算します。
テストのピーク時には、クラスター内の SLO と予想されるチャーンを維持しながら、クラスター内のリソースの数を確認しています。
リソースタイプ | テスト中に達した最大値 |
#Nodes | 5,000 |
#Namespaces | 50 |
#Pods | 170,000* |
#Pods per node | 30* |
#Deployments | 16,000 |
#Services | 8,000 |
#Endpoints | 8,000 |
#Endpoints slice count | 8,000 |
#Secrets | 16,000 |
#ConfigMaps | 16,000 |
#CRDs | 4 |
#Jobs | 150 |
* 負荷テストでは、ノードあたり 30 個のアプリケーション Pod を実行します。 Pod の総数には、プラグインと DaemonSet の Pod が含まれます。
SLO はアクションまたはリクエストを完了するまでの時間の閾値を定義するため、Kubernetes リソースの総数は実際にはこれらのテストの成功の決定要因ではないことに注意してください。例えば、Pod が起動するまでの時間のほうが、Pod の総数よりもクラスターのパフォーマンスをより深く把握できます。
クラスターの SLO
Kubernetes が SLO をどのように定義しているか、Amazon EKS がクラスターのパフォーマンスをどのように測定するかを見てきました。Amazon EKS クラスターが、設定、プラグイン拡張機能、およびワークロードでどのように機能しているかを知りたいと思うかもしれません。既存の Amazon EKS クラスターで同じパフォーマンスベンチマークを確認するのに、5,000 ノードの負荷テストを全部実行する必要はありません。Amazon EKS クラスターの Kubernetes リソースから Prometheus メトリクスを収集すると、Kubernetes コントロールプレーンコンポーネントのパフォーマンスについてより深い洞察を得ることができます。使用できるメトリクスと Prometheus クエリの詳細については、EKS ベストプラクティスガイドの「スケーラビリティ」セクションを参照してください。
SLO はクラスター内の Kubernetes コンポーネントのパフォーマンスに重点を置いていますが、他にも確認できるメトリクスが存在し、クラスターについて異なる視点が得られることを考慮してください。kube-state-metrics のような Kubernetes コミュニティプロジェクトは、クラスター内の傾向をすばやく分析するのに役立ちます。Kubernetes コミュニティのコミュニティプラグインやドライバーは Prometheus メトリクスを出力することが多く、オートスケーラーやカスタムスケジューラーなどを調べることができます。 Observability Best Practices ガイドには、さらなる洞察を得るために使用できるその他の Kubernetes メトリクスの例が掲載されています。
Kubernetes コミュニティとの連携について
Amazon EKS は Kubernetes コミュニティに貢献しています。Amazon EKS チームは Scalability SIG と協力して、ネットワークプログラミングレイテンシー SLO のスケーラビリティテストを実施しました。また、Amazon EKS チームは Kubernetes コミュニティと協力して、Kubernetes クラスターをプロビジョニングするためのコミュニティツールである kOps を使用して、AWS で 5,000 ノードのテストを実施しました。このテストは、Kubernetes のコード変更がパフォーマンスに悪影響を及ぼさないことを確認するために定期的に実施され、その結果はコミュニティのパフォーマンスダッシュボードで確認できます。これらのスケーラビリティテストの 1 つが失敗すると、Amazon EKS チームに通知され、調査を手伝ってもらえます。これらのテストの結果は、Kubernetes コミュニティのパフォーマンスダッシュボードで確認できます。
Amazon EKS チームは、アップストリームの Kubernetes コミュニティと同じパフォーマンスメトリクスをモニタリングするために、社内で 5,000 ノードで同じ負荷テストを実施しています。同じテストを同じ規模で使用することで、Amazon EKS 固有のコンポーネントがアップストリームの Kubernetes テストと同じレベルのパフォーマンスを維持できることを確認できます。
この作業は出発点に過ぎません。Kubernetes コントロールプレーンをスケーリングするに従って QPS や処理中リクエストのオプションを増やすなど、お客様が実際の使用で遭遇するボトルネックや問題に基づいて Amazon EKS クラスターのスケーラビリティを常に向上させています。Amazon EKS では、こうした改善がクラスターに自動的にデプロイされるため、スケーラビリティの問題が発生する前に回避できます。
まとめ
この投稿では、Kubernetes コミュニティによって定義された SLO と、Amazon EKS がスケーラビリティをテストする方法について説明しました。1 つのクラスターを 1,000 ノードまたは 50,000 Pod を超えてスケーリングする場合は、ぜひご相談ください。Amazon EKS には大規模なクラスターを実行しているお客様がいます。可能な限り最高のパフォーマンスを提供するために、クラスターのスケーラビリティの向上に常に取り組んでいます。スケーリングについては、AWS アカウントチーム(ソリューションアーキテクトまたはテクニカルアカウントマネージャー)、AWS サポートチーム、または AWS Containers Roadmap に問い合わせてください。Kubernetes ワークロードを大規模に実行する方法の詳細については、EKS ベストプラクティスガイドのスケーラビリティセクションをご覧ください。
本記事は Deep dive into Amazon EKS scalability testing (2024 年 1 月 31 日公開) を翻訳したものです。翻訳は、ソリューションアーキテクトの吉田が担当しました。