Kubernetes StatefulSet基礎(chǔ)

2022-06-27 09:01 更新

StatefulSet 基礎(chǔ)

本教程介紹了如何使用 StatefulSet 來管理應(yīng)用。 演示了如何創(chuàng)建、刪除、擴容/縮容和更新 StatefulSet 的 Pod。

在開始之前

在開始本教程之前,你應(yīng)該熟悉以下 Kubernetes 的概念:

  • Pods
  • Cluster DNS
  • Headless Services
  • PersistentVolumes
  • PersistentVolume Provisioning
  • StatefulSets
  • kubectl 命令行工具
說明:
本教程假設(shè)你的集群被配置為動態(tài)制備 PersistentVolume 卷。 如果沒有這樣配置,在開始本教程之前,你需要手動準(zhǔn)備 2 個 1 GiB 的存儲卷。

教程目標(biāo)

StatefulSet 旨在與有狀態(tài)的應(yīng)用及分布式系統(tǒng)一起使用。然而在 Kubernetes 上管理有狀態(tài)應(yīng)用和分布式系統(tǒng)是一個寬泛而復(fù)雜的話題。 為了演示 StatefulSet 的基本特性,并且不使前后的主題混淆,你將會使用 StatefulSet 部署一個簡單的 web 應(yīng)用。

在閱讀本教程后,你將熟悉以下內(nèi)容:

  • 如何創(chuàng)建 StatefulSet
  • StatefulSet 怎樣管理它的 Pod
  • 如何刪除 StatefulSet
  • 如何對 StatefulSet 進(jìn)行擴容/縮容
  • 如何更新一個 StatefulSet 的 Pod

創(chuàng)建 StatefulSet

作為開始,使用如下示例創(chuàng)建一個 StatefulSet。它和 StatefulSet 概念中的示例相似。 它創(chuàng)建了一個 Headless Service ?nginx ?用來發(fā)布 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: k8s.gcr.io/nginx-slim:0.8
        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.yaml?。

你需要使用兩個終端窗口。在第一個終端中,使用 ?kubectl get? 來監(jiān)視 StatefulSet 的 Pod 的創(chuàng)建情況。

kubectl get pods -w -l app=nginx

在另一個終端中,使用 ?kubectl apply? 來創(chuàng)建定義在 ?web.yaml? 中的 Headless Service 和 StatefulSet。

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

上面的命令創(chuàng)建了兩個 Pod,每個都運行了一個 NginX Web 服務(wù)器。 獲取 ?nginx ?Service:

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

然后獲取 ?web ?StatefulSet,以驗證兩者均已成功創(chuàng)建:

kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         1         20s

順序創(chuàng)建 Pod 

對于一個擁有 n 個副本的 StatefulSet,Pod 被部署時是按照 {0..n-1} 的序號順序創(chuàng)建的。 在第一個終端中使用 ?kubectl get? 檢查輸出。這個輸出最終將看起來像下面的樣子。

kubectl get pods -w -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 處于 Running 并 Ready 狀態(tài)后,?web-1? Pod 才會被啟動。

StatefulSet 中的 Pod

StatefulSet 中的每個 Pod 擁有一個唯一的順序索引和穩(wěn)定的網(wǎng)絡(luò)身份標(biāo)識。

檢查 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

如同 StatefulSet 概念中所提到的, StatefulSet 中的每個 Pod 擁有一個具有黏性的、獨一無二的身份標(biāo)志。 這個標(biāo)志基于 StatefulSet 控制器分配給每個 Pod 的唯一順序索引。 Pod 的名稱的形式為 ?<statefulset 名稱>-<序號索引>?。 ?web ?StatefulSet 擁有兩個副本,所以它創(chuàng)建了兩個 Pod:?web-0? 和 ?web-1?。

使用穩(wěn)定的網(wǎng)絡(luò)身份標(biāo)識

每個 Pod 都擁有一個基于其順序索引的穩(wěn)定的主機名。使用 ?kubectl exec? 在每個 Pod 中執(zhí)行 ?hostname?:

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

使用 ?kubectl run? 運行一個提供 ?nslookup ?命令的容器,該命令來自于 ?dnsutils ?包。 通過對 Pod 的主機名執(zhí)行 ?nslookup?,你可以檢查他們在集群內(nèi)部的 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

(現(xiàn)在可以退出容器 Shell:?exit?)

headless service 的 CNAME 指向 SRV 記錄(記錄每個 Running 和 Ready 狀態(tài)的 Pod)。 SRV 記錄指向一個包含 Pod IP 地址的記錄表項。

