StatefulSet 基礎概念

本教學課程簡介如何使用 StatefulSet 管理應用程式。它示範如何建立、刪除、擴展和更新 StatefulSet 的 Pod。

開始之前

在開始本教學課程之前,您應該熟悉以下 Kubernetes 概念:

您需要有一個 Kubernetes 叢集,並且 kubectl 命令列工具必須設定為與您的叢集通訊。建議在至少有兩個節點且未充當控制平面主機的叢集上執行本教學課程。如果您還沒有叢集,可以使用 minikube 建立一個,或者您可以使用這些 Kubernetes playground 之一:

您應該設定 kubectl 以使用預設命名空間的內容。如果您正在使用現有的叢集,請確保可以使用該叢集的預設命名空間進行練習。理想情況下,在未執行任何實際工作負載的叢集中練習。

閱讀關於 StatefulSet 的概念頁面也很有用。

目標

StatefulSet 旨在與有狀態應用程式和分散式系統一起使用。然而,在 Kubernetes 上管理有狀態應用程式和分散式系統是一個廣泛而複雜的主題。為了示範 StatefulSet 的基本功能,而不是將前一個主題與後一個主題混淆,您將使用 StatefulSet 部署一個簡單的 Web 應用程式。

完成本教學課程後,您將熟悉以下內容。

  • 如何建立 StatefulSet
  • StatefulSet 如何管理其 Pod
  • 如何刪除 StatefulSet
  • 如何擴展 StatefulSet
  • 如何更新 StatefulSet 的 Pod

建立 StatefulSet

首先,使用以下範例建立 StatefulSet(以及它所依賴的 Service)。它與 StatefulSet 概念中呈現的範例相似。它建立一個無頭服務 nginx,以發佈 StatefulSet web 中 Pod 的 IP 位址。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.21
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

您將需要使用至少兩個終端機視窗。在第一個終端機中,使用 kubectl get監看 StatefulSet 的 Pod 建立。

# use this terminal to run commands that specify --watch
# end this watch when you are asked to start a new watch
kubectl get pods --watch -l app=nginx

在第二個終端機中,使用 kubectl apply 來建立無頭服務和 StatefulSet

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created

上述命令會建立兩個 Pod,每個 Pod 都執行一個 NGINX Web 伺服器。取得 nginx 服務...

kubectl get service nginx
NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    12s

...然後取得 web StatefulSet,以驗證兩者都已成功建立

kubectl get statefulset web
NAME   READY   AGE
web    2/2     37s

有序 Pod 建立

StatefulSet 預設以嚴格的順序建立其 Pod。

對於具有 n 個副本的 StatefulSet,當部署 Pod 時,它們會依序建立,順序從 {0..n-1}。檢查第一個終端機中 kubectl get 命令的輸出。最終,輸出將如下面的範例所示。

# Do not start a new watch;
# this should already be running
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

請注意,web-1 Pod 要等到 web-0 Pod 處於執行中 (參閱Pod Phase) 和就緒 (參閱 Pod Conditions 中的 type) 狀態才會啟動。

在本教學稍後的章節,您將練習平行啟動

StatefulSet 中的 Pod

StatefulSet 中的 Pod 具有唯一的序號索引和穩定的網路身分。

檢查 Pod 的序號索引

取得 StatefulSet 的 Pod

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

StatefulSets 概念中所述,StatefulSet 中的 Pod 具有黏性的唯一身分。此身分基於由 StatefulSet 控制器 指派給每個 Pod 的唯一序號索引。
Pod 的名稱採用 <statefulset 名稱>-<序號索引> 的形式。由於 web StatefulSet 有兩個副本,因此它會建立兩個 Pod,web-0web-1

使用穩定的網路身分

每個 Pod 都有一個基於其序號索引的穩定主機名稱。使用 kubectl exec 在每個 Pod 中執行 hostname 命令

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1

使用 kubectl run 執行一個容器,該容器從 dnsutils 套件提供 nslookup 命令。在 Pod 的主機名稱上使用 nslookup,您可以檢查它們在叢集內的 DNS 位址

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

這會啟動一個新的 shell。在新的 shell 中,執行

# Run this in the dns-test container shell
nslookup web-0.nginx

輸出結果類似於

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.6

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.6

(現在退出容器 shell:exit)

