このページに記載されている情報は古い可能性があります
このページの更新日は英語版よりも古いため、記載されている情報が古い可能性があります。最新の情報をご覧になりたい方は英語版のページをご覧ください: Assigning Pods to Nodes
Node上へのPodのスケジューリング
Podを特定のNodeで実行するように 制限 したり、特定のNodeで実行することを 優先 させたりといった制約をかけることができます。 これを実現するためにはいくつかの方法がありますが、推奨されている方法は、すべてラベルセレクターを使用して選択を容易にすることです。 多くの場合、このような制約を設定する必要はなく、スケジューラーが自動的に妥当な配置を行います(例えば、Podを複数のNodeに分散させ、空きリソースが十分でないNodeにPodを配置しないようにすることができます)。 しかし、例えばSSDが接続されているNodeにPodが配置されるようにしたり、多くの通信を行う2つの異なるサービスのPodを同じアベイラビリティーゾーンに配置したりする等、どのNodeに配置するかを制御したい状況もあります。
Kubernetesが特定のPodの配置場所を選択するために、以下の方法があります:
- nodeラベルに対してマッチングを行うnodeSelectorフィールド
- アフィニティとアンチアフィニティ
- nodeNameフィールド
- Podのトポロジー分散制約
Nodeラベル
他の多くのKubernetesオブジェクトと同様に、Nodeにもラベルがあります。手動でラベルを付けることができます。 また、Kubernetesはクラスター内のすべてのNodeに対し、いくつかの標準ラベルを付けます。Nodeラベルの一覧についてはよく使われるラベル、アノテーションとtaintを参照してください。
kubernetes.io/hostname
の値はある環境ではNode名と同じになり、他の環境では異なる値になることがあります。Nodeの分離/制限
Nodeにラベルを追加することで、Podを特定のNodeまたはNodeグループ上でのスケジューリングの対象にすることができます。この機能を使用すると、特定のPodが一定の独立性、安全性、または規制といった属性を持ったNode上でのみ実行されるようにすることができます。
Node分離するのにラベルを使用する場合、kubeletが修正できないラベルキーを選択してください。 これにより、侵害されたNodeが自身でそれらのラベルを設定することで、スケジューラーがそのNodeにワークロードをスケジュールしてしまうのを防ぐことができます。
NodeRestriction
アドミッションプラグインは、kubeletがnode-restriction.kubernetes.io/
というプレフィックスを持つラベルを設定または変更するのを防ぎます。
ラベルプレフィックスをNode分離に利用するには:
- Node認可を使用していることと、
NodeRestriction
アドミッションプラグインが 有効 になっていることを確認します。 node-restriction.kubernetes.io/
プレフィックスを持つラベルをNodeに追加し、 nodeSelectorでそれらのラベルを使用します。 例えば、example.com.node-restriction.kubernetes.io/fips=true
やexample.com.node-restriction.kubernetes.io/pci-dss=true
などです。
nodeSelector
nodeSelector
は、Node選択制約の中で最もシンプルな推奨形式です。
Podのspec(仕様)にnodeSelector
フィールドを追加することで、ターゲットNodeが持つべきNodeラベルを指定できます。
Kubernetesは指定された各ラベルを持つNodeにのみ、Podをスケジューリングします。
詳しい情報についてはPodをNodeに割り当てるを参照してください。
アフィニティとアンチアフィニティ
nodeSelector
はPodを特定のラベルが付与されたNodeに制限する最も簡単な方法です。
アフィニティとアンチアフィニティでは、定義できる制約の種類が拡張されています。
アフィニティとアンチアフィニティのメリットは以下の通りです。
- アフィニティとアンチアフィニティで使われる言語は、より表現力が豊かです。
nodeSelector
は指定されたラベルを全て持つNodeを選択するだけです。アフィニティとアンチアフィニティは選択ロジックをより細かく制御することができます。 - ルールが柔軟であったり優先での指定ができたりするため、一致するNodeが見つからない場合でも、スケジューラーはPodをスケジュールします。
- Node自体のラベルではなく、Node(または他のトポロジカルドメイン)上で稼働している他のPodのラベルを使ってPodを制約することができます。これにより、Node上にどのPodを共存させるかのルールを定義することができます。
アフィニティ機能は、2種類のアフィニティで構成されています:
- Nodeアフィニティは
nodeSelector
フィールドと同様に機能しますが、より表現力が豊かで、より柔軟にルールを指定することができます。 - Pod間アフィニティとアンチアフィニティは、他のPodのラベルを元に、Podを制約することができます。
Nodeアフィニティ
Nodeアフィニティは概念的には、NodeのラベルによってPodがどのNodeにスケジュールされるかを制限するnodeSelector
と同様です。
Nodeアフィニティには2種類あります:
requiredDuringSchedulingIgnoredDuringExecution
: スケジューラーは、ルールが満たされない限り、Podをスケジュールすることができません。これはnodeSelector
と同じように機能しますが、より表現力豊かな構文になっています。preferredDuringSchedulingIgnoredDuringExecution
: スケジューラーは、対応するルールを満たすNodeを探そうとします。 一致するNodeが見つからなくても、スケジューラーはPodをスケジュールします。
IgnoredDuringExecution
は、KubernetesがPodをスケジュールした後にNodeラベルが変更されても、Podは実行し続けることを意味します。Podのspec(仕様)にある.spec.affinity.nodeAffinity
フィールドを使用して、Nodeアフィニティを指定することができます。
例えば、次のようなPodのspec(仕様)を考えてみましょう:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- antarctica-east1
- antarctica-west1
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0
この例では、以下のルールが適用されます:
- Nodeには
topology.kubernetes.io/zone
をキーとするラベルが必要で、そのラベルの値はantarctica-east1
またはantarctica-west1
のいずれかでなければなりません。 - Nodeにはキー名が
another-node-label-key
で、値がanother-node-label-value
のラベルを持つことが望ましいです。
operator
フィールドを使用して、Kubernetesがルールを解釈する際に使用できる論理演算子を指定することができます。In
、NotIn
、Exists
、DoesNotExist
、Gt
、Lt
が使用できます。
NotIn
とDoesNotExist
を使って、Nodeのアンチアフィニティ動作を定義することができます。また、NodeのTaintを使用して、特定のNodeからPodをはじくこともできます。
nodeSelector
とnodeAffinity
の両方を指定した場合、両方の条件を満たさないとPodはNodeにスケジュールされません。
nodeAffinity
タイプに関連付けられたnodeSelectorTerms
内に、複数の条件を指定した場合、Podは指定した条件のいずれかを満たしたNodeへスケジュールされます(条件はORされます)。
nodeSelectorTerms
内の条件に関連付けられた1つのmatchExpressions
フィールド内に、複数の条件を指定した場合、Podは全ての条件を満たしたNodeへスケジュールされます(条件はANDされます)。
詳細についてはNodeアフィニティを利用してPodをNodeに割り当てるを参照してください。
Nodeアフィニティの重み
preferredDuringSchedulingIgnoredDuringExecution
アフィニティタイプの各インスタンスに、1から100の範囲のweight
を指定できます。
Podの他のスケジューリング要件をすべて満たすNodeを見つけると、スケジューラーはそのNodeが満たすすべての優先ルールを繰り返し実行し、対応する式のweight
値を合計に加算します。
最終的な合計は、そのNodeの他の優先度関数のスコアに加算されます。合計スコアが最も高いNodeが、スケジューラーがPodのスケジューリングを決定する際に優先されます。
例えば、次のようなPodのspec(仕様)を考えてみましょう:
apiVersion: v1
kind: Pod
metadata:
name: with-affinity-anti-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: label-1
operator: In
values:
- key-1
- weight: 50
preference:
matchExpressions:
- key: label-2
operator: In
values:
- key-2
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0
preferredDuringSchedulingIgnoredDuringExecution
ルールにマッチするNodeとして、一つはlabel-1:key-1
ラベル、もう一つはlabel-2:key-2
ラベルの2つの候補がある場合、スケジューラーは各Nodeのweight
を考慮し、その重みとNodeの他のスコアを加え、最終スコアが最も高いNodeにPodをスケジューリングします。
kubernetes.io/os=linux
ラベルを持つ既存のNodeが必要です。スケジューリングプロファイルごとのNodeアフィニティ
Kubernetes v1.20 [beta]
複数のスケジューリングプロファイルを設定する場合、プロファイルにNodeアフィニティを関連付けることができます。これは、プロファイルが特定のNode群にのみ適用される場合に便利です。スケジューラーの設定にある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)で指定されたNodeアフィニティに加え、.spec.schedulerName
をfoo-scheduler
に設定したすべてのPodに適用されます。つまり、Podにマッチするためには、NodeはaddedAffinity
とPodの.spec.NodeAffinity
を満たす必要があるのです。
addedAffinity
はエンドユーザーには見えないので、その動作はエンドユーザーにとって予期しないものになる可能性があります。スケジューラープロファイル名と明確な相関関係のあるNodeラベルを使用すべきです。
nodeAffinity
ルールに優先して従います。Pod間のアフィニティとアンチアフィニティ
Pod間のアフィニティとアンチアフィニティは、Nodeのラベルではなく、すでにNode上で稼働しているPodのラベルに従って、PodがどのNodeにスケジュールされるかを制限できます。
XはNodeや、ラック、クラウドプロバイダーのゾーンやリージョン等を表すトポロジードメインで、YはKubernetesが満たそうとするルールである場合、Pod間のアフィニティとアンチアフィニティのルールは、"XにてルールYを満たすPodがすでに稼働している場合、このPodもXで実行すべき(アンチアフィニティの場合はすべきではない)"という形式です。
これらのルール(Y)は、オプションの関連する名前空間のリストを持つラベルセレクターで表現されます。PodはKubernetesの名前空間オブジェクトであるため、Podラベルも暗黙的に名前空間を持ちます。Kubernetesが指定された名前空間でラベルを探すため、Podラベルのラベルセレクターは、名前空間を指定する必要があります。
トポロジードメイン(X)はtopologyKey
で表現され、システムがドメインを示すために使用するNodeラベルのキーになります。具体例はよく知られたラベル、アノテーションとTaintを参照してください。
topologyKey
に合致する適切なラベルが必要になります。
一部、または全部のNodeにtopologyKey
ラベルが指定されていない場合、意図しない挙動に繋がる可能性があります。Pod間のアフィニティとアンチアフィニティの種類
Nodeアフィニティと同様に、Podアフィニティとアンチアフィニティにも下記の2種類があります:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
例えば、requiredDuringSchedulingIgnoredDuringExecution
アフィニティを使用して、2つのサービスのPodはお互いのやり取りが多いため、同じクラウドプロバイダーゾーンに併置するようにスケジューラーに指示することができます。
同様に、preferredDuringSchedulingIgnoredDuringExecution
アンチアフィニティを使用して、あるサービスのPodを複数のクラウドプロバイダーゾーンに分散させることができます。
Pod間アフィニティを使用するには、Pod仕様(spec)のaffinity.podAffinity
フィールドで指定します。Pod間アンチアフィニティを使用するには、Pod仕様(spec)のaffinity.podAntiAffinity
フィールドで指定します。
Podアフィニティ使用例
次のようなPod仕様(spec)を考えてみましょう:
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
containers:
- name: with-pod-affinity
image: registry.k8s.io/pause:2.0
この例では、PodアフィニティルールとPodアンチアフィニティルールを1つずつ定義しています。
Podアフィニティルールは"ハード"なrequiredDuringSchedulingIgnoredDuringExecution
を使用し、アンチアフィニティルールは"ソフト"なpreferredDuringSchedulingIgnoredDuringExecution
を使用しています。
アフィニティルールは、スケジューラーがNodeにPodをスケジュールできるのは、そのNodeが、security=S1
ラベルを持つ1つ以上の既存のPodと同じゾーンにある場合のみであることを示しています。より正確には、現在Podラベルsecurity=S1
を持つPodが1つ以上あるNodeが、そのゾーン内に少なくとも1つ存在する限り、スケジューラーはtopology.kubernetes.io/zone=V
ラベルを持つNodeにPodを配置しなければなりません。
アンチアフィニティルールは、security=S2
ラベルを持つ1つ以上のPodと同じゾーンにあるNodeには、スケジューラーがPodをスケジュールしないようにすることを示しています。より正確には、PodラベルSecurity=S2
を持つPodが稼働している他のNodeが、同じゾーン内に存在する場合、スケジューラーはtopology.kubernetes.io/zone=R
ラベルを持つNodeにはPodを配置しないようにしなければなりません。
Podアフィニティとアンチアフィニティの使用例についてもっと知りたい方はデザイン案を参照してください。
Podアフィニティとアンチアフィニティのoperator
フィールドで使用できるのは、In
、NotIn
、 Exists
、 DoesNotExist
です。
原則として、topologyKey
には任意のラベルキーが指定できますが、パフォーマンスやセキュリティの観点から、以下の例外があります:
- Podアフィニティとアンチアフィニティでは、
requiredDuringSchedulingIgnoredDuringExecution
とpreferredDuringSchedulingIgnoredDuringExecution
内のどちらも、topologyKey
フィールドが空であることは許可されていません。 - Podアンチアフィニティルールの
requiredDuringSchedulingIgnoredDuringExecution
では、アドミッションコントローラーLimitPodHardAntiAffinityTopology
がtopologyKey
をkubernetes.io/hostname
に制限しています。アドミッションコントローラーを修正または無効化すると、トポロジーのカスタマイズができるようになります。
labelSelector
とtopologyKey
に加え、labelSelector
とtopologyKey
と同じレベルのnamespaces
フィールドを使用して、labelSelector
が合致すべき名前空間のリストを任意に指定することができます。省略または空の場合、namespaces
がデフォルトで、アフィニティとアンチアフィニティが定義されたPodの名前空間に設定されます。
名前空間セレクター
Kubernetes v1.24 [stable]
namespaceSelector
を使用し、ラベルで名前空間の集合に対して検索することによって、名前空間を選択することができます。
アフィニティ項はnamespaceSelector
とnamespaces
フィールドによって選択された名前空間に適用されます。
要注意なのは、空のnamespaceSelector
({})はすべての名前空間にマッチし、nullまたは空のnamespaces
リストとnullのnamespaceSelector
は、ルールが定義されているPodの名前空間にマッチします。
実践的なユースケース
Pod間アフィニティとアンチアフィニティは、ReplicaSet、StatefulSet、Deploymentなどのより高レベルなコレクションと併せて使用するとさらに有用です。これらのルールにより、ワークロードのセットが同じ定義されたトポロジーに併置されるように設定できます。たとえば、2つの関連するPodを同じNodeに配置することが好ましい場合です。
例えば、3つのNodeで構成されるクラスターを想像してください。そのクラスターを使用してウェブアプリケーションを実行し、さらにインメモリーキャッシュ(Redisなど)を使用します。この例では、ウェブアプリケーションとメモリーキャッシュの間のレイテンシーは実用的な範囲の低さも想定しています。Pod間アフィニティやアンチアフィニティを使って、ウェブサーバーとキャッシュをなるべく同じ場所に配置することができます。
以下のRedisキャッシュのDeploymentの例では、各レプリカはラベルapp=store
が付与されています。podAntiAffinity
ルールは、app=store
ラベルを持つ複数のレプリカを単一Nodeに配置しないよう、スケジューラーに指示します。これにより、各キャッシュが別々のNodeに作成されます。
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を持つNodeに配置するようスケジューラーに指示します。Podアンチアフィニティルールは、1つのNodeに複数の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に配置されます。
node-1 | node-2 | node-3 |
---|---|---|
webserver-1 | webserver-2 | webserver-3 |
cache-1 | cache-2 | cache-3 |
全体的な効果として、各キャッシュインスタンスは、同じNode上で実行している単一のクライアントによってアクセスされる可能性が高いです。この方法は、スキュー(負荷の偏り)とレイテンシーの両方を最小化することを目的としています。
Podアンチアフィニティを使用する理由は他にもあります。 この例と同様の方法で、アンチアフィニティを用いて高可用性を実現したStatefulSetの使用例はZooKeeperチュートリアルを参照してください。
nodeName
nodeName
はアフィニティやnodeSelector
よりも直接的なNode選択形式になります。nodeName
はPod仕様(spec)内のフィールドです。nodeName
フィールドが空でない場合、スケジューラーはPodを考慮せずに、指定されたNodeにあるkubeletがそのNodeにPodを配置しようとします。nodeName
を使用すると、nodeSelector
やアフィニティおよびアンチアフィニティルールを使用するよりも優先されます。
nodeName
を使ってNodeを選択する場合の制約は以下の通りです:
- 指定されたNodeが存在しない場合、Podは実行されず、場合によっては自動的に削除されることがあります。
- 指定されたNodeがPodを収容するためのリソースを持っていない場合、Podの起動は失敗し、OutOfmemoryやOutOfcpuなどの理由が表示されます。
- クラウド環境におけるNode名は、常に予測可能で安定したものではありません。
nodeName
は、カスタムスケジューラーや、設定済みのスケジューラーをバイパスする必要がある高度なユースケースで使用することを目的としています。
スケジューラーをバイパスすると、割り当てられたNodeに過剰なPodの配置をしようとした場合には、Podの起動に失敗することがあります。
NodeアフィニティまたはnodeSelector
フィールドを使用すれば、スケジューラーをバイパスせずに、特定のNodeにPodを割り当てることができます。以下は、nodeName
フィールドを使用したPod仕様(spec)の例になります:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: kube-01
上記のPodはkube-01
というNodeでのみ実行されます。
Podトポロジー分散制約
トポロジー分散制約 を使って、リージョン、ゾーン、Nodeなどの障害ドメイン間、または定義したその他のトポロジードメイン間で、クラスター全体にどのようにPodを分散させるかを制御することができます。これにより、パフォーマンス、予想される可用性、または全体的な使用率を向上させることができます。
詳しい仕組みについては、トポロジー分散制約を参照してください。
次の項目
- TaintとTolerationについてもっと読む。
- NodeアフィニティとPod間アフィニティ/アンチアフィニティのデザインドキュメントを読む。
- トポロジーマネージャーがNodeレベルのリソース割り当ての決定にどのように関与しているかについて学ぶ。
- nodeSelectorの使用方法について学ぶ。
- アフィニティとアンチアフィニティの使用方法について学ぶ。