真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

揭秘:如何為Kubernetes實(shí)現(xiàn)原地升級(jí)

揭秘:如何為 Kubernetes 實(shí)現(xiàn)原地升級(jí)

作者 | 王思宇(酒祝) 阿里云技術(shù)專家

創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比清原網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式清原網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋清原地區(qū)。費(fèi)用合理售后完善,十多年實(shí)體公司更值得信賴。

參與阿里巴巴云原生文末留言互動(dòng),即有機(jī)會(huì)獲得贈(zèng)書福利及作者答疑!

概念介紹

原地升級(jí)一詞中,“升級(jí)”不難理解,是將應(yīng)用實(shí)例的版本由舊版替換為新版。那么如何結(jié)合 Kubernetes 環(huán)境來理解“原地”呢?

我們先來看看 K8s 原生 workload 的發(fā)布方式。這里假設(shè)我們需要部署一個(gè)應(yīng)用,包括 foo、bar 兩個(gè)容器在 Pod 中。其中,foo 容器第一次部署時(shí)用的鏡像版本是 v1,我們需要將其升級(jí)為 v2 版本鏡像,該怎么做呢?

  • 如果這個(gè)應(yīng)用使用 Deployment 部署,那么升級(jí)過程中 Deployment 會(huì)觸發(fā)新版本 ReplicaSet 創(chuàng)建 Pod,并刪除舊版本 Pod。如下圖所示:

揭秘:如何為 Kubernetes 實(shí)現(xiàn)原地升級(jí)

在本次升級(jí)過程中,原 Pod 對(duì)象被刪除,一個(gè)新 Pod 對(duì)象被創(chuàng)建。新 Pod 被調(diào)度到另一個(gè) Node 上,分配到一個(gè)新的 IP,并把 foo、bar 兩個(gè)容器在這個(gè) Node 上重新拉取鏡像、啟動(dòng)容器。

  • 如果這個(gè)應(yīng)該使用 StatefulSet 部署,那么升級(jí)過程中 StatefulSet 會(huì)先刪除舊 Pod 對(duì)象,等刪除完成后用同樣的名字在創(chuàng)建一個(gè)新的 Pod 對(duì)象。如下圖所示:

揭秘:如何為 Kubernetes 實(shí)現(xiàn)原地升級(jí)

值得注意的是,盡管新舊兩個(gè) Pod 名字都叫 pod-0,但其實(shí)是兩個(gè)完全不同的 Pod 對(duì)象(uid也變了)。StatefulSet 等到原先的 pod-0 對(duì)象完全從 Kubernetes 集群中被刪除后,才會(huì)提交創(chuàng)建一個(gè)新的 pod-0 對(duì)象。而這個(gè)新的 Pod 也會(huì)被重新調(diào)度、分配IP、拉鏡像、啟動(dòng)容器。

  • 而所謂原地升級(jí)模式,就是在應(yīng)用升級(jí)過程中避免將整個(gè) Pod 對(duì)象刪除、新建,而是基于原有的 Pod 對(duì)象升級(jí)其中某一個(gè)或多個(gè)容器的鏡像版本:

揭秘:如何為 Kubernetes 實(shí)現(xiàn)原地升級(jí)

在原地升級(jí)的過程中,我們僅僅更新了原 Pod 對(duì)象中 foo 容器的 image 字段來觸發(fā) foo 容器升級(jí)到新版本。而不管是 Pod 對(duì)象,還是 Node、IP 都沒有發(fā)生變化,甚至 foo 容器升級(jí)的過程中 bar 容器還一直處于運(yùn)行狀態(tài)。

總結(jié):這種只更新 Pod 中某一個(gè)或多個(gè)容器版本、而不影響整個(gè) Pod 對(duì)象、其余容器的升級(jí)方式,被我們稱為 Kubernetes 中的原地升級(jí)。

收益分析

那么,我們?yōu)槭裁匆?Kubernetes 中引入這種原地升級(jí)的理念和設(shè)計(jì)呢?

