Azure Kubernetes Service (AKS) での高度なスケジューラ機能に関するベスト プラクティス

Azure Kubernetes Service (AKS) でクラスターを管理する際は、多くの場合、チームとワークロードを分離する必要があります。 Kubernetes スケジューラで提供される高度な機能を使用すると、次のことが制御できます。

  • 特定のノードでスケジュールできるポッド。
  • マルチポッド アプリケーションをクラスター全体へ適切に分散させる方法。

このベスト プラクティス記事では、クラスター オペレーター向けに Kubernetes の高度なスケジュール機能について説明します。 この記事では、次のことについて説明します。

  • テイントと容認を使用して、ノードでスケジュールできるポッドを制限します。
  • ノード セレクターまたはノード アフィニティを使用して、特定のノードで実行するポッドに優先順位を指定します。
  • ポッド間アフィニティまたは非アフィニティを使用して、ポッドを分割したりグループ化したりします。
  • スケジュール可能な GPU を持つノードでのみ GPU を必要とするワークロードのスケジュール設定を制限します。

テイントと容認を使用して専用のノードを提供する

ベスト プラクティスのガイダンス:

イングレス コントローラーなどのリソースを多量に消費するアプリケーションへのアクセスを特定のノードに制限します。 ノードのリソースをそれが必要なワークロードで使用できるように保ち、そのノードでは他のワークロードをスケジュールできないようにします。

AKS クラスターを作成するときは、GPU のサポートや多数の強力な CPU を備えたノードをデプロイできます。 詳細については、AKS での GPU の使用に関するページを参照してください。 このようなノードは、機械学習 (ML) や人工知能 (AI) などの大規模なデータ処理ワークロードに使用できます。

このノード リソース ハードウェアは通常、デプロイにコストがかかるため、これらのノードでスケジュールできるワークロードを制限します。 代わりに、クラスター内にある一部のノードをイングレス サービスの実行専用にして、他のワークロードを防ぐこともできます。

さまざまなノードに対するこのサポートは、複数のノード プールを使用して提供されます。 AKS クラスターでは、1 つ以上のノード プールがサポートされます。

Kubernetes スケジューラでは、テイントと容認を使用して、ノードで実行できるワークロードを制限できます。

  • テイントをノードに適用して、特定のポッドのみをノードにスケジュールできることを示します。
  • 次に、ポッドに容認を適用して、ノードのテイントを容認できるようにします。

ポッドを AKS クラスターにデプロイすると、Kubernetes によってテイントが容認と一致するノードでのみポッドがスケジュールされます。 テイントと容認は、連携動作して、ポッドが不適切なノードにスケジュールされないようにします。 1 つ以上のテイントがノードに適用され、ノードがそのテイントを許容しないポッドを受け入れないようにノードをマークします。

たとえば、GPU をサポートしているノードのプールを AKS クラスターに追加した場合を考えてください。 gpu などの名前と、スケジュールのための値を定義します。 この値を NoSchedule に設定すると、Kubernetes スケジューラによってノード上で未定義の容認を使用したポッドがスケジュールされることが制限されます。

az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --name taintnp \
    --node-taints sku=gpu:NoSchedule \
    --no-wait

ノード プールのノードにテイントを適用した上で、それらのノードへのスケジュールを許可するポッドの仕様内で容認を定義します。 次の例では、sku: gpueffect: NoSchedule を指定することで、前のステップでノード プールに適用したテイントを容認するように設定してます。

kind: Pod
apiVersion: v1
metadata:
  name: tf-mnist
spec:
  containers:
  - name: tf-mnist
    image: mcr.microsoft.com/azuredocs/samples-tf-mnist-demo:gpu
    resources:
      requests:
        cpu: 0.5
        memory: 2Gi
      limits:
        cpu: 4.0
        memory: 16Gi
  tolerations:
  - key: "sku"
    operator: "Equal"
    value: "gpu"
    effect: "NoSchedule"

