Skip to content

Kubernetes Pod Anti-Affinity 與 Deployment 策略

利用 Pod 反親和性確保服務副本分散到不同節點,搭配 Recreate 策略解決 ReadWriteOnce PVC 的滾動更新衝突。

概述

Kubernetes 的預設排程器著重資源均衡,但不保證將同一 Deployment 的多個副本分散到不同節點。若多個副本集中在同一節點,節點故障時服務會整體中斷,不符合高可用性要求。

Pod Anti-Affinity(Pod 反親和性)讓你能明確指示排程器「這些 Pod 必須分散到不同拓撲域(通常是不同節點)」,是 Kubernetes 高可用部署的核心排程技術之一。

此外,對於使用 ReadWriteOnce PVC 的 Deployment,預設的 RollingUpdate 策略會因為新舊 Pod 同時嘗試掛載同一 Volume 而卡住——需要改用 Recreate 策略來規避此衝突。

核心內容

Pod Anti-Affinity 設定結構

Anti-Affinity 設定加在 Pod template spec 的 affinity.podAntiAffinity 欄位,有兩個關鍵子欄位:

labelSelector 指定「哪些 Pod 不能與我同節點」,通常設為與自己相同的 label。例如 app: squid 表示「不要把我排程到已有 app=squid Pod 的節點上」。

topologyKey 指定節點的分組方式。最常用 kubernetes.io/hostname(以主機名稱為界,每個節點各自獨立)。若需要跨可用區分散,可改用 topology.kubernetes.io/zone

硬性限制 vs. 軟性限制

類型欄位名稱行為
硬性限制requiredDuringSchedulingIgnoredDuringExecution嚴格執行。若可用節點數少於副本數,多餘的 Pod 會卡在 Pending 狀態,直到有新節點加入為止
軟性限制preferredDuringSchedulingIgnoredDuringExecution盡力分散,但節點不足時允許多個 Pod 共用同一節點,不阻擋排程

選擇依據:

  • 對高可用性要求嚴格的基礎設施服務(Proxy、Ingress Controller)→ 硬性限制,但需確保叢集節點數 ≥ 副本數
  • 有彈性擴縮需求、副本數可能超過節點數的應用 → 軟性限制

Deployment Strategy:ReadWriteOnce PVC 衝突

Kubernetes 的 RollingUpdate 策略(預設)會先啟動新 Pod,再終止舊 Pod。若 Pod 使用 ReadWriteOnce PVC,同一時間只允許一個 Pod 掛載,新 Pod 嘗試掛載時因舊 Pod 仍在使用中而失敗,整個更新就會卡住。

解決方案是改用 Recreate 策略:先終止所有舊 Pod(PVC 釋放),再啟動新 Pod:

yaml
spec:
  strategy:
    type: Recreate

代價是更新期間服務有短暫中斷(downtime)。對需要零中斷的服務,應考慮改用 ReadWriteMany StorageClass,或架構層面改為無狀態設計。

關鍵要點

  • topologyKey: "kubernetes.io/hostname" 是跨節點分散的標準設定
  • 硬性限制需確保叢集節點數 ≥ 副本數,否則 Pod 會 Pending
  • ReadWriteOnce PVC + RollingUpdate 策略 = 滾動更新卡住,必須改 Recreate
  • Recreate 策略有短暫停機,零中斷需求應選 ReadWriteMany 或無狀態架構

實際應用

在部署 Squid Proxy 等需要高可用性的代理服務時,設定 3 個副本搭配硬性 Anti-Affinity,確保每個節點各執行一個 Squid Pod。即使某節點故障,其餘節點上的副本仍正常服務,系統可用性從 0/1(單點故障)提升至 2/3(可容一個節點故障)。

ReadWriteOnce PVC 的問題常出現在帶有持久化儲存的單一副本服務(如日誌收集器、小型資料庫 Sidecar),部署時務必留意更新策略。

部署設定參考

以下為完整的 YAML 設定,供日後查詢與複製使用。

Deployment 完整 YAML(含硬性 Pod Anti-Affinity)

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: squid-deployment
  namespace: ingress-basic
spec:
  replicas: 3
  selector:
    matchLabels:
      app: squid
  template:
    metadata:
      labels:
        app: squid
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - squid
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: squid
        image: ubuntu/squid:edge
        ports:
        - containerPort: 3128
          name: squid
          protocol: TCP

Deployment Strategy(ReadWriteOnce PVC)

yaml
spec:
  strategy:
    type: Recreate

相關概念

來源