StatefulSet 基礎

本教程介紹如何使用 StatefulSet 管理應用。它演示瞭如何建立、刪除、擴縮和更新 StatefulSet 的 Pod。

準備工作

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

您需要擁有一個 Kubernetes 叢集,並且 kubectl 命令列工具必須配置為與您的叢集通訊。建議在至少有兩個不充當控制平面主機的節點叢集上執行本教程。如果您還沒有叢集,可以透過使用 minikube 建立一個叢集,或者您可以使用以下 Kubernetes 演練場之一:

您應該配置 kubectl 以使用 default 名稱空間所在的上下文。如果您正在使用現有叢集,請確保可以在該叢集的預設名稱空間中進行練習。理想情況下,在不執行任何實際工作負載的叢集中進行練習。

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

目標

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

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

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

建立 StatefulSet

首先使用下面的示例建立 StatefulSet(及其所依賴的 Service)。它類似於 StatefulSets 概念中介紹的示例。它建立了一個無頭 Service 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 建立無頭 Service 和 StatefulSet

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

上面的命令建立了兩個 Pod,每個 Pod 都執行一個 NGINX Web 伺服器。獲取 nginx Service...

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-0 Pod 處於 執行中(參見 Pod 階段)並 就緒(參見 Pod 條件中的 type),web-1 Pod 才啟動。

本教程稍後將練習並行啟動

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 的活動成員,您應該查詢無頭 Service 的 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 轉換為執行中和就緒狀態時能夠發現它們的地址。

如果您的應用希望在 StatefulSet 中找到任何健康的 Pod,因此不需要跟蹤每個特定的 Pod,您也可以連線到由該 StatefulSet 中的 Pod 支援的 type: ClusterIP Service 的 IP 地址。您可以使用跟蹤 StatefulSet 的相同 Service(在 StatefulSet 的 serviceName 中指定)或選擇正確 Pod 集的單獨 Service。

寫入穩定儲存

獲取 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 Web 伺服器預設從 /usr/share/nginx/html/index.html 提供索引檔案。StatefulSet spec 中的 volumeMounts 欄位確保 /usr/share/nginx/html 目錄由 PersistentVolume 支援。

將 Pod 的主機名寫入其 index.html 檔案,並驗證 NGINX Web 伺服器提供主機名。

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 https:///; 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

驗證 Web 伺服器是否繼續提供其主機名。

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl https:///; 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 轉換為終止狀態。

# 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 縮減造成的時,這仍然是正確的。

更新 StatefulSet

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 將恢復到以前的版本。透過這種方式,控制器試圖在間歇性故障存在的情況下保持應用程式健康和更新的一致性。

獲取 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 的替換(透過優雅的刪除實現,然後在刪除完成後建立新的 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 的序號小於分割槽。當指定分割槽時,所有序號大於或等於分割槽的 Pod 都將在 StatefulSet 的 .spec.template 更新時更新。如果序號小於分割槽的 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", "rollingUpdate": null}}}'
statefulset.apps/web patched

當您選擇此更新策略時,當對 StatefulSet 的 .spec.template 欄位進行修改時,StatefulSet 控制器不會自動更新 Pod。您需要自行管理釋出 — 無論是手動還是使用單獨的自動化。

刪除 StatefulSet

StatefulSet 支援非級聯刪除和級聯刪除。在非級聯刪除中,當 StatefulSet 被刪除時,StatefulSet 的 Pod 不會被刪除。在級聯刪除中,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 無頭服務已存在,也曾嘗試建立它。

檢查第一個終端中執行的 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。由於您重新建立了 replicas 等於 2 的 StatefulSet,一旦 web-0 被重新建立,並且 web-1 被確定已經處於執行中和就緒狀態,web-2 就被終止了。

現在再次檢視 Pod 的 Web 伺服器提供的 index.html 檔案的內容。

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

儘管您刪除了 StatefulSet 和 web-0 Pod,但它仍然提供最初輸入到其 index.html 檔案中的主機名。這是因為 StatefulSet 從不刪除與 Pod 關聯的 PersistentVolumes。當您重新建立 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 https:///; done
web-0
web-1

儘管您完全刪除了 StatefulSet 及其所有 Pod,但 Pod 會在其 PersistentVolumes 掛載的情況下重新建立,並且 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

有序就緒 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

因為這是預設設定,所以您已經練習過使用它。

並行 Pod 管理

替代方案 Parallel Pod 管理告訴 StatefulSet 控制器並行啟動或終止所有 Pod,並且在啟動或終止另一個 Pod 之前不等待 Pod 變為 RunningReady 或完全終止。

Parallel Pod 管理選項僅影響擴縮操作的行為。更新不受影響;Kubernetes 仍然按順序推出更改。對於本教程,應用程式非常簡單:一個 Web 伺服器,它告訴您它的主機名(因為這是一個 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

此清單與您上面下載的清單相同,只是 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 能夠以可預測的命名相互識別,並且尤其是在您有時需要快速提供大量額外容量時,此方法非常有用。如果本教程中的簡單 Web 服務突然每分鐘收到額外的 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

刪除本教程中使用的 PersistentVolumes 的永續性儲存介質。

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.