首先,這種原地升級(jí)的模式極大地提升了應(yīng)用發(fā)布的效率,根據(jù)非完全統(tǒng)計(jì)數(shù)據(jù),在阿里環(huán)境下原地升級(jí)至少比完全重建升級(jí)提升了 80% 以上的發(fā)布速度。這其實(shí)很容易理解,原地升級(jí)為發(fā)布效率帶來了以下優(yōu)化點(diǎn):

  1. 節(jié)省了調(diào)度的耗時(shí),Pod 的位置、資源都不發(fā)生變化;
  2. 節(jié)省了分配網(wǎng)絡(luò)的耗時(shí),Pod 還使用原有的 IP;
  3. 節(jié)省了分配、掛載遠(yuǎn)程盤的耗時(shí),Pod 還使用原有的 PV(且都是已經(jīng)在 Node 上掛載好的);
  4. 節(jié)省了大部分拉取鏡像的耗時(shí),因?yàn)?Node 上已經(jīng)存在了應(yīng)用的舊鏡像,當(dāng)拉取新版本鏡像時(shí)只需要下載很少的幾層 layer。

其次,當(dāng)我們升級(jí) Pod 中一些 sidecar 容器(如采集日志、監(jiān)控等)時(shí),其實(shí)并不希望干擾到業(yè)務(wù)容器的運(yùn)行。但面對(duì)這種場景,Deployment 或 StatefulSet 的升級(jí)都會(huì)將整個(gè) Pod 重建,勢必會(huì)對(duì)業(yè)務(wù)造成一定的影響。而容器級(jí)別的原地升級(jí)變動(dòng)的范圍非??煽?,只會(huì)將需要升級(jí)的容器做重建,其余容器包括網(wǎng)絡(luò)、掛載盤都不會(huì)受到影響。

最后,原地升級(jí)也為我們帶來了集群的穩(wěn)定性和確定性。當(dāng)一個(gè) Kubernetes 集群中大量應(yīng)用觸發(fā)重建 Pod 升級(jí)時(shí),可能造成大規(guī)模的 Pod 飄移,以及對(duì) Node 上一些低優(yōu)先級(jí)的任務(wù) Pod 造成反復(fù)的搶占遷移。這些大規(guī)模的 Pod 重建,本身會(huì)對(duì) apiserver、scheduler、網(wǎng)絡(luò)/磁盤分配等中心組件造成較大的壓力,而這些組件的延遲也會(huì)給 Pod 重建帶來惡性循環(huán)。而采用原地升級(jí)后,整個(gè)升級(jí)過程只會(huì)涉及到 controller 對(duì) Pod 對(duì)象的更新操作和 kubelet 重建對(duì)應(yīng)的容器。

技術(shù)背景

在阿里巴巴內(nèi)部,絕大部分電商應(yīng)用在云原生環(huán)境都統(tǒng)一用原地升級(jí)的方式做發(fā)布,而這套支持原地升級(jí)的控制器就位于 OpenKruise 開源項(xiàng)目中。

也就是說,阿里內(nèi)部的云原生應(yīng)用都是統(tǒng)一使用 OpenKruise 中的擴(kuò)展 workload 做部署管理的,而并沒有采用原生 Deployment/StatefulSet 等。


那么 OpenKruise 是如何實(shí)現(xiàn)原地升級(jí)能力的呢?在介紹原地升級(jí)實(shí)現(xiàn)原理之前,我們先來看一些原地升級(jí)功能所依賴的原生 Kubernetes 功能:

背景 1:Kubelet 針對(duì) Pod 容器的版本管理

每個(gè) Node 上的 Kubelet,會(huì)針對(duì)本機(jī)上所有 Pod.spec.containers 中的每個(gè) container 計(jì)算一個(gè) hash 值,并記錄到實(shí)際創(chuàng)建的容器中。

