iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
Kubernetes

不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA系列 第 21 篇

Day 21|StatefulSet、StorageClass 與 PV:為什麼 Database 不只是另一個 Deployment?

  • 分享至 

  • xImage
  •  

前面我們已經把 PostgreSQL 放進 Kubernetes,大概長這樣:

Deployment
    ↓
PostgreSQL Pod
    ↓
PVC

在正式開始之前,我們先把今天最重要的新名詞講清楚:

StatefulSet

先搞懂:什麼叫 Stateful?

Stateful 可以翻成:

有狀態的。

所謂「狀態」,可以簡單理解成:

這個程式有一些不能隨便消失、不能隨便換掉的資訊。

例如 PostgreSQL 裡面存了:

使用者帳號
訂單資料
交易紀錄
文章內容

這些東西就是 Database 的 State。

假設 PostgreSQL Pod 被刪掉:

postgres Pod
    X

我們不能接受:

Pod 沒了
↓
資料也全部沒了

所以 Database 就屬於典型的:

Stateful Application

也就是:

有需要被保存的狀態的應用程式。


那什麼叫 Stateless?

相反的概念叫:

Stateless

也就是:

程式本身不依賴某一個特定 Pod 裡面的資料或身份。

例如我們前面做的 FastAPI:

FastAPI Pod A
FastAPI Pod B
FastAPI Pod C

理想情況下三顆都一樣。

如果:

Pod A

死掉,Deployment 重新建立一顆:

Pod D

通常沒差。

因為真正的資料可能放在:

PostgreSQL
Redis
外部 Storage

而不是 FastAPI Pod 本身。

所以 FastAPI 這種應用通常比較接近:

Stateless Application

那 StatefulSet 到底是什麼?

現在才來看今天主角。

StatefulSet 是 Kubernetes 裡的一種 Workload Controller。

這裡又出現新名詞:

Workload

Workload 可以理解成:

你希望 Kubernetes 幫你執行與管理的應用程式。

例如我們之前學過:

Deployment
Job
CronJob
DaemonSet

它們都是不同類型的 Workload。

而 Controller 可以理解成:

負責持續觀察並維持你指定狀態的 Kubernetes 管理器。

例如你告訴 Deployment:

我要 3 顆 Pod

Deployment 就會一直想辦法維持:

Pod = 3

死掉一顆,就再建立一顆。


所以:

StatefulSet 是 Kubernetes 專門用來管理「需要固定身份、固定 Storage、可預期建立順序」這類 Stateful Application 的 Workload Controller。

例如:

PostgreSQL
MySQL
Kafka
ZooKeeper
某些 Redis Cluster

都可能使用 StatefulSet。

但這不代表:

Database 一定只能使用 StatefulSet

而是它提供的特性通常更符合 Stateful Application 的需求。


Deployment 有什麼問題?

Deployment 建立的 Pod 通常長這樣:

api-7dc84bfc75-abc12
api-7dc84bfc75-def34

名字後面帶有產生出來的字串。

如果第一顆 Pod 壞掉:

api-7dc84bfc75-abc12

新的 Pod 可能變成:

api-7dc84bfc75-x82jd

這其實是刻意設計的。

因為 Deployment 的概念是:

我不在乎你是哪一顆 Pod,只要有足夠數量的 Pod 可以提供服務就好。

所以這些 Pod 可以被視為:

Disposable

Disposable 就是:

可丟棄、可替換的。

例如 FastAPI Pod:

Pod A 壞掉
↓
換 Pod B

沒什麼關係。


但是 Database 常常不能這樣想

假設我們有三個 Database Node:

database-0
database-1
database-2

這三個可能不是完全一樣的角色。

例如:

database-0
可能是 Primary

database-1
可能是 Replica

database-2
可能是另一個 Replica

這裡的 Replica 是:

另一份用來同步資料的 Database 節點。

所以有些系統會需要知道:

我是 database-0

你是 database-1

這就是所謂的:

Stable Identity

也就是:

穩定、不會因為 Pod 重建就改變的身份。


StatefulSet 第一個特色:Stable Identity

StatefulSet 建立出來的 Pod 名稱不是亂數,而是:

postgres-0
postgres-1
postgres-2

假設:

postgres-0

被刪掉。

