將有狀態的應用程式部署到Kubernetes是棘手的, StatefulSet使它變得容易得多,但是它們仍然不能解決所有問題,最大的挑戰之一是如何縮小StatefulSet而不將資料留在斷開連接的PersistentVolume成為孤立物件上,在這篇博客中,我將描述該問題和兩種可能的解決方案,
通過StatefulSet創建的每個Pod都有自己的PersistentVolumeClaim(PVC)和PersistentVolume(PV),當按一個副本按比例縮小StatefulSet的大小時,其Pod之一將終止,但關聯的PersistentVolumeClaim和系結到其的PersistentVolume保持不變,在隨后擴大規模時,它們會重新連接到Pod,
Scaling a StatefulSet
現在,想象一下使用StatefulSet部署一個有狀態的應用程式,其資料在其pod中進行磁區,每個實體僅保存和處理一部分資料,當您縮小有狀態應用的規模時,其中一個實體將終止,其資料應重新分配到其余的Pod,如果您不重新分配資料,則在再次進行擴展之前,它仍然不可訪問,
Redistributing data on scale-down
在正常關機期間重新分發資料
您可能會想:“既然Kubernetes支持Pod正常關閉的機制,那么Pod是否可以在關閉程序中簡單地將其資料重新分配給其他實體呢?”事實上,它不能,為什么不這樣做有兩個原因:
- Pod(或更確切地說,其容器)可能會收到除縮容以外的其他原因的終止信號,容器中運行的應用程式不知道為什么終止該程式,因此不知道是否要清空資料,
- 即使該應用程式可以區分是縮容還是由于其他原因而終止,它也需要保證即使經過數小時或數天也可以完成關閉程式, Kubernetes不提供該保證,如果應用程式行程在關閉程序中死掉,它將不會重新啟動,因此也就沒有機會完全分發資料,
因此,相信在正常關閉期間Pod能夠重新分發(或以其他方式處理其所有資料)并不是一個好主意,并且會導致系統非常脆弱,
使用 tear-down 容器?
如果您不是Kubernetes的新手,那么你很可能知道什么是初始化容器,它們在容器的主要容器之前運行,并且必須在主要容器啟動之前全部完成,
如果我們有tear-down容器(類似于init容器),但是在Pod的主容器終止后又會運行,該怎么辦?他們可以在我們的有狀態Pod中執行資料重新分發嗎?

假設tear-down容器能夠確定Pod是否由于縮容而終止,并假設Kubernetes(更具體地說是Kubelet)將確保tear-down容器成功完成(通過在每次回傳非零退出代碼時重新啟動它),如果這兩個假設都成立,我們將擁有一種機制,可確保有狀態的容器始終能夠按比例縮小規模重新分配其資料,
但是?
可悲的是,當tear-down容器本身發生瞬態錯誤,并且一次或多次重新啟動容器最終使它成功完成時,像上述的tear-down容器機制將只處理那些情況,但是,在tear-down程序中托管Pod的集群節點死掉的那些不幸時刻又如何呢?顯然,該程序無法完成,因此無法訪問資料,
現在很明顯,我們不應該在Pod關閉時執行資料重新分配,相反,我們應該創建一個新的Pod(可能安排在一個完全不同的集群節點上)以執行重新分發程序,
這為我們帶來了以下解決方案:
縮小StatefulSet時,必須創建一個新的容器并將其系結到孤立的PersistentVolumeClaim,我們稱其為“drain pod”,因為它的作業是將資料重新分發到其他地方(或以其他方式處理),Pod必須有權訪問孤立的資料,并且可以使用它做任何想做的事情,由于每個應用程式的重新分發程式差異很大,因此新的容器應該是完全可配置的-用戶應該能夠在drain Pod內運行他們想要的任何容器,
StatefulSet Drain Controller
由于StatefulSet控制器當前尚不提供此功能,因此我們可以實作一個額外的控制器,其唯一目的是處理StatefulSet縮容,我最近實作了這種控制器的概念驗證,您可以在GitHub上找到源代碼:
luksa/statefulset-scaledown-controllergithub.com
下面我們解釋一下它是如何作業的,
在將控制器部署到Kubernetes集群后,您只需在StatefulSet清單中添加注釋,即可將drain容器模板添加到任何StatefulSet中,這是一個例子:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: datastore
annotations:
statefulsets.kubernetes.io/drainer-pod-template: |
{
"metadata": {
"labels": {
"app": "datastore-drainer"
}
},
"spec": {
"containers": [
{
"name": "drainer",
"image": "my-drain-container",
"volumeMounts": [
{
"name": "data",
"mountPath": "/var/data"
}
]
}
]
}
}
spec:
...
該模板與StatefulSet中的主要Pod模板沒有太大區別,只不過它是通過注釋定義的,您可以像平常一樣部署和擴展StatefulSet,
當控制器檢測到按比例縮小了StatefulSet時,它將根據指定的模板創建新的drain容器,并確保將其系結到PersistentVolumeClaim,該PersistentVolumeClaim先前已系結至因按比例縮小而洗掉的有狀態容器,
Drain容器獲得與已洗掉的有狀態容器相同的身份(即名稱和主機名),這樣做有兩個原因:
- 一些有狀態的應用程式需要穩定的身份-這也可能在資料重新分發程序中適用,
- 如果在執行drain程序時再次擴容StatefulSet,則這將阻止StatefulSet控制器創建重復的容器并將其附加到同一PVC,
如果drain pod或其主機節點崩潰,則drain pod將重新安排到另一個節點上,在該節點上可以重試/恢復其操作,Drain pod完成后, Pod和PVC將被洗掉,備份StatefulSet時,將創建一個新的PVC,
示例
首先部署drain控制器:
$ kubectl apply -f https://raw.githubusercontent.com/luksa/statefulset-drain-controller/master/artifacts/cluster-scoped.yaml
接著部署示例StatefulSet:
$ kubectl apply -f https://raw.githubusercontent.com/luksa/statefulset-drain-controller/master/example/statefulset.yaml
這將運行三個有狀態的Pod,將StatefulSet縮小為兩個時,您會看到其中一個Pod開始終止,然后,洗掉Pod后,drain控制器將立即創建一個具有相同名稱的新drain Pod:
$ kubectl scale statefulset datastore --replicas 2
statefulset.apps/datastore scaled
$ kubectl get po
NAME READY STATUS RESTARTS AGE
datastore-0 1/1 Running 0 3m
datastore-1 1/1 Running 0 2m
datastore-2 1/1 Terminating 0 49s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
datastore-0 1/1 Running 0 3m
datastore-1 1/1 Running 0 3m
datastore-2 1/1 Running 0 5s <-- the drain pod
當drain pod 完成其作業時,控制器將其洗掉并洗掉PVC:
$ kubectl get po
NAME READY STATUS RESTARTS AGE
datastore-0 1/1 Running 0 3m
datastore-1 1/1 Running 0 3m
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ...
data-datastore-0 Bound pvc-57224b8f-... 1Mi ...
data-datastore-1 Bound pvc-5acaf078-... 1Mi ...
控制器的另一個好處是它可以釋放PersistentVolume,因為它不再受PersistentVolumeClaim約束,如果您的集群在云環境中運行,則可以降低存盤成本,
總結
請記住,這僅是概念驗證,要成為StatefulSet縮容問題的正確解決方案,需要進行大量作業和測驗,理想情況下,Kubernetes StatefulSet控制器本身將支持這樣的運行drain容器,而不是需要一個與原始控制器競爭的附加控制器(當您縮容并立即再次擴容時),
通過將此功能直接集成到Kubernetes中,可以在StatefulSet規范中用常規欄位替換注釋,因此它將具有模板,volumeClaimTemplates和rainePodTemplate,與使用注釋相比,一切都變得更好了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/243211.html
標籤:其他
下一篇:sql sever 2008中photo列是image型別,存的是二進制資料,現在想在vb6.0中通過image顯示出圖片,但總是提示我檔案打開途徑錯誤