如果我們修改了 Pod 中某個(gè) container 的 image 字段,kubelet 會(huì)發(fā)現(xiàn) container 的 hash 發(fā)生了變化、與機(jī)器上過去創(chuàng)建的容器 hash 不一致,而后 kubelet 就會(huì)把舊容器停掉,然后根據(jù)最新 Pod spec 中的 container 來創(chuàng)建新的容器。

這個(gè)功能,其實(shí)就是針對(duì)單個(gè) Pod 的原地升級(jí)的核心原理。

背景 2:Pod 更新限制

在原生 kube-apiserver 中,對(duì) Pod 對(duì)象的更新請(qǐng)求有嚴(yán)格的 validation 校驗(yàn)邏輯:

// validate updateable fields:
// 1.  spec.containers[*].image
// 2.  spec.initContainers[*].image
// 3.  spec.activeDeadlineSeconds

簡單來說,對(duì)于一個(gè)已經(jīng)創(chuàng)建出來的 Pod,在 Pod Spec 中只允許修改 containers/initContainers 中的 image 字段,以及 activeDeadlineSeconds 字段。對(duì) Pod Spec 中所有其他字段的更新,都會(huì)被 kube-apiserver 拒絕。

背景 3:containerStatuses 上報(bào)

kubelet 會(huì)在 pod.status 中上報(bào) containerStatuses,對(duì)應(yīng) Pod 中所有容器的實(shí)際運(yùn)行狀態(tài):

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
status:
  containerStatuses:
  - name: nginx
    image: nginx:mainline
    imageID: docker-pullable://nginx@sha256:2f68b99bc0d6d25d0c56876b924ec20418544ff28e1fb89a4c27679a40da811b

絕大多數(shù)情況下,spec.containers[x].image 與 status.containerStatuses[x].image 兩個(gè)鏡像是一致的。

但是也有上述這種情況,kubelet 上報(bào)的與 spec 中的 image 不一致(spec 中是 nginx:latest,但 status 中上報(bào)的是 nginx:mainline)。

這是因?yàn)椋琸ubelet 所上報(bào)的 image 其實(shí)是從 CRI 接口中拿到的容器對(duì)應(yīng)的鏡像名。而如果 Node 機(jī)器上存在多個(gè)鏡像對(duì)應(yīng)了一個(gè) imageID,那么上報(bào)的可能是其中任意一個(gè):

$ docker images | grep nginx
nginx            latest              2622e6cca7eb        2 days ago          132MB
nginx            mainline            2622e6cca7eb        2 days ago

因此,一個(gè) Pod 中 spec 和 status 的 image 字段不一致,并不意味著宿主機(jī)上這個(gè)容器運(yùn)行的鏡像版本和期望的不一致。

背景 4:ReadinessGate 控制 Pod 是否 Ready

在 Kubernetes 1.12 版本之前,一個(gè) Pod 是否處于 Ready 狀態(tài)只是由 kubelet 根據(jù)容器狀態(tài)來判定:如果 Pod 中容器全部 ready,那么 Pod 就處于 Ready 狀態(tài)。

但事實(shí)上,很多時(shí)候上層 operator 或用戶都需要能控制 Pod 是否 Ready 的能力。因此,Kubernetes 1.12 版本之后提供了一個(gè) readinessGates 功能來滿足這個(gè)場景。如下:

apiVersion: v1
kind: Pod
spec:
  readinessGates:
  - conditionType: MyDemo
status:
  conditions:
  - type: MyDemo
    status: "True"
  - type: ContainersReady
    status: "True"
  - type: Ready
    status: "True"

目前 kubelet 判定一個(gè) Pod 是否 Ready 的兩個(gè)前提條件:

  1. Pod 中容器全部 Ready(其實(shí)對(duì)應(yīng)了 ContainersReady condition 為 True);
  2. 如果 pod.spec.readinessGates 中定義了一個(gè)或多個(gè) conditionType,那么需要這些 conditionType 在 pod.status.conditions 中都有對(duì)應(yīng)的 status: “true” 的狀態(tài)。

只有滿足上述兩個(gè)前提,kubelet 才會(huì)上報(bào) Ready condition 為 True。

實(shí)現(xiàn)原理

了解了上面的四個(gè)背景之后,接下來分析一下 OpenKruise 是如何在 Kubernetes 中實(shí)現(xiàn)原地升級(jí)的原理。

1. 單個(gè) Pod 如何原地升級(jí)?

由“背景 1”可知,其實(shí)我們對(duì)一個(gè)存量 Pod 的 spec.containers[x] 中字段做修改,kubelet 會(huì)感知到這個(gè) container 的 hash 發(fā)生了變化,隨即就會(huì)停掉對(duì)應(yīng)的舊容器,并用新的 container 來拉鏡像、創(chuàng)建和啟動(dòng)新容器。

由“背景 2”可知,當(dāng)前我們對(duì)一個(gè)存量 Pod 的 spec.containers[x] 中的修改,僅限于 image 字段。

因此,得出第一個(gè)實(shí)現(xiàn)原理:**對(duì)于一個(gè)現(xiàn)有的 Pod 對(duì)象,我們能且只能修改其中的 spec.containers[x].image 字段,來觸發(fā) Pod 中對(duì)應(yīng)容器升級(jí)到一個(gè)新的 image。

2. 如何判斷 Pod 原地升級(jí)成功?

接下來的問題是,當(dāng)我們修改了 Pod 中的 spec.containers[x].image 字段后,如何判斷 kubelet 已經(jīng)將容器重建成功了呢?

由“背景 3”可知,比較 spec 和 status 中的 image 字段是不靠譜的,因?yàn)楹苡锌赡?status 中上報(bào)的是 Node 上存在的另一個(gè)鏡像名(相同 imageID)。

因此,得出第二個(gè)實(shí)現(xiàn)原理: 判斷 Pod 原地升級(jí)是否成功,相對(duì)來說比較靠譜的辦法,是在原地升級(jí)前先將 status.containerStatuses[x].imageID 記錄下來。在更新了 spec 鏡像之后,如果觀察到 Pod 的 status.containerStatuses[x].imageID 變化了,我們就認(rèn)為原地升級(jí)已經(jīng)重建了容器。

但這樣一來,我們對(duì)原地升級(jí)的 image 也有了一個(gè)要求: 不能用 image 名字(tag)不同、但實(shí)際對(duì)應(yīng)同一個(gè) imageID 的鏡像來做原地升級(jí),否則可能一直都被判斷為沒有升級(jí)成功(因?yàn)?status 中 imageID 不會(huì)變化)。

當(dāng)然,后續(xù)我們還可以繼續(xù)優(yōu)化。OpenKruise 即將開源鏡像預(yù)熱的能力,會(huì)通過 DaemonSet 在每個(gè) Node 上部署一個(gè) NodeImage Pod。通過 NodeImage 上報(bào)我們可以得知 pod spec 中的 image 所對(duì)應(yīng)的 imageID,然后和 pod status 中的 imageID 比較即可準(zhǔn)確判斷原地升級(jí)是否成功。

3. 如何確保原地升級(jí)過程中流量無損?

在 Kubernetes 中,一個(gè) Pod 是否 Ready 就代表了它是否可以提供服務(wù)。因此,像 Service 這類的流量入口都會(huì)通過判斷 Pod Ready 來選擇是否能將這個(gè) Pod 加入 endpoints 端點(diǎn)中。

由“背景 4”可知,從 Kubernetes 1.12+ 之后,operator/controller 這些組件也可以通過設(shè)置 readinessGates 和更新 pod.status.conditions 中的自定義 type 狀態(tài),來控制 Pod 是否可用。

因此,得出第三個(gè)實(shí)現(xiàn)原理: 可以在 pod.spec.readinessGates 中定義一個(gè)叫 InPlaceUpdateReady 的 conditionType。

在原地升級(jí)時(shí):

  1. 先將 pod.status.conditions 中的 InPlaceUpdateReady condition 設(shè)為 “False”,這樣就會(huì)觸發(fā) kubelet 將 Pod 上報(bào)為 NotReady,從而使流量組件(如 endpoint controller)將這個(gè) Pod 從服務(wù)端點(diǎn)摘除;
  2. 再更新 pod spec 中的 image 觸發(fā)原地升級(jí)。

