

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Kubernetes コントロールプレーン
<a name="scale-control-plane"></a>

**ヒント**  
 Amazon EKS [https://aws-experience.com/emea/smb/events/series/get-hands-on-with-amazon-eks?trk=4a9b4147-2490-4c63-bc9f-f8a84b122c8c&sc_channel=el](https://aws-experience.com/emea/smb/events/series/get-hands-on-with-amazon-eks?trk=4a9b4147-2490-4c63-bc9f-f8a84b122c8c&sc_channel=el)ワークショップを通じてベストプラクティスをご覧ください。

Kubernetes コントロールプレーンは、Kubernetes API Server、Kubernetes Controller Manager、スケジューラ、および Kubernetes が機能するために必要なその他のコンポーネントで構成されます。これらのコンポーネントのスケーラビリティ制限は、クラスターで実行している内容によって異なりますが、スケーリングに最も影響がある領域には、Kubernetes のバージョン、使用率、個々のノードスケーリングが含まれます。

クラスターのコントロールプレーンを 2 つのモードのいずれかで実行して、さまざまなワークロード要件を満たすことができます。
+  **標準モード** – デフォルトでは、すべての EKS クラスターは標準モードを使用します。コントロールプレーンは、ワークロードの需要に応じて自動的にスケールアップおよびスケールダウンします。標準モードでは、十分なコントロールプレーン容量が動的に割り当てられ、ほとんどのユースケースで推奨されるオプションです。
+  **プロビジョンドモード** – ワークロードがコントロールプレーンのスケーリングによるパフォーマンスの変動を許容できない場合、または非常に高いコントロールプレーン容量が必要な場合は、プロビジョンドモードを使用できます。プロビジョンドモードでは、厳しい要件に対応できるコントロールプレーン容量を事前に割り当てます。一貫した予測可能なパフォーマンスが得られます。

EKS プロビジョンドモードでは、一連のスケーリング階層 (XL、2XL、4XL、8XL) から選択できます。階層ごとに、クラスターのコントロールプレーンから高い予測可能なパフォーマンスが得られます。プロビジョンドモードは、以下のユースケースで特に役立ちます。
+ パフォーマンスが重要なワークロード
+ 大規模な AI および機械学習オペレーション
+ 予想されるオンデマンドイベント
+ ステージングと本番環境全体で整合性が必要な環境

プロビジョンドモードでは、コントロールプレーンの容量を事前に割り当てて、1 分間隔で測定される拡張 99.99% サービスレベルアグリーメント (SLA) を活用できます。階層の仕様や料金など、EKS プロビジョンドモードの詳細については、[「EKS ユーザーガイド」の「EKS プロビジョンドコントロールプレーン](https://docs.aws.amazon.com/eks/latest/userguide/eks-provisioned-control-plane.html)」を参照してください。 **

## ワークロードとノードのバーストを制限する
<a name="_limit_workload_and_node_bursting"></a>

**重要**  
コントロールプレーンの API 制限に達しないようにするには、クラスターサイズを一度に 2 桁のパーセンテージで増やすスケーリングスパイクを制限する必要があります (たとえば、1000 ノードから 1100 ノード、4000 から 4500 ポッドに一度に増やすなど）。

EKS コントロールプレーンは、クラスターが大きくなるにつれて自動的にスケーリングされますが、スケーリング速度には制限があります。EKS クラスターを初めて作成すると、コントロールプレーンはすぐに数百のノードや数千のポッドにスケールできなくなります。EKS によるスケーリングの改善の詳細については、[このブログ記事](https://aws.amazon.com/blogs/containers/amazon-eks-control-plane-auto-scaling-enhancements-improve-speed-by-4x/)を参照してください。

大規模なアプリケーションのスケーリングには、インフラストラクチャが完全に準備できるように適応する必要があります (ロードバランサーのウォームなど）。スケーリングの速度を制御するには、アプリケーションに適したメトリクスに基づいてスケーリングしていることを確認してください。CPU とメモリのスケーリングではアプリケーションの制約を正確に予測できない場合があります。Kubernetes Horizontal Pod Autoscaler (HPA) でカスタムメトリクス (1 秒あたりのリクエスト数など) を使用すると、スケーリングオプションが適している可能性があります。

カスタムメトリクスを使用するには、[Kubernetes ドキュメント](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics)の例を参照してください。より高度なスケーリングニーズがある場合、または外部ソース (AWS SQS キューなど) に基づいてスケーリングする必要がある場合は、イベントベースのワークロードスケーリングに [KEDA](https://keda.sh) を使用します。

## ノードとポッドを安全にスケールダウンする
<a name="_scale_nodes_and_pods_down_safely"></a>

### 長時間実行されているインスタンスを置き換える
<a name="_replace_long_running_instances"></a>

ノードを定期的に置き換えることで、設定のドリフトや、長時間の稼働後にのみ発生する問題 (メモリリークの遅延など) を回避することで、クラスターを正常に保つことができます。自動交換は、ノードのアップグレードとセキュリティパッチ適用の適切なプロセスとプラクティスを提供します。クラスター内のすべてのノードが定期的に置き換えられると、継続的なメンテナンスのために個別のプロセスを維持するのに必要な労力が少なくなります。

Karpenter の[有効期限 (TTL)](https://docs.aws.amazon.com/eks/latest/best-practices/karpenter.html#_creating_nodepools) 設定を使用して、指定した時間実行されたインスタンスを置き換えます。セルフマネージド型ノードグループは、 `max-instance-lifetime`設定を使用してノードを自動的にサイクルできます。マネージド型ノードグループには現在この機能はありませんが、[GitHub でリクエスト](https://github.com/aws/containers-roadmap/issues/1190)を追跡できます。

### 使用率の低いノードを削除する
<a name="_remove_underutilized_nodes"></a>

Kubernetes Cluster Autoscaler のスケールダウンしきい値を使用して実行中のワークロードがない場合、 でノードを削除する[https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#how-does-scale-down-work](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#how-does-scale-down-work)か、Karpenter で`ttlSecondsAfterEmpty`プロビジョナー設定を使用できます。

### ポッドの中断予算と安全なノードのシャットダウンを使用する
<a name="_use_pod_disruption_budgets_and_safe_node_shutdown"></a>

Kubernetes クラスターからポッドとノードを削除するには、コントローラーが複数のリソース (EndpointSlices など) を更新する必要があります。これを頻繁に行うか、速すぎると、変更がコントローラーに伝達されるにつれて、API サーバーのスロットリングやアプリケーションの停止が発生する可能性があります。[Pod Disruption Budgets](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/) は、クラスター内でノードが削除または再スケジュールされるときにワークロードの可用性を保護するために、解約を遅らせるためのベストプラクティスです。

## Kubectl の実行時にクライアント側のキャッシュを使用する
<a name="_use_client_side_cache_when_running_kubectl"></a>

kubectl コマンドを非効率的に使用すると、Kubernetes API Server に追加の負荷がかかる可能性があります。kubectl を繰り返し使用するスクリプトやオートメーション (ループ用 など) を実行したり、ローカルキャッシュなしでコマンドを実行したりしないでください。

 `kubectl` には、クラスターから検出情報をキャッシュして、必要な API コールの量を減らすクライアント側のキャッシュがあります。キャッシュはデフォルトで有効になっており、10 分ごとに更新されます。

コンテナから、またはクライアント側のキャッシュなしで kubectl を実行すると、API スロットリングの問題が発生する可能性があります。不要な API コールが発生しないように`--cache-dir`、 をマウントしてクラスターキャッシュを保持することをお勧めします。

## kubectl 圧縮を無効にする
<a name="_disable_kubectl_compression"></a>

kubeconfig ファイルで kubectl 圧縮を無効にすると、API とクライアントの CPU 使用率が低下する可能性があります。デフォルトでは、サーバーはクライアントに送信されたデータを圧縮してネットワーク帯域幅を最適化します。これにより、すべてのリクエストに対してクライアントとサーバーに CPU 負荷がかかり、十分な帯域幅がある場合、圧縮を無効にするとオーバーヘッドとレイテンシーが軽減されます。圧縮を無効にするには、 `--disable-compression=true`フラグを使用するか、kubeconfig ファイル`disable-compression: true`で を設定します。

```
apiVersion: v1
clusters:
- cluster:
    server: serverURL
    disable-compression: true
  name: cluster
```

## シャードクラスターオートスケーラー
<a name="_shard_cluster_autoscaler"></a>

[Kubernetes Cluster Autoscaler は、最大 1000 ノードまでスケールアップするようにテストされています](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/proposals/scalability_tests.md)。1000 個を超えるノードを持つ大規模なクラスターでは、Cluster Autoscaler の複数のインスタンスをシャードモードで実行することをお勧めします。各 Cluster Autoscaler インスタンスは、一連のノードグループをスケールするように設定されています。次の例は、各スケール 4 ノードグループに設定された 2 つのクラスターの自動スケーリング設定を示しています。

ClusterAutoscaler-1

```
autoscalingGroups:
- name: eks-core-node-grp-20220823190924690000000011-80c1660e-030d-476d-cb0d-d04d585a8fcb
  maxSize: 50
  minSize: 2
- name: eks-data_m1-20220824130553925600000011-5ec167fa-ca93-8ca4-53a5-003e1ed8d306
  maxSize: 450
  minSize: 2
- name: eks-data_m2-20220824130733258600000015-aac167fb-8bf7-429d-d032-e195af4e25f5
  maxSize: 450
  minSize: 2
- name: eks-data_m3-20220824130553914900000003-18c167fa-ca7f-23c9-0fea-f9edefbda002
  maxSize: 450
  minSize: 2
```

ClusterAutoscaler-2

```
autoscalingGroups:
- name: eks-data_m4-2022082413055392550000000f-5ec167fa-ca86-6b83-ae9d-1e07ade3e7c4
  maxSize: 450
  minSize: 2
- name: eks-data_m5-20220824130744542100000017-02c167fb-a1f7-3d9e-a583-43b4975c050c
  maxSize: 450
  minSize: 2
- name: eks-data_m6-2022082413055392430000000d-9cc167fa-ca94-132a-04ad-e43166cef41f
  maxSize: 450
  minSize: 2
- name: eks-data_m7-20220824130553921000000009-96c167fa-ca91-d767-0427-91c879ddf5af
  maxSize: 450
  minSize: 2
```

## API の優先度と公平性
<a name="_api_priority_and_fairness"></a>

![APF](http://docs.aws.amazon.com/ja_jp/eks/latest/best-practices/images/scalability/APF.jpg)


### 概要
<a name="_overview"></a>

リクエストが増加している間に自分自身が過負荷にならないように、API Server は、特定の時点で未処理の可能性がある処理中のリクエストの数を制限します。この制限を超えると、API Server はリクエストの拒否を開始し、「リクエストが多すぎます」の 429 HTTP レスポンスコードをクライアントに返します。サーバーがリクエストを削除し、後でクライアントに再試行させるのは、リクエスト数にサーバー側の制限を課さず、コントロールプレーンをオーバーロードして、パフォーマンスが低下したり、使用不能になったりする可能性があるためです。

これらの処理中のリクエストをさまざまなリクエストタイプに分割する方法を設定するために Kubernetes が使用するメカニズムは、[API Priority and Fairness](https://kubernetes.io/docs/concepts/cluster-administration/flow-control/) と呼ばれます。API Server は、 フラグ`--max-requests-inflight`と `--max-mutating-requests-inflight`フラグで指定された値を合計することで、受け入れることができる処理中のリクエストの合計数を設定します。EKS は、これらのフラグに対して 400 リクエストと 200 リクエストのデフォルト値を使用し、一度に合計 600 リクエストをディスパッチできます。ただし、使用率とワークロードのチャーンの増加に応じてコントロールプレーンをより大きなサイズにスケールするため、それに応じて 2000 年までインフライトリクエストクォータが増加します (変更される可能性があります）。APF は、これらの処理中のリクエストクォータをさまざまなリクエストタイプにさらに分割する方法を指定します。EKS コントロールプレーンは、各クラスターに登録された少なくとも 2 つの API Server で高可用性であることに注意してください。つまり、クラスターが処理できる処理中のリクエストの総数は、kube-apiserver ごとに設定された処理中のクォータの 2 倍 (水平方向にスケールアウトしている場合はそれ以上) になります。これは、最大の EKS クラスターで 1 秒あたり数千のリクエストに相当します。

PriorityLevelConfigurations と FlowSchemas と呼ばれる 2 種類の Kubernetes オブジェクトは、リクエストの合計数を異なるリクエストタイプに分割する方法を設定します。これらのオブジェクトは API Server によって自動的に維持され、EKS は指定された Kubernetes マイナーバージョンのこれらのオブジェクトのデフォルト設定を使用します。PriorityLevelConfigurations は、許可されたリクエストの合計数の一部を表します。たとえば、ワークロードの高い PriorityLevelConfiguration には、合計 600 件のリクエストのうち 98 件が割り当てられます。すべての PriorityLevelConfigurations に割り当てられたリクエストの合計は 600 に等しくなります (または、特定のレベルにリクエストの一部が付与されると API サーバーが切り上げられるため、600 をわずかに上回ります）。クラスター内の PriorityLevelConfigurations と、それぞれに割り当てられたリクエストの数を確認するには、次のコマンドを実行します。EKS 1.32 のデフォルトは次のとおりです。

```
$ kubectl get --raw /metrics | grep apiserver_flowcontrol_nominal_limit_seats
apiserver_flowcontrol_nominal_limit_seats{priority_level="catch-all"} 13
apiserver_flowcontrol_nominal_limit_seats{priority_level="exempt"} 0
apiserver_flowcontrol_nominal_limit_seats{priority_level="global-default"} 49
apiserver_flowcontrol_nominal_limit_seats{priority_level="leader-election"} 25
apiserver_flowcontrol_nominal_limit_seats{priority_level="node-high"} 98
apiserver_flowcontrol_nominal_limit_seats{priority_level="system"} 74
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-high"} 98
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245
```

オブジェクトの 2 番目のタイプは FlowSchemas です。特定のプロパティセットを持つ API サーバーリクエストは、同じ FlowSchema で分類されます。これらのプロパティには、認証されたユーザー、または API グループ、名前空間、リソースなどのリクエストの属性が含まれます。FlowSchema は、このタイプのリクエストをマッピングする PriorityLevelConfiguration も指定します。2 つのオブジェクトは、「このタイプのリクエストをこの処理中のリクエストのシェアにカウントします」と言います。リクエストが API サーバーに到達すると、必要なすべてのプロパティに一致するものが見つかるまで、各 FlowSchemas がチェックされます。複数の FlowSchemas がリクエストに一致する場合、API サーバーは一致する優先順位が最も小さい FlowSchema を選択します。この優先順位は オブジェクトのプロパティとして指定されます。

FlowSchemas と PriorityLevelConfigurations のマッピングは、次のコマンドを使用して表示できます。

```
$ kubectl get flowschemas
NAME                           PRIORITYLEVEL     MATCHINGPRECEDENCE   DISTINGUISHERMETHOD   AGE     MISSINGPL
exempt                         exempt            1                    <none>                7h19m   False
eks-exempt                     exempt            2                    <none>                7h19m   False
probes                         exempt            2                    <none>                7h19m   False
system-leader-election         leader-election   100                  ByUser                7h19m   False
endpoint-controller            workload-high     150                  ByUser                7h19m   False
workload-leader-election       leader-election   200                  ByUser                7h19m   False
system-node-high               node-high         400                  ByUser                7h19m   False
system-nodes                   system            500                  ByUser                7h19m   False
kube-controller-manager        workload-high     800                  ByNamespace           7h19m   False
kube-scheduler                 workload-high     800                  ByNamespace           7h19m   False
kube-system-service-accounts   workload-high     900                  ByNamespace           7h19m   False
eks-workload-high              workload-high     1000                 ByUser                7h14m   False
service-accounts               workload-low      9000                 ByUser                7h19m   False
global-default                 global-default    9900                 ByUser                7h19m   False
catch-all                      catch-all         10000                ByUser                7h19m   False
```

PriorityLevelConfigurations には、キュー、拒否、または免除のタイプを指定できます。Queue 型と Reject 型では、その優先度レベルの処理中のリクエストの最大数に制限が適用されますが、その制限に達した場合の動作は異なります。たとえば、ワークロードの高い PriorityLevelConfiguration は Queue 型を使用し、コントローラーマネージャー、エンドポイントコントローラー、スケジューラ、eks 関連コントローラー、および kube-system 名前空間で実行されているポッドから 98 個のリクエストを使用できます。Queue 型が使用されるため、API Server はリクエストをメモリに保持しようとし、これらのリクエストがタイムアウトする前に処理中のリクエストの数が 98 を下回ることを期待します。特定のリクエストがキューでタイムアウトした場合、またはすでにキューに入れられているリクエストが多すぎる場合、API Server はリクエストを削除してクライアントに 429 を返します。キューイングによりリクエストが 429 を受信できなくなる場合がありますが、リクエストに対するend-to-endのレイテンシーの増加というトレードオフが伴います。

次に、タイプが Reject のキャッチオール PriorityLevelConfiguration にマッピングされるキャッチオール FlowSchema について考えます。クライアントが 13 件の処理中のリクエストの制限に達すると、API Server はキューイングを実行せず、429 レスポンスコードでリクエストを即座に削除します。最後に、Exempt 型の PriorityLevelConfiguration へのマッピングリクエストは 429 を受信せず、常にすぐにディスパッチされます。これは、healthz リクエストや system:masters グループからのリクエストなど、優先度の高いリクエストに使用されます。

### APF およびドロップされたリクエストのモニタリング
<a name="_monitoring_apf_and_dropped_requests"></a>

APF が原因でリクエストが削除されているかどうかを確認するために、 の API サーバーメトリクス`apiserver_flowcontrol_rejected_requests_total`をモニタリングして、影響を受ける FlowSchemas と PriorityLevelConfigurations を確認できます。例えば、このメトリクスは、サービスアカウント FlowSchema からの 100 件のリクエストが、ワークロードの低いキューでリクエストがタイムアウトしたために削除されたことを示しています。

```
% kubectl get --raw /metrics | grep apiserver_flowcontrol_rejected_requests_total
apiserver_flowcontrol_rejected_requests_total{flow_schema="service-accounts",priority_level="workload-low",reason="time-out"} 100
```

PriorityLevelConfiguration が 429 秒の受信またはキューイングによるレイテンシーの増加にどれだけ近いかを確認するには、同時実行数の制限と使用中の同時実行数の差を比較できます。この例では、100 リクエストのバッファがあります。

```
% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_nominal_limit_seats.*workload-low'
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_executing_seats.*workload-low'
apiserver_flowcontrol_current_executing_seats{flow_schema="service-accounts",priority_level="workload-low"} 145
```

特定の PriorityLevelConfiguration でキューイングが発生しているが、必ずしもリクエストが削除されていないかどうかを確認するには、 の メトリクスを参照`apiserver_flowcontrol_current_inqueue_requests`できます。

```
% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_inqueue_requests.*workload-low'
apiserver_flowcontrol_current_inqueue_requests{flow_schema="service-accounts",priority_level="workload-low"} 10
```

その他の便利な Prometheus メトリクスは次のとおりです。
+ apiserver\_flowcontrol\_dispatched\_requests\_total
+ apiserver\_flowcontrol\_request\_execution\_seconds
+ apiserver\_flowcontrol\_request\_wait\_duration\_seconds

[APF メトリクス](https://kubernetes.io/docs/concepts/cluster-administration/flow-control/#observability)の完全なリストについては、アップストリームのドキュメントを参照してください。

### ドロップされたリクエストの防止
<a name="_preventing_dropped_requests"></a>

#### ワークロードを変更して 429 を防ぐ
<a name="_prevent_429s_by_changing_your_workload"></a>

特定の PriorityLevelConfiguration が許可される処理中のリクエストの最大数を超えたために APF がリクエストを削除している場合、影響を受ける FlowSchemas のクライアントは、特定の時間に実行されるリクエストの数を減らすことができます。これは、429 秒間に行われたリクエストの合計数を減らすことで実現できます。高価なリスト呼び出しなどの長時間実行されるリクエストは、実行中の全期間にわたって処理中のリクエストとしてカウントされるため、特に問題があることに注意してください。これらの高価なリクエストの数を減らしたり、これらのリスト呼び出しのレイテンシーを最適化したり (たとえば、リクエストごとにフェッチされるオブジェクトの数を減らしたり、監視リクエストを使用して に切り替えるなど）、特定のワークロードに必要な合計同時実行数を減らすことができます。

#### APF 設定を変更して 429 を防ぐ
<a name="_prevent_429s_by_changing_your_apf_settings"></a>

**警告**  
デフォルトの APF 設定を変更するのは、何をしているかがわかっている場合のみです。APF 設定を誤って設定すると、API Server リクエストがドロップされ、ワークロードが大幅に中断される可能性があります。

リクエストの削除を防ぐもう 1 つの方法は、EKS クラスターにインストールされているデフォルトの FlowSchemas または PriorityLevelConfigurations を変更することです。EKS は、特定の Kubernetes マイナーバージョンの FlowSchemas と PriorityLevelConfigurations のアップストリームデフォルト設定をインストールします。API Server は、オブジェクトの次の注釈が false に設定されない限り、変更されると、これらのオブジェクトを自動的にデフォルトに調整します。

```
  metadata:
    annotations:
      apf.kubernetes.io/autoupdate-spec: "false"
```

大まかに言うと、APF 設定は次のいずれかに変更できます。
+ 関心のあるリクエストに、より多くの処理中の容量を割り当てます。
+ 他のリクエストタイプの容量を枯渇させる可能性のある、重要でない、または高価なリクエストを分離します。

これは、デフォルトの FlowSchemas と PriorityLevelConfigurations を変更するか、これらのタイプの新しいオブジェクトを作成することで実現できます。オペレーターは、関連する PriorityLevelConfigurations オブジェクトの assuredConcurrencyShares の値を増やして、割り当てられた処理中のリクエストの割合を増やすことができます。さらに、リクエストがディスパッチされる前にキューに入れられたことによる追加のレイテンシーをアプリケーションが処理できる場合、特定の時間にキューに入れることができるリクエストの数を増やすこともできます。

または、顧客のワークロードに固有の新しい FlowSchema オブジェクトと PriorityLevelConfigurations オブジェクトを作成することもできます。よりassuredConcurrencyShares を既存の PriorityLevelConfigurations または新しい PriorityLevelConfigurations に割り当てると、API Server ごとに全体的な制限が 600 インフライトのままになるため、他のバケットで処理できるリクエストの数が減少します。

APF のデフォルトを変更するときは、これらのメトリクスを非本番稼働用クラスターでモニタリングして、設定を変更しても意図しない 429 が発生しないようにする必要があります。

1. のすべての FlowSchemas の メトリクスをモニタリングして、バケットがリクエストの削除を開始しないように`apiserver_flowcontrol_rejected_requests_total`する必要があります。

1. `apiserver_flowcontrol_nominal_limit_seats` と の値を比較して、使用中の同時実行がその優先度レベルの制限を超過するリスクがないことを確認する`apiserver_flowcontrol_current_executing_seats`必要があります。

新しい FlowSchema と PriorityLevelConfiguration を定義するための一般的なユースケースの 1 つは、分離用です。長時間実行されるリストイベント呼び出しをポッドから独自のリクエスト共有に分離するとします。これにより、既存のサービスアカウント FlowSchema を使用するポッドからの重要なリクエストが 429 を受信し、リクエスト容量が枯渇するのを防ぐことができます。処理中のリクエストの合計数は有限ですが、この例では、APF 設定を変更して特定のワークロードのリクエスト容量をより適切に分割できることを示しています。

リストイベントリクエストを分離する FlowSchema オブジェクトの例:

```
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
  name: list-events-default-service-accounts
spec:
  distinguisherMethod:
    type: ByUser
  matchingPrecedence: 8000
  priorityLevelConfiguration:
    name: catch-all
  rules:
  - resourceRules:
    - apiGroups:
      - '*'
      namespaces:
      - default
      resources:
      - events
      verbs:
      - list
    subjects:
    - kind: ServiceAccount
      serviceAccount:
        name: default
        namespace: default
```
+ この FlowSchema は、デフォルトの名前空間でサービスアカウントによって行われたすべてのリストイベント呼び出しをキャプチャします。
+ 一致する優先順位 8000 は、既存の service-accounts FlowSchema で使用される 9000 の値よりも小さいため、これらのリストイベント呼び出しは service-accounts ではなく list-events-default-service-accounts と一致します。
+ キャッチオール PriorityLevelConfiguration を使用して、これらのリクエストを分離しています。このバケットでは、これらの長時間実行されるリストイベント呼び出しで使用できる処理中のリクエストは 13 個のみです。ポッドは、これらのリクエストのうち 13 件以上を同時に発行しようとすると、すぐに 429 件の受信を開始します。

## API サーバーでのリソースの取得
<a name="_retrieving_resources_in_the_api_server"></a>

API サーバーから情報を取得することは、あらゆるサイズのクラスターで想定される動作です。クラスター内のリソース数をスケールすると、リクエストの頻度とデータ量がすぐにコントロールプレーンのボトルネックになり、API のレイテンシーと速度低下につながる可能性があります。レイテンシーの重要度によっては、注意しないと予期しないダウンタイムが発生します。

リクエストしている内容と、これらのタイプの問題を回避するための最初のステップの頻度を認識します。スケーリングのベストプラクティスに基づいてクエリの量を制限するガイダンスを次に示します。このセクションの提案は、最適なスケーリングが知られているオプションから順に提供されています。

### 共有インフォマーを使用する
<a name="_use_shared_informers"></a>

Kubernetes API と統合するコントローラーとオートメーションを構築する場合、多くの場合、Kubernetes リソースから情報を取得する必要があります。これらのリソースを定期的にポーリングすると、API サーバーに大きな負荷がかかる可能性があります。

client-go ライブラリの[インフォマー](https://pkg.go.dev/k8s.io/client-go/informers)を使用すると、変更をポーリングするのではなく、イベントに基づいてリソースへの変更を監視する利点があります。情報提供者は、イベントと変更に共有キャッシュを使用して負荷をさらに軽減するため、同じリソースを監視する複数のコントローラーが負荷を追加しません。

コントローラーは、特に大規模なクラスターでは、ラベルやフィールドセレクタなしでクラスター全体のリソースをポーリングしないようにする必要があります。フィルタリングされていない各ポーリングでは、クライアントによってフィルタリングされるために、API サーバーを介して etcd から大量の不要なデータを送信する必要があります。ラベルと名前空間に基づいてフィルタリングすることで、API サーバーがクライアントに送信されるリクエストとデータをフルフィルするために実行する必要がある作業の量を減らすことができます。

### Kubernetes API の使用を最適化する
<a name="_optimize_kubernetes_api_usage"></a>

カスタムコントローラーまたはオートメーションを使用して Kubernetes API を呼び出す場合は、必要なリソースのみに呼び出しを制限することが重要です。制限がないと、API サーバーや etcd に不要な負荷が発生する可能性があります。

可能な限り、Watch 引数を使用することをお勧めします。引数がない場合、デフォルトの動作はオブジェクトを一覧表示することです。リストの代わりに watch を使用するには、 API リクエストの末尾`?watch=true`に を追加できます。たとえば、ウォッチを使用してデフォルトの名前空間内のすべてのポッドを取得するには、次のようにします。

```
/api/v1/namespaces/default/pods?watch=true
```

オブジェクトを一覧表示する場合は、一覧表示するオブジェクトの範囲と返されるデータの量を制限する必要があります。リクエストに引`limit=500`数を追加することで、返されるデータを制限できます。`fieldSelector` 引数と`/namespace/`パスは、リストの範囲を必要に応じて絞り込むのに役立ちます。たとえば、デフォルトの名前空間で実行中のポッドのみを一覧表示するには、次の API パスと引数を使用します。

```
/api/v1/namespaces/default/pods?fieldSelector=status.phase=Running&limit=500
```

または、 で実行されているすべてのポッドを一覧表示します。

```
/api/v1/pods?fieldSelector=status.phase=Running&limit=500
```

監視呼び出しやリストされたオブジェクトを制限するもう 1 つのオプションは、[`resourceVersions`Kubernetes ドキュメントで参照できる ](https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions)を使用することです。`resourceVersion` 引数がないと、利用可能な最新バージョンが返されます。このバージョンでは、データベースの読み込みが最もコストが高く、最も遅い etcd クォーラム読み込みが必要になります。resourceVersion は、クエリしようとしているリソースによって異なり、 `metadata.resourseVersion`フィールドにあります。これは、単に通話を一覧表示するのではなく、ウォッチコールを使用する場合にも推奨されます。

API サーバーキャッシュから結果を返す特別な が`resourceVersion=0`利用可能です。これにより、 etcd 負荷を減らすことができますが、ページ分割はサポートされていません。

```
/api/v1/namespaces/default/pods?resourceVersion=0
```

resourceVersion を前述のリストまたはウォッチから受信した最新の既知の値に設定して watch を使用することをお勧めします。これは client-go で自動的に処理されます。ただし、他の言語で k8s クライアントを使用している場合は、再確認することをお勧めします。

```
/api/v1/namespaces/default/pods?watch=true&resourceVersion=362812295
```

引数なしで API を呼び出すと、API サーバーや etcd にとって最もリソースを大量に消費します。この呼び出しでは、ページ分割やスコープの制限なしですべての名前空間内のすべてのポッドを取得し、etcd からのクォーラム読み取りが必要になります。

```
/api/v1/pods
```

### DaemonSet が群れを雷撃するのを防ぐ
<a name="_prevent_daemonset_thundering_herds"></a>

DaemonSet は、すべての (または一部の) ノードがポッドのコピーを実行するようにします。ノードがクラスターに参加すると、daemonset-controller はそれらのノードのポッドを作成します。ノードがクラスターを離れると、それらのポッドはガベージコレクションされます。DaemonSet を削除すると、作成したポッドがクリーンアップされます。

DaemonSet の一般的な用途は次のとおりです。
+ すべてのノードでクラスターストレージデーモンを実行する
+ すべてのノードでログ収集デーモンを実行する
+ すべてのノードでノードモニタリングデーモンを実行する

数千のノードがあるクラスターでは、新しい DaemonSet の作成、DaemonSet の更新、またはノード数の増加により、コントロールプレーンに高い負荷がかかる可能性があります。DaemonSet ポッドがポッドの起動時に高価な API サーバーリクエストを発行すると、多数の同時リクエストからコントロールプレーンで大量のリソースが使用される可能性があります。

通常のオペレーションでは、 を使用して`RollingUpdate`、新しい DaemonSet ポッドを段階的にロールアウトできます。`RollingUpdate` 更新戦略では、DaemonSet テンプレートを更新すると、コントローラーは古い DaemonSet ポッドを強制終了し、制御された方法で新しい DaemonSet ポッドを自動的に作成します。DaemonSet の最大 1 つのポッドは、更新プロセス全体で各ノードで実行されます。を 1、0、60 `maxSurge` `maxUnavailable`に設定することで、段階的なロールアウトを実行できます`minReadySeconds`。更新戦略を指定しない場合、Kubernetes はデフォルトで を 1、0、`minReadySeconds`0 `maxSurge` `RollingUpdate``maxUnavailable`として作成します。

```
minReadySeconds: 60
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 0
    maxUnavailable: 1
```

DaemonSet が既に作成されており、すべてのノードで予想される数のポッドがある場合、 は新しい DaemonSet `Ready` ポッドを段階的にロールアウト`RollingUpdate`します。群れの問題は、`RollingUpdate`戦略の対象ではない特定の条件下で発生する可能性があります。

#### DaemonSet の作成時に群れが雷鳴するのを防ぐ
<a name="_prevent_thundering_herds_on_daemonset_creation"></a>

デフォルトでは、`RollingUpdate`設定に関係なく、kube-controller-manager の daemonset-controller は、新しい DaemonSet を作成するときに、一致するすべてのノードのポッドを同時に作成します。DaemonSet の作成後にポッドの段階的なロールアウトを強制するには、 `NodeSelector`または を使用できます`NodeAffinity`。これにより、ゼロノードに一致する DaemonSet が作成され、ノードを徐々に更新して、DaemonSet からポッドを制御されたレートで実行できるようになります。このアプローチに従うことができます。
+ のすべてのノードにラベルを追加します`run-daemonset=false`。

```
kubectl label nodes --all run-daemonset=false
```
+ `run-daemonset=false` ラベルのないノードと一致する`NodeAffinity`設定で DaemonSet を作成します。これにより、最初は DaemonSet に対応するポッドがなくなります。

```
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: NotIn
          values:
          - "false"
```
+ 制御されたレートでノードから`run-daemonset=false`ラベルを削除します。この bash スクリプトを例として使用できます。

```
#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Removing run-daemonset label from node $node"
   kubectl label nodes $node run-daemonset-
   sleep 5
done
```
+ 必要に応じて、DaemonSet オブジェクトから `NodeAffinity`設定を削除します。また、これにより がトリガー`RollingUpdate`され、DaemonSet テンプレートが変更されたため、既存のすべての DaemonSet ポッドが徐々に置き換えられることに注意してください。

#### ノードスケールアウトで群れが雷鳴するのを防ぐ
<a name="_prevent_thundering_herds_on_node_scale_outs"></a>

DaemonSet の作成と同様に、新しいノードを高速で作成すると、多数の DaemonSet ポッドが同時に起動する可能性があります。コントローラーがこの同じレートで DaemonSet ポッドを作成できるように、制御されたレートで新しいノードを作成する必要があります。これが不可能な場合は、 を使用して、新しいノードを最初に既存の DaemonSet の対象外にすることができます`NodeAffinity`。次に、新しいノードにラベルを徐々に追加して、daemonset-controller が制御されたレートでポッドを作成できるようにします。このアプローチに従うことができます。
+ のすべての既存のノードにラベルを追加する `run-daemonset=true` 

```
kubectl label nodes --all run-daemonset=true
```
+ DaemonSet を、`run-daemonset=true`ラベルを持つ任意のノードと一致するように`NodeAffinity`設定で更新します。また、これにより がトリガー`RollingUpdate`され、DaemonSet テンプレートが変更されたため、既存のすべての DaemonSet ポッドが徐々に置き換えられることに注意してください。次のステップに進む前に`RollingUpdate`、 が完了するまで待つ必要があります。

```
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: In
          values:
          - "true"
```
+ クラスターに新しいノードを作成します。これらのノードには `run-daemonset=true`ラベルがないため、DaemonSet はそれらのノードと一致しないことに注意してください。
+ `run-daemonset=true` ラベルを新しいノード (現在 `run-daemonset`ラベルがありません) に制御されたレートで追加します。この bash スクリプトを例として使用できます。

```
#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes?labelSelector=%21run-daemonset" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Adding run-daemonset=true label to node $node"
   kubectl label nodes $node run-daemonset=true
   sleep 5
done
```
+ 必要に応じて、DaemonSet オブジェクトから `NodeAffinity`設定を削除し、すべてのノードから `run-daemonset`ラベルを削除します。

#### DaemonSet の更新で群れが騙されるのを防ぐ
<a name="_prevent_thundering_herds_on_daemonset_updates"></a>

`RollingUpdate` ポリシーは、 である DaemonSet ポッド`maxUnavailable`の設定のみを尊重します`Ready`。DaemonSet に`NotReady`ポッドのみまたは`NotReady`ポッドの大部分があり、テンプレートを更新すると、daemonset-controller は任意のポッドに対して同時に新しい`NotReady`ポッドを作成します。これにより、`NotReady`大量のポッドがある場合に群れの問題が発生する可能性があります。たとえば、ポッドが継続的にクラッシュループしている場合や、イメージのプルに失敗している場合などです。

DaemonSet を更新したときにポッドを段階的にロールアウトするには、DaemonSet `NotReady` の更新戦略を から `RollingUpdate`に一時的に変更できます`OnDelete`。では`OnDelete`、DaemonSet テンプレートを更新すると、コントローラーは古いポッドを手動で削除した後に新しいポッドを作成し、新しいポッドのロールアウトを制御できるようにします。このアプローチに従うことができます。
+ DaemonSet `NotReady` にポッドがあるかどうかを確認します。
+ そうでない場合は、DaemonSet テンプレートを安全に更新でき、`RollingUpdate`戦略によって段階的なロールアウトが保証されます。
+ 「はい」の場合は、まず `OnDelete`戦略を使用するように DaemonSet を更新する必要があります。

```
updateStrategy:
  type: OnDelete
```
+ 次に、必要な変更で DaemonSet テンプレートを更新します。
+ この更新後、管理されたレートでポッド削除リクエストを発行することで、古い DaemonSet ポッドを削除できます。この bash スクリプトは、kube-system 名前空間で DaemonSet 名が fluentd-elasticsearch である例として使用できます。

```
#!/bin/bash

daemonset_pods=$(kubectl get --raw "/api/v1/namespaces/kube-system/pods?labelSelector=name%3Dfluentd-elasticsearch" | jq -r '.items | .[].metadata.name')

for pod in ${daemonset_pods[@]}; do
   echo "Deleting pod $pod"
   kubectl delete pod $pod -n kube-system
   sleep 5
done
```
+ 最後に、DaemonSet を以前の`RollingUpdate`戦略に更新できます。