無頭服務的 CNAME 指向 SRV 記錄 (每個執行中且就緒的 Pod 各有一個)。SRV 記錄指向包含 Pod IP 位址的 A 記錄項目。

在一個終端機中,監看 StatefulSet 的 Pod

# Start a new watch
# End this watch when you've seen that the delete is finished
kubectl get pod --watch -l app=nginx

在第二個終端機中,使用 kubectl delete 刪除 StatefulSet 中的所有 Pod

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

等待 StatefulSet 重新啟動它們,並等待兩個 Pod 都轉換為執行中和就緒狀態

# This should already be running
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

使用 kubectl execkubectl run 檢視 Pod 的主機名稱和叢集內 DNS 項目。首先,檢視 Pod 的主機名稱

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

然後,執行

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

這會啟動一個新的 shell。
在新的 shell 中,執行

# Run this in the dns-test container shell
nslookup web-0.nginx

輸出結果類似於

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.7

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.8

(現在退出容器 shell:exit)

Pod 的序號、主機名稱、SRV 記錄和 A 記錄名稱沒有改變,但與 Pod 相關聯的 IP 位址可能已變更。在本教學中使用的叢集中,它們已經變更。這就是為什麼重要的是不要將其他應用程式設定為透過特定 Pod 的 IP 位址連接到 StatefulSet 中的 Pod (透過解析其主機名稱連接到 Pod 是可以的)。

探索 StatefulSet 中特定的 Pod

如果您需要尋找並連接到 StatefulSet 的作用中成員,您應該查詢無頭服務的 CNAME (nginx.default.svc.cluster.local)。與 CNAME 相關聯的 SRV 記錄將僅包含 StatefulSet 中處於執行中和就緒狀態的 Pod。

如果您的應用程式已經實作了測試活性和就緒性的連線邏輯,則可以使用 Pod 的 SRV 記錄 (web-0.nginx.default.svc.cluster.localweb-1.nginx.default.svc.cluster.local),因為它們是穩定的,並且您的應用程式將能夠在 Pod 轉換為執行中和就緒狀態時探索到 Pod 的位址。

如果您的應用程式想要尋找 StatefulSet 中任何健康的 Pod,因此不需要追蹤每個特定的 Pod,您也可以連接到由該 StatefulSet 中的 Pod 支援的 type: ClusterIP 服務的 IP 位址。您可以使用追蹤 StatefulSet 的同一個服務 (在 StatefulSet 的 serviceName 中指定) 或選擇正確 Pod 集合的另一個服務。

寫入穩定儲存空間

取得 web-0web-1 的 PersistentVolumeClaims

kubectl get pvc -l app=nginx

輸出結果類似於

NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

StatefulSet 控制器建立了兩個 PersistentVolumeClaims,它們綁定到兩個 PersistentVolumes

由於本教學中使用的叢集配置為動態佈建 PersistentVolumes,因此 PersistentVolumes 會自動建立和綁定。

NGINX 網頁伺服器預設從 /usr/share/nginx/html/index.html 提供索引檔案。StatefulSet spec 中的 volumeMounts 欄位確保 /usr/share/nginx/html 目錄由 PersistentVolume 支援。

將 Pod 的主機名稱寫入其 index.html 檔案,並驗證 NGINX 網頁伺服器是否提供主機名稱

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html'; done

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

在一個終端機中,監看 StatefulSet 的 Pod

# End this watch when you've reached the end of the section.
# At the start of "Scaling a StatefulSet" you'll start a new watch.
kubectl get pod --watch -l app=nginx

在第二個終端機中,刪除 StatefulSet 的所有 Pod

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

檢查第一個終端機中 kubectl get 命令的輸出,並等待所有 Pod 都轉換為執行中和就緒狀態。

# This should already be running
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

驗證網頁伺服器是否繼續提供其主機名稱

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

即使 web-0web-1 被重新排程,它們仍然繼續提供其主機名稱,因為與其 PersistentVolumeClaims 相關聯的 PersistentVolumes 會重新掛載到其 volumeMounts。無論 web-0web-1 排程在哪個節點上,它們的 PersistentVolumes 都將掛載到適當的掛載點。

擴展 StatefulSet

擴展 StatefulSet 是指增加或減少副本數量 (水平擴展)。這是透過更新 replicas 欄位來完成的。您可以使用 kubectl scalekubectl patch 來擴展 StatefulSet。

