メインコンテンツまでスキップ

Pod Affinity と Anti-Affinity

Podは特定のノードや特定の状況下で実行されるよう制約をかけることができます。これには、ノードごとに1つのアプリケーションPodのみを実行したい場合や、複数のPodをノード上でペアリングしたい場合などが含まれます。さらに、ノードアフィニティを使用する場合、Podには優先的または必須の制約を設定できます。

このレッスンでは、checkout-redis Podをノードごとに1つのインスタンスのみ実行するようにし、checkout Podをcheckout-redis Podが存在するノードにのみ1つのインスタンスを実行するように、Pod間のアフィニティとアンチアフィニティに焦点を当てます。これにより、キャッシングPod(checkout-redis)が最高のパフォーマンスを得るためにcheckout Podインスタンスとローカルで実行されるようになります。

まず最初に、checkoutcheckout-redis Podが実行されていることを確認しましょう:

~$kubectl get pods -n checkout
NAME                              READY   STATUS    RESTARTS   AGE
checkout-698856df4d-vzkzw         1/1     Running   0          125m
checkout-redis-6cfd7d8787-kxs8r   1/1     Running   0          127m

両方のアプリケーションがクラスター内で1つのPodを実行していることがわかります。次に、それらがどこで実行されているかを確認しましょう:

~$kubectl get pods -n checkout \
-o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.nodeName}{"\n"}'
checkout-698856df4d-vzkzw       ip-10-42-11-142.us-west-2.compute.internal
checkout-redis-6cfd7d8787-kxs8r ip-10-42-10-225.us-west-2.compute.internal

上記の結果から、checkout-698856df4d-vzkzw Podはip-10-42-11-142.us-west-2.compute.internalノードで実行され、checkout-redis-6cfd7d8787-kxs8r Podはip-10-42-10-225.us-west-2.compute.internalノードで実行されていることがわかります。

注記

あなたの環境では、最初にPodが同じノード上で実行されている場合があります

checkoutデプロイメントにpodAffinitypodAntiAffinityポリシーを設定して、ノードごとに1つのcheckout Podが実行され、checkout-redis Podが既に実行されているノードでのみ実行されるようにしましょう。優先的な動作ではなく要件とするためにrequiredDuringSchedulingIgnoredDuringExecutionを使用します。

次のKustomizationはcheckoutデプロイメントにpodAffinitypodAntiAffinityポリシーの両方を指定したaffinityセクションを追加します:

~/environment/eks-workshop/modules/fundamentals/affinity/checkout/checkout.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout
namespace: checkout
spec:
template:
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values:
- service
- key: app.kubernetes.io/instance
operator: In
values:
- checkout
topologyKey: kubernetes.io/hostname

上記のマニフェストでは、podAffinityセクションが次のことを保証します:

  • CheckoutのPodはRedisのPodが実行されているノードでのみスケジュールされます。
  • これはapp.kubernetes.io/component: redisというラベルを持つPodとマッチングすることで実施されます。
  • topologyKey: kubernetes.io/hostnameはこのルールがノードレベルで適用されることを保証します。

podAntiAffinityセクションは次のことを保証します:

  • ノードごとに1つのcheckout Podのみが実行されます。
  • これはapp.kubernetes.io/component: serviceapp.kubernetes.io/instance: checkoutのラベルを持つPodが同じノード上で実行されないようにすることで実現されます。

変更を適用するために、次のコマンドを実行してクラスター内のcheckoutデプロイメントを変更します:

~$kubectl delete -n checkout deployment checkout
~$kubectl apply -k ~/environment/eks-workshop/modules/fundamentals/affinity/checkout/
namespace/checkout unchanged
serviceaccount/checkout unchanged
configmap/checkout unchanged
service/checkout unchanged
service/checkout-redis unchanged
deployment.apps/checkout configured
deployment.apps/checkout-redis unchanged
~$kubectl rollout status deployment/checkout \
-n checkout --timeout 180s

podAffinityセクションは、checkout-redis Podが既にノード上で実行されていることを保証します - これはcheckout Podが正しく実行されるためにcheckout-redisを必要とすると仮定できるからです。podAntiAffinityセクションは、**app.kubernetes.io/component=service**ラベルと一致するノード上に既にcheckout Podが実行されていないことを要求します。では、デプロイメントをスケールアップして設定が機能しているか確認しましょう:

~$kubectl scale -n checkout deployment/checkout --replicas 2

各Podがどこで実行されているか検証します:

~$kubectl get pods -n checkout \
-o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.nodeName}{"\n"}'
checkout-6c7c9cdf4f-p5p6q       ip-10-42-10-120.us-west-2.compute.internal
checkout-6c7c9cdf4f-wwkm4
checkout-redis-6cfd7d8787-gw59j ip-10-42-10-120.us-west-2.compute.internal

この例では、最初のcheckout Podは既存のcheckout-redis Podと同じノード上で実行されており、設定したpodAffinityルールを満たしています。2番目のPodはまだPending状態です。これは、私たちが定義したpodAntiAffinityルールにより、同じノード上で2つのcheckout Podを起動できないためです。2番目のノードにはcheckout-redis Podが実行されていないため、Pendingのままです。

次に、私たちの2つのノード用にcheckout-redisを2つのインスタンスにスケールしますが、最初にcheckout-redisデプロイメントポリシーを変更して、checkout-redisインスタンスが各ノードに分散されるようにしましょう。これを行うには、podAntiAffinityルールを作成するだけです。

~/environment/eks-workshop/modules/fundamentals/affinity/checkout-redis/checkout-redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-redis
labels:
app.kubernetes.io/created-by: eks-workshop
app.kubernetes.io/team: database
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname

上記のマニフェストでは、podAntiAffinityセクションが次のことを保証します:

  • RedisのPodは異なるノードに分散されます。
  • これはapp.kubernetes.io/component: redisというラベルを持つ複数のPodが同じノード上で実行されないようにすることで実施されます。
  • topologyKey: kubernetes.io/hostnameはこのルールがノードレベルで適用されることを保証します。

次のコマンドでそれを適用します:

~$kubectl delete -n checkout deployment checkout-redis
~$kubectl apply -k ~/environment/eks-workshop/modules/fundamentals/affinity/checkout-redis/
namespace/checkout unchanged
serviceaccount/checkout unchanged
configmap/checkout unchanged
service/checkout unchanged
service/checkout-redis unchanged
deployment.apps/checkout unchanged
deployment.apps/checkout-redis configured
~$kubectl rollout status deployment/checkout-redis \
-n checkout --timeout 180s

podAntiAffinityセクションは、**app.kubernetes.io/component=redis**ラベルと一致するノード上に既にcheckout-redis Podが実行されていないことを要求します。

~$kubectl scale -n checkout deployment/checkout-redis --replicas 2

実行中のPodを確認して、それぞれ2つずつ実行されていることを検証します:

~$kubectl get pods -n checkout
NAME                             READY   STATUS    RESTARTS   AGE
checkout-5b68c8cddf-6ddwn        1/1     Running   0          4m14s
checkout-5b68c8cddf-rd7xf        1/1     Running   0          4m12s
checkout-redis-7979df659-cjfbf   1/1     Running   0          19s
checkout-redis-7979df659-pc6m9   1/1     Running   0          22s

また、Podがどこで実行されているかを確認し、podAffinitypodAntiAffinityポリシーが守られていることを確認することもできます:

~$kubectl get pods -n checkout \
-o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.nodeName}{"\n"}'
checkout-5b68c8cddf-bn8bp       ip-10-42-11-142.us-west-2.compute.internal
checkout-5b68c8cddf-clnps       ip-10-42-12-31.us-west-2.compute.internal
checkout-redis-7979df659-57xcb  ip-10-42-11-142.us-west-2.compute.internal
checkout-redis-7979df659-r7kkm  ip-10-42-12-31.us-west-2.compute.internal

Podのスケジューリングは問題なさそうですが、checkout Podをさらにスケールして、3つ目のPodがどこにデプロイされるかを確認することで、さらに検証できます:

~$kubectl scale --replicas=3 deployment/checkout --namespace checkout

実行中のPodを確認すると、3つ目のcheckout Podは、すでに2つのノードにPodがデプロイされており、3つ目のノードにはcheckout-redis Podが実行されていないため、Pending状態になっていることがわかります。

~$kubectl get pods -n checkout
NAME                             READY   STATUS    RESTARTS   AGE
checkout-5b68c8cddf-bn8bp        1/1     Running   0          4m59s
checkout-5b68c8cddf-clnps        1/1     Running   0          6m9s
checkout-5b68c8cddf-lb69n        0/1     Pending   0          6s
checkout-redis-7979df659-57xcb   1/1     Running   0          35s
checkout-redis-7979df659-r7kkm   1/1     Running   0          2m10s

Pending状態のPodを削除してこのセクションを終了しましょう:

~$kubectl scale --replicas=2 deployment/checkout --namespace checkout