kubectl apply -f gpu-toleration.yaml を使用してこのポッドをデプロイすると、Kubernetes によってテイントが適用されたノードでポッドが正常にスケジュールできます。 この論理的な分離を使用することで、クラスター内のリソースへのアクセスを制御できます。

テイントを適用するときは、アプリケーションの開発者や所有者と協力して、デプロイで必要な容認を定義できるようにします。

AKS で複数のノード プールを使用する方法の詳細については、「AKS のクラスターに対する複数のノード プールの作成」を参照してください。

AKS でのテイントと容認の動作

AKS でノード プールをアップグレードすると、テイントと容認は新しいノードに適用されるときに、次のように設定されたパターンに従います。

Azure Virtual Machine Scale Sets を使用する既定のクラスター

AKS API からノード プールのテイントを設定して、新たにスケールアウトされたノードが API で指定されたノードのテイントを受け取るようにすることができます。

次のような場合を考えてみましょう。

  1. 2 ノード クラスター、node1node2 から始めます。
  2. ノード プールをアップグレードします。
  3. node3node4 という 2 つの追加ノードが作成されます。
  4. テイントはそれぞれ渡されます。
  5. 元の node1node2 は削除されます。

Virtual Machine Scale Sets のサポートがないクラスター

ここでも、次のことを想定してみましょう。

  1. 2 つのノード クラスター、node1node2 があるとします。
  2. ノード プールをアップグレードします。
  3. node3 という追加のノードが作成されます。
  4. node1 のテイントが node3 に適用されます。
  5. node1 が削除されます。
  6. 新しい node1 が作成され、元の node1 と置き換えられます。
  7. node2 のテイントが新しい node1 に適用されます。
  8. node2 が削除されます。

つまり、node1node3 になり、node2 が新しい node1 になります。

AKS でノード プールをスケーリングするとき、テイントと容認は設計上持ち越されません。

ノード セレクターとアフィニティを使用してポッドのスケジュールを制御する

ベスト プラクティスのガイダンス

ノード セレクター、ノードのアフィニティ、またはポッド間のアフィニティを使用して、ノード上のポッドのスケジュールを制御します。 これらの設定を使用すると、Kubernetes のスケジューラは、ノードのハードウェアなどによって、ワークロードを論理的に分離できます。

テイントと容認は、ハードカット オフでリソースを論理的に分離します。 ポッドがノードのテイントを許容していない場合、そのノードはノード上でスケーリングされていません。

または、ノード セレクターを使用することもできます。 たとえば、ローカルにアタッチされた SSD ストレージや大容量のメモリを示すラベルをノードに付けてから、ポッドの仕様でノード セレクターを定義します。 Kubernetes は一致するノード上にこれらのポッドをスケジュールします。

容認とは異なり、一致するノード セレクターのないポッドは、ラベルが付いているノードでもスケジュールできます。 この動作により、ノード上にある未使用のリソースを消費できますが、一致するノード セレクターが定義されているポッドが優先されます。

大容量メモリを装備したノードの例を見てみましょう。 これらのノードでは、大量のメモリを要求するポッドが優先されます。 リソースがアイドル状態にならないようにするために、他のポッドの実行も許可されます。 次のサンプル コマンドでは、hardware=highmem というラベルを持つノード プールを、myResourceGroupmyAKSCluster に追加します。 そのノード プールのすべてのノードがこのラベルを持ちます。

az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --name labelnp \
    --node-count 1 \
    --labels hardware=highmem \
    --no-wait

ポッドの仕様に、ノードで設定されているラベルと一致するノード セレクターを定義する nodeSelector プロパティを追加します。

kind: Pod
apiVersion: v1
metadata:
  name: tf-mnist
