主線

KAI K8s之路 07|StatefulSet 是什麼?副本不再只是「隨便換一個」

Deployment 把 Pod 當成可互換副本,但資料庫、broker 和有狀態叢集常常需要固定名字、獨立儲存與啟停順序。這篇用宿舍房號拆解 StatefulSet、Headless Service、ordinal、volumeClaimTemplates 與實務排查路徑。

中文 主線 第 7 篇
先抓住

StatefulSet 不是「有磁碟的 Deployment」;它替每個 Pod 保留可辨認的身份,讓名字、網路位置與獨立儲存能在重建後重新對上。

讀完帶走

先判斷副本是否真的不可互換。需要固定 ordinal、穩定 DNS、每個副本獨立 PVC 或啟停順序時才選 StatefulSet;排查時沿 StatefulSet、Pod ordinal、Headless Service、PVC 與 Events 一層層看。

上一篇我們把 Volume、PV 和 PVC 想清楚了:

Pod 可以換,但有業務意義的資料不能跟著消失。

可是「資料留下來」只解決了一半。

假設現在有三個副本:

  • 第一個負責 leader 工作
  • 第二個保存 shard A
  • 第三個保存 shard B

這時候你不能只說:

掛一顆磁碟給它就好。

因為新的問題是:哪個 Pod 應該接回哪一份資料?其他成員要怎麼找到它?啟動順序要不要管?

我會先記這句:

Deployment 管的是一群可互換副本;StatefulSet 管的是一群有名字、有位置、有自己資料的成員。

KAI 手寫筆記風格圖:web-0、web-1、web-2 各自保留固定名字、網路身份和獨立 PVC,Pod 重建後仍能找回原本位置。
StatefulSet 不保證 Pod 永遠不死;它保證替代者知道自己接的是哪個位置、哪個名字和哪一份資料。

先不要把 StatefulSet 想成不會死的 Pod

StatefulSet 裡的 Pod 一樣會:

  • crash
  • 被 evict
  • 被重新排程
  • 因更新而重建
  • 跟著 node 故障而消失

它沒有把 Pod 變成寵物,也沒有讓 process 永生。

它真正提供的是 sticky identity

也就是:

Pod 可以被替換,但它代表的那個成員位置要能被重新認出來。

例如 web-0 掛了,controller 會建立新的 web-0

新的 Pod IP 可能不同、所在 node 也可能不同,但它仍然可以:

  • 使用 web-0 這個名字
  • 回到自己的 DNS 身份
  • 重新掛上屬於 web-0 的 PVC

所以 StatefulSet 的重點從來不是「不變」。

而是 變動之後,身份仍然對得回來

用宿舍房號想 StatefulSet

我會把 Deployment 想成一群活動工作人員。

每個人穿同一件制服、做同一份工作;今天 A 休息,換 B 上場,外面的人通常不需要知道差別。

StatefulSet 比較像一排有房號的宿舍:

  • web-0 是 0 號房
  • web-1 是 1 號房
  • web-2 是 2 號房
  • 每個房間有自己的信箱
  • 每個房間有自己的置物櫃

住戶可以換,但房號不能亂。

0 號房的新住戶要接回 0 號房的信箱和置物櫃,不能隨手拿走 2 號房的東西。

KAI 手寫筆記風格的宿舍房號比喻:StatefulSet 每個 ordinal 像固定房號,各自連到自己的 DNS 信箱與 PVC 置物櫃。
副本規格可以相同,但成員不是可互換的:ordinal 像房號,DNS 像信箱,PVC 像每個房間自己的置物櫃。

這個比喻對應到 Kubernetes:

宿舍StatefulSet
房號Pod ordinal,例如 web-0
信箱地址穩定的 Pod DNS
私人置物櫃每個 Pod 自己的 PVC
按房號安排入住ordered create / scale
倒序退房reverse ordered scale-down

真正要判斷的不是「這個 app 有沒有寫資料」。

而是:

這些副本是不是可互換?還是每個成員都必須被點名?

StatefulSet 穩定的是哪三件事

1) 穩定 ordinal

預設有 N 個副本時,Pod 會取得從 0N-1 的 ordinal:

web-0
web-1
web-2

名字不是隨機 suffix。

這讓 application、監控和運維流程可以清楚知道自己面對哪個成員。

但不要過度解讀:web-0 不會自動變成 leader。

ordinal 只提供身份,leader election、replication 和 quorum 還是 application 自己的協議。

2) 穩定網路身份

