StatefulSet 基礎
本教程介紹如何使用 StatefulSet 管理應用。它演示瞭如何建立、刪除、擴縮和更新 StatefulSet 的 Pod。
準備工作
在開始本教程之前,您應該熟悉以下 Kubernetes 概念:
您需要擁有一個 Kubernetes 叢集,並且 kubectl 命令列工具必須配置為與您的叢集通訊。建議在至少有兩個不充當控制平面主機的節點叢集上執行本教程。如果您還沒有叢集,可以透過使用 minikube 建立一個叢集,或者您可以使用以下 Kubernetes 演練場之一:
您應該配置 kubectl
以使用 default
名稱空間所在的上下文。如果您正在使用現有叢集,請確保可以在該叢集的預設名稱空間中進行練習。理想情況下,在不執行任何實際工作負載的叢集中進行練習。
閱讀有關 StatefulSets 的概念頁面也很有用。
注意
本教程假設您的叢集配置為動態製備 PersistentVolumes。您還需要有一個預設 StorageClass。如果您的叢集未配置為動態製備儲存,則在開始本教程之前,您必須手動製備兩個 1 GiB 的卷,並設定叢集,以便這些 PersistentVolumes 對映到 StatefulSet 定義的 PersistentVolumeClaim 模板。目標
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
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-0
和 web-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 exec
和 kubectl 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.local
、web-1.nginx.default.svc.cluster.local
),因為它們是穩定的,並且您的應用在 Pod 轉換為執行中和就緒狀態時能夠發現它們的地址。
如果您的應用希望在 StatefulSet 中找到任何健康的 Pod,因此不需要跟蹤每個特定的 Pod,您也可以連線到由該 StatefulSet 中的 Pod 支援的 type: ClusterIP
Service 的 IP 地址。您可以使用跟蹤 StatefulSet 的相同 Service(在 StatefulSet 的 serviceName
中指定)或選擇正確 Pod 集的單獨 Service。
寫入穩定儲存
獲取 web-0
和 web-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
注意
如果上述 curl 命令看到 403 Forbidden 響應,您需要透過執行以下命令來修復 volumeMounts
掛載的目錄的許可權(由於使用 hostPath 卷時的一個錯誤):
for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
然後重試上面的 curl
命令。
在一個終端中,監視 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-0
和 web-1
已被重新排程,但它們繼續提供其主機名,因為與其 PersistentVolumeClaims 關聯的 PersistentVolumes 被重新掛載到它們的 volumeMounts
。無論 web-0
和 web-1
排程到哪個節點,它們的 PersistentVolumes 都將掛載到適當的掛載點。
擴縮 StatefulSet
擴縮 StatefulSet 是指增加或減少副本數量(水平擴縮)。這透過更新 replicas
欄位來完成。您可以使用 kubectl scale
或 kubectl 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-4
和 web-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 現在都執行以前的容器映象。
注意
您還可以使用kubectl rollout status sts/<name>
檢視 StatefulSet 滾動更新的狀態。暫存更新
透過指定 .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 的後繼者完全終止。
注意
儘管級聯刪除會刪除 StatefulSet 及其 Pod,但級聯不會刪除與 StatefulSet 關聯的無頭 Service。您必須手動刪除nginx
Service。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-0
和 web-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-0
、app-1
和 app-2
,Kubernetes 將首先更新 app-0
並檢查它。一旦檢查透過,Kubernetes 會更新 app-1
,最後更新 app-2
。
如果您再新增兩個 Pod,Kubernetes 將設定 app-3
並等待它變得健康,然後才部署 app-4
。
因為這是預設設定,所以您已經練習過使用它。
並行 Pod 管理
替代方案 Parallel
Pod 管理告訴 StatefulSet 控制器並行啟動或終止所有 Pod,並且在啟動或終止另一個 Pod 之前不等待 Pod 變為 Running
和 Ready
或完全終止。
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.