あるゾーン内で ECI ベースの Pod の作成に失敗した場合、そのゾーンに集中しているすべてのレプリカが利用不可になります。この障害には、主に以下の 2 つのパターンがあります:スケジューリング時にすべてのレプリカが同一ゾーンに割り当てられること、および一部のゾーンで ECI の作成に失敗した際に、分散保証が静かに違反されることです。本トピックでは、ACK 管理型 Pro クラスターにおいて、Kubernetes のトポロジ分散制約(topology spread constraints)およびアフィニティルールを活用して、これらの問題を防止する方法について説明します。
前提条件
開始する前に、以下の条件を満たしていることを確認してください。
以下の要件を満たす ACK 管理型 Pro クラスター:
Kubernetes バージョン 1.22 以降
ACK Virtual Node コンポーネントのバージョン 2.10.0 以降
kube-scheduler コンポーネントのバージョン 5.9 以降、および 仮想ノードベースの Pod スケジューリングポリシーが有効になっていること
eci-profile に複数のゾーン(vSwitch)が設定されており、Pod が複数ゾーンにわたってスケジュール可能であること
Pod の仕様(spec)に
nodeAffinity、podAffinity、またはtopologySpreadConstraintsフィールドが設定されていること、または Pod に対して ResourcePolicy が設定されていること
ARM ベースの仮想ノードに Pod をスケジュールする場合は、当該ノードのテイントと一致する Toleration を追加してください。
注意事項
topologyKeyはtopology.kubernetes.io/zoneに設定してください。以下の条件が満たされている場合、本トピックで説明するスケジューリング動作は適用されません。
Pod に
k8s.aliyun.com/eci-schedule-strategy: "VSwitchOrdered"アノテーションが設定されている場合(マルチゾーンスケジューリングは固定の vSwitch 順序に従います)。Pod に
k8s.aliyun.com/eci-fail-strategy: "fail-fast"アノテーションが設定されている場合。
アプローチの選択
Kubernetes には、異なる目的に対応する 2 つのメカニズムがあります。
| 目的 | メカニズム | 使用タイミング |
|---|---|---|
| 利用可能なすべてのゾーンに Pod を均等に分散配置する | topologySpreadConstraints | 高可用性 — ゾーン単位の障害発生時でも、影響を受けるレプリカ数を最小限に抑えるため |
| Pod を特定のゾーンに固定する、または Pod を同一ゾーン内に集約する | podAffinity + nodeAffinity | パフォーマンス — Pod 間のレイテンシ低減が重要である場合、またはワークロードを特定のゾーンに配置する必要がある場合 |
以下の例では、両方とも ECI に必須となる共通のフィールドを使用しています:テイント virtual-kubelet.io/provider に対する Toleration、および仮想ノードよりも Elastic Compute Service (ECS) ノードを優先する preferredDuringSchedulingIgnoredDuringExecution 型のノードアフィニティです。変更されるのは、分散またはアフィニティルールのみです。
例 1:ゾーン間で Pod を均等に分散配置する
この例では、利用可能なすべてのゾーンに均等に分散される 10 個のレプリカを持つ Deployment を作成します。
[ステップ 2] スケジューリング結果の確認
以下のコマンドを実行して、各 Pod がどのノードに配置されたかを確認します。
kubectl get po -lapp=with-pod-topology-spread \
-o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
--no-headers | grep -v "<none>"ゾーンごとに Pod の数をカウントするには:
kubectl get po -lapp=with-pod-topology-spread \
-o custom-columns=NODE:.spec.nodeName \
--no-headers | grep -v "<none>" \
| xargs -I {} kubectl get no {} -o json \
| jq '.metadata.labels["topology.kubernetes.io/zone"]' \
| sort | uniq -c例 2:特定のゾーンに Pod をデプロイする
この例では、すべてのレプリカが同一ゾーンに配置される 3 個のレプリカを持つ Deployment を作成します。ゾーン間分散よりも Pod 間のレイテンシ低減が重要な場合に、このパターンを使用します。
[ステップ 1] Deployment の作成
以下の YAML を deployment.yaml として保存し、kubectl apply -f deployment.yaml を実行します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: with-affinity
labels:
app: with-affinity
spec:
replicas: 3
selector:
matchLabels:
app: with-affinity
template:
metadata:
labels:
app: with-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- with-affinity
topologyKey: topology.kubernetes.io/zone
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: type
operator: NotIn
values:
- virtual-kubelet
tolerations:
- key: "virtual-kubelet.io/provider"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: with-affinity
image: registry.k8s.io/pause:2.0スケジューリングに関連する 3 つのセクションは以下のとおりです。
| セクション | 機能 |
|---|---|
podAffinity (requiredDuringSchedulingIgnoredDuringExecution、topologyKey: topology.kubernetes.io/zone) | app: with-affinity ラベルを持つすべての Pod を同一ゾーンに配置することを要求します。kube-scheduler は、この条件を満たせない限り Pod を配置しません。詳細については、「ノードアフィニティ」をご参照ください。 |
nodeAffinity (preferredDuringSchedulingIgnoredDuringExecution、type NotIn virtual-kubelet) | ECS ノードを優先します。ECS ノードが利用できない場合は仮想ノードへフォールバックします。詳細については、「ノードアフィニティ」をご参照ください。 |
tolerations (virtual-kubelet.io/provider: Exists, NoSchedule) | kube-scheduler による仮想ノードへの Pod 配置を許可します。詳細については、「Taints and tolerations」をご参照ください。 |
Pod を最初の Pod が配置された場所ではなく、特定のゾーンに固定するには、podAffinity ブロックを requiredDuringSchedulingIgnoredDuringExecution 型のノードアフィニティに置き換えます。以下の構成では、Pod を北京ゾーン A にのみスケジュールします。
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- cn-beijing-a[ステップ 2] スケジューリング結果の確認
以下のコマンドを実行して、各 Pod がどのノードに配置されたかを確認します。
kubectl get po -lapp=with-affinity \
-o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
--no-headers | grep -v "<none>"ゾーンごとの Pod の数をカウントするには:
kubectl get po -lapp=with-affinity \
-o custom-columns=NODE:.spec.nodeName \
--no-headers | grep -v "<none>" \
| xargs -I {} kubectl get no {} -o json \
| jq '.metadata.labels["topology.kubernetes.io/zone"]' \
| sort | uniq -cStrict な ECI Pod トポロジ分散
デフォルトでは、kube-scheduler は均等なゾーン分散を目標としますが、一部のゾーンで ECI の作成に失敗した場合でも、Pod の配置をブロックしません。これにより、maxSkew 制約が静かに違反される可能性があります。
たとえば、maxSkew: 1 かつ 3 つのゾーン(A、B、C)が存在する場合、kube-scheduler はワークロードの Pod をすべてのゾーンに均等にデプロイしようとします。しかし、ゾーン B およびゾーン C で ECI の作成に失敗した場合、Pod はゾーン A のみで実行され、maxSkew で指定された制約が違反されます。
Strict な ECI Pod トポロジ分散を有効化することで、制約が確実に遵守されることを保証できます。Strict モードが有効の場合、kube-scheduler はまず各ゾーンに 1 つずつ Pod を配信し、スケジュール済みの Pod が実際に作成されるまで保留中の Pod を待機させます。以下の図は初期状態を示しており、各ゾーンに 1 つの Pod が配信され、それ以外のすべての Pod が保留中です。
Pod A1 が作成された後も、kube-scheduler は次の Pod をスケジュールしません。これは、ゾーン B またはゾーン C が失敗した場合に制約が違反されるためです。Pod B1 も作成された後に初めて、kube-scheduler はゾーン C に Pod をスケジュールします。緑色で強調表示された Pod は、作成済みの Pod を示します。
制約の遵守を無視して kube-scheduler が Pod を自由にスケジュールできるようにするには、whenUnsatisfiable: ScheduleAnyway を設定します。パラメーターの詳細については、「分散制約の定義」をご参照ください。