Kubernetes StatefulSet - 有狀態應用程式部署
StatefulSet 是專門用來管理 有狀態 (Stateful) 應用程式的 Workload API 物件。 與 Deployment 不同,StatefulSet 會為每個 Pod 維護一個 固定的識別符 (Sticky Identity)。
什麼是「有狀態」應用?
- 無狀態 (Stateless): 如 Nginx, API Server。隨時可以被殺掉、重啟,不需要保留資料,也不在乎這台機器是誰。Deployment 專門處理這種。
- 有狀態 (Stateful): 如 MySQL, Redis, ZooKeeper。它們需要:
- 穩定的網路識別符: 例如
mysql-0,mysql-1,不能隨便變動。 - 穩定的儲存:
mysql-0掛掉重啟後,必須接回原本那顆硬碟 (PVC),資料不能丟。 - 有序部署與縮放: 必須先啟動主節點 (Master),再啟動從節點 (Slave)。
- 穩定的網路識別符: 例如
定義 StatefulSet
建立 StatefulSet 通常需要配合 Headless Service (ClusterIP: None) 來控制網路網域名稱。
# nginx-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None # 關鍵:Headless Service,不分配 Cluster IP
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: 'nginx' # 必須匹配 Headless Service 名稱
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts: # 掛載儲存卷
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates: # 自動建立 PVC 的模板
- metadata:
name: www
spec:
accessModes: ['ReadWriteOnce']
storageClassName: 'standard' # 指定 StorageClass,若不指定則使用叢集預設值
resources:
requests:
storage: 1Gi
StatefulSet 的特性
1. 穩定的網路 ID
StatefulSet 建立的 Pod 名稱是固定的,格式為 <statefulset-name>-<ordinal>。
例如上面的範例會依序建立:
web-0web-1web-2
它們的 DNS 名稱也會固定:web-0.nginx.default.svc.cluster.local。這讓叢集內的節點可以互相找到彼此(Discovery)。
2. 有序部署與擴展
- 部署: 0 -> 1 -> 2。前一個 Ready 之後,才會建立下一個。
- 縮減: 2 -> 1 -> 0。從最後一個開始刪除。
3. 穩定的儲存 (volumeClaimTemplates)
StatefulSet 可以透過 volumeClaimTemplates 自動為每個 Pod 申請一個專屬的 PVC (PersistentVolumeClaim)。
每個 Pod 會綁定一個對應編號的 PVC(例如 www-web-0, www-web-1)。
重點: 當 Pod 被刪除或重啟時,它會重新掛載原本的 PVC,確保資料(如資料庫檔案)不會遺失。即便 Pod 被調度到不同節點,PVC 依然會跟隨(由 Kubernetes PV 系統保證)。
StatefulSet 操作實務
擴展與縮減 (Scaling)
擴展 StatefulSet 時,它會依序建立 Pod (0 -> 1 -> 2)。縮減時,則會依序刪除 (2 -> 1 -> 0),這對分散式資料庫的數據同步非常重要。
# 擴展到 5 個副本
kubectl scale statefulset web --replicas=5
# 縮減回 3 個副本
kubectl scale statefulset web --replicas=3
更新策略 (Update Strategy)
StatefulSet 預設使用 RollingUpdate 策略。更新時會以 逆序 (Reverse Ordinal) 方式逐一更新 Pod (2 -> 1 -> 0)。確保在更新任何一個 Pod 之前,其後的 Pod 都已經 Ready 並且 Running。
Partition (分區更新):
如果你只想更新部分的 Pod(例如做金絲雀佈署 Canary Release),可以使用 partition 參數。
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
設定 partition: 2 表示只有 web-2 (及以上) 的 Pod 會被更新,web-0 和 web-1 會保持舊版本。這個功能常用於大型資料庫的灰度升級。
刪除 StatefulSet 與 PVC 保留
這是 StatefulSet 最獨特的設計之一。當你刪除 StatefulSet 時,PVC 不會被自動刪除。這是為了防止意外誤刪導致珍貴的資料遺失。
# 刪除 StatefulSet (Pod 會被終止)
kubectl delete statefulset web
刪除後,你依然會看到 PVC 存在:
kubectl get pvc
# 輸出範例:
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
# www-web-0 Bound pvc-12345678-1234-1234-1234-1234567890ab 1Gi RWO standard 10m
# www-web-1 Bound pvc-87654321-4321-4321-4321-ba0987654321 1Gi RWO standard 10m
如果你確定要刪除資料,必須手動刪除 PVC:
kubectl delete pvc www-web-0 www-web-1 www-web-2
何時使用 StatefulSet?
- 資料庫 (Database): MySQL, PostgreSQL, MongoDB, Cassandra
- 分散式協調系統: ZooKeeper, Etcd
- 訊息佇列: Kafka, RabbitMQ
如果你的應用不需要這些特性,請優先使用 Deployment。