website/content/ja/docs/concepts/scheduling-eviction/assign-pod-node.md

29 KiB
Raw Blame History

title content_type weight
ード上へのPodのスケジューリング concept 20

{{< glossary_tooltip text="Pod" term_id="pod" >}}を特定の{{< glossary_tooltip text="ノード" term_id="node" >}}で実行するように 制限 したり、特定のノードで実行することを 優先 させたりといった制約をかけることができます。 これを実現するためにはいくつかの方法がありますが、推奨されている方法は、すべてラベルセレクターを使用して選択を容易にすることです。 多くの場合、このような制約を設定する必要はなく、{{< glossary_tooltip text="スケジューラー" term_id="kube-scheduler" >}}が自動的に妥当な配置を行います(例えば、Podを複数のードに分散させ、空きリソースが十分でないードにPodを配置しないようにすることができます)。 しかし、例えばSSDが接続されているードにPodが配置されるようにしたり、多くの通信を行う2つの異なるサービスのPodを同じアベイラビリティーゾーンに配置したりする等、どのードに配置するかを制御したい状況もあります。

Kubernetesが特定のPodの配置場所を選択するために、以下の方法があります:

ノードラベル

他の多くのKubernetesオブジェクトと同様に、ードにもラベルがあります。手動でラベルを付けることができます。 また、Kubernetesはクラスター内のすべてのードに対し、いくつかの標準ラベルを付けます。ードラベルの一覧についてはよく使われるラベル、アテーションとtaintを参照してください。

{{}} これらのラベルの値はクラウドプロバイダー固有のもので、信頼性を保証できません。 例えば、kubernetes.io/hostnameの値はある環境ではノード名と同じになり、他の環境では異なる値になることがあります。 {{}}

ノードの分離/制限

ードにラベルを追加することで、Podを特定のードまたはードグループ上でのスケジューリングの対象にすることができます。この機能を使用すると、特定のPodが一定の独立性、安全性、または規制といった属性を持ったード上でのみ実行されるようにすることができます。

ノード分離するのにラベルを使用する場合、{{<glossary_tooltip text="kubelet" term_id="kubelet">}}が修正できないラベルキーを選択してください。 これにより、侵害されたノードが自身でそれらのラベルを設定することで、スケジューラーがそのノードにワークロードをスケジュールしてしまうのを防ぐことができます。

NodeRestrictionアドミッションプラグインは、kubeletがnode-restriction.kubernetes.io/というプレフィックスを持つラベルを設定または変更するのを防ぎます。

ラベルプレフィックスをNode分離に利用するには:

  1. ノード認可を使用していることと、NodeRestriction アドミッションプラグインが 有効 になっていることを確認します。
  2. node-restriction.kubernetes.io/プレフィックスを持つラベルをノードに追加し、 nodeSelectorでそれらのラベルを使用します。 例えば、example.com.node-restriction.kubernetes.io/fips=trueexample.com.node-restriction.kubernetes.io/pci-dss=trueなどです。

nodeSelector

nodeSelectorは、ノード選択制約の中で最もシンプルな推奨形式です。 Podのspec(仕様)にnodeSelectorフィールドを追加することで、ターゲットノードが持つべきノードラベルを指定できます。 Kubernetesは指定された各ラベルを持つードにのみ、Podをスケジューリングします。

詳しい情報についてはPodをードに割り当てるを参照してください。

アフィニティとアンチアフィニティ

nodeSelectorはPodを特定のラベルが付与されたードに制限する最も簡単な方法です。 アフィニティとアンチアフィニティでは、定義できる制約の種類が拡張されています。 アフィニティとアンチアフィニティのメリットは以下の通りです。

  • アフィニティとアンチアフィニティで使われる言語は、より表現力が豊かです。nodeSelectorは指定されたラベルを全て持つノードを選択するだけです。アフィニティとアンチアフィニティは選択ロジックをより細かく制御することができます。
  • ルールが柔軟であったり優先での指定ができたりするため、一致するードが見つからない場合でも、スケジューラーはPodをスケジュールします。
  • ノード自体のラベルではなく、ノード(または他のトポロジカルドメイン)上で稼働している他のPodのラベルを使ってPodを制約することができます。これにより、ード上にどのPodを共存させるかのルールを定義することができます。