在一個終端中監(jiān)視 StatefulSet 的 Pod:

kubectl get pod -w -l app=nginx

在另一個終端中使用 ?kubectl delete? 刪除 StatefulSet 中所有的 Pod:

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

等待 StatefulSet 重啟它們,并且兩個 Pod 都變成 Running 和 Ready 狀態(tài):

kubectl get pod -w -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 的主機名和集群內(nèi)部的 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 /bin/sh

這將啟動一個新的 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

(現(xiàn)在可以退出容器 Shell:?exit?)

Pod 的序號、主機名、SRV 條目和記錄名稱沒有改變,但和 Pod 相關(guān)聯(lián)的 IP 地址可能發(fā)生了改變。 在本教程中使用的集群中它們就改變了。這就是為什么不要在其他應(yīng)用中使用 StatefulSet 中 Pod 的 IP 地址進(jìn)行連接,這點很重要。

如果你需要查找并連接一個 StatefulSet 的活動成員,你應(yīng)該查詢 Headless Service 的 CNAME。 和 CNAME 相關(guān)聯(lián)的 SRV 記錄只會包含 StatefulSet 中處于 Running 和 Ready 狀態(tài)的 Pod。

如果你的應(yīng)用已經(jīng)實現(xiàn)了用于測試是否已存活(liveness)并就緒(readiness)的連接邏輯, 你可以使用 Pod 的 SRV 記錄(?web-0.nginx.default.svc.cluster.local?、 ?web-1.nginx.default.svc.cluster.local?)。因為他們是穩(wěn)定的,并且當(dāng)你的 Pod 的狀態(tài)變?yōu)?nbsp;Running 和 Ready 時,你的應(yīng)用就能夠發(fā)現(xiàn)它們的地址。

寫入穩(wěn)定的存儲

獲取 ?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 控制器創(chuàng)建了兩個 PersistentVolumeClaims, 綁定到兩個 PersistentVolumes。

由于本教程使用的集群配置為動態(tài)制備 PersistentVolume 卷,所有的 PersistentVolume 卷都是自動創(chuàng)建和綁定的。

NginX Web 服務(wù)器默認(rèn)會加載位于 ?/usr/share/nginx/html/index.html? 的 index 文件。 StatefulSet ?spec ?中的 ?volumeMounts ?字段保證了 ?/usr/share/nginx/html? 文件夾由一個 PersistentVolume 卷支持。

將 Pod 的主機名寫入它們的 ?index.html? 文件并驗證 NginX Web 服務(wù)器使用該主機名提供服務(wù):

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
說明:
請注意,如果你看見上面的 curl 命令返回了 403 Forbidden 的響應(yīng),你需要像這樣修復(fù)使用 ?volumeMounts ?(原因歸咎于使用 hostPath 卷時存在的缺陷) 掛載的目錄的權(quán)限 運行:
? for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done?
在你重新嘗試上面的 ?curl ?命令之前。

在一個終端監(jiān)視 StatefulSet 的 Pod:

kubectl get pod -w -l app=nginx

在另一個終端刪除 StatefulSet 所有的 Pod:

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

在第一個終端里檢查 ?kubectl get? 命令的輸出,等待所有 Pod 變成 Running 和 Ready 狀態(tài)。

kubectl get pod -w -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 服務(wù)器在繼續(xù)使用它們的主機名提供服務(wù):

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

雖然 ?web-0? 和 ?web-1? 被重新調(diào)度了,但它們?nèi)匀焕^續(xù)監(jiān)聽各自的主機名,因為和它們的 PersistentVolumeClaim 相關(guān)聯(lián)的 PersistentVolume 卷被重新掛載到了各自的 ?volumeMount ?上。 不管 ?web-0? 和 ?web-1? 被調(diào)度到了哪個節(jié)點上,它們的 PersistentVolume 卷將會被掛載到合適的掛載點上。

擴容/縮容 StatefulSet

擴容/縮容 StatefulSet 指增加或減少它的副本數(shù)。這通過更新 ?replicas ?字段完成。 你可以使用 ?kubectl scale? 或者 ?kubectl patch? 來擴容/縮容一個 StatefulSet。

擴容

在一個終端窗口監(jiān)視 StatefulSet 的 Pod:

kubectl get pods -w -l app=nginx

在另一個終端窗口使用 ?kubectl scale? 擴展副本數(shù)為 5:

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

在第一個 終端中檢查 ?kubectl get? 命令的輸出,等待增加的 3 個 Pod 的狀態(tài)變?yōu)?nbsp;Running 和 Ready。

kubectl get pods -w -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 控制器擴展了副本的數(shù)量。StatefulSet 按序號索引順序創(chuàng)建各個 Pod,并且會等待前一個 Pod 變?yōu)?nbsp;Running 和 Ready 才會啟動下一個 Pod。

縮容

在一個終端監(jiān)視 StatefulSet 的 Pod:

kubectl get pods -w -l app=nginx

在另一個終端使用 ?kubectl patch? 將 StatefulSet 縮容回三個副本:

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

等待 ?web-4? 和 ?web-3? 狀態(tài)變?yōu)?nbsp;Terminating。

kubectl get pods -w -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 前會等待上一個被完全關(guān)閉。

獲取 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 和五個 PersistentVolume 卷仍然存在。 查看 Pod 的穩(wěn)定存儲,我們發(fā)現(xiàn)當(dāng)刪除 StatefulSet 的 Pod 時,掛載到 StatefulSet 的 Pod 的 PersistentVolume 卷不會被刪除。 當(dāng)這種刪除行為是由 StatefulSet 縮容引起時也是一樣的。

更新 StatefulSet

從 Kubernetes 1.7 版本開始,StatefulSet 控制器支持自動更新。 更新策略由 StatefulSet API 對象的 ?spec.updateStrategy? 字段決定。這個特性能夠用來更新一個 StatefulSet 中 Pod 的的容器鏡像、資源請求和限制、標(biāo)簽和注解。

?RollingUpdate ?更新策略是 StatefulSet 默認(rèn)策略。

滾動更新

?RollingUpdate ?更新策略會更新一個 StatefulSet 中的所有 Pod,采用與序號索引相反的順序并遵循 StatefulSet 的保證。

對 ?web ?StatefulSet 應(yīng)用 Patch 操作來應(yīng)用 ?RollingUpdate ?更新策略:

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

在一個終端窗口中對 ?web ?StatefulSet 執(zhí)行 patch 操作來再次改變?nèi)萜麋R像:

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

在另一個終端監(jiān)控 StatefulSet 中的 Pod:

kubectl get pod -l app=nginx -w

輸出類似于:

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 采用和序號相反的順序更新。在更新下一個 Pod 前,StatefulSet 控制器終止每個 Pod 并等待它們變成 Running 和 Ready。 請注意,雖然在順序后繼者變成 Running 和 Ready 之前 StatefulSet 控制器不會更新下一個 Pod,但它仍然會重建任何在更新過程中發(fā)生故障的 Pod,使用的是它們當(dāng)前的版本。

已經(jīng)接收到更新請求的 Pod 將會被恢復(fù)為更新的版本,沒有收到請求的 Pod 則會被恢復(fù)為之前的版本。 像這樣,控制器嘗試?yán)^續(xù)使應(yīng)用保持健康并在出現(xiàn)間歇性故障時保持更新的一致性。

獲取 Pod 來查看它們的容器鏡像:

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

StatefulSet 中的所有 Pod 現(xiàn)在都在運行之前的容器鏡像。

說明:
你還可以使用 ?kubectl rollout status sts/<名稱>? 來查看 StatefulSet 的滾動更新狀態(tài)。

分段更新

你可以使用 ?RollingUpdate ?更新策略的 ?partition ?參數(shù)來分段更新一個 StatefulSet。 分段的更新將會使 StatefulSet 中的其余所有 Pod 保持當(dāng)前版本的同時允許改變 StatefulSet 的 ?.spec.template?。

對 ?web ?StatefulSet 執(zhí)行 Patch 操作以為 ?updateStrategy ?字段添加一個分區(qū):

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

再次 Patch StatefulSet 來改變?nèi)萜麋R像:

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

刪除 StatefulSet 中的 Pod:

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

等待 Pod 變成 Running 和 Ready。

kubectl get pod -l app=nginx -w
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}}'
k8s.gcr.io/nginx-slim:0.8

請注意,雖然更新策略是 ?RollingUpdate?,StatefulSet 還是會使用原始的容器恢復(fù) Pod。 這是因為 Pod 的序號比 ?updateStrategy ?指定的 ?partition ?更小。

金絲雀發(fā)布

你可以通過減少上文指定的 ?partition ?來進(jìn)行金絲雀發(fā)布,以此來測試你的程序的改動。

通過 patch 命令修改 StatefulSet 來減少分區(qū):

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

等待 ?web-2? 變成 Running 和 Ready。

kubectl get pod -l app=nginx -w
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}}'
k8s.gcr.io/nginx-slim:0.7

當(dāng)你改變 ?partition ?時,StatefulSet 會自動更新 ?web-2? Pod,這是因為 Pod 的序號大于或等于 ?partition?。

刪除 ?web-1? Pod:

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

等待 ?web-1? 變成 Running 和 Ready。

kubectl get pod -l app=nginx -w

輸出類似于:

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}}'
k8s.gcr.io/nginx-slim:0.8

?web-1? 被按照原來的配置恢復(fù),因為 Pod 的序號小于分區(qū)。當(dāng)指定了分區(qū)時,如果更新了 StatefulSet 的 ?.spec.template?,則所有序號大于或等于分區(qū)的 Pod 都將被更新。 如果一個序號小于分區(qū)的 Pod 被刪除或者終止,它將被按照原來的配置恢復(fù)。

分階段的發(fā)布

你可以使用類似金絲雀發(fā)布的方法執(zhí)行一次分階段的發(fā)布 (例如一次線性的、等比的或者指數(shù)形式的發(fā)布)。 要執(zhí)行一次分階段的發(fā)布,你需要設(shè)置 ?partition ?為希望控制器暫停更新的序號。

分區(qū)當(dāng)前為 2。請將分區(qū)設(shè)置為 0:

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

等待 StatefulSet 中的所有 Pod 變成 Running 和 Ready。

kubectl get pod -l app=nginx -w

輸出類似于:

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 的容器鏡像詳細(xì)信息:

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

將 ?partition? 改變?yōu)?nbsp;0 以允許 StatefulSet 繼續(xù)更新過程。

OnDelete 策略

?OnDelete ?更新策略實現(xiàn)了傳統(tǒng)(1.7 之前)行為,它也是默認(rèn)的更新策略。 當(dāng)你選擇這個更新策略并修改 StatefulSet 的 ?.spec.template? 字段時,StatefulSet 控制器將不會自動更新 Pod。

刪除 StatefulSet

StatefulSet 同時支持級聯(lián)和非級聯(lián)刪除。使用非級聯(lián)方式刪除 StatefulSet 時,StatefulSet 的 Pod 不會被刪除。使用級聯(lián)刪除時,StatefulSet 和它的 Pod 都會被刪除。

非級聯(lián)刪除

在一個終端窗口監(jiān)視 StatefulSet 中的 Pod。

kubectl get pods -w -l app=nginx

使用 ?kubectl delete? 刪除 StatefulSet。請確保提供了 ?--cascade=orphan? 參數(shù)給命令。這個參數(shù)告訴 Kubernetes 只刪除 StatefulSet 而不要刪除它的任何 Pod。

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

獲取 Pod 來檢查它們的狀態(tài):

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 ?已經(jīng)被刪除了,但所有 Pod 仍然處于 Running 和 Ready 狀態(tài)。 刪除 ?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 已經(jīng)被刪除,?web-0? 沒有被重新啟動。

在一個終端監(jiān)控 StatefulSet 的 Pod。

kubectl get pods -w -l app=nginx

在另一個終端里重新創(chuàng)建 StatefulSet。請注意,除非你刪除了 ?nginx ?Service(你不應(yīng)該這樣做),你將會看到一個錯誤,提示 Service 已經(jīng)存在。

kubectl apply -f web.yaml
statefulset.apps/web created
service/nginx unchanged

請忽略這個錯誤。它僅表示 kubernetes 進(jìn)行了一次創(chuàng)建 nginx headless Service 的嘗試,盡管那個 Service 已經(jīng)存在。

在第一個終端中運行并檢查 ?kubectl get? 命令的輸出。

kubectl get pods -w -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

當(dāng)重新創(chuàng)建 ?web ?StatefulSet 時,?web-0? 被第一個重新啟動。 由于 ?web-1? 已經(jīng)處于 Running 和 Ready 狀態(tài),當(dāng) ?web-0? 變成 Running 和 Ready 時, StatefulSet 會接收這個 Pod。由于你重新創(chuàng)建的 StatefulSet 的 ?replicas ?等于 2, 一旦 ?web-0? 被重新創(chuàng)建并且 ?web-1? 被認(rèn)為已經(jīng)處于 Running 和 Ready 狀態(tài)時,?web-2? 將會被終止。

讓我們再看看被 Pod 的 Web 服務(wù)器加載的 ?index.html? 的內(nèi)容:

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? 文件的主機名進(jìn)行服務(wù)。 這是因為 StatefulSet 永遠(yuǎn)不會刪除和一個 Pod 相關(guān)聯(lián)的 PersistentVolume 卷。 當(dāng)你重建這個 StatefulSet 并且重新啟動了 ?web-0? 時,它原本的 PersistentVolume 卷會被重新掛載。

級聯(lián)刪除

在一個終端窗口監(jiān)視 StatefulSet 里的 Pod。

kubectl get pods -w -l app=nginx

在另一個窗口中再次刪除這個 StatefulSet。這次省略 ?--cascade=orphan? 參數(shù)。

kubectl delete statefulset web
statefulset.apps "web" deleted

在第一個終端檢查 ?kubectl get? 命令的輸出,并等待所有的 Pod 變成 Terminating 狀態(tài)。

kubectl get pods -w -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 后繼者被完全終止。

說明:
盡管級聯(lián)刪除會刪除 StatefulSet 及其 Pod,但級聯(lián)不會刪除與 StatefulSet 關(guān)聯(lián)的 Headless Service。你必須手動刪除 ?nginx ?Service。
kubectl delete service nginx
service "nginx" deleted

再一次重新創(chuàng)建 StatefulSet 和 headless Service:

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

當(dāng) StatefulSet 所有的 Pod 變成 Running 和 Ready 時,獲取它們的 ?index.html? 文件的內(nèi)容:

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

即使你已經(jīng)刪除了 StatefulSet 和它的全部 Pod,這些 Pod 將會被重新創(chuàng)建并掛載它們的 PersistentVolume 卷,并且 ?web-0? 和 ?web-1? 將繼續(xù)使用它的主機名提供服務(wù)。

最后刪除 ?nginx ?service

kubectl delete service nginx
service "nginx" deleted

并且刪除 ?web ?StatefulSet:

kubectl delete statefulset web
statefulset "web" deleted

Pod 管理策略 

對于某些分布式系統(tǒng)來說,StatefulSet 的順序性保證是不必要和/或者不應(yīng)該的。 這些系統(tǒng)僅僅要求唯一性和身份標(biāo)志。為了解決這個問題,在 Kubernetes 1.7 中 我們針對 StatefulSet API 對象引入了 ?.spec.podManagementPolicy?。 此選項僅影響擴縮操作的行為。更新不受影響。

OrderedReady Pod 管理策略

?OrderedReady ?Pod 管理策略是 StatefulSet 的默認(rèn)選項。它告訴 StatefulSet 控制器遵循上文展示的順序性保證。

Parallel Pod 管理策略

?Parallel ?Pod 管理策略告訴 StatefulSet 控制器并行的終止所有 Pod, 在啟動或終止另一個 Pod 前,不必等待這些 Pod 變成 Running 和 Ready 或者完全終止?fàn)顟B(tài)。

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: k8s.gcr.io/nginx-slim:0.8
        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-parallel.yaml?。

這份清單和你在上文下載的完全一樣,只是 ?web? StatefulSet 的 ?.spec.podManagementPolicy ?設(shè)置成了 ?Parallel?。

在一個終端窗口監(jiān)視 StatefulSet 中的 Pod。

kubectl get pod -l app=nginx -w

在另一個終端窗口創(chuàng)建清單中的 StatefulSet 和 Service:

kubectl apply -f web-parallel.yaml
service/nginx created
statefulset.apps/web created

查看你在第一個終端中運行的 ?kubectl get? 命令的輸出。

kubectl get pod -l app=nginx -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-1     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
web-1     1/1       Running   0         10s

StatefulSet 控制器同時啟動了 ?web-0? 和 ?web-1?。

保持第二個終端打開,并在另一個終端窗口中擴容 StatefulSet:

kubectl scale statefulset/web --replicas=4
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     1/1       Running   0         10s
web-3     1/1       Running   0         26s

StatefulSet 啟動了兩個新的 Pod,而且在啟動第二個之前并沒有等待第一個變成 Running 和 Ready 狀態(tài)。

清理

你應(yīng)該打開兩個終端,準(zhǔn)備在清理過程中運行 ?kubectl ?命令。

kubectl delete sts web
# sts is an abbreviation for statefulset

你可以監(jiān)視 ?kubectl get? 來查看那些 Pod 被刪除

kubectl get pod -l app=nginx -w
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 將并發(fā)的刪除所有 Pod,在刪除一個 Pod 前不會等待它的順序后繼者終止。

關(guān)閉 ?kubectl get? 命令運行的終端并刪除 ?nginx ?Service:

kubectl delete svc nginx
說明:
你需要刪除本教程中用到的 PersistentVolume 卷的持久化存儲介質(zhì)。
基于你的環(huán)境、存儲配置和制備方式,按照必須的步驟保證回收所有的存儲。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號