向上擴展

向上擴展表示新增更多副本。前提是您的應用程式能夠在 StatefulSet 中分配工作,新的較大 Pod 集合可以執行更多該工作。

在一個終端機視窗中,監看 StatefulSet 中的 Pod

# If you already have a watch running, you can continue using that.
# Otherwise, start one.
# End this watch when there are 5 healthy Pods for the StatefulSet
kubectl get pods --watch -l app=nginx

在另一個終端機視窗中,使用 kubectl scale 將副本數量擴展到 5

kubectl scale sts web --replicas=5
statefulset.apps/web scaled

檢查第一個終端機中 kubectl get 命令的輸出,並等待另外三個 Pod 轉換為執行中和就緒狀態。

# This should already be running
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s

StatefulSet 控制器擴展了副本數量。與 StatefulSet 建立 一樣,StatefulSet 控制器依序建立每個 Pod,並針對其序號索引,並且在啟動後續 Pod 之前,等待每個 Pod 的前導者處於執行中和就緒狀態。

向下擴展

向下擴展表示減少副本數量。例如,您可能會因為服務的流量水平降低,並且在目前規模下存在閒置資源而執行此操作。

在一個終端機中,監看 StatefulSet 的 Pod

# End this watch when there are only 3 Pods for the StatefulSet
kubectl get pod --watch -l app=nginx

在另一個終端機中,使用 kubectl patch 將 StatefulSet 向下擴展回三個副本

kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched

等待 web-4web-3 轉換為 Terminating 狀態。

# This should already be running
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s

有序 Pod 終止

控制平面一次刪除一個 Pod,順序與其序號索引相反,並且在刪除下一個 Pod 之前,等待每個 Pod 完全關閉。

取得 StatefulSet 的 PersistentVolumeClaims

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h

仍然有五個 PersistentVolumeClaims 和五個 PersistentVolumes。在探索 Pod 的 穩定儲存空間 時,您看到當 StatefulSet 的 Pod 被刪除時,掛載到 StatefulSet Pod 的 PersistentVolumes 不會被刪除。當 Pod 刪除是由於向下擴展 StatefulSet 引起時,情況仍然如此。

更新 StatefulSets

StatefulSet 控制器支援自動更新。使用的策略由 StatefulSet API 物件的 spec.updateStrategy 欄位決定。此功能可用於升級 StatefulSet 中 Pod 的容器映像、資源請求和/或限制、標籤和註解。

有兩種有效的更新策略,RollingUpdate (預設) 和 OnDelete

RollingUpdate

RollingUpdate 更新策略將更新 StatefulSet 中的所有 Pod,以相反的序號順序,同時尊重 StatefulSet 保證。

您可以透過指定 .spec.updateStrategy.rollingUpdate.partition,將使用 RollingUpdate 策略的 StatefulSet 更新拆分為分割區。您將在本教學稍後練習。

首先,嘗試簡單的滾動更新。

在一個終端機視窗中,修補 web StatefulSet 以再次變更容器映像

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.24"}]'
statefulset.apps/web patched

在另一個終端機中,監看 StatefulSet 中的 Pod

# End this watch when the rollout is complete
#
# If you're not sure, leave it running one more minute
kubectl get pod -l app=nginx --watch

輸出結果類似於

NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s

StatefulSet 中的 Pod 以相反的序號順序更新。StatefulSet 控制器終止每個 Pod,並等待它轉換為執行中和就緒狀態,然後再更新下一個 Pod。請注意,即使 StatefulSet 控制器在序號後繼者處於執行中和就緒狀態之前不會繼續更新下一個 Pod,它也會將更新期間失敗的任何 Pod 還原到該 Pod 的現有版本。

已接收更新的 Pod 將還原到更新後的版本,而尚未接收更新的 Pod 將還原到先前的版本。透過這種方式,控制器嘗試在間歇性故障的情況下繼續保持應用程式健康,並保持更新一致。

取得 Pod 以檢視其容器映像

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.24
registry.k8s.io/nginx-slim:0.24
registry.k8s.io/nginx-slim:0.24

StatefulSet 中的所有 Pod 現在都執行先前的容器映像。

暫存更新

您可以透過指定 .spec.updateStrategy.rollingUpdate.partition,將使用 RollingUpdate 策略的 StatefulSet 更新拆分為分割區