StatefulSet 通常配一個 Headless Service:

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  clusterIP: None
  selector:
    app: web
  ports:
    - name: http
      port: 80

clusterIP: None 代表它不提供一般 Service 的單一 virtual IP load balancing。

它更像一份可查詢的成員名冊,讓 client 可以找到具體 Pod,例如:

web-0.web.default.svc.cluster.local
web-1.web.default.svc.cluster.local

這很適合需要 peer discovery 的系統。

但 Headless Service 不是公開流量入口,也不是 StatefulSet 自動送你的贈品;你仍要自己建立,selector 和 serviceName 也必須對得上。

3) 穩定獨立儲存

volumeClaimTemplates 會替每個 Pod 建立自己的 PVC。

例如:

volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:
        - ReadWriteOncePod
      storageClassName: fast
      resources:
        requests:
          storage: 10Gi

配合三個副本,概念上會得到:

data-web-0 -> PVC/PV for web-0
data-web-1 -> PVC/PV for web-1
data-web-2 -> PVC/PV for web-2

web-0 被重建時,會重新引用 data-web-0

這就是身份和資料能重新對上的關鍵。

預設情況下,刪除 Pod、縮容,甚至刪除 StatefulSet,都不會自動把相關 PVC 當垃圾清掉。

這是資料安全取向,不代表你永遠不用治理它們。

新版本 Kubernetes 可以用 .spec.persistentVolumeClaimRetentionPolicy 設定 StatefulSet 被刪除或縮容時是否保留或刪除 PVC;但你必須明確審查 cluster 版本、資料保留需求和底層 PV reclaim policy,不能只看到 Delete 就一路按下去。

一個夠用的 StatefulSet 範例

下面不是 production database manifest,只是把關係串起來:

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  clusterIP: None
  selector:
    app: web
  ports:
    - name: http
      port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: web
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - name: http
              containerPort: 80
          volumeMounts:
            - name: data
              mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes:
          - ReadWriteOncePod
        storageClassName: fast
        resources:
          requests:
            storage: 10Gi

責任切分是:

  • Headless Service 管成員的網路 domain
  • serviceName 把 StatefulSet 接到這個 domain
  • ordinal 給每個 Pod 穩定名字
  • volumeClaimTemplates 給每個 Pod 自己的 claim
  • StorageClass / PV provisioner 提供真正 storage
  • application 自己處理 replication、leader、quorum 和資料一致性

StatefulSet 管身份與編排,不替 application 發明分散式系統協議。

順序不是裝飾

預設 podManagementPolicyOrderedReady

建立三個副本時,通常會按這個順序:

web-0 Ready
  -> web-1 Ready
    -> web-2 Ready

縮容時,則反過來:

web-2
  -> web-1
    -> web-0

RollingUpdate 也會從最高 ordinal 往最低 ordinal 更新,並等待更新後的 Pod Running and Ready 才繼續。

這對需要 bootstrap 順序的 workload 有價值。

但它也帶來一個代價:

前面的成員不 Ready,後面的動作可能一起被卡住。

所以 StatefulSet 卡在 rollout 時,不要只看最後一個 Pod。

你要找出是哪個 ordinal 擋住了整條隊伍。

如果 application 不需要嚴格 scaling 順序,可以考慮 podManagementPolicy: Parallel

但 Parallel 只放寬建立和縮容的等待方式,不會把成員身份變成可互換,也不等於所有 stateful system 都適合並行啟動。

還有一個重要邊界:直接刪除 StatefulSet 時,Kubernetes 不保證 Pod 會按 ordinal 優雅終止。如果 application 很在意有序 shutdown,應先縮到 0,確認資料層狀態,再刪除 StatefulSet。

Deployment 還是 StatefulSet

我會先問一句:

如果其中一個 Pod 消失,任何新副本都能無差別頂上嗎?

如果答案是「可以」,通常先用 Deployment。

如果答案是「不行,新副本必須知道自己是誰、接回哪份資料、被其他成員用固定地址找到」,才往 StatefulSet 看。

判斷DeploymentStatefulSet
副本是否可互換通常是通常不是
Pod 名稱隨機 suffix穩定 ordinal
個別 Pod DNS通常不重要常是設計的一部分
每副本獨立 PVC需另外設計volumeClaimTemplates 原生支援
建立/縮容順序不強調預設有順序
典型 workloadweb/API、stateless workerdatabase、broker、需要固定成員的叢集

「有 PVC」不是選 StatefulSet 的充分條件。

