Kubernetes—StatefulSet部署有狀態應用詳解(二十四)


技術公眾號:后端技術解憂鋪
關注微信公眾號:CodingTechWork,一起學習進步。

引言

  我們都知道在k8s集群中,Deployment是用來部署無狀態的服務,那有狀態的服務是用什么資源對象來部署呢?無狀態和有狀態服務部署的區別是什么?有狀態的pod肯定需要獨立的存儲卷,這樣才能保證故障后尋找數據就地恢復原狀態,那如何實現多個pod擁有自己獨立存儲卷?下面我們來看看如何演進方案。

演進

手動創建多個pod

  手動創建多個pod,每個pod使用一個獨立的持久卷聲明,但是需要我們手動管理這些pod,當發生故障后,需要重新手動創建這些pod,從而保證有狀態恢復。
手動創建多個pod

1個RS對應1個pod

  手動創建pod,肯定不便於維護,我們在每個pod的上一層來操作,創建多個ReplicaSet,每個ReplicaSet的副本數設置為1,這樣pod和ReplicaSet是一一對應的,每個ReplicaSet的pod模板都關聯一個獨立的持久卷聲明。這種可以達到某個節點故障或pod誤刪時自動重新調度創建pod的效果,但是對於伸縮副本時,又需要手動創建或刪除ReplicaSet,還是達不到一次性創建、更新、刪除后,后期自動調度的效果。
1個RS對應1個pod

所有pod共享同一個PV

  創建多個ReplicaSet對應多個pod,還是會有伸縮問題,且不好維護,如果只創建一個ReplicaSet,讓所有pod共享同一持久卷,但每個pod是使用同一持久卷的不同目錄。
所有pod使用同一數據卷中不同目錄

創建StatefulSet

  所有pod使用同一數據卷中不同目錄要求實例之間相互協作,由於不能在一個pod模板中給所有pod做不同目錄的指定,需要讓pod自己識別選擇一個其他實例沒有使用的目錄,這種共享存儲將會給集群帶來性能問題。
  若每個pod擁有自己的網絡標識,就算失敗了,再恢復時,還是原有的穩定的網絡標識。這就需要使用StatefulSet資源來部署這些服務,這些服務中每個實例都是不可替代的,都有穩定的名字和網絡標識。
StatefulSet部署有狀態服務創建pod

StatefulSet介紹

有狀態服務集群特點

  1. 每個節點都有固定的身份ID,通過ID可以使集群內成員相互發現並通信;
  2. 集群規模比較固定;
  3. 集群內每個節點有狀態,一般會持久化數據到永久存儲中,這樣失敗了的實例可以通過持久化數據再恢復原有狀態;
  4. 磁盤損壞,則某個節點無法正常工作,集群功能將受損受阻;

Statefulset概述

  StatefulSet是k8s從1.4版本引入的PetSet資源對象發展到1.5版本更名而來,在k8s集群中用於部署有狀態服務。為何之前叫PetSet?是因為拿寵物和牛作類比,把應用看做是寵物,給每個實例都起一個名字,在寵物店里,若一個寵物死掉,我們買不到一只一模一樣的,用戶肯定會察覺到差異,若要代替這只寵物,我們必須找到一只屬性及行為和之前完全一致的寵物,同樣的,對於應用而言,我們需要找到狀態和標識和之前一致的實例來代替之前故障實例。

StatefulSet特點

  1. StatefulSet中每個Pod有穩定、唯一的網絡標識(用於發現集群內其他成員),Pod名稱由StatefulSet名+有序數字組成,如ZK服務對應的StatefulSet名為test-zk,副本數為3,則第一個pod名為test-zk-0,第二個pod名稱為test-zk-1,第三個pod名稱為test-zk-2。
  2. StatefulSet所控制的pod副本啟停順序是有序的。
  3. StatefulSet中的Pod采用穩定的持久化存儲卷(PV或者PVC實現),刪除pod時,默認不會刪除與StatefulSet相關的存儲卷。
  4. StatefulSet需要和Headless Service(沒有Cluster IP的Service)進行配合,一般在StatefulSet中指定spec.serviceName的名稱與Service資源中的metadata.name保持一致。

穩定的網絡標識

