複数のアプリケーションが同じノードで実行されると、CPU リソースの競合や頻繁なコンテキストスイッチにより、センシティブなアプリケーションでパフォーマンスジッターが発生する可能性があります。CPU トポロジー対応スケジューリングは、アプリケーションプロセスを特定の CPU コアに排他的にバインド (CPU ピニング) します。これにより、コアスイッチングや NUMA ノード間のメモリアクセスに起因するパフォーマンスの不確実性が低減されます。
仕組み
デフォルトでは、Kubernetes はカーネルの Completely Fair Scheduler (CFS) に依存して CPU 負荷を分散します。このスケジューラは、CPU タイムスライスを公平に割り当てることで、すべてのコアに負荷を分散します。しかし、このスケジューラは物理 CPU トポロジーを無視するため、センシティブなアプリケーションでパフォーマンスジッターを引き起こす可能性があります。
Kubernetes CPU マネージャー (static ポリシー) は、Pod を専用の CPU コアにバインドできますが、以下の制限があります。
スケジューラの認識不足:ネイティブの kube-scheduler はノードレベルでのみ決定を下します。クラスター全体の CPU トポロジーを認識できないため、グローバルなスケールで Pod に最適な物理コアのレイアウトを見つけることができません。
トポロジー認識の欠如: ノードでコアを割り当てる際、
staticポリシーは NUMA アーキテクチャを認識しません。これにより、NUMA ノード間のメモリアクセスが発生し、余分なレイテンシーが生じる可能性があります。柔軟性の欠如:このポリシーでは、Pod の QoS クラスが
<a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#guaranteed" id="5b2a90624cgtc">Guaranteed</a>である必要があります。<a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#burstable" id="174c05a5978cf">Burstable</a>または<a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#burstable" id="6cada2573fokq">BestEffort</a>の Pod には適用できません。
これらの制限に対処するため、Container Service for Kubernetes (ACK) は、新しい Scheduling Framework に基づく強化された CPU トポロジー対応スケジューリングを提供します。この機能は、ACK の kube-scheduler と ack-koordinator の連携によって実現されます。
ノードトポロジーの報告:ack-koordinator コンポーネントは、ソケット、NUMA ノード、キャッシュなどのローカル物理 CPU トポロジーをリアルタイムで検出し、そのトポロジーをスケジューリングセンターに報告します。
グローバルトポロジー対応スケジューリング:kube-scheduler は、グローバルなトポロジー情報を使用して、クラスター全体から Pod に最適なノードを選択し、コア割り当てスキームを計画します。例えば、最適なノードを選択する際、スケジューラはデフォルトでバインドされているアプリケーションが最も少ないコアを見つけます。この割り当てスキームは、スケジューリング結果の一部として Pod のアノテーションに書き込まれます。
ローカルコアピニング:Pod がターゲットノードにスケジュールされると、ack-koordinator は Pod のアノテーションを読み取り、Pod の対応する Cgroup 内の
cpuset.cpusファイルを変更して、Pod を物理コアにバインドします。
利用シーン
パフォーマンスセンシティブなアプリケーション:高頻度取引やリアルタイムデータ処理など、CPU コンテキストスイッチのレイテンシに非常に敏感なアプリケーション。
NUMA センシティブなアプリケーション:Elastic Bare Metal Instances (Intel および AMD) など、マルチコアおよびマルチソケットのサーバーにデプロイされるアプリケーション。これらのパフォーマンスはメモリアクセスのレイテンシに大きく影響され、NUMA ノード間のメモリアクセスを避ける必要があります。
決定論的な計算能力要件:科学計算やビッグデータ分析タスクなど、安定的で予測可能な計算能力を必要とするアプリケーション。
レガシーアプリケーションの適応:まだクラウドネイティブ環境に適応していないアプリケーション。例えば、アプリケーションがコンテナーの仕様ではなく、マシン全体の物理コア数に基づいてスレッド数を設定し、パフォーマンスが低下する場合があります。
以下のシナリオでは、この機能を有効にしないでください:
CPU オーバーコミットメント環境:コアピニングの排他的なリソース特性は、オーバーコミットメントのリソース共有モデルと互換性がありません。これにより、リソースの無駄遣いやオーバーコミットメントのスケジューリングロジックへの干渉が発生する可能性があります。
汎用または I/O 集中型アプリケーション:Web サービスやミドルウェアなど、ほとんどのアプリケーションは CPU コアスイッチングに敏感ではなく、この機能を必要としません。
事前準備
Pro 版の ACK マネージドクラスターがあり、ノードプールの CPU ポリシー (
cpuManagerPolicy) がnoneに設定されています。cpuManagerPolicyの値の設定方法の詳細については、「ノードプールの kubelet 構成をカスタマイズする」をご参照ください。ack-koordinator がインストールされており、コンポーネントのバージョンが 0.2.0 以降であること。
ステップ 1:サンプルアプリケーションのデプロイ
このトピックでは、Nginx アプリケーションを例に、CPU トポロジー対応スケジューリングを有効にして CPU ピニングを実現する方法を説明します。
nginx-app.yamlファイルを作成します。アプリケーションをデプロイします。
kubectl apply -f nginx-app.yamlPod が配置されているノードにログインし、Pod UID とコンテナー ID を取得します。
Pod 名を取得します。
kubectl get pods -n <your-namespace>コマンドを実行して、nginx-deployment-6f5899*****のような Pod 名を確認します。Pod UID を取得します。
# <your-pod-name> を実際の Pod 名に置き換えます kubectl get pod <your-pod-name> -n default -o jsonpath='{.metadata.uid}{"\n"}'期待される出力:
uid: a78a02b5-c87f-4e74-9ddd-254c163*****コンテナー ID を取得します。
# <your-pod-name> を実際の Pod 名に置き換えます kubectl describe pod <your-pod-name> -n default出力で
Containersフィールドを探し、Container IDを見つけて、containerd://のようなプレフィックスを削除します。期待される出力:
Containers: nginx: Container ID: containerd://b8b88a70096aabb0aea197dd2aba78d15bcbe9145198ef46a0474b31*****
ノードの cgroup バージョンを確認します。
stat -fc %T /sys/fs/cgroup/出力が
cgroup_rootの場合、システムは cgroup v1 を使用しています。出力が
cgroup2fsの場合、システムは cgroup v2 を使用しています。
cgroup バージョンに基づいて対応する検証コマンドを実行し、CPU ピニングの状態を確認します。
cgroup パスでは、Pod UID のハイフン (
-) をアンダースコア (_) に置き換えます。例えば、元の Pod UID がa78a02b5-c87f-4e74-9ddd-254c163*****の場合、パスで使用されるフォーマットはa78a02b5_c87f_4e74_9ddd_254c163*****です。cgroup v1:
# <POD_UID> と <CONTAINER_ID> を実際の値に置き換えます cat /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpuscgroup v2:
# <POD_UID> と <CONTAINER_ID> を実際の値に置き換えます cat /sys/fs/cgroup/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpus.effective
期待される出力は範囲であり、これはコンテナーがノード上のすべてのコアを使用でき、ピニングされていないことを意味します。
0-31
ステップ 2:CPU トポロジー対応スケジューリングの有効化
Pod にアノテーションを追加することで、CPU トポロジー対応スケジューリングを有効にできます。
一般的なピニングポリシー:1:1 のピニング原則に従う一般的なポリシーです。
resources.limits.cpuで指定されたコア数を Pod にバインドします。最適なメモリアクセスパフォーマンスを確保するために、同じ NUMA ノード内の CPU コアを優先します。自動ピニングポリシー:特定のハードウェアに最適化されたポリシーです。完全な物理コアクラスター (AMD CPU の CCX/CCD など) のバインドを優先し、CPU ローカリティを最大化して並行性を向上させます。これは、特定のラージスケール (32 コア以上) の AMD マシンタイプに推奨されます。
CPU トポロジー対応スケジューリングを有効にする場合、Pod に nodeName を直接指定しないでください。このような Pod のスケジューリングプロセスには、kube-scheduler は参加しません。スケジューリング対象のノードを指定するには、nodeSelector などのフィールドを使用してアフィニティポリシーを設定してください。
一般的なピニングポリシー
構成:
YAML ファイルに
cpuset-scheduler: "true"アノテーションを追加します。Pod の場合:アノテーションを
metadata.annotationsフィールドに追加します。ワークロード (Deployment など) の場合:アノテーションを
spec.template.metadata.annotationsフィールドに追加します。
Containersフィールドで、resources.limits.cpuの値 (整数である必要があります) を設定して、CPU ピニングの範囲を定義します。
構成例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: default labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: annotations: # true に設定して CPU トポロジー対応スケジューリングを有効にします cpuset-scheduler: "true" labels: app: nginx spec: containers: - name: nginx image: alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/nginx_optimized:20240221-1.20.1-2.3.0 ports: - containerPort: 80 command: - "sleep" - "infinity" resources: requests: cpu: 4 memory: 8Gi limits: # CPU の値を設定します。整数である必要があります cpu: 4 memory: 8Gi検証:
2 つの方法で確認できます。
ノードの Cgroup ファイルの確認
Pod が実行された後、再度そのノードにログインし、CPU ピニングの状態を確認します。
Pod UID のハイフン (
-) をアンダースコア (_) に置き換えます。cgroup v1:
# <POD_UID> と <CONTAINER_ID> を実際の値に置き換えます cat /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpuscgroup v2:
# <POD_UID> と <CONTAINER_ID> を実際の値に置き換えます cat /sys/fs/cgroup/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpus.effective
期待される出力は、
limits.cpu: 4の設定に一致するコア ID のセットです。これは、ピニングが成功したことを示します。0-3Pod アノテーションの確認
Pod がターゲットノードにスケジュールされると、ack-koordinator は Pod のアノテーションを読み取り、Pod の対応する Cgroup 内の
cpuset.cpusファイルを変更して、Pod を物理コアにバインドします。Pod の
cpuset情報を確認します。# <your-pod-name> を実際の Pod 名に置き換えます kubectl get pod <your-pod-name> -n default -o yaml | grep "cpuset:"期待される出力:
cpuset: '{"nginx":{"0":{"elems":{"0":{},"1":{},"2":{},"3":{}}}}}'出力:
"nginx":{...}:nginxという名前のコンテナーの設定が含まれています。"0":{...}:最も外側のキー"0"は、Non-Uniform Memory Access (NUMA) ノード ID を表します。この例では、バインドされたすべてのコアが同じ NUMA ノード 0 上にあります。この構成により、NUMA をまたぐメモリアクセスによるパフォーマンスの低下を防ぎます。"elems":{"0":{},"1":{},"2":{},"3":{}}:キーは、コンテナーがピニングされている物理 CPU コア ID を表します。この例では、コンテナーはコア 0、1、2、3 にピニングされており、これはlimits.cpu: 4の設定に対応しています。
自動ピニングポリシー
構成:
cpuset-scheduler: "true"とcpu-policy: "static-burst"の 2 つのアノテーションを追加します。Pod の場合:アノテーションを
metadata.annotationsフィールドに追加します。ワークロード (Deployment など) の場合:アノテーションを
spec.template.metadata.annotationsフィールドに追加します。
Containersフィールドで、resources.limits.cpuの値 (整数である必要があります) を設定して、CPU ピニングの範囲を定義します。
構成例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: default labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: annotations: # true に設定して CPU トポロジー対応スケジューリングを有効にします cpuset-scheduler: "true" # static-burst に設定して自動ピニングと NUMA アフィニティポリシーを有効にします cpu-policy: "static-burst" labels: app: nginx spec: containers: - name: nginx image: alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/nginx_optimized:20240221-1.20.1-2.3.0 ports: - containerPort: 80 command: - "sleep" - "infinity" resources: requests: cpu: 4 memory: 8Gi limits: # CPU の値を設定します。整数である必要があります cpu: 4 memory: 8Gi検証:
自動ピニングポリシーは、ノードの CPU トポロジーとリソース使用量をリアルタイムで分析します。ピニングされるコアの数は、Pod に対して明示的にリクエストしたコアの数よりも多くなる場合があります。ピニングの状態は、以下の 2 つの方法で確認できます。
ノードの Cgroup ファイルの確認
Pod が実行された後、再度そのノードにログインし、CPU ピニングの状態を確認します。
Pod UID のハイフン (
-) をアンダースコア (_) に置き換えます。cgroup v1:
# <POD_UID> と <CONTAINER_ID> を実際の値に置き換えます cat /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpuscgroup v2:
# <POD_UID> と <CONTAINER_ID> を実際の値に置き換えます cat /sys/fs/cgroup/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpus.effective
期待される出力は、特定のコア ID のセットです。これは、ピニングが成功したことを示します。
0-7Pod アノテーションの確認
Pod がターゲットノードにスケジュールされると、ack-koordinator は Pod のアノテーションを読み取り、Pod の対応する Cgroup 内の
cpuset.cpusファイルを変更して、Pod を物理コアにバインドします。Pod の
cpuset情報を確認します。# <your-pod-name> を実際の Pod 名に置き換えます kubectl get pod <your-pod-name> -n default -o yaml | grep "cpuset:"期待される出力:
cpuset: '{"nginx":{"0":{"elems":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{}}}}}'出力の説明:
"nginx":{...}:nginxという名前のコンテナーの設定。"0":{...}:最も外側のキー"0"は NUMA ノード ID を表します。この例では、バインドされたすべてのコアが同じ NUMA ノード 0 上にあります。これにより、NUMA ノードをまたぐメモリアクセスによるパフォーマンスの損失を回避します。"elems":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{}}:キーは、ピニングされている物理 CPU コア ID を表します。この例では、コンテナーはコア 0 から 7 にピニングされています。
関連操作
CPU トポロジー対応スケジューリングの無効化 (CPU コアのアンピン)
アプリケーションの YAML ファイルを編集します。
spec.template.metadata.annotationsフィールドからcpuset-scheduler: "true"とcpu-policy: "static-burst"(存在する場合) のアノテーションを削除します。オフピーク時に変更後の YAML ファイルを適用します。変更は Pod の再起動後に有効になります。
CPU コアのアンピン後、Pod プロセスは特定の物理コアにバインドされなくなり、ノード上の利用可能なすべての CPU コア間を切り替える可能性があります。これには、以下の影響が考えられます:
コア間のコンテキストスイッチにより、CPU 使用率がわずかに増加する可能性があります。
計算集約型アプリケーションの場合、コアが排他的でなくなるため、CPU リソースの競合によるパフォーマンスジッターが再発する可能性があります。
複数の高負荷 Pod のプロセスが同じコアにスケジュールされると、そのコアの負荷が急上昇し、コンテナーの CPU スロットリングがトリガーされる可能性があります。
本番環境での推奨事項
可観測性:コアピニングを有効にする前後で、Alibaba Cloud Prometheus モニタリングとの連携を行います。応答時間 (RT) や QPS などの主要なアプリケーションパフォーマンスメトリック、および CPU 使用率や CPU スロットリングなどのノードメトリックを注意深く監視し、コアピニングによるパフォーマンスの変化を観察します。
段階的なロールアウト:複数のレプリカを持つアプリケーションの場合、カナリアリリースや段階的な更新を使用して、ピニングポリシーを徐々に有効または無効にします。これにより、変更に伴うリスクを管理できます。
課金
ack-koordinator コンポーネントのインストールと使用は無料です。ただし、以下のシナリオでは追加料金が発生する場合があります:
ack-koordinator は自己管理コンポーネントであり、インストール後にワーカーノードのリソースを消費します。コンポーネントのインストール時に、各モジュールのリソースリクエストを設定できます。
デフォルトでは、ack-koordinator は、リソースプロファイリングや詳細なスケジューリングなどの機能のモニタリングメトリックを Prometheus フォーマットで公開します。コンポーネントの設定時に [ack-koordinator の Prometheus モニタリングを有効にする] オプションを選択し、Alibaba Cloud Prometheus サービスを使用する場合、これらのメトリックはカスタムメトリックと見なされ、料金が発生します。料金は、クラスターのサイズやアプリケーションの数などの要因によって異なります。この機能を有効にする前に、Alibaba Cloud Prometheus のPrometheus インスタンスの課金に関するドキュメントをよく読み、カスタムメトリックの無料クォータと課金ポリシーを理解してください。使用量データのクエリによって、リソース使用量を監視および管理できます。
関連ドキュメント
ACK は GPU トポロジー対応スケジューリングをサポートしており、ノード上の GPU の最適な組み合わせを選択して、最高のトレーニング速度を実現できます。
クラスター内で割り当てられているが使用されていないリソースを定量化し、それらを優先度の低いジョブに提供できます。これにより、動的リソースオーバーコミットメントが実現します。