原地升級(jí)結(jié)束后,再將 InPlaceUpdateReady condition 設(shè)為 “True”,使 Pod 重新回到 Ready 狀態(tài)。

另外在原地升級(jí)的兩個(gè)步驟中,第一步將 Pod 改為 NotReady 后,流量組件異步 watch 到變化并摘除端點(diǎn)可能是需要一定時(shí)間的。因此我們也提供優(yōu)雅原地升級(jí)的能力,即通過 gracePeriodSeconds 配置在修改 NotReady 狀態(tài)和真正更新 image 觸發(fā)原地升級(jí)兩個(gè)步驟之間的靜默期時(shí)間。

4. 組合發(fā)布策略

原地升級(jí)和 Pod 重建升級(jí)一樣,可以配合各種發(fā)布策略來執(zhí)行:

  • partition:如果配置 partition 做灰度,那么只會(huì)將 replicas-partition 數(shù)量的 Pod 做原地升級(jí);
  • maxUnavailable:如果配置 maxUnavailable,那么只會(huì)將滿足 unavailable 數(shù)量的 Pod 做原地升級(jí);
  • maxSurge:如果配置 maxSurge 做彈性,那么當(dāng)先擴(kuò)出來 maxSurge 數(shù)量的 Pod 之后,存量的 Pod 仍然使用原地升級(jí);
  • priority/scatter:如果配置了發(fā)布優(yōu)先級(jí)/打散策略,會(huì)按照策略順序?qū)?Pod 做原地升級(jí)。

總結(jié)

如上文所述, OpenKruise 結(jié)合 Kubernetes 原生提供的 kubelet 容器版本管理、readinessGates 等功能,實(shí)現(xiàn)了針對(duì) Pod 的原地升級(jí)能力。

而原地升級(jí)也為應(yīng)用發(fā)布帶來大幅的效率、穩(wěn)定性提升。值得關(guān)注的是,隨著集群、應(yīng)用規(guī)模的增大,這種提升的收益越加明顯。正是這種原地升級(jí)能力,在近兩年幫助了阿里巴巴超大規(guī)模的應(yīng)用容器平穩(wěn)遷移到了基于 Kubernetes 的云原生環(huán)境,而原生 Deployment/StatefulSet 是完全無法在這種體量的環(huán)境下鋪開使用的。(歡迎加入釘釘交流群:23330762)

- 贈(zèng)書福利 -

揭秘:如何為 Kubernetes 實(shí)現(xiàn)原地升級(jí)

6 月 19 日 12:00 前在【阿里巴巴云原生公眾號(hào)】留言區(qū) 提出你的疑問,精選留言點(diǎn)贊第 1 名將免費(fèi)獲得此書,屆時(shí)我們還會(huì)請(qǐng)本文作者針對(duì)留言點(diǎn)贊前 5 名的問題進(jìn)行答疑!

課程推薦

為了更多開發(fā)者能夠享受到 Serverless 帶來的紅利,這一次,我們集結(jié)了 10+ 位阿里巴巴 Serverless 領(lǐng)域技術(shù)專家,打造出最適合開發(fā)者入門的 Serverless 公開課,讓你即學(xué)即用,輕松擁抱云計(jì)算的新范式——Serverless。

點(diǎn)擊即可免費(fèi)觀看課程: https://developer.aliyun.com/learning/roadmap/serverless

“ 阿里巴巴云原生關(guān)注微服務(wù)、Serverless、容器、Service Mesh 等技術(shù)領(lǐng)域、聚焦云原生流行技術(shù)趨勢、云原生大規(guī)模的落地實(shí)踐,做最懂云原生開發(fā)者的公眾號(hào)。”


文章名稱:揭秘:如何為Kubernetes實(shí)現(xiàn)原地升級(jí)
URL地址:http://weahome.cn/article/psodig.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部