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

技術公衆號:後端技術解憂鋪
關注微信公衆號:CodingTechWork,一塊兒學習進步。nginx

引言

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

演進

手動建立多個pod

  手動建立多個pod,每一個pod使用一個獨立的持久卷聲明,可是須要咱們手動管理這些pod,當發生故障後,須要從新手動建立這些pod,從而保證有狀態恢復。
手動建立多個podtomcat

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部署有狀態服務建立podapp

StatefulSet介紹

有狀態服務集羣特色

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

Statefulset概述

  StatefulSet是k8s從1.4版本引入的PetSet資源對象發展到1.5版本改名而來,在k8s集羣中用於部署有狀態服務。爲什麼以前叫PetSet?是由於拿寵物和牛做類比,把應用看作是寵物,給每一個實例都起一個名字,在寵物店裏,若一個寵物死掉,咱們買不到一隻如出一轍的,用戶確定會察覺到差別,若要代替這隻寵物,咱們必須找到一隻屬性及行爲和以前徹底一致的寵物,一樣的,對於應用而言,咱們須要找到狀態和標識和以前一致的實例來代替以前故障實例。less

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-一、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。spa

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以及一些須要保存日誌的應用等服務;
相關文章
相關標籤/搜索