一個單副本 app 可以由 Deployment 掛 PVC;一個把 state 放在外部 database 的 app 也可能完全不需要 StatefulSet。

反過來,StatefulSet 也不是 database 的魔法保護罩。

它不自動提供:

  • backup
  • replication
  • failover correctness
  • data consistency
  • quorum
  • disaster recovery

新手最常踩的坑

1) 以為 StatefulSet Pod 不會被重建

會。

穩定的是 identity,不是 Pod UID、IP、node 或 process。

2) 以為 web-0 自動就是 leader

不是。

ordinal 可以讓 application 有穩定成員編號,但角色仍要由 application config、operator 或選舉協議決定。

3) 忘記建立 Headless Service

沒有 governing Headless Service,穩定 Pod DNS 這條路就不完整。

還要檢查:

  • StatefulSet .spec.serviceName
  • Service 名稱
  • namespace
  • selector
  • Pod label

4) 以為刪 StatefulSet 會順便清乾淨資料

預設不會自動刪除關聯 PVC,這是為了避免資料事故。

清理前要同時看 PVC retention policy、PV reclaim policy、備份和實際 storage backend。

5) 直接 force delete 卡住的 Pod

StatefulSet 的身份必須保持唯一。

在 node 狀態不明時強制刪除,有機會讓同一 identity 同時出現兩個 instance,形成 split-brain 風險。

先確認舊 instance 真的不可能再運作,再處理強制刪除。

6) 把 OrderedReady 當成 application readiness

Kubernetes 只知道 probe 和 Pod 狀態。

如果 readiness probe 沒有反映真正的 replication、recovery 或 quorum 狀態,controller 看到 Ready,不代表資料層真的安全。

我會怎麼 inspect

看到 StatefulSet rollout 卡住、Pod identity 對不上或 storage 沒掛好,我會沿這條路走:

kubectl get statefulset -n <ns>
kubectl describe statefulset <name> -n <ns>
kubectl rollout status statefulset/<name> -n <ns>
kubectl get pods -n <ns> -l app=<label> -o wide
kubectl describe pod <name>-<ordinal> -n <ns>
kubectl get service <headless-service> -n <ns> -o yaml
kubectl get endpointslice -n <ns> -l kubernetes.io/service-name=<headless-service>
kubectl get pvc -n <ns>
kubectl describe pvc <claim-name> -n <ns>
kubectl get events -n <ns> --sort-by=.lastTimestamp

我會重點看:

  • READY / CURRENT / UPDATED replicas 是否一致
  • 哪個 ordinal 第一個不 Ready
  • Pod 名稱、ownerReference 和 label 是否符合預期
  • Headless Service 是否真的是 clusterIP: None
  • serviceName、Service selector 和 Pod label 是否吻合
  • 每個 ordinal 是否拿到自己的 PVC
  • PVC 是否 Bound
  • Events 裡有沒有 scheduling、attach、mount、probe 或 termination 問題
  • update strategy、partition、pod management policy 是否阻擋 rollout
KAI 手寫筆記風格的 StatefulSet 排查路徑:先找卡住的 ordinal,再檢查 Headless Service、對應 PVC、Pod Events 與 rollout policy。
StatefulSet 卡住時,先找第一個不 Ready 的 ordinal,再沿 identity、DNS、PVC 和 Events 查,不要只重 apply YAML。

一句話版本:

先找哪個成員掉隊,再查它的名字、地址和資料有沒有重新對上。

我對 StatefulSet 的記法

我不會把 StatefulSet 記成「Kubernetes 跑 database 的物件」。

這個說法太粗,也很容易讓人以為用了它就自動安全。

我會這樣記:

StatefulSet 是一張固定座位表:人可以換,但座位號、聯絡地址和置物櫃不能亂。

Deployment 追求的是:

少一個副本,就補一個能工作的副本。

StatefulSet 追求的是:

少的是 1 號成員,就把 1 號成員帶著自己的身份和資料補回來。

這個差異,就是 stateless replica 和 stateful member 的分界。

這篇先記住三件事

  1. StatefulSet 保留的是 sticky identity,不是讓 Pod 永遠不死
  2. ordinal、Headless Service 與 volumeClaimTemplates 把名字、網路身份和獨立儲存對在一起
  3. StatefulSet 只負責 Kubernetes 層的身份與順序;replication、leader、quorum、backup 仍是 application 的責任

下一篇會接著看:Job / CronJob 是什麼?不是每個 workload 都要一直活著,有些工作跑完就應該正式結束。

技術校對參考: