您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Kubernetes 服務(wù)部署中如何提高服務(wù)可用性,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
怎樣提高我們部署服務(wù)的可用性呢?K8S 設(shè)計(jì)本身就考慮到了各種故障的可能性,并提供了一些自愈機(jī)制以提高系統(tǒng)的容錯(cuò)性,但有些情況還是可能導(dǎo)致較長(zhǎng)時(shí)間不可用,拉低服務(wù)可用性的指標(biāo)。下面將結(jié)合生產(chǎn)實(shí)踐經(jīng)驗(yàn),為大家提供一些最佳實(shí)踐來(lái)最大化的提高服務(wù)可用性。
K8S 的設(shè)計(jì)就是假設(shè)節(jié)點(diǎn)是不可靠的。節(jié)點(diǎn)越多,發(fā)生軟硬件故障導(dǎo)致節(jié)點(diǎn)不可用的幾率就越高,所以我們通常需要給服務(wù)部署多個(gè)副本,根據(jù)實(shí)際情況調(diào)整 replicas 的值,如果值為 1 就必然存在單點(diǎn)故障,如果大于 1 但所有副本都調(diào)度到同一個(gè)節(jié)點(diǎn)了,那還是有單點(diǎn)故障,有時(shí)候還要考慮到災(zāi)難,比如整個(gè)機(jī)房不可用。
所以我們不僅要有合理的副本數(shù)量,還需要讓這些不同副本調(diào)度到不同的拓?fù)溆?節(jié)點(diǎn)、可用區(qū)),打散調(diào)度以避免單點(diǎn)故障,這個(gè)可以利用 Pod 反親和性來(lái)做到,反親和主要分強(qiáng)反親和與弱反親和兩種。更多親和與反親和信息可參考官方文檔Affinity and anti-affinity。
先來(lái)看個(gè)強(qiáng)反親和的示例,將 DNS 服務(wù)強(qiáng)制打散調(diào)度到不同節(jié)點(diǎn)上:
affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: k8s-app operator: In values: - kube-dns topologyKey: kubernetes.io/hostname
labelSelector.matchExpressions
寫(xiě)該服務(wù)對(duì)應(yīng) pod 中 labels 的 key 與 value,因?yàn)?Pod 反親和性是通過(guò)判斷 replicas 的 pod label 來(lái)實(shí)現(xiàn)的。
topologyKey
指定反親和的拓?fù)溆颍垂?jié)點(diǎn) label 的 key。這里用的 kubernetes.io/hostname
表示避免 pod 調(diào)度到同一節(jié)點(diǎn),如果你有更高的要求,比如避免調(diào)度到同一個(gè)可用區(qū),實(shí)現(xiàn)異地多活,可以用 failure-domain.beta.kubernetes.io/zone
。通常不會(huì)去避免調(diào)度到同一個(gè)地域,因?yàn)橐话阃粋€(gè)集群的節(jié)點(diǎn)都在一個(gè)地域,如果跨地域,即使用專(zhuān)線時(shí)延也會(huì)很大,所以 topologyKey
一般不至于用 failure-domain.beta.kubernetes.io/region
。
requiredDuringSchedulingIgnoredDuringExecution
調(diào)度時(shí)必須滿足該反親和性條件,如果沒(méi)有節(jié)點(diǎn)滿足條件就不調(diào)度到任何節(jié)點(diǎn) (Pending)。
如果不用這種硬性條件可以使用 preferredDuringSchedulingIgnoredDuringExecution
來(lái)指示調(diào)度器盡量滿足反親和性條件,即弱反親和性,如果實(shí)在沒(méi)有滿足條件的,只要節(jié)點(diǎn)有足夠資源,還是可以讓其調(diào)度到某個(gè)節(jié)點(diǎn),至少不會(huì) Pending。
我們?cè)賮?lái)看個(gè)弱反親和的示例:
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: k8s-app operator: In values: - kube-dns topologyKey: kubernetes.io/hostname
注意到了嗎?相比強(qiáng)反親和有些不同哦,多了一個(gè) weight
,表示此匹配條件的權(quán)重,而匹配條件被挪到了 podAffinityTerm
下面。
有時(shí)候我們需要對(duì)節(jié)點(diǎn)進(jìn)行維護(hù)或進(jìn)行版本升級(jí)等操作,操作之前需要對(duì)節(jié)點(diǎn)執(zhí)行驅(qū)逐 (kubectl drain),驅(qū)逐時(shí)會(huì)將節(jié)點(diǎn)上的 Pod 進(jìn)行刪除,以便它們漂移到其它節(jié)點(diǎn)上,當(dāng)驅(qū)逐完畢之后,節(jié)點(diǎn)上的 Pod 都漂移到其它節(jié)點(diǎn)了,這時(shí)我們就可以放心的對(duì)節(jié)點(diǎn)進(jìn)行操作了。
有一個(gè)問(wèn)題就是,驅(qū)逐節(jié)點(diǎn)是一種有損操作,驅(qū)逐的原理:
封鎖節(jié)點(diǎn) (設(shè)為不可調(diào)度,避免新的 Pod 調(diào)度上來(lái))。
將該節(jié)點(diǎn)上的 Pod 刪除。
ReplicaSet 控制器檢測(cè)到 Pod 減少,會(huì)重新創(chuàng)建一個(gè) Pod,調(diào)度到新的節(jié)點(diǎn)上。
這個(gè)過(guò)程是先刪除,再創(chuàng)建,并非是滾動(dòng)更新,因此更新過(guò)程中,如果一個(gè)服務(wù)的所有副本都在被驅(qū)逐的節(jié)點(diǎn)上,則可能導(dǎo)致該服務(wù)不可用。
我們?cè)賮?lái)下什么情況下驅(qū)逐會(huì)導(dǎo)致服務(wù)不可用:
服務(wù)存在單點(diǎn)故障,所有副本都在同一個(gè)節(jié)點(diǎn),驅(qū)逐該節(jié)點(diǎn)時(shí),就可能造成服務(wù)不可用。
服務(wù)沒(méi)有單點(diǎn)故障,但剛好這個(gè)服務(wù)涉及的 Pod 全部都部署在這一批被驅(qū)逐的節(jié)點(diǎn)上,所以這個(gè)服務(wù)的所有 Pod 同時(shí)被刪,也會(huì)造成服務(wù)不可用。
服務(wù)沒(méi)有單點(diǎn)故障,也沒(méi)有全部部署到這一批被驅(qū)逐的節(jié)點(diǎn)上,但驅(qū)逐時(shí)造成這個(gè)服務(wù)的一部分 Pod 被刪,短時(shí)間內(nèi)服務(wù)的處理能力下降導(dǎo)致服務(wù)過(guò)載,部分請(qǐng)求無(wú)法處理,也就降低了服務(wù)可用性。
針對(duì)第一點(diǎn),我們可以使用前面講的反親和性來(lái)避免單點(diǎn)故障。
針對(duì)第二和第三點(diǎn),我們可以通過(guò)配置 PDB (PodDisruptionBudget) 來(lái)避免所有副本同時(shí)被刪除,驅(qū)逐時(shí) K8S 會(huì) "觀察" nginx 的當(dāng)前可用與期望的副本數(shù),根據(jù)定義的 PDB 來(lái)控制 Pod 刪除速率,達(dá)到閥值時(shí)會(huì)等待 Pod 在其它節(jié)點(diǎn)上啟動(dòng)并就緒后再繼續(xù)刪除,以避免同時(shí)刪除太多的 Pod 導(dǎo)致服務(wù)不可用或可用性降低,下面給出兩個(gè)示例。
示例一 (保證驅(qū)逐時(shí) nginx 至少有 90% 的副本可用):
apiVersion: policy/v1beta1kind: PodDisruptionBudgetmetadata: name: zk-pdbspec: minAvailable: 90% selector: matchLabels: app: zookeeper
示例二 (保證驅(qū)逐時(shí) zookeeper 最多有一個(gè)副本不可用,相當(dāng)于逐個(gè)刪除并等待在其它節(jié)點(diǎn)完成重建):
apiVersion: policy/v1beta1kind: PodDisruptionBudgetmetadata: name: zk-pdbspec: maxUnavailable: 1 selector: matchLabels: app: zookeeper
解決了服務(wù)單點(diǎn)故障和驅(qū)逐節(jié)點(diǎn)時(shí)導(dǎo)致的可用性降低問(wèn)題后,我們還需要考慮一種可能導(dǎo)致可用性降低的場(chǎng)景,那就是滾動(dòng)更新。為什么服務(wù)正常滾動(dòng)更新也可能影響服務(wù)的可用性呢?別急,下面我來(lái)解釋下原因。
假如集群內(nèi)存在服務(wù)間調(diào)用:
當(dāng) server 端發(fā)生滾動(dòng)更新時(shí):
發(fā)生兩種尷尬的情況:
舊的副本很快銷(xiāo)毀,而 client 所在節(jié)點(diǎn) kube-proxy 還沒(méi)更新完轉(zhuǎn)發(fā)規(guī)則,仍然將新連接調(diào)度給舊副本,造成連接異常,可能會(huì)報(bào) "connection refused" (進(jìn)程停止過(guò)程中,不再接受新請(qǐng)求) 或 "no route to host" (容器已經(jīng)完全銷(xiāo)毀,網(wǎng)卡和 IP 已不存在)。
新副本啟動(dòng),client 所在節(jié)點(diǎn) kube-proxy 很快 watch 到了新副本,更新了轉(zhuǎn)發(fā)規(guī)則,并將新連接調(diào)度給新副本,但容器內(nèi)的進(jìn)程啟動(dòng)很慢 (比如 Tomcat 這種 java 進(jìn)程),還在啟動(dòng)過(guò)程中,端口還未監(jiān)聽(tīng),無(wú)法處理連接,也造成連接異常,通常會(huì)報(bào) "connection refused" 的錯(cuò)誤。
針對(duì)第一種情況,可以給 container 加 preStop,讓 Pod 真正銷(xiāo)毀前先 sleep 等待一段時(shí)間,等待 client 所在節(jié)點(diǎn) kube-proxy 更新轉(zhuǎn)發(fā)規(guī)則,然后再真正去銷(xiāo)毀容器。這樣能保證在 Pod Terminating 后還能繼續(xù)正常運(yùn)行一段時(shí)間,這段時(shí)間如果因?yàn)?client 側(cè)的轉(zhuǎn)發(fā)規(guī)則更新不及時(shí)導(dǎo)致還有新請(qǐng)求轉(zhuǎn)發(fā)過(guò)來(lái),Pod 還是可以正常處理請(qǐng)求,避免了連接異常的發(fā)生。聽(tīng)起來(lái)感覺(jué)有點(diǎn)不優(yōu)雅,但實(shí)際效果還是比較好的,分布式的世界沒(méi)有銀彈,我們只能盡量在當(dāng)前設(shè)計(jì)現(xiàn)狀下找到并實(shí)踐能夠解決問(wèn)題的最優(yōu)解。
針對(duì)第二種情況,可以給 container 加 ReadinessProbe (就緒檢查),讓容器內(nèi)進(jìn)程真正啟動(dòng)完成后才更新 Service 的 Endpoint,然后 client 所在節(jié)點(diǎn) kube-proxy 再更新轉(zhuǎn)發(fā)規(guī)則,讓流量進(jìn)來(lái)。這樣能夠保證等 Pod 完全就緒了才會(huì)被轉(zhuǎn)發(fā)流量,也就避免了鏈接異常的發(fā)生。
我們都知道,給 Pod 配置健康檢查也是提高服務(wù)可用性的一種手段,配置 ReadinessProbe (就緒檢查) 可以避免將流量轉(zhuǎn)發(fā)給還沒(méi)啟動(dòng)完全或出現(xiàn)異常的 Pod;配置 LivenessProbe (存活檢查) 可以讓存在 bug 導(dǎo)致死鎖或 hang 住的應(yīng)用重啟來(lái)恢復(fù)。但是,如果配置配置不好,也可能引發(fā)其它問(wèn)題,這里根據(jù)一些踩坑經(jīng)驗(yàn)總結(jié)了一些指導(dǎo)性的建議:
不要輕易使用 LivenessProbe,除非你了解后果并且明白為什么你需要它,參考 Liveness Probes are Dangerous
如果使用 LivenessProbe,不要和 ReadinessProbe 設(shè)置成一樣 (failureThreshold 更大)
探測(cè)邏輯里不要有外部依賴(lài) (db, 其它 pod 等),避免抖動(dòng)導(dǎo)致級(jí)聯(lián)故障
業(yè)務(wù)程序應(yīng)盡量暴露 HTTP 探測(cè)接口來(lái)適配健康檢查,避免使用 TCP 探測(cè),因?yàn)槌绦?hang 死時(shí), TCP 探測(cè)仍然能通過(guò) (TCP 的 SYN 包探測(cè)端口是否存活在內(nèi)核態(tài)完成,應(yīng)用層不感知)
關(guān)于Kubernetes 服務(wù)部署中如何提高服務(wù)可用性就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。