pod名有序

  StatefulSet部署有狀態應用時,創建出的每個pod都有命名規則,pod名是由StatefulSet名+有序數字組成,每個pod都有一個從0開始的順序索引,這個順序索引體現在pod名稱、主機名以及pod對應的固定存儲上(pvc名稱同樣會有順序索引)。如:3節點的zk的StatefulSet集群對應的StatefulSet名稱為test-zk-ss,則對應的3個pod名稱為test-zk-ss-0,test-zk-ss-1,test-zk-ss-2。

pvc名有序

  如果k8s集群中沒有StorageClass的動態存儲卷,我們也可以提前手動創建多個PV、PVC,手動創建的PVC名稱必須符合之后創建的StatefulSet命名規則:$(volumeClaimTemplates.name)-$(pod_name),如Statefulset控制的3個pod對應名稱為test-zk-ss-0,test-zk-ss-1,test-zk-ss-2,volumeClaimTemplates.name=test-pvc,則自動創建出來的pvc名稱分別為:test-pvc-test-zk-ss-0、test-pvc-test-zk-ss-1、test-pvc-test-zk-ss-2。
StatefulSet創建穩定網絡標識的pod

Headless Service

  Headless Service是沒有Cluster IP的Service(與普通Service的區別),在Headless Service中可以看到spec.ClusterIP=None
  若解析Headless Service的DNS域名,返回的是該Service對應的全部pod的Endpoint列表,StatefulSet在Headless Service基礎上為StatefulSet控制的每個pod實例創建DNS域名,格式為$(pod_name).$(headless_service_name),全限定域名為:FQDN:$(pod_name).$(headless_service_name).$(namespace_name).svc.cluster.local
  如:3節點的zk的StatefulSet集群對應的StatefulSet名稱為test-zk-ss,Headless Service名稱為test-zk-svc,則StatefulSet控制的3個pod對應DNS分別為:test-zk-ss-0.test-zk-svc,test-zk-ss-1.test-zk-svc,test-zk-ss-2.test-zk-svc。若命名空間名稱為test-ns,則3個pod對應的FQDN分別為test-zk-ss-0.test-zk-svc.test-ns.svc.cluster.local,test-zk-ss-1.test-zk-svc.test-ns.svc.cluster.local,test-zk-ss-2.test-zk-svc.test-ns.svc.cluster.local。

StatefulSet運行原理

pod啟停過程

  StatefulSet控制的pod啟停過程,類似於擴縮容過程。
  假設有N個副本數。
  啟動時,先啟動pod序號為0的,然后依次遞增至N-1,操作第N個pod時,前N-1個pod已經是運行並准備好的狀態(Running狀態)。
StatefulSet資源對象的pod啟動順序
  停止時,先停止pod序號為最大的N-1,然后依次遞減至0,操作第N-1個pod時,第N個pod已經是停止狀態。
StatefulSet資源對象的pod停止順序

重啟pod流程

  我們先看一下ReplicaSet管理的一個pod如果消失時,如何重啟一個新的pod來替換舊的。
ReplicaSet重啟pod無法保持一致的標識

  當一個StatefulSet管理的一個pod因為發生故障或被人為刪除而消失后,StatefulSet可以保證再去重啟一個新的pod實例去替換它,這個新pod實例與之前的pod保持完全一致的行為(pod名、主機名)。
StatefulSet重啟pod保持一致的標識

  我們可以從ReplicaSet和StatefulSet重啟pod替換消失pod的過程中看出兩種過程中的標識是很明顯的差別,也是無狀態和有狀態的差異。

pod擴縮容過程

pod擴容過程

  假設StatefulSet名稱為A,從副本數1擴容到3。擴容一個StatefulSet時會使用下一個還沒有使用到的順序索引值進行新pod實例的創建,依次遞增1。
StatefulSet擴容過程

pod縮容過程

  假設StatefulSet名稱為A,從副本數3縮容到1。縮容一個StatefulSet時,StatefulSet是明確知道先刪除最高索引值的實例,縮容刪除哪個pod是可控預知的,而ReplicaSet是不知道會刪除哪個實例。
  由於StatefulSet縮容時是從高索引挨個刪除,每次只會操作一個pod實例,所以有狀態應用的縮容過程很慢。
StatefulSet縮容過程

StatefulSet的at-most-one語義

  在副本數縮容再擴容時,如果k8s沒有保障機制,很容易出現正在縮容的pod還在運行,又新建一個一樣標識的pod進行pvc綁定,這會帶來問題。
  對於ReplicaSet的pod來說,會以一個隨機的標識來創建pod,不會存在兩個相同標識的進程同時運行。而StatefulSet是必須在准確確認一個pod不再運行后,才會去創建替換的pod,從而保證兩個擁有相同標記和綁定相同PVC的有狀態的pod實例不會同時運行,這便是at-most-one的語義。

StatefulSet提供穩定的獨立存儲

  一個有狀態的pod需要有自己專屬的存儲,該pod被重新調度室,新的pod與舊pod保持一致的標識,且新的pod實例掛載相同的存儲。

持久卷聲明模板

如何在同一個pod模板中為所有pod實例關聯不同的持久卷?
  Statefulset在pod模板中添加了卷聲明模板,自動創建的pvc名稱將會符合規則:$(volumeClaimTemplates.metadata.name).$(pod_name)
StatefulSet為每個pod提供穩定的獨立存儲

持久卷創建和刪除

  當StatefulSet增加一個副本時,會創建對應的pvc持久卷聲明。創建N個副本,就會有N個PVC與之對應。
  當StatefulSet減少副本時,會從高索引值的pod名開始刪除pod,但是PVC不會被刪除。如果需要釋放特定的持久卷,需要手動刪除對應的持久卷聲明
  當先縮容,再擴容時,由於舊pod對應的pvc不會被自動刪除,擴容重建的pod實例會綁定到對應序號的pvc上。
StatefulSet縮容再擴容時重新掛載pvc
  從上圖我們可以看出,StatefulSet A先從副本2縮容為副本1,對應的Pod A-1會自動刪除,但是PVC A-1仍然保留不刪除。當StatefulSet A從副本1擴容到副本2時,自動創建新的Pod A-1與舊pod保持一致的標識,PVC A-1被重新掛載到Pod A-1上。

StatefulSet使用命令

假設某個應用的StatefulSet的yaml模板為test-zk-ss.yaml,StatefulSet名稱為test-zk-ss,副本數為3個。更新后的模板為test-zk-ss-new.yaml

創建

基於模板創建
kubectl create -f test-zk-ss.yaml

刪除

  • 基於模板刪除:
    kubectl delete -f test-zk-ss.yaml
  • 基於名稱刪除:
    kubectl delete statefulset test-zk-ss

更新

  • 基於模板更新:
    kubectl apply -f test-zk-ss-new.yaml
  • 基於名稱更新:
    kubectl edit statefulset test-zk-ss

查詢

  • 基於模板查看:
    kubectl get statefulset test-zk-ss -o yaml
  • 基於名稱查看:
    kubectl describe statefulset test-zk-ss

Q&A

k8s中無狀態服務和有狀態服務部署的區別?

無狀態

  1. pod命名:pod名由資源名+隨機的字符串組成;
  2. 數據存儲:多個實例pod可以共享相同的持久化數據,存儲不是必要條件;
  3. 擴縮容:可以隨意擴縮容某個pod,不會指定某個pod進行擴縮容;
  4. 啟停順序:因為pod名的序號是隨機串,無啟停順序之分;
  5. 無狀態k8s資源:ReplicaSet、ReplicationController、Deployment、DaemonSet、Job等資源;
  6. 無狀態服務:tomcat、nginx等;


有狀態
這里假設有N個pod;

  1. pod命名:pod名由statefulset資源名+有序的數字組成(0,1,2...N-1),且pod有特定的網絡標識;
  2. 數據存儲:有狀態的服務對應實例需要有自己的獨立持久卷存儲;
  3. 擴縮容:擴縮容不可隨意,縮容是從數字最大的開始遞減,擴容是在原有pod序號基礎上遞增1。
  4. 啟停順序:pod啟停是有順序的,啟動時,先啟動pod序號為0的,然后依次遞增至N-1;停止時,先停止pod序號為最大的N-1,然后依次遞減至0;
  5. 有狀態k8s資源:StatefulSet資源;
  6. 有狀態服務:Kafka、ZooKeeper、MySql、MongoDB以及一些需要保存日志的應用等服務;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM