這篇文章主要講解了“基于Kubernetes和OpenKruise的方法是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“基于Kubernetes和OpenKruise的方法是什么”吧!
在云原生逐漸盛行的現在,不可變基礎設施的理念已經逐漸深入人心。不可變基礎設施最早是由Chad Fowler于2013年提出的,其核心思想為任何基礎設施的實例一旦創建之后變成為只讀狀態,如需要修改和升級,則使用新的實例進行替換。這一理念的指導下,實現了運行實例的一致,因此在提升發布效率、彈性伸縮、升級回滾方面體現出了無與倫比的優勢。
kubernetes是不可變基礎設施理念的一個極佳實踐平臺。Pod作為k8s的最小單元,承擔了應用實例這一角色。通過ReplicaSet從而對Pod的副本數進行控制,從而實現Pod的彈性伸縮。而進行更新時,Deployment通過控制兩個ReplicaSet的副本數此消彼長,從而進行實例的整體替換,實現升級和回滾操作。
我們進一步思考,我們是否需要將Pod作為一個完全不可變的基礎設施實例呢?其實在kubernetes本身,已經提供了一個替換image的功能,來實現Pod不變的情況下,通過更換image字段,實現Container的替換。這樣的優勢在于無需重新創建Pod,即可實現升級,直接的優勢在于免去了重新調度等的時間,使得容器可以快速啟動。
從這個思路延伸開來,那么我們其實可以將Pod和Container分為兩層來看。將Container作為不可變的基礎設施,確保應用實例的完整替換;而將Pod看為可變的基礎設施,可以進行動態的改變,亦即可變層。
對于應用的升級變化種類,我們來進行一下分類討論,將其分為以下幾類:
升級變化類型 | 說明 |
---|---|
規格的變化 | cpu、內存等資源使用量的修改 |
配置的變化 | 環境變量、配置文件等的修改 |
鏡像的變化 | 代碼修改后鏡像更新 |
健康檢查的變化 | readinessProbe、livenessProbe配置的修改 |
其他變化 | 調度域、標簽修改等其他修改 |
(滑動查看完整表格)
針對不同的變化類型,我們做過一次抽樣調查統計,可以看到下圖的一個統計結果。
在一次升級變化中如果含有多個變化,則統計為多次。
可以看到支持鏡像的替換可以覆蓋一半左右的的升級變化,但是仍然有相當多的情況下導致不得不重新創建Pod。這點來說,不是特別友好。所以我們做了一個設計,將對于Pod的變化分為了三種Dynamic,Rebuild,Static三種。
修改類型 | 修改類型說明 | 修改舉例 | 對應變化類型 |
---|---|---|---|
Dynamic 動態修改 | Pod不變,容器無需重建 | 修改了健康檢查端口 | 健康檢查的變化 |
Rebuild 原地更新 | Pod不變,容器需要重新創建 | 更新了鏡像、配置文件或者環境變量 | 鏡像的變化,配置的變化 |
Static 靜態修改 | Pod需要重新創建 | 修改了容器規格 | 規格的變化 |
(滑動查看完整表格)
這樣動態修改和原地更新的方式可以覆蓋90%以上的升級變化。在Pod不變的情況下帶來的收益也是顯而易見的。
減少了調度、網絡創建等的時間。
由于同一個應用的鏡像大部分層都是復用的,大大縮短了鏡像拉取的時間。
資源鎖定,防止在集群資源緊缺時由于出讓資源重新創建進入調度后,導致資源被其他業務搶占而無法運行。
IP不變,對于很多有狀態的服務十分友好。
那么如何來實現Dynamic和Rebuild更新呢?這里需要對kubernetes進行一下定制。
liveness和readiness的動態修改支持相對來說較為簡單,主要修改點在與prober_manager中增加了UpdatePod函數,用以判斷當liveness或者readiness的配置改變時,停止原先的worker,重新啟動新的worker。而后將UpdatePod嵌入到kubelet的HandlePodUpdates的流程中即可。
func (m *manager) UpdatePod(pod *v1.Pod) {
m.workerLock.Lock()
defer m.workerLock.Unlock()
key := probeKey{podUID: pod.UID}
for _, c := range pod.Spec.Containers {
key.containerName = c.Name
{
key.probeType = readiness
worker, ok := m.workers[key]
if ok {
if c.ReadinessProbe == nil {
//readiness置空了,原worker停止
worker.stop()
} else if !reflect.DeepEqual(*worker.spec, *c.ReadinessProbe) {
//readiness配置改變了,原worker停止
worker.stop()
}
}
if c.ReadinessProbe != nil {
if !ok || (ok && !reflect.DeepEqual(*worker.spec, *c.ReadinessProbe)) {
//readiness配置改變了,啟動新的worker
w := newWorker(m, readiness, pod, c)
m.workers[key] = w
go w.run()
}
}
}
{
//liveness與readiness相似
......
}
}
}
原地更新定制
kubernetes原生支持了image的修改,對于env和volume的修改是未做支持的。因此我們對env和volume也支持了修改功能,以便其可以進行環境變量和配置文件的替換。這里利用了一個小技巧,就是我們在增加了一個ExcludedHash,用于計算Container內,包含env,volume在內的各項配置。
func HashContainerExcluded(container *v1.Container) uint64 { copyContainer := container.DeepCopy() copyContainer.Resources = v1.ResourceRequirements{} copyContainer.LivenessProbe = &v1.Probe{} copyContainer.ReadinessProbe = &v1.Probe{} hash := fnv.New32a() hashutil.DeepHashObject(hash, copyContainer) return uint64(hash.Sum32())}
這樣當env,volume或者image發生變化時,就可以直接感知到。在SyncPod時,用于在計算computePodActions時,發現容器的相關配置發生了變化,則將該容器進行Rebuild。
func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus) podActions { ...... for idx, container := range pod.Spec.Containers { ...... if expectedHash, actualHash, changed := containerExcludedChanged(&container, containerStatus); changed { // 當env,volume或者image更換時,則重建該容器。 reason = fmt.Sprintf("Container spec exclude resources hash changed (%d vs %d).", actualHash, expectedHash) restart = true } ...... message := reason if restart { //將該容器加入到重建的列表中 message = fmt.Sprintf("%s. Container will be killed and recreated.", message) changes.ContainersToStart = append(changes.ContainersToStart, idx) }...... return changes}
Pod的生命周期
在Pod從調度完成到創建Running中,會有一個ContaienrCreating的狀態用以標識容器在創建中。而原生中當image替換時,先前的一個容器銷毀,后一個容器創建過程中,Pod狀態會一直處于Running,容易有錯誤流量導入,用戶也無法識別此時容器的狀態。
因此我們為原地更新,在ContainerStatus里增加了ContaienrRebuilding的狀態,同時在容器創建成功前Pod的Ready Condition置為False,以便表達容器整在重建中,應用在此期間不可用。利用此標識,可以在此期間方便識別Pod狀態、隔斷流量。
OpenKruise是阿里開源的一個項目,提供了一套在Kubernetes核心控制器之外的擴展 workload 管理和實現。其中Advanced StatefulSet,基于原生 StatefulSet 之上的增強版本,默認行為與原生完全一致,在此之外提供了原地升級、并行發布(最大不可用)、發布暫停等功能。
Advanced StatefulSet中的原地升級即與本文中的Rebuild一致,但是原生只支持替換鏡像。因此我們在OpenKruise的基礎上進行了定制,使其不僅可以支持image的原地更新,也可以支持當env、volume的原地更新以及livenessProbe、readinessProbe的動態更新。這個主要在shouldDoInPlaceUpdate
函數中進行一下判斷即可。這里就不再做代碼展示了。
另外,還有個小坑,就是在pod里為了標識不同的版本,加入了controller-revision-hash值。
[root@xxx ~]# kubectl get pod -n predictor -o yaml predictor-0 apiVersion: v1kind: Podmetadata: labels: controller-revision-hash: predictor-85f9455f6...
一般來說,該值應該只使用hash值作為value就可以了,但是OpenKruise中采用了{sts-name}+{hash}的方式,這帶來的一個小問題就是sts-name
就要因為label value的長度受到限制了。
感謝各位的閱讀,以上就是“基于Kubernetes和OpenKruise的方法是什么”的內容了,經過本文的學習后,相信大家對基于Kubernetes和OpenKruise的方法是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。