如需更多背景資訊,您可以閱讀 StatefulSet 概念頁面中的 分割區滾動更新

您可以使用 .spec.updateStrategy.rollingUpdate 中的 partition 欄位來暫存 StatefulSet 的更新。對於此更新,您將保持 StatefulSet 中現有的 Pod 不變,同時變更 StatefulSet 的 Pod 範本。然後,您 - 或在教學之外的某些外部自動化 - 可以觸發該準備好的更新。

首先,修補 web StatefulSet 以將分割區新增至 updateStrategy 欄位

# The value of "partition" determines which ordinals a change applies to
# Make sure to use a number bigger than the last ordinal for the
# StatefulSet
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched

再次修補 StatefulSet 以變更此 StatefulSet 使用的容器映像

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.21"}]'
statefulset.apps/web patched

刪除 StatefulSet 中的一個 Pod

kubectl delete pod web-2
pod "web-2" deleted

等待替換的 web-2 Pod 處於執行中和就緒狀態

# End the watch when you see that web-2 is healthy
kubectl get pod -l app=nginx --watch
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

取得 Pod 的容器映像

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24

請注意,即使更新策略是 RollingUpdate,StatefulSet 仍使用原始容器映像還原了 Pod。這是因為 Pod 的序號小於 updateStrategy 指定的 partition

推出金絲雀

您現在將嘗試該暫存變更的 金絲雀發布

您可以透過遞減您在 上方 指定的 partition 來推出金絲雀 (以測試修改後的範本)。

修補 StatefulSet 以遞減分割區

# The value of "partition" should match the highest existing ordinal for
# the StatefulSet
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched

控制平面觸發 web-2 的替換 (透過優雅的 delete 實作,然後在刪除完成後建立新的 Pod)。等待新的 web-2 Pod 處於執行中和就緒狀態。

# This should already be running
kubectl get pod -l app=nginx --watch
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

取得 Pod 的容器

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.21

當您變更 partition 時,StatefulSet 控制器會自動更新 web-2 Pod,因為 Pod 的序號大於或等於 partition

刪除 web-1 Pod

kubectl delete pod web-1
pod "web-1" deleted

等待 web-1 Pod 處於執行中和就緒狀態。

# This should already be running
kubectl get pod -l app=nginx --watch

輸出結果類似於

NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

取得 web-1 Pod 的容器映像

kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24

web-1 還原為其原始配置,因為 Pod 的序號小於分割區。當指定分割區時,當 StatefulSet 的 .spec.template 更新時,序號大於或等於分割區的所有 Pod 都將更新。如果序號小於分割區的 Pod 被刪除或以其他方式終止,它將還原為其原始配置。

分階段推出

您可以透過分割區滾動更新以類似於您如何推出 金絲雀 的方式執行分階段推出 (例如線性、幾何或指數推出)。若要執行分階段推出,請將 partition 設定為您希望控制器暫停更新的序號。

分割區目前設定為 2。將分割區設定為 0

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched

等待 StatefulSet 中的所有 Pod 都變成執行中和就緒狀態。

# This should already be running
kubectl get pod -l app=nginx --watch

輸出結果類似於

NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

取得 StatefulSet 中 Pod 的容器映像詳細資訊

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.21
registry.k8s.io/nginx-slim:0.21
registry.k8s.io/nginx-slim:0.21

透過將 partition 移動到 0,您允許 StatefulSet 繼續更新程序。

OnDelete

您可以透過將 .spec.template.updateStrategy.type 設定為 OnDelete 來為 StatefulSet 選擇此更新策略。

修補 web StatefulSet 以使用 OnDelete 更新策略

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
statefulset.apps/web patched

當您選擇此更新策略時,當 StatefulSet 的 .spec.template 欄位被修改時,StatefulSet 控制器不會自動更新 Pod。您需要自行管理發布 - 手動或使用個別的自動化。

刪除 StatefulSets

StatefulSet 支援非級聯級聯刪除。在非級聯 delete 中,當 StatefulSet 被刪除時,StatefulSet 的 Pod 不會被刪除。在級聯 delete 中,StatefulSet 及其 Pod 都會被刪除。

閱讀 在叢集中使用級聯刪除 以大致了解級聯刪除。

非級聯刪除

在一個終端機視窗中,監看 StatefulSet 中的 Pod。