アフィニティ機能は、2種類のアフィニティで構成されています:

  • ノードアフィニティnodeSelectorフィールドと同様に機能しますが、より表現力が豊かで、より柔軟にルールを指定することができます。
  • Pod間アフィニティとアンチアフィニティは、他のPodのラベルを元に、Podを制約することができます。

ノードアフィニティ

ードアフィニティは概念的には、ードのラベルによってPodがどのードにスケジュールされるかを制限するnodeSelectorと同様です。

ードアフィニティには2種類あります:

  • requiredDuringSchedulingIgnoredDuringExecution: スケジューラーは、ルールが満たされない限り、Podをスケジュールすることができません。これはnodeSelectorと同じように機能しますが、より表現力豊かな構文になっています。
  • preferredDuringSchedulingIgnoredDuringExecution: スケジューラーは、対応するルールを満たすノードを探そうとします。 一致するードが見つからなくても、スケジューラーはPodをスケジュールします。

{{}} 上記の2種類にあるIgnoredDuringExecutionは、KubernetesがPodをスケジュールした後にードラベルが変更されても、Podは実行し続けることを意味します。 {{}}

Podのspec(仕様)にある.spec.affinity.nodeAffinityフィールドを使用して、ノードアフィニティを指定することができます。

例えば、次のようなPodのspec(仕様)を考えてみましょう:

{{% codenew file="pods/pod-with-node-affinity.yaml" %}}

この例では、以下のルールが適用されます:

  • ノードにはtopology.kubernetes.io/zoneをキーとするラベルが必要で、そのラベルの値はantarctica-east1またはantarctica-west1のいずれかでなければなりません。
  • ノードにはキー名がanother-node-label-keyで、値がanother-node-label-valueのラベルを持つことが望ましいです。

operatorフィールドを使用して、Kubernetesがルールを解釈する際に使用できる論理演算子を指定することができます。InNotInExistsDoesNotExistGtLtが使用できます。

NotInDoesNotExistを使って、ノードのアンチアフィニティ動作を定義することができます。また、ードのTaintを使用して、特定のードからPodをはじくこともできます。

{{}} nodeSelectornodeAffinityの両方を指定した場合、両方の条件を満たさないとPodはードにスケジュールされません。

nodeAffinityタイプに関連付けられたnodeSelectorTerms内に、複数の条件を指定した場合、Podは指定した条件のいずれかを満たしたードへスケジュールされます(条件はORされます)。

nodeSelectorTerms内の条件に関連付けられた1つのmatchExpressionsフィールド内に、複数の条件を指定した場合、Podは全ての条件を満たしたードへスケジュールされます(条件はANDされます)。 {{}}

詳細についてはードアフィニティを利用してPodをードに割り当てるを参照してください。

ノードアフィニティの重み

preferredDuringSchedulingIgnoredDuringExecutionアフィニティタイプの各インスタンスに、1から100の範囲のweightを指定できます。 Podの他のスケジューリング要件をすべて満たすードを見つけると、スケジューラーはそのードが満たすすべての優先ルールを繰り返し実行し、対応する式のweight値を合計に加算します。

最終的な合計は、そのードの他の優先度関数のスコアに加算されます。合計スコアが最も高いードが、スケジューラーがPodのスケジューリングを決定する際に優先されます。

例えば、次のようなPodのspec(仕様)を考えてみましょう:

{{% codenew file="pods/pod-with-affinity-anti-affinity.yaml" %}}

