關注微信公衆號:CodingTechWork,一塊兒學習進步。nginx
咱們都知道在k8s集羣中,Deployment是用來部署無狀態的服務,那有狀態的服務是用什麼資源對象來部署呢?無狀態和有狀態服務部署的區別是什麼?有狀態的pod確定須要獨立的存儲卷,這樣才能保證故障後尋找數據就地恢復原狀態,那如何實現多個pod擁有本身獨立存儲卷?下面咱們來看看如何演進方案。後端
手動建立多個pod,每一個pod使用一個獨立的持久卷聲明,可是須要咱們手動管理這些pod,當發生故障後,須要從新手動建立這些pod,從而保證有狀態恢復。
tomcat
手動建立pod,確定不便於維護,咱們在每一個pod的上一層來操做,建立多個ReplicaSet,每一個ReplicaSet的副本數設置爲1,這樣pod和ReplicaSet是一一對應的,每一個ReplicaSet的pod模板都關聯一個獨立的持久卷聲明。這種能夠達到某個節點故障或pod誤刪時自動從新調度建立pod的效果,可是對於伸縮副本時,又須要手動建立或刪除ReplicaSet,仍是達不到一次性建立、更新、刪除後,後期自動調度的效果。
微信
建立多個ReplicaSet對應多個pod,仍是會有伸縮問題,且很差維護,若是隻建立一個ReplicaSet,讓全部pod共享同一持久卷,但每一個pod是使用同一持久卷的不一樣目錄。
網絡
全部pod使用同一數據卷中不一樣目錄要求實例之間相互協做,因爲不能在一個pod模板中給全部pod作不一樣目錄的指定,須要讓pod本身識別選擇一個其餘實例沒有使用的目錄,這種共享存儲將會給集羣帶來性能問題。
若每一個pod擁有本身的網絡標識,就算失敗了,再恢復時,仍是原有的穩定的網絡標識。這就須要使用StatefulSet資源來部署這些服務,這些服務中每一個實例都是不可替代的,都有穩定的名字和網絡標識。
app
StatefulSet是k8s從1.4版本引入的PetSet
資源對象發展到1.5版本改名而來,在k8s集羣中用於部署有狀態服務
。爲什麼以前叫PetSet?是由於拿寵物和牛做類比,把應用看作是寵物,給每一個實例都起一個名字,在寵物店裏,若一個寵物死掉,咱們買不到一隻如出一轍的,用戶確定會察覺到差別,若要代替這隻寵物,咱們必須找到一隻屬性及行爲和以前徹底一致的寵物,一樣的,對於應用而言,咱們須要找到狀態和標識和以前一致的實例來代替以前故障實例。less
Headless Service
(沒有Cluster IP的Service)進行配合,通常在StatefulSet中指定spec.serviceName
的名稱與Service資源中的metadata.name
保持一致。 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。性能
若是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。
學習
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啓停過程,相似於擴縮容過程。
假設有N個副本數。
啓動時,先啓動pod序號爲0的,而後依次遞增至N-1,操做第N個pod時,前N-1個pod已是運行並準備好的狀態(Running狀態)。
中止時,先中止pod序號爲最大的N-1,而後依次遞減至0,操做第N-1個pod時,第N個pod已是中止狀態。
咱們先看一下ReplicaSet管理的一個pod若是消失時,如何重啓一個新的pod來替換舊的。
當一個StatefulSet管理的一個pod由於發生故障或被人爲刪除而消失後,StatefulSet能夠保證再去重啓一個新的pod實例去替換它,這個新pod實例與以前的pod保持徹底一致的行爲(pod名、主機名)。
咱們能夠從ReplicaSet和StatefulSet重啓pod替換消失pod的過程當中看出兩種過程當中的標識是很明顯的差異,也是無狀態和有狀態的差別。
假設StatefulSet名稱爲A,從副本數1擴容到3。擴容一個StatefulSet時會使用下一個尚未使用到的順序索引值進行新pod實例的建立,依次遞增1。
假設StatefulSet名稱爲A,從副本數3縮容到1。縮容一個StatefulSet時,StatefulSet是明確知道先刪除最高索引值的實例,縮容刪除哪一個pod是可控預知的,而ReplicaSet是不知道會刪除哪一個實例。
因爲StatefulSet縮容時是從高索引挨個刪除,每次只會操做一個pod實例,因此有狀態應用的縮容過程很慢。
在副本數縮容再擴容時,若是k8s沒有保障機制,很容易出現正在縮容的pod還在運行,又新建一個同樣標識的pod進行pvc綁定,這會帶來問題。
對於ReplicaSet的pod來講,會以一個隨機的標識來建立pod,不會存在兩個相同標識的進程同時運行。而StatefulSet是必須在準確確認一個pod再也不運行後,纔會去建立替換的pod
,從而保證兩個擁有相同標記和綁定相同PVC的有狀態的pod實例不會同時運行,這即是at-most-one
的語義。
一個有狀態的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的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
無狀態:
有狀態
這裏假設有N個pod;