# End this watch when there are no Pods for the StatefulSet
kubectl get pods --watch -l app=nginx

使用 kubectl delete 刪除 StatefulSet。請務必提供 --cascade=orphan 參數給指令。此參數會告知 Kubernetes 只刪除 StatefulSet,而不要刪除其任何 Pod。

kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted

取得 Pod,以檢查其狀態

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m

即使 web 已被刪除,所有 Pod 仍然處於「執行中」和「就緒」狀態。刪除 web-0

kubectl delete pod web-0
pod "web-0" deleted

取得 StatefulSet 的 Pod

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m

由於 web StatefulSet 已被刪除,因此 web-0 未被重新啟動。

在一個終端機中,監看 StatefulSet 的 Pod。

# Leave this watch running until the next time you start a watch
kubectl get pods --watch -l app=nginx

在第二個終端機中,重新建立 StatefulSet。請注意,除非您刪除了 nginx Service(您不應該這樣做),否則您會看到一個錯誤,指出 Service 已存在。

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
statefulset.apps/web created
service/nginx unchanged

忽略此錯誤。它僅表示嘗試建立 nginx 無頭 Service,即使該 Service 已存在。

檢查在第一個終端機中執行的 kubectl get 指令的輸出。

# This should already be running
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m

web StatefulSet 被重新建立時,它首先重新啟動了 web-0。由於 web-1 已經處於「執行中」和「就緒」狀態,當 web-0 轉換為「執行中」和「就緒」時,它採用了這個 Pod。由於您使用等於 2 的 replicas 重新建立了 StatefulSet,一旦 web-0 被重新建立,並且一旦 web-1 被確定已處於「執行中」和「就緒」狀態,web-2 就會被終止。

現在再次查看 Pod 的網頁伺服器所提供的 index.html 檔案內容

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

即使您刪除了 StatefulSet 和 web-0 Pod,它仍然提供最初輸入到其 index.html 檔案中的主機名稱。這是因為 StatefulSet 永遠不會刪除與 Pod 關聯的 PersistentVolume。當您重新建立 StatefulSet 並重新啟動 web-0 時,其原始 PersistentVolume 會被重新掛載。

串聯刪除

在一個終端機視窗中,監看 StatefulSet 中的 Pod。

# Leave this running until the next page section
kubectl get pods --watch -l app=nginx

在另一個終端機中,再次刪除 StatefulSet。這次,省略 --cascade=orphan 參數。

kubectl delete statefulset web
statefulset.apps "web" deleted

檢查在第一個終端機中執行的 kubectl get 指令的輸出,並等待所有 Pod 轉換為「終止中」。

# This should already be running
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m

正如您在縮減規模章節中所看到的,Pod 會依序終止,順序與其序數索引相反。在終止 Pod 之前,StatefulSet 控制器會等待該 Pod 的後繼者完全終止。

kubectl delete service nginx
service "nginx" deleted

再次重新建立 StatefulSet 和無頭 Service

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created

當所有 StatefulSet 的 Pod 轉換為「執行中」和「就緒」時,檢索其 index.html 檔案的內容

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

即使您完全刪除了 StatefulSet 及其所有 Pod,重新建立的 Pod 仍會掛載其 PersistentVolume,而 web-0web-1 繼續提供其主機名稱。

最後,刪除 nginx Service...

kubectl delete service nginx
service "nginx" deleted

...以及 web StatefulSet

kubectl delete statefulset web
statefulset "web" deleted

Pod 管理策略

對於某些分散式系統,StatefulSet 的排序保證是不必要和/或不受歡迎的。這些系統僅需要唯一性和身分。

您可以指定Pod 管理策略來避免這種嚴格的排序;可以是 OrderedReady(預設值)或 Parallel

OrderedReady Pod 管理

OrderedReady Pod 管理是 StatefulSet 的預設值。它告訴 StatefulSet 控制器要遵守上面示範的排序保證。

當您的應用程式需要或期望變更(例如,推出新版本的應用程式)按照 StatefulSet 提供的序數(Pod 號碼)的嚴格順序發生時,請使用此選項。換句話說,如果您有 Pod app-0app-1app-2,Kubernetes 會先更新 app-0 並檢查它。一旦檢查通過,Kubernetes 會更新 app-1,最後更新 app-2

如果您新增了兩個以上的 Pod,Kubernetes 會設定 app-3 並等待其變成健康狀態,然後再部署 app-4

由於這是預設設定,您已經練習過使用它。

Parallel Pod 管理

另一種選擇 Parallel Pod 管理,告訴 StatefulSet 控制器並行啟動或終止所有 Pod,而無需等待 Pod 變成 RunningReady 狀態,或在啟動或終止另一個 Pod 之前完全終止。

Parallel Pod 管理選項僅影響擴展操作的行為。更新不受影響;Kubernetes 仍然依序推出變更。在本教學中,應用程式非常簡單:一個網頁伺服器告訴您它的主機名稱(因為這是 StatefulSet,所以每個 Pod 的主機名稱都不同且可預測)。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  podManagementPolicy: "Parallel"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.24
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

此 Manifest 與您上面下載的 Manifest 相同,不同之處在於 web StatefulSet 的 .spec.podManagementPolicy 設定為 Parallel

在一個終端機中,監看 StatefulSet 中的 Pod。

# Leave this watch running until the end of the section
kubectl get pod -l app=nginx --watch

在另一個終端機中,將 StatefulSet 重新配置為 Parallel Pod 管理

kubectl apply -f https://k8s.io/examples/application/web/web-parallel.yaml
service/nginx updated
statefulset.apps/web updated

保持您正在執行監看的終端機開啟。在另一個終端機視窗中,擴展 StatefulSet

kubectl scale statefulset/web --replicas=5
statefulset.apps/web scaled

檢查執行 kubectl get 指令的終端機的輸出。它可能看起來像這樣

web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-2     1/1       Running   0         8s
web-4     0/1       ContainerCreating   0         4s
web-3     1/1       Running   0         26s
web-4     1/1       Running   0         2s

StatefulSet 啟動了三個新的 Pod,並且它沒有等待第一個 Pod 變成「執行中」和「就緒」狀態,才啟動第二個和第三個 Pod。

如果您的工作負載具有狀態元件,或需要 Pod 能夠以可預測的命名方式互相識別,尤其是在您有時需要快速提供更多容量的情況下,這種方法非常有用。如果本教學的簡單網頁服務突然每分鐘收到額外 1,000,000 個請求,那麼您會想要執行更多 Pod - 但您也不想等待每個新的 Pod 啟動。並行啟動額外的 Pod 可以縮短請求額外容量與使其可供使用之間的時間。

清理

您應該開啟兩個終端機,準備好執行 kubectl 指令作為清理的一部分。

kubectl delete sts web
# sts is an abbreviation for statefulset

您可以監看 kubectl get 以查看這些 Pod 被刪除。

# end the watch when you've seen what you need to
kubectl get pod -l app=nginx --watch
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m

在刪除期間,StatefulSet 會同時移除所有 Pod;它不會等待 Pod 的序數後繼者終止,才刪除該 Pod。

關閉執行 kubectl get 指令的終端機,並刪除 nginx Service

kubectl delete svc nginx

刪除本教學中使用的 PersistentVolume 的永久儲存媒體。

kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-2bf00408-d366-4a12-bad0-1869c65d0bee   1Gi        RWO            standard       25m
www-web-1   Bound    pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4   1Gi        RWO            standard       24m
www-web-2   Bound    pvc-cba6cfa6-3a47-486b-a138-db5930207eaf   1Gi        RWO            standard       15m
www-web-3   Bound    pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752   1Gi        RWO            standard       15m
www-web-4   Bound    pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e   1Gi        RWO            standard       14m
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752   1Gi        RWO            Delete           Bound    default/www-web-3   standard                15m
pvc-2bf00408-d366-4a12-bad0-1869c65d0bee   1Gi        RWO            Delete           Bound    default/www-web-0   standard                25m
pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e   1Gi        RWO            Delete           Bound    default/www-web-4   standard                14m
pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4   1Gi        RWO            Delete           Bound    default/www-web-1   standard                24m
pvc-cba6cfa6-3a47-486b-a138-db5930207eaf   1Gi        RWO            Delete           Bound    default/www-web-2   standard                15m
kubectl delete pvc www-web-0 www-web-1 www-web-2 www-web-3 www-web-4
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
kubectl get pvc
No resources found in default namespace.
上次修改時間:2024 年 7 月 4 日下午 7:39 PST:Adding glossary tooltip for watch in k/docs (5053a95f53)