天天看點

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

本文主要描述Kubernetes—StatefulSet部署有狀态應用詳解(二十四)

關注微信公衆号:CodingTechWork,一起學習進步。

引言

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

演進

手動建立多個pod

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

1個RS對應1個pod

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

所有pod共享同一個PV

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

建立StatefulSet

  所有pod使用同一資料卷中不同目錄要求執行個體之間互相協作,由于不能在一個pod模闆中給所有pod做不同目錄的指定,需要讓pod自己識别選擇一個其他執行個體沒有使用的目錄,這種共享存儲将會給叢集帶來性能問題。

  若每個pod擁有自己的網絡辨別,就算失敗了,再恢複時,還是原有的穩定的網絡辨別。這就需要使用StatefulSet資源來部署這些服務,這些服務中每個執行個體都是不可替代的,都有穩定的名字和網絡辨別。

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。

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狀态)。

  停止時,先停止pod序号為最大的N-1,然後依次遞減至0,操作第N-1個pod時,第N個pod已經是停止狀态。

重新開機pod流程

  我們先看一下ReplicaSet管理的一個pod如果消失時,如何重新開機一個新的pod來替換舊的。

  當一個StatefulSet管理的一個pod因為發生故障或被人為删除而消失後,StatefulSet可以保證再去重新開機一個新的pod執行個體去替換它,這個新pod執行個體與之前的pod保持完全一緻的行為(pod名、主機名)。

  我們可以從ReplicaSet和StatefulSet重新開機pod替換消失pod的過程中看出兩種過程中的辨別是很明顯的差别,也是無狀态和有狀态的差異。

pod擴縮容過程

pod擴容過程

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

pod縮容過程

  假設StatefulSet名稱為A,從副本數3縮容到1。縮容一個StatefulSet時,StatefulSet是明确知道先删除最高索引值的執行個體,縮容删除哪個pod是可控預知的,而ReplicaSet是不知道會删除哪個執行個體。

  由于StatefulSet縮容時是從高索引挨個删除,每次隻會操作一個pod執行個體,是以有狀态應用的縮容過程很慢。

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增加一個副本時,會建立對應的pvc持久卷聲明。建立N個副本,就會有N個PVC與之對應。

  當StatefulSet減少副本時,會從高索引值的pod名開始删除pod,但是PVC不會被删除。如果需要釋放特定的持久卷,需要

手動删除對應的持久卷聲明

  當先縮容,再擴容時,由于舊pod對應的pvc不會被自動删除,擴容重建的pod執行個體會綁定到對應序号的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以及一些需要儲存日志的應用等服務;

燒不死的鳥就是鳳凰

繼續閱讀