Kubernetes 建立新的 Pod 時,新 Pod 還是:

postgres-0

而不是:

postgres-x82jd

所以我們可以理解成:

Deployment
↓
我只在乎「有幾顆」

StatefulSet
↓
除了有幾顆,我還在乎「這一顆是誰」

StatefulSet 第二個特色:每顆 Pod 可以有自己的 Storage

對 Database 來說,只有名字固定還不夠。

我們還希望:

postgres-0

一直使用自己的硬碟。

例如:

postgres-0
    ↓
自己的資料空間

postgres-1
    ↓
另一份資料空間

在 Kubernetes 裡,通常會變成:

postgres-0
↓
data-postgres-0

postgres-1
↓
data-postgres-1

其中:

data-postgres-0

就是 PVC。


先複習:PVC 是什麼?

PVC 全名:

PersistentVolumeClaim

可以理解成:

Pod 向 Kubernetes 提出的 Storage 使用申請。

例如:

resources:
  requests:
    storage: 1Gi

意思就是:

我要一個至少 1Gi 的 Storage。

PVC 本身不是硬碟。

它比較像:

Storage 申請單

那 PV 又是什麼?

PV 全名:

PersistentVolume

它代表 Kubernetes 可以使用的一份實際儲存資源。

所以可以先這樣記:

PVC
= 我要 Storage

PV
= Kubernetes 給你的 Storage

最後可能變成:

Pod
↓
PVC
↓
PV
↓
真正的 Disk

StatefulSet 怎麼替每顆 Pod 建 PVC?

StatefulSet 有一個非常重要的功能:

volumeClaimTemplates

Template 就是:

模板。

所以 volumeClaimTemplates 可以理解成:

建立 PVC 的模板。

例如:

volumeClaimTemplates:
  - metadata:
      name: data

    spec:
      accessModes:
        - ReadWriteOnce

      resources:
        requests:
          storage: 1Gi

這不是只建立一個 PVC。

而是在告訴 StatefulSet:

每建立一個 Pod,都按照這個模板替它建立自己的 PVC。

所以:

postgres-0
↓
data-postgres-0

如果有第二顆:

postgres-1
↓
data-postgres-1

第三顆:

postgres-2
↓
data-postgres-2

Pod 被刪掉之後呢?

這裡就是 StatefulSet 最重要的地方。

假設:

postgres-0
↓
data-postgres-0

今天 postgres-0 掛掉了。

StatefulSet 建立新的:

postgres-0

新的 postgres-0 還會重新掛回:

data-postgres-0

因此:

Pod 可以重建
但 PVC 還在

這也是為什麼 Database 資料不應該直接存在 Pod 本身。


StorageClass 又是什麼?

(它是一份「規格範本」)

StorageClass 可以理解成:

Kubernetes 裡「怎麼建立 Storage」的一組規則。

例如一間公司可能同時有:

高速 SSD
普通 Disk
NFS
Cloud Disk

不同 StorageClass 可以代表不同種類的 Storage。

查看目前 Cluster:

kubectl get storageclass

或縮寫:

kubectl get sc

看到:

https://ithelp.ithome.com.tw/upload/images/20260922/20168537Ev6ZtWco01.png

其中:

(default)

代表:

如果 PVC 沒特別指定 StorageClass,就預設使用它。

在 Kubernetes 的權責劃分中:

  • Developer(開發者) 不該知道底層硬碟是用 AWS EBS、GCP Persistent Disk、還是機房的 Ceph。
  • Cluster Admin(維運者) 負責把底層儲存打包成易懂的「服務等級(SLA)」。

StorageClass(簡稱 SC)就是維運人員開出來的「儲存型態範本(Profile)」。

看一個真實的 StorageClass YAML 結構:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd               # StorageClass 名稱
provisioner: ebs.csi.aws.com   # 指定由哪一個外掛負責動工
parameters:
  type: gp3                    # 傳給雲端硬碟的具體參數(如 AWS EBS gp3)
  iops: "3000"
reclaimPolicy: Delete          # PVC 刪除後,這顆實體硬碟是要保留還是刪除
volumeBindingMode: WaitForFirstConsumer # 等 Pod 被排程到特定節點後才建立硬碟

關鍵欄位解析:

  1. provisioner:告訴 K8s「該叫哪一個驅動程式去建硬碟」。
  2. parameters:這是給 Provisioner 看的參數(例如要 gp3 還是 io2、檔案系統是 ext4 還是 xfs),K8s 本身不干涉這些細節。
  3. volumeBindingMode:決定「何時」建硬碟。如果是 WaitForFirstConsumer,它會等到 Pod 確定落在哪個 Availability Zone(可用區)後才建硬碟,避免硬碟建在 Zone A 但 Pod 跑在 Zone B 掛載不到的悲劇。
  4. (default) 標記:當 SC 的 metadata 加上 storageclass.kubernetes.io/is-default-class: "true",開發者寫 PVC 時就算完全不寫 storageClassName,K8s 也會自動套用它。

StorageClass 裡面的 Provisioner 欄位又是什麼?

(它是真正的「驅動外掛」)

StorageClass 背後通常會指定:

Provisioner

Provisioner 可以理解成:

真正負責幫 Kubernetes 建立 Storage 的元件。

所以完整流程其實是:

PVC
│
│ 我要 1Gi
▼
StorageClass
│
│ 告訴 Kubernetes 要用哪種 Storage
▼
Provisioner
│
│ 實際建立 Storage
▼
PV
│
▼
Real Storage

例如在 Cloud 環境裡,Provisioner 可能會真的幫你建立一顆 Cloud Disk。

Kubernetes 本身是個容器調度引擎,它天生不會操作 AWS、Google Cloud 或實體機房的儲存設備。

  • Provisioner 的真身:通常是一個跑在 K8s 叢集裡的 Pod(即 CSI - Container Storage Interface 驅動程式,例如 ebs.csi.aws.com 或 nfs.csi.k8s.io)。
  • 它的日常工作:
  1. 一直在監聽 K8s 的 API Server。
  2. 當看到有新的 PVC 指定了自己的 StorageClass。
  3. 它拿著被授權的 Cloud 憑據(IAM Role 或 Secret),打外部 API:「請幫我建一顆 10Gi 的 gp3 硬碟」。
  4. 拿到硬碟建立完成的通知與 ID(例如 vol-0a1b2c3d)。
  5. 回頭在 K8s 裡替你寫出一張 PV 物件,並填上硬碟 ID。

Dynamic Provisioning 是什麼?

這裡又會看到一個很常見的名詞:

Dynamic Provisioning

意思其實很簡單:

PVC 建立之後,Kubernetes 自動替你建立對應的 Storage。

以前可能需要 Administrator:

先手動建立 PV
↓
Developer 建 PVC
↓
找到適合的 PV
↓
Binding

其中 Binding 就是:

PVC 和 PV 配對成功。

現在有 StorageClass 後,可以:

Developer 建 PVC
↓
StorageClass
↓
Provisioner
↓
自動建立 PV

這就是:

Dynamic Provisioning

也就是:

動態、自動產生 Storage。

前面的示意圖如果改用時間軸(Sequence)來看,就不會產生「多此一舉」的疑惑:

[ Developer ]              [ K8s API ]           [ Provisioner (CSI) ]        [ Cloud Provider (如 AWS) ]
      │                         │                          │                               │
   1. │ 建立 PVC (指定 sc: ssd)   │                          │                               │
      ├────────────────────────>│                          │                               │
      │                         │ 2. 廣播事件:「有新 PVC!」│                               │
      │                         ├─────────────────────────>│                               │
      │                         │                          │ 3. 呼叫雲端 API 建立硬碟        │
      │                         │                          ├──────────────────────────────>│
      │                         │                          │                               │ (硬碟建立中...)
      │                         │                          │ 4. 回傳實體硬碟 ID (vol-xxxx)   │
      │                         │                          │<──────────────────────────────┤
      │                         │ 5. 自動建立 PV (含硬碟ID) │                               │
      │                         │<─────────────────────────┤                               │
      │                         │                          │                               │
      │                         │ 6. 自動綁定 (Binding):   │                               │
      │                         │    PVC <───> PV          │                               │
      │                         │                          │                               │
   7. │ PVC 狀態變成 Bound!     │                          │                               │
      │<────────────────────────┤                          │                               │


一張表理清四者的角色本質

元件 它是什麼? 比喻 負責人
PVC 儲存需求單(要多大、什麼存取模式) 點餐單 開發者(Developer)
StorageClass 儲存規格定義(哪種等級、用哪台機器) 菜單項目 / 配方 維運工程師(Admin)
Provisioner 真正執行建立/刪除實體硬碟的背景程式 外掛機器人 / 廚師 雲端廠商 / CSI 插件
PV 一筆指針紀錄(指向已建好的實體硬碟) 取餐號碼牌 Provisioner 自動產出

動態配置(Dynamic Provisioning)的核心價值在於解耦:開發者只需要專注要多少容量(PVC),維運只要預先制定好規則(StorageClass),中間調用實體硬碟與登錄 PV 的髒活全交給 Provisioner 自動完成。


AccessMode 是什麼?

PVC 裡常看到:

accessModes:
  - ReadWriteOnce

AccessMode 指的是:

這個 Volume 可以用什麼方式被掛載與使用。

常見有:

ReadWriteOnce
ReadOnlyMany
ReadWriteMany
ReadWriteOncePod

例如:

ReadWrite

代表:

可以讀,也可以寫

ReadOnly:

只能讀

但不要直接把:

ReadWriteOnce

背成:

只能一個 Pod

更精準的理解是:

它描述 Storage 能以什麼方式被 Node / Pod 掛載,而真正的行為也和 Storage Driver 有關。

Storage Driver 則是:

Kubernetes 與實際儲存系統溝通的驅動程式。


Reclaim Policy 是什麼?

接下來介紹一個非常重要的 Storage 名詞:

Reclaim Policy

它是在決定:

PVC 不用了之後,背後 Storage 要怎麼處理。

先看 PV:

kubectl get pv

再:

kubectl get pv <PV_NAME> -o yaml

裡面可能看到:

persistentVolumeReclaimPolicy: Delete

常見有:

Delete
Retain

Delete:

PVC 被刪
↓
PV 被處理掉
↓
底層 Storage 可能一起刪掉

Retain:

PVC 被刪
↓
底層資料保留
↓
之後由 Administrator 人工處理

對 Database 來說非常重要。

因為:

刪掉 Kubernetes Object

跟:

刪掉真正的資料

完全不是同一件事情。


實際建立 StatefulSet

現在概念都介紹完了,再來做實作。

建立 Namespace:

kubectl create namespace cka-lab \
  --dry-run=client \
  -o yaml | kubectl apply -f -

先建立 PostgreSQL Password:

kubectl create secret generic postgres-secret \
  -n cka-lab \
  --from-literal=POSTGRES_PASSWORD='cka-lab-password'

這裡的 Secret 是 Kubernetes 專門拿來存放:

密碼
Token
Credential

這類敏感設定的 Object。


建立:

vim postgres-statefulset.yaml

內容:

apiVersion: v1
kind: Service
metadata:
  name: postgres-headless
  namespace: cka-lab

spec:
  clusterIP: None

  selector:
    app: postgres

  ports:
    - port: 5432
      targetPort: 5432

---
apiVersion: apps/v1
kind: StatefulSet

metadata:
  name: postgres
  namespace: cka-lab

spec:
  serviceName: postgres-headless

  replicas: 1

  selector:
    matchLabels:
      app: postgres

  template:
    metadata:
      labels:
        app: postgres

    spec:
      containers:
        - name: postgres

          image: postgres:16

          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_PASSWORD

            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata

          ports:
            - containerPort: 5432

          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data

  volumeClaimTemplates:
    - metadata:
        name: data

      spec:
        accessModes:
          - ReadWriteOnce

        resources:
          requests:
            storage: 1Gi

套用:

kubectl apply -f postgres-statefulset.yaml

Headless Service 又是什麼?

剛剛 YAML 裡有一個:

clusterIP: None

這種 Service 叫:

Headless Service

一般 Service 通常會有一個固定 IP:

Client
↓
Service IP
↓
某一顆 Pod

Client 不需要知道後面是哪一顆 Pod。

但 StatefulSet 有時候反而希望:

我可以直接找到某一顆特定 Pod。

例如:

postgres-0
postgres-1

所以 Headless Service 不幫你提供一般的單一 Virtual IP,而是協助提供每顆 StatefulSet Pod 的 DNS Identity。

例如:

postgres-0.postgres-headless

因此 StatefulSet 不只是:

名字固定

還可以擁有:

穩定的網路身份

實際觀察 StatefulSet

查看:

kubectl get statefulset -n cka-lab

也可以:

kubectl get sts -n cka-lab

你應該看到:

postgres

<>
https://ithelp.ithome.com.tw/upload/images/20260923/201685376HnmcMb2gn.png

再:

kubectl get pod -n cka-lab

你應該看到:

postgres-0

https://ithelp.ithome.com.tw/upload/images/20260923/20168537Cx6zf3AjFv.png

注意:

不是亂數名稱

而是固定的:

postgres-0

再看 PVC:

kubectl get pvc -n cka-lab

會看到類似:

data-postgres-0

https://ithelp.ithome.com.tw/upload/images/20260923/20168537PmZ0YFjatp.png

所以關係是:

StatefulSet
↓
postgres-0
↓
data-postgres-0 PVC
↓
PV
↓
Real Storage

實際測試資料會不會留下來

先進 PostgreSQL:

kubectl exec -it \
  -n cka-lab \
  postgres-0 \
  -- psql -U postgres

建立資料:

CREATE TABLE demo (
    id SERIAL PRIMARY KEY,
    name TEXT
);

INSERT INTO demo(name)
VALUES ('StatefulSet Test');

SELECT * FROM demo;

離開:

\q

現在故意刪掉 Pod:

kubectl delete pod postgres-0 -n cka-lab

觀察:

kubectl get pod -n cka-lab -w

你會發現:

postgres-0

會重新出現。

等它 Ready 後再次進入:

kubectl exec -it \
  -n cka-lab \
  postgres-0 \
  -- psql -U postgres

再:

SELECT * FROM demo;

應該仍然可以看到:

StatefulSet Test

<>

為什麼?

因為:

舊 postgres-0 Pod
        ↓
被刪除

新 postgres-0 Pod
        ↓
重新掛載
data-postgres-0 PVC
        ↓
資料仍然存在

StatefulSet 不等於 Database 高可用

最後一定要理解:

StatefulSet
≠
Database HA

這裡的 HA 是:

High Availability

也就是:

高可用性,某個節點故障時,服務仍然能繼續運作。

假設你把:

replicas: 1

改成:

replicas: 3

Kubernetes 會建立:

postgres-0
postgres-1
postgres-2

但是它不知道:

哪一台是 Primary
哪一台是 Replica
資料怎麼同步
Primary 掛了誰接手

其中資料同步叫:

Replication

Primary 掛掉之後由其他節點接手叫:

Failover

這些都是 PostgreSQL 本身需要處理的事情。

StatefulSet 只是在 Kubernetes 層幫你提供:

固定 Pod Identity
固定 Storage
固定 Network Identity
可預期的 Pod 建立 / 終止方式

那 Operator 又是什麼?

因此實際 Production 裡常常還會看到:

Operator

Operator 可以先簡單理解成:

把某個複雜軟體的維運知識寫進 Kubernetes Controller 裡。

例如 PostgreSQL Operator 可能知道:

怎麼建立 Primary
怎麼建立 Replica
怎麼做 Failover
怎麼 Backup
怎麼升級

所以:

StatefulSet

只是底層 Primitive。

Primitive 可以理解成:

Kubernetes 提供的基礎積木。

真正完整的 Database HA,通常還需要更多工具與 Database 本身的能力。


Production 不可以直接亂搬 Database

假設今天 Production 是:

Deployment
↓
PostgreSQL
↓
PVC
↓
重要資料

不要因為學到 StatefulSet 就直接:

kubectl delete deployment postgres
kubectl apply -f postgres-statefulset.yaml

因為這涉及:

Data Migration

也就是:

把現有資料安全搬到新的架構。

正式搬遷通常還需要考慮:

Backup
資料一致性
Downtime
Service 切換
Rollback

其中 Rollback 就是:

新的版本出問題時,可以退回原本架構。

所以:

StatefulSet Migration 不只是換一份 YAML,而是一個真正的資料遷移工程。


Day 21 最後整理

徹底搞懂 StatefulSet 與 Deployment 的本質

在 Kubernetes 中,挑選 Workload 控制器前,必須先看清楚應用的本質:

Stateless(無狀態) ──> Pod 換掉通常沒關係 ──> 適合 Deployment
Stateful(有狀態)   ──> 資料、身分或狀態需保存 ──> 適合 StatefulSet

很多人誤以為「有存資料就一定要用 StatefulSet」,這是不精確的。兩者的核心界限在於:Pod 之間到底有沒有「獨立性」與「身分認同(Identity)」的差別。

一、 核心分水嶺:Stateless vs. Stateful 的深層邏輯

1. Stateless(無狀態):Pod 換掉通常沒關係

  • 底層思維:服務本身「沒有記憶」。它只負責運算,不負責保留上下文與狀態。
  • 換掉沒關係的原因:
    • 無差異性:一個送進來的 HTTP 請求,由 api-pod-A 處理或由 api-pod-B 處理,得到的結果完全一樣。
    • 即拋即換(免洗筷哲學):如果 api-pod-A 崩潰,控制器直接將它銷毀,隨機生出一個 api-pod-C 頂替,用戶端完全無感。
    • 不依賴在地資源:所有的持久化數據早就交給後端資料庫或外部 S3 物件儲存,Pod 內部空空如也,即使重啟或重新排程到其他節點,也不會遺失任何資料。

2. Stateful(有狀態):有資料、身份或狀態需要被保存

  • 底層思維:服務具備「連續性的記憶與責任分工」。節點本身就是系統狀態的一部分。
  • 不能隨便換掉的原因:
    • 資料不能亂(Data):在分散式系統中(如 Elasticsearch、MongoDB、PostgreSQL),每個節點通常只負責一部分的數據分片(Shard)或特定複本(Replica)。節點如果掛掉,重啟時必須精確拿回原有的那塊硬碟,否則整個叢集的索引、副本同步或資料一致性會瞬間破裂。
    • 身分不能丟(Identity):像 Kafka 或 MySQL 主從架構,節點之間必須清楚知道「誰是 Broker-0、誰是 Leader、誰是 Follower」。如果一個 Pod 掛掉重啟後拿到隨機的新名字與隨機 IP,其他節點將無法辨識它是原先的成員,會引發叢集重新選舉、腦裂(Split-brain)或同步中斷。
    • 狀態需延續(State Order):節點加入或退出叢集通常伴隨著資料重平衡(Rebalance)。如果重啟時順序顛倒(例如 Slave 比 Master 先開),服務可能根本無法正常初始化。

二、 兩大控制器的關鍵差異

1. 儲存層面:共享(吃大鍋飯)vs. 專屬(一人一盤)

  • Deployment 掛載 PVC(吃大鍋飯):

    若開了 3 個副本且指定同一個 PVC,這 3 個 Pod 會嘗試搶著掛載同一個儲存資源。在 AWS EBS、GCP PD 等僅支援單節點讀寫(ReadWriteOnce)的硬碟架構下,第二個 Pod 會因磁區鎖定衝突而直接報錯無法啟動;即使底層是 NFS 等支援多點讀寫(ReadWriteMany)的儲存,所有 Pod 看到的也是同一份資料,無法實現分散式節點的獨立數據隔離。

  • StatefulSet 掛載 PVC(一人一盤):

    StatefulSet 具備 volumeClaimTemplates 機制。若宣告 3 個副本,Kubernetes 會依序自動建立 3 個專屬的獨立 PVC:

    • postgres-0 綁定 data-postgres-0

    • postgres-1 綁定 data-postgres-1

    • postgres-2 綁定 data-postgres-2

      即使 postgres-1 故障漂移到其他節點重啟,它只會精確掛載回原有的 data-postgres-1,資料絕不搞混。

2. 身分層面:無差別代工 vs. 各司其職的團隊

分散式系統高度依賴節點身分:

比較維度 Deployment(無差別代工) StatefulSet(各司其職的團隊)
Pod 命名 隨機雜湊碼(app-7df4f8-abcde) 固定序號(db-0、db-1、db-2)
重啟後的身分 舊的銷毀,生成全新隨機名稱 舊的銷毀,新生的 Pod 依舊沿用 db-0
網路身分 共用單一 Service IP,無法指定訪問特定節點 搭配 Headless Service,擁有專屬 DNS 紀錄(如 db-0.db-service)
啟動與終止順序 副本同時平行開關,無順序要求 依序啟動(0→1→2),逆序安全退場(2→1→0)

