iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0
Cloud Native

從 Docker 到 K8s:我的 30 天雲原生筆記系列 第 17

Day 17: 管理不同身份 - StatefulSet vs. Deployment

  • 分享至 

  • xImage
  •  

哈囉,大家好,歡迎來到 K8s 的最後一天!

這幾天我們一直在用 Deployment 來操作 K8s 的工作負載。像我們之前的 ota-backend,它其實是一個典型的無狀態 (Stateless) 應用。什麼意思呢?就是這個服務不管跑幾個 Pod,每個 Pod 都是一模一樣的。它們沒有「誰是誰」的差別,也不需要額外記住什麼狀態。只要有請求進來,任何一個 Pod 都能處理,掛掉一個也沒差,因為新的 Pod 起來就跟舊的一模一樣。

不過,並不是所有服務都能這麼「隨便」。想像一下,我們要部署的不是 ota-backend,而是一個資料庫叢集(像 MariaDB 的主從架構)。這時候情況就完全不同了。

在資料庫叢集裡,db-master 這個 Pod 是獨一無二的,它專門負責寫入資料。旁邊的 db-slave-1db-slave-2,則各自從 master 複製資料。這三個 Pod 的身份絕對不能搞錯,更不可能隨便替換。假如 master 突然被 Deployment 當成「可替換的副本」給重建掉,那整個資料流向就會亂套。再加上,每一個資料庫 Pod 都需要專屬的儲存空間,它寫過的資料不能跟別人混在一起,否則資料一致性會出大問題。

這種情況下,我們就需要 StatefulSet 來幫忙。

Part 1:StatefulSet 的核心承諾

StatefulSet 是專門為這種「有狀態」的應用設計的。它的存在目的,就是保證每個 Pod 都有一個穩定且獨一無二的身份,不會被隨意替換。

在 Deployment 底下,Pod 名稱通常是隨機產生的,像 ota-backend-6b8f...。掉了一個,就直接補上一個新的,名稱可能完全不同。對無狀態服務來說,這沒什麼影響。

但在 StatefulSet 裡,Pod 的命名方式就不一樣了。它會固定為 db-0db-1db-2 這樣的格式。就算 db-1 壞掉,重建後還是叫 db-1,不會變成其他名字。這種穩定性,讓我們可以很安心地把資料卷或角色綁定到特定的 Pod 身上。

還有一點,StatefulSet 啟動和關閉 Pod 的方式非常謹慎。它不會一次性把所有副本丟出來,而是照順序慢慢建:先把 db-0 建好並確認能正常工作,再來才會啟動 db-1,最後才是 db-2。縮容時也一樣,從最後一個開始慢慢收掉。對於依賴順序啟動的系統,這一點非常重要。

再來就是儲存問題。在 StatefulSet 裡,每個 Pod 都會擁有自己專屬的磁碟卷,像 data-db-0data-db-1。就算 Pod 被調度到另一台節點,它還是會掛回屬於自己的那份資料,不會被覆蓋或搞混。這就是 StatefulSet 給我們的三大承諾:穩定的身份、順序的啟動、持久的儲存

https://ithelp.ithome.com.tw/upload/images/20250924/20178656RYfK9nzGl6.png

Part 2:StatefulSet 的兩個重要夥伴

不過 StatefulSet 並不是單打獨鬥,它要能順利運作,還需要兩個重要夥伴:Headless ServicevolumeClaimTemplates

  • Headless Service
    幫每個 Pod 都建立一個獨立的 DNS 名稱。只要在 Service 裡寫 clusterIP: None,它就不再做負載平衡,而是給出像 db-0.mysql-svc.svc.cluster.local 這樣的記錄。這樣應用程式就能直接找到 db-0db-1,而不是隨機打到誰。
  • volumeClaimTemplates
    名字雖然長,但概念很簡單:就是 PVC 的範本。你不用自己為每個 Pod 寫一份 PVC,StatefulSet 會自動在建 Pod 的時候,替它申請一份專屬的儲存。像 db-0 就會有 data-db-0db-1 就會有 data-db-1。這確保了每個 Pod 的資料是獨立且穩定的,不會互相干擾。

https://ithelp.ithome.com.tw/upload/images/20250924/201786564A9SXY6IBf.png

Part 3:實戰演練

下面我們來看一個簡單的範例。這份 YAML 建立了一個有三個副本的 Nginx StatefulSet,每個副本都有自己的儲存卷。套用這份檔案後,你會看到:

  • Pod 會依序啟動,分別叫做 web-0web-1web-2
  • 每個 Pod 都會擁有一個自己的 PVC,例如 www-web-0www-web-1

這就是 StatefulSet 最直觀的效果。

# statefulset.yaml

# 建立一個 Headless Service,讓 StatefulSet 中的 Pod 能夠有固定且可預測的網路名稱
# Pod 之間就可以透過 DNS 名稱互相辨識,例如 web-0.nginx-svc、web-1.nginx-svc
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: project-space
spec:
  clusterIP: None   # 設為 None 就是 Headless Service
  selector:
    app: nginx-stateful
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: project-space
spec:
  serviceName: "nginx-svc"   # StatefulSet 必須綁定一個 Headless Service
  replicas: 3                # 建立三個 Pod:web-0, web-1, web-2
  selector:
    matchLabels:
      app: nginx-stateful
  template:
    metadata:
      labels:
        app: nginx-stateful
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html   # 把持久化儲存掛載到 nginx 預設網站目錄
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]  # 每個 PVC 只能被單一 Pod 掛載
      resources:
        requests:
          storage: 1Gi   # 每個 Pod 會自動生成一個 1Gi 的 PVC(www-web-0, www-web-1...)

Part 4:一張圖看懂 Deployment vs. StatefulSet

https://ithelp.ithome.com.tw/upload/images/20250924/20178656HyWInwxyzf.png

結論

今天,我們補上了 Kubernetes 核心工作負載的最後一塊拼圖。Deployment 和 StatefulSet 其實是兩種截然不同的哲學:

  • Deployment 適合無狀態應用,每個 Pod 都是可替換的。
  • StatefulSet 則適合有狀態應用(像資料庫),幫每個 Pod 建立獨立的身份和專屬的儲存。

搭配 Headless Service 和 volumeClaimTemplates,StatefulSet 才能真正發揮它的威力。
到這裡,我們已經能用 Kubernetes 部署大部分的應用程式了。接下來,我們會往 DevOps 的方向前進,開始探索 CI/CD,看看怎麼把程式碼提交一路自動部署到 K8s 上。明天見!


上一篇
Day 16: 標準化包裝:Helm
下一篇
Day 18: CI/CD 是什麼?為什麼它是現代開發的標準配備?
系列文
從 Docker 到 K8s:我的 30 天雲原生筆記21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言