preferredDuringSchedulingIgnoredDuringExecutionルールにマッチするノードとして、一つはlabel-1:key-1ラベル、もう一つはlabel-2:key-2ラベルの2つの候補がある場合、スケジューラーは各ードのweightを考慮し、その重みとードの他のスコアを加え、最終スコアが最も高いードにPodをスケジューリングします。

{{}} この例でKubernetesにPodを正常にスケジュールさせるには、kubernetes.io/os=linuxラベルを持つ既存のノードが必要です。 {{}}

スケジューリングプロファイルごとのノードアフィニティ

{{< feature-state for_k8s_version="v1.20" state="beta" >}}

複数のスケジューリングプロファイルを設定する場合、プロファイルにノードアフィニティを関連付けることができます。これは、プロファイルが特定のノード群にのみ適用される場合に便利です。スケジューラーの設定にあるNodeAffinityプラグインargsフィールドにaddedAffinityを追加すると実現できます。例えば:

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

addedAffinityは、Podの仕様(spec)で指定されたノードアフィニティに加え、.spec.schedulerNamefoo-schedulerに設定したすべてのPodに適用されます。つまり、Podにマッチするためには、ードはaddedAffinityとPodの.spec.NodeAffinityを満たす必要があるのです。

addedAffinityはエンドユーザーには見えないので、その動作はエンドユーザーにとって予期しないものになる可能性があります。スケジューラープロファイル名と明確な相関関係のあるノードラベルを使用すべきです。

{{< note >}} DaemonSetのPodを作成するDaemonSetコントローラーは、スケジューリングプロファイルをサポートしていません。DaemonSetコントローラーがPodを作成すると、デフォルトのKubernetesスケジューラーがそれらのPodを配置し、DaemonSetコントローラーのnodeAffinityルールに優先して従います。 {{< /note >}}

Pod間のアフィニティとアンチアフィニティ

Pod間のアフィニティとアンチアフィニティは、ードのラベルではなく、すでにード上で稼働しているPodのラベルに従って、Podがどのードにスケジュールされるかを制限できます。

Xはードや、ラック、クラウドプロバイダーのゾーンやリージョン等を表すトポロジードメインで、YはKubernetesが満たそうとするルールである場合、Pod間のアフィニティとアンチアフィニティのルールは、"XにてルールYを満たすPodがすでに稼働している場合、このPodもXで実行すべき(アンチアフィニティの場合はすべきではない)"という形式です。

これらのルール(Y)は、オプションの関連する名前空間のリストを持つラベルセレクターで表現されます。PodはKubernetesの名前空間オブジェクトであるため、Podラベルも暗黙的に名前空間を持ちます。Kubernetesが指定された名前空間でラベルを探すため、Podラベルのラベルセレクターは、名前空間を指定する必要があります。

トポロジードメイン(X)はtopologyKeyで表現され、システムがドメインを示すために使用するノードラベルのキーになります。具体例はよく知られたラベル、アテーションとTaintを参照してください。

{{< note >}} Pod間アフィニティとアンチアフィニティはかなりの処理量を必要とするため、大規模クラスターでのスケジューリングが大幅に遅くなる可能性があります そのため、数百台以上のノードから成るクラスターでの使用は推奨されません。 {{< /note >}}

{{< note >}} Podのアンチアフィニティは、ードに必ず一貫性の持つラベルが付与されている必要があります。 言い換えると、クラスターの全てのノードが、topologyKeyに合致する適切なラベルが必要になります。 一部、または全部のノードにtopologyKeyラベルが指定されていない場合、意図しない挙動に繋がる可能性があります。 {{< /note >}}

Pod間のアフィニティとアンチアフィニティの種類

ノードアフィニティと同様に、Podアフィニティとアンチアフィニティにも下記の2種類があります:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

例えば、requiredDuringSchedulingIgnoredDuringExecutionアフィニティを使用して、2つのサービスのPodはお互いのやり取りが多いため、同じクラウドプロバイダーゾーンに併置するようにスケジューラーに指示することができます。 同様に、preferredDuringSchedulingIgnoredDuringExecutionアンチアフィニティを使用して、あるサービスのPodを複数のクラウドプロバイダーゾーンに分散させることができます。

Pod間アフィニティを使用するには、Pod仕様(spec)のaffinity.podAffinityフィールドで指定します。Pod間アンチアフィニティを使用するには、Pod仕様(spec)のaffinity.podAntiAffinityフィールドで指定します。

Podアフィニティ使用例

次のようなPod仕様(spec)を考えてみましょう:

{{% codenew file="pods/pod-with-pod-affinity.yaml" %}}

この例では、PodアフィニティルールとPodアンチアフィニティルールを1つずつ定義しています。 Podアフィニティルールは"ハード"なrequiredDuringSchedulingIgnoredDuringExecutionを使用し、アンチアフィニティルールは"ソフト"なpreferredDuringSchedulingIgnoredDuringExecutionを使用しています。

アフィニティルールは、スケジューラーがードにPodをスケジュールできるのは、そのードが、security=S1ラベルを持つ1つ以上の既存のPodと同じゾーンにある場合のみであることを示しています。より正確には、現在Podラベルsecurity=S1を持つPodが1つ以上あるードが、そのゾーン内に少なくとも1つ存在する限り、スケジューラーはtopology.kubernetes.io/zone=Vラベルを持つードにPodを配置しなければなりません。

アンチアフィニティルールは、security=S2ラベルを持つ1つ以上のPodと同じゾーンにあるードには、スケジューラーがPodをスケジュールしないようにすることを示しています。より正確には、PodラベルSecurity=S2を持つPodが稼働している他のードが、同じゾーン内に存在する場合、スケジューラーはtopology.kubernetes.io/zone=Rラベルを持つードにはPodを配置しないようにしなければなりません。

Podアフィニティとアンチアフィニティの使用例についてもっと知りたい方はデザイン案を参照してください。

Podアフィニティとアンチアフィニティのoperatorフィールドで使用できるのは、InNotInExistsDoesNotExistです。

原則として、topologyKeyには任意のラベルキーが指定できますが、パフォーマンスやセキュリティの観点から、以下の例外があります:

  • Podアフィニティとアンチアフィニティでは、requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution内のどちらも、topologyKeyフィールドが空であることは許可されていません。
  • PodアンチアフィニティルールのrequiredDuringSchedulingIgnoredDuringExecutionでは、アドミッションコントローラーLimitPodHardAntiAffinityTopologytopologyKeykubernetes.io/hostnameに制限しています。アドミッションコントローラーを修正または無効化すると、トポロジーのカスタマイズができるようになります。

labelSelectortopologyKeyに加え、labelSelectortopologyKeyと同じレベルのnamespacesフィールドを使用して、labelSelectorが合致すべき名前空間のリストを任意に指定することができます。省略または空の場合、namespacesがデフォルトで、アフィニティとアンチアフィニティが定義されたPodの名前空間に設定されます。

名前空間セレクター

{{< feature-state for_k8s_version="v1.24" state="stable" >}}

namespaceSelectorを使用し、ラベルで名前空間の集合に対して検索することによって、名前空間を選択することができます。 アフィニティ項はnamespaceSelectornamespacesフィールドによって選択された名前空間に適用されます。 要注意なのは、空のnamespaceSelector({})はすべての名前空間にマッチし、nullまたは空のnamespacesリストとnullのnamespaceSelectorは、ルールが定義されているPodの名前空間にマッチします。

実践的なユースケース

Pod間アフィニティとアンチアフィニティは、ReplicaSet、StatefulSet、Deploymentなどのより高レベルなコレクションと併せて使用するとさらに有用です。これらのルールにより、ワークロードのセットが同じ定義されたトポロジーに併置されるように設定できます。たとえば、2つの関連するPodを同じードに配置することが好ましい場合です。

例えば、3つのードで構成されるクラスターを想像してください。そのクラスターを使用してウェブアプリケーションを実行し、さらにインメモリーキャッシュ(Redisなど)を使用します。この例では、ウェブアプリケーションとメモリーキャッシュの間のレイテンシーは実用的な範囲の低さも想定しています。Pod間アフィニティやアンチアフィニティを使って、ウェブサーバーとキャッシュをなるべく同じ場所に配置することができます。

以下のRedisキャッシュのDeploymentの例では、各レプリカはラベルapp=storeが付与されています。podAntiAffinityルールは、app=storeラベルを持つ複数のレプリカを単一ノードに配置しないよう、スケジューラーに指示します。これにより、各キャッシュが別々のノードに作成されます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

次のウェブサーバーのDeployment例では、app=web-storeラベルが付与されたレプリカを作成します。Podアフィニティルールは、各レプリカを、app=storeラベルが付与されたPodを持つードに配置するようスケジューラーに指示します。Podアンチアフィニティルールは、1つのードに複数のapp=web-storeサーバーを配置しないようにスケジューラーに指示します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

上記2つのDeploymentが生成されると、以下のようなクラスター構成になり、各ウェブサーバーはキャッシュと同位置に、3つの別々のードに配置されます。

node-1 node-2 node-3
webserver-1 webserver-2 webserver-3
cache-1 cache-2 cache-3

全体的な効果として、各キャッシュインスタンスは、同じノード上で実行している単一のクライアントによってアクセスされる可能性が高いです。この方法は、スキュー(負荷の偏り)とレイテンシーの両方を最小化することを目的としています。

Podアンチアフィニティを使用する理由は他にもあります。 この例と同様の方法で、アンチアフィニティを用いて高可用性を実現したStatefulSetの使用例はZooKeeperチュートリアルを参照してください。

nodeName

nodeNameはアフィニティやnodeSelectorよりも直接的なノード選択形式になります。nodeNameはPod仕様(spec)内のフィールドです。nodeNameフィールドが空でない場合、スケジューラーはPodを考慮せずに、指定されたードにあるkubeletがそのードにPodを配置しようとします。nodeNameを使用すると、nodeSelectorやアフィニティおよびアンチアフィニティルールを使用するよりも優先されます。

nodeNameを使ってノードを選択する場合の制約は以下の通りです:

  • 指定されたードが存在しない場合、Podは実行されず、場合によっては自動的に削除されることがあります。
  • 指定されたードがPodを収容するためのリソースを持っていない場合、Podの起動は失敗し、OutOfmemoryやOutOfcpuなどの理由が表示されます。
  • クラウド環境におけるノード名は、常に予測可能で安定したものではありません。

{{< note >}} nodeNameは、カスタムスケジューラーや、設定済みのスケジューラーをバイパスする必要がある高度なユースケースで使用することを目的としています。 スケジューラーをバイパスすると、割り当てられたードに過剰なPodの配置をしようとした場合には、Podの起動に失敗することがあります。 ノードアフィニティまたはnodeSelectorフィールドを使用すれば、スケジューラーをバイパスせずに、特定のードにPodを割り当てることができます。 {{</ note >}}

以下は、nodeNameフィールドを使用したPod仕様(spec)の例になります:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

上記のPodはkube-01というNodeでのみ実行されます。

Podトポロジー分散制約

トポロジー分散制約 を使って、リージョン、ゾーン、ノードなどの障害ドメイン間、または定義したその他のトポロジードメイン間で、クラスター全体にどのように{{< glossary_tooltip text="Pod" term_id="Pod" >}}を分散させるかを制御することができます。これにより、パフォーマンス、予想される可用性、または全体的な使用率を向上させることができます。

詳しい仕組みについては、トポロジー分散制約を参照してください。

{{% heading "whatsnext" %}}