三、 StatefulSet 串連儲存的完整骨幹解析

為了滿足「保存身分與資料」的要求,StatefulSet 設計了一條環環相扣的控制鏈:

Plaintext

StatefulSet (控制器)
     │
     ▼
Stable Pod Identity (固定身分:postgres-0)
     │
     ▼
volumeClaimTemplates (專屬宣告範本)
     │
     ▼
PVC (專屬帳單:data-postgres-0)
     │
     ▼
StorageClass (儲存規格策略)
     │
     ▼
Provisioner (CSI 驅動外掛)
     │
     ▼
PV (K8s 儲存資源實體映射)
     │
     ▼
Real Storage (底層實體硬碟:AWS EBS / 機房 SAN)

每一層的詳細職責與銜接關係:

  1. StatefulSet(控制器)
    • 職責:掌管整個有狀態應用的生命週期。保證 Pod 按照嚴格的順序(0 → 1 → 2)依次啟動、更新與逆序關閉,並確保每個 Pod 都有獨一無二的固定序號。
  2. Stable Pod Identity(固定身分,如 postgres-0)
    • 職責:解決 Stateful 「身分不能丟」 的需求。無論 Pod 重啟多少次,名字永遠是 postgres-0。搭配 Headless Service(無頭服務)時,叢集內部會為它分配專屬的內部網域名稱(postgres-0.db-service),即使 Pod 內部 IP 每次漂移更換,這個身分網址永久有效。
  3. volumeClaimTemplates(專屬宣告範本)
    • 職責:解決 Stateful 「不能吃大鍋飯」 的儲存需求。一般的 Deployment 只能掛載預先寫死的單一 PVC;而 StatefulSet 透過這套範本機制,會在建立 postgres-0 的當下,自動以 <範本名稱>-<Pod名稱> 的格式推導出這個身分的專屬儲存需求。
  4. PVC(PersistentVolumeClaim,如 data-postgres-0)
    • 職責:解決 Stateful 「資料不能亂」 的綁定需求。系統為 postgres-0 獨立簽發一張名為 data-postgres-0 的需求清單。這個 PVC 在 Pod 刪除或重啟時絕對不會被刪除。當新的 postgres-0 再次啟動時,它只會指名掛載回同一張 data-postgres-0。
  5. StorageClass(儲存規格策略)
    • 職責:抽象化底層硬碟的技術參數。定義這塊硬碟該用什麼等級(例如:SSD 還是 HDD、IOPS 要多少、檔案系統是 ext4 還是 xfs),並指派給對應的硬體驅動。
  6. Provisioner(CSI 驅動外掛)
    • 職責:扮演打通雲端/實體硬體 API 的自動化工人。接收到 data-postgres-0 的要求與 StorageClass 的規格後,呼叫底層 API(如 AWS、GCP 或機房儲存設備)實際割出一顆符合規格的實體硬碟,並回報實體硬碟 ID。
  7. PV(PersistentVolume)
    • 職責:Kubernetes 內部的儲存資源登記卡。Provisioner 割完硬碟後,在 K8s 裡替你登錄這筆紀錄,並標記「此 PV 已專屬綁定給 data-postgres-0」。
  8. Real Storage(實體硬碟)
    • 職責:真實存放二進位資料的物理媒介。當 Pod 啟動並調度到某個 Node 上時,K8s 會指令主機把這顆實體硬碟 Attach(掛載)進該主機,並 Mount(映射)進 postgres-0 容器內的路徑。

一句話總結

Deployment 的核心哲學是「無名氏可替換」;而 StatefulSet 的核心哲學是「精確歸位」——透過固定名稱(postgres-0)鎖定專屬 PVC(data-postgres-0),進而鎖定底層實體硬碟,達成資料、身分與狀態的完整保存。


上一篇
Day 20|不是所有 Pod 都要永遠活著:Job、CronJob 與 DaemonSet
下一篇
Day 22|CNI 到底是什麼?砍掉整個 Cluster,再用 Calico 重建一次
系列文
不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言