spec:
  containers:
  - name: tf-mnist
    image: mcr.microsoft.com/azuredocs/samples-tf-mnist-demo:gpu
    resources:
      requests:
        cpu: 0.5
        memory: 2Gi
      limits:
        cpu: 4.0
        memory: 16Gi
  nodeSelector:
      hardware: highmem

これらのスケジューラ オプションを使用するときは、アプリケーションの開発者および所有者と協力して、ポッドの仕様を正しく定義できるようにします。

ノード セレクターの使用について詳しくは、「Assigning Pods to Nodes」 (ノードへのポッドの割り当て) をご覧ください。

ノード アフィニティ

ノード セレクターは、特定のノードにポッドを割り当てる際の基本的なソリューションです。 ノードのアフィニティにより柔軟性が向上し、ポッドがノードと一致できない場合に起こる事象を定義できるようになります。 次の操作を行います。

  • Kubernetes スケジューラがポッドをラベルの付いたホストと一致させる必要があります。 または、
  • 一致させることを優先しますが、一致するものがない場合は、ポッドが別のホストでスケジュールできるようにします。

次の例では、ノード アフィニティを requiredDuringSchedulingIgnoredDuringExecution に設定しています。 このアフィニティでは、一致するラベルを持つノードを使用することが要求されます。 使用できるノードがない場合、ポッドはスケジュールの継続を待機する必要があります。 ポッドを別のノードでスケジュールできるようにするには、値をpreferredDuringSchedulingIgnoreDuringExecution に設定します。

kind: Pod
apiVersion: v1
metadata:
  name: tf-mnist
spec:
  containers:
  - name: tf-mnist
    image: mcr.microsoft.com/azuredocs/samples-tf-mnist-demo:gpu
    resources:
      requests:
        cpu: 0.5
        memory: 2Gi
      limits:
        cpu: 4.0
        memory: 16Gi
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: hardware
            operator: In
            values:
            - highmem

設定の IgnoredDuringExecution の部分は、ノードのラベルが変更された場合に、ポッドをノードから削除してはならないことを示しています。 Kubernetes スケジューラでは、新しくスケジュールされるポッドに対してのみ更新されたノード ラベルが使用され、ノードで既にスケジュールされているポッドには使用されません。

詳しくは、「Affinity and anti-affinity」 (アフィニティと非アフィニティ) をご覧ください。

ポッド間アフィニティと非アフィニティ

Kubernetes スケジューラでワークロードを論理的に分離する最後の方法では、ポッド間アフィニティまたは非アフィニティを使用します。 これらの設定は、一致するポッドが既にあるノードでポッドをスケジュールする必要がないか、あるいは必要があるかのいずれかを定義できます。 既定では、Kubernetes スケジューラは、ノードをまたぐレプリカ セットに複数のポッドのスケジュールを試みます。 この動作に関しては、さらに具体的なルールを定義できます。

たとえば、Azure Cache for Redis も使用する Web アプリケーションがあります。

  • ポッドの非アフィニティ ルールを使用して、Kubernetes スケジューラによってノード間でレプリカを分散するように要求します。
  • アフィニティ ルールを使用して、各 Web アプリ コンポーネントが対応するキャッシュと同じホストにスケジュールされるようにします。

複数ノードへのポッドの分散は、次の例のようになります。

ノード 1 ノード 2 ノード 3
webapp-1 webapp-2 webapp-3
cache-1 cache-2 cache-3

ポッド間のアフィニティと非アフィニティは、ノード セレクターまたはノード アフィニティよりも複雑なデプロイを提供します。 デプロイすることで、リソースを論理的に分離し、Kubernetes がノードでポッドをスケジュールする方法を制御できます。

Azure Cache for Redis を使用するこの Web アプリケーションの完全な例については、同じノードへのポッドの併置に関する記事をご覧ください。

次のステップ

この記事では、Kubernetes の高度なスケジューラ機能に注目しました。 AKS でのクラスター操作の詳細については、次のベスト プラクティスを参照してください。