成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Kubernetes 中如何保證優(yōu)雅地停止 Pod

Berwin / 1974人閱讀

摘要:假如我們先告訴網(wǎng)關(guān)或服務(wù)注冊中心我們要下線,等對方完成服務(wù)摘除操作再中止進(jìn)程,那不會有任何流量受到影響這是優(yōu)雅停止,將單個組件的啟停對整個系統(tǒng)影響最小化。與此同時,會將從對應(yīng)的上摘除。

作者:吳葉磊

一直以來我對優(yōu)雅地停止 Pod 這件事理解得很單純:不就利用是?PreStop hook?做優(yōu)雅退出嗎?但最近發(fā)現(xiàn)很多場景下 PreStop Hook 并不能很好地完成需求,這篇文章就簡單分析一下“優(yōu)雅地停止 Pod”這回事兒。

何謂優(yōu)雅停止?

優(yōu)雅停止(Graceful shutdown)這個說法來自于操作系統(tǒng),我們執(zhí)行關(guān)機之后都得 OS 先完成一些清理操作,而與之相對的就是硬中止(Hard shutdown),比如拔電源。

到了分布式系統(tǒng)中,優(yōu)雅停止就不僅僅是單機上進(jìn)程自己的事了,往往還要與系統(tǒng)中的其它組件打交道。比如說我們起一個微服務(wù),網(wǎng)關(guān)把一部分流量分給我們,這時:

假如我們一聲不吭直接把進(jìn)程殺了,那這部分流量就無法得到正確處理,部分用戶受到影響。不過還好,通常來說網(wǎng)關(guān)或者服務(wù)注冊中心會和我們的服務(wù)保持一個心跳,過了心跳超時之后系統(tǒng)會自動摘除我們的服務(wù),問題也就解決了;這是硬中止,雖然我們整個系統(tǒng)寫得不錯能夠自愈,但還是會產(chǎn)生一些抖動甚至錯誤。

假如我們先告訴網(wǎng)關(guān)或服務(wù)注冊中心我們要下線,等對方完成服務(wù)摘除操作再中止進(jìn)程,那不會有任何流量受到影響;這是優(yōu)雅停止,將單個組件的啟停對整個系統(tǒng)影響最小化。

按照慣例,SIGKILL 是硬終止的信號,而 SIGTERM 是通知進(jìn)程優(yōu)雅退出的信號,因此很多微服務(wù)框架會監(jiān)聽 SIGTERM 信號,收到之后去做反注冊等清理操作,實現(xiàn)優(yōu)雅退出。

PreStop Hook

回到 Kubernetes(下稱 K8s),當(dāng)我們想干掉一個 Pod 的時候,理想狀況當(dāng)然是 K8s 從對應(yīng)的 Service(假如有的話)把這個 Pod 摘掉,同時給 Pod 發(fā) SIGTERM 信號讓 Pod 中的各個容器優(yōu)雅退出就行了。但實際上 Pod 有可能犯各種幺蛾子:

已經(jīng)卡死了,處理不了優(yōu)雅退出的代碼邏輯或需要很久才能處理完成。

優(yōu)雅退出的邏輯有 BUG,自己死循環(huán)了。

代碼寫得野,根本不理會 SIGTERM。

因此,K8s 的 Pod 終止流程中還有一個“最多可以容忍的時間”,即 grace period(在 Pod 的?.spec.terminationGracePeriodSeconds?字段中定義),這個值默認(rèn)是 30 秒,我們在執(zhí)行?kubectl delete?的時候也可通過?--grace-period?參數(shù)顯式指定一個優(yōu)雅退出時間來覆蓋 Pod 中的配置。而當(dāng) grace period 超出之后,K8s 就只能選擇 SIGKILL 強制干掉 Pod 了。

很多場景下,除了把 Pod 從 K8s 的 Service 上摘下來以及進(jìn)程內(nèi)部的優(yōu)雅退出之外,我們還必須做一些額外的事情,比如說從 K8s 外部的服務(wù)注冊中心上反注冊。這時就要用到 PreStop Hook 了,K8s 目前提供了?Exec?和?HTTP?兩種 PreStop Hook,實際用的時候,需要通過 Pod 的?.spec.containers[].lifecycle.preStop?字段為 Pod 中的每個容器多帶帶配置,比如:

spec:
  contaienrs:
  - name: my-awesome-container
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh","-c","/pre-stop.sh"]

/pre-stop.sh?腳本里就可以寫我們自己的清理邏輯。

最后我們串起來再整個表述一下 Pod 退出的流程(官方文檔里更嚴(yán)謹(jǐn)哦):

用戶刪除 Pod。

2.1. Pod 進(jìn)入 Terminating 狀態(tài)。

2.2. 與此同時,K8s 會將 Pod 從對應(yīng)的 service 上摘除。

2.3. 與此同時,針對有 PreStop Hook 的容器,kubelet 會調(diào)用每個容器的 PreStop Hook,假如 PreStop Hook 的運行時間超出了 grace period,kubelet 會發(fā)送 SIGTERM 并再等 2 秒。

2.4. 與此同時,針對沒有 PreStop Hook 的容器,kubelet 發(fā)送 SIGTERM。

grace period 超出之后,kubelet 發(fā)送 SIGKILL 干掉尚未退出的容器。

這個過程很不錯,但它存在一個問題就是我們無法預(yù)測 Pod 會在多久之內(nèi)完成優(yōu)雅退出,也無法優(yōu)雅地應(yīng)對“優(yōu)雅退出”失敗的情況。而在我們的產(chǎn)品?TiDB Operator?中,這就是一個無法接受的事情。

有狀態(tài)分布式應(yīng)用的挑戰(zhàn)

為什么說無法接受這個流程呢?其實這個流程對無狀態(tài)應(yīng)用來說通常是 OK 的,但下面這個場景就稍微復(fù)雜一點:

TiDB?中有一個核心的分布式 KV 存儲層?TiKV。TiKV 內(nèi)部基于 Multi-Raft 做一致性存儲,這個架構(gòu)比較復(fù)雜,這里我們可以簡化描述為一主多從的架構(gòu),Leader 寫入,F(xiàn)ollower 同步。而我們的場景是要對 TiKV 做計劃性的運維操作,比如滾動升級,遷移節(jié)點。

在這個場景下,盡管系統(tǒng)可以接受小于半數(shù)的節(jié)點宕機,但對于預(yù)期性的停機,我們要盡量做到優(yōu)雅停止。這是因為數(shù)據(jù)庫場景本身就是非常嚴(yán)苛的,基本上都處于整個架構(gòu)的核心部分,因此我們要把抖動做到越小越好。要做到這點,就得做不少清理工作,比如說我們要在停機前將當(dāng)前節(jié)點上的 Leader 全部遷移到其它節(jié)點上。

得益于系統(tǒng)的良好設(shè)計,大多數(shù)時候這類操作都很快,然而分布式系統(tǒng)中異常是家常便飯,優(yōu)雅退出耗時過長甚至失敗的場景是我們必須要考慮的。假如類似的事情發(fā)生了,為了業(yè)務(wù)穩(wěn)定和數(shù)據(jù)安全,我們就不能強制關(guān)閉 Pod,而應(yīng)該停止操作過程,通知工程師介入。?這時,上面所說的 Pod 退出流程就不再適用了。

小心翼翼:手動控制所有流程

這個問題其實 K8s 本身沒有開箱即用的解決方案,于是我們在自己的 Controller 中(TiDB 對象本身就是一個 CRD)與非常細(xì)致地控制了各種操作場景下的服務(wù)啟停邏輯。

拋開細(xì)節(jié)不談,最后的大致邏輯是在每次停服務(wù)前,由 Controller 通知集群進(jìn)行節(jié)點下線前的各種遷移操作,操作完成后,才真正下線節(jié)點,并進(jìn)行下一個節(jié)點的操作。

而假如集群無法正常完成遷移等操作或耗時過久,我們也能“守住底線”,不會強行把節(jié)點干掉,這就保證了諸如滾動升級,節(jié)點遷移之類操作的安全性。

但這種辦法存在一個問題就是實現(xiàn)起來比較復(fù)雜,我們需要自己實現(xiàn)一個控制器,在其中實現(xiàn)細(xì)粒度的控制邏輯并且在 Controller 的控制循環(huán)中不斷去檢查能否安全停止 Pod。

另辟蹊徑:解耦 Pod 刪除的控制流

復(fù)雜的邏輯總是沒有簡單的邏輯好維護,同時寫 CRD 和 Controller 的開發(fā)量也不小,能不能有一種更簡潔,更通用的邏輯,能實現(xiàn)“保證優(yōu)雅關(guān)閉(否則不關(guān)閉)”的需求呢?

有,辦法就是?ValidatingAdmissionWebhook。

這里先介紹一點點背景知識,Kubernetes 的 apiserver 一開始就有 AdmissionController 的設(shè)計,這個設(shè)計和各類 Web 框架中的 Filter 或 Middleware 很像,就是一個插件化的責(zé)任鏈,責(zé)任鏈中的每個插件針對 apiserver 收到的請求做一些操作或校驗。舉兩個插件的例子:

DefaultStorageClass,為沒有聲明 storageClass 的 PVC 自動設(shè)置 storageClass。

ResourceQuota,校驗 Pod 的資源使用是否超出了對應(yīng) Namespace 的 Quota。

雖然說這是插件化的,但在 1.7 之前,所有的 plugin 都需要寫到 apiserver 的代碼中一起編譯,很不靈活。而在 1.7 中 K8s 就引入了?Dynamic Admission Control?機制,允許用戶向 apiserver 注冊 webhook,而 apiserver 則通過 webhook 調(diào)用外部 server 來實現(xiàn) filter 邏輯。1.9 中,這個特性進(jìn)一步做了優(yōu)化,把 webhook 分成了兩類:?MutatingAdmissionWebhook?和?ValidatingAdmissionWebhook,顧名思義,前者就是操作 api 對象的,比如上文例子中的?DefaultStroageClass,而后者是校驗 api 對象的,比如?ResourceQuota。拆分之后,apiserver 就能保證在校驗(Validating)之前先做完所有的修改(Mutating),下面這個示意圖非常清晰:

而我們的辦法就是,利用?ValidatingAdmissionWebhook,在重要的 Pod 收到刪除請求時,先在 webhook server 上請求集群進(jìn)行下線前的清理和準(zhǔn)備工作,并直接返回拒絕。這時候重點來了,Control Loop 為了達(dá)到目標(biāo)狀態(tài)(比如說升級到新版本),會不斷地進(jìn)行 reconcile,嘗試刪除 Pod,而我們的 webhook 則會不斷拒絕,除非集群已經(jīng)完成了所有的清理和準(zhǔn)備工作

下面是這個流程的分步描述:

用戶更新資源對象。

controller-manager watch 到對象變更。

controller-manager 開始同步對象狀態(tài),嘗試刪除第一個 Pod。

apiserver 調(diào)用外部 webhook。

webhook server 請求集群做 tikv-1 節(jié)點下線前的準(zhǔn)備工作(這個請求是冪等的),并查詢準(zhǔn)備工作是否完成,假如準(zhǔn)備完成,允許刪除,假如沒有完成,則拒絕,整個流程會因為 controller manager 的控制循環(huán)回到第 2 步。

好像一下子所有東西都清晰了,這個 webhook 的邏輯很清晰,就是要保證所有相關(guān)的 Pod 刪除操作都要先完成優(yōu)雅退出前的準(zhǔn)備,完全不用關(guān)心外部的控制循環(huán)是怎么跑的,也因此它非常容易編寫和測試,非常優(yōu)雅地滿足了我們“保證優(yōu)雅關(guān)閉(否則不關(guān)閉)”的需求,目前我們正在考慮用這種方式替換線上的舊方案。

后記

其實?Dynamic Admission Control?的應(yīng)用很廣,比如 Istio 就是用?MutatingAdmissionWebhook?來實現(xiàn) envoy 容器的注入的。從上面的例子中我們也可以看到它的擴展能力很強,而且常常能站在一個正交的視角上,非常干凈地解決問題,與其它邏輯做到很好的解耦。

當(dāng)然了,Kubernetes 中還有?非常多的擴展點,從 kubectl 到 apiserver,scheduler,kubelet(device plugin,flexvolume),自定義 Controller 再到集群層面的網(wǎng)絡(luò)(CNI),存儲(CSI)可以說是處處可以做事情。以前做一些常規(guī)的微服務(wù)部署對這些并不熟悉也沒用過,而現(xiàn)在面對 TiDB 這樣復(fù)雜的分布式系統(tǒng),尤其在 Kubernetes 對有狀態(tài)應(yīng)用和本地存儲的支持還不夠好的情況下,得在每一個擴展點上去悉心考量,做起來非常有意思,因此后續(xù)可能還有一些?TiDB Operator?中思考過的解決方案分享。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/32965.html

相關(guān)文章

  • Kubernetes 如何保證優(yōu)雅停止 Pod

    摘要:假如我們先告訴網(wǎng)關(guān)或服務(wù)注冊中心我們要下線,等對方完成服務(wù)摘除操作再中止進(jìn)程,那不會有任何流量受到影響這是優(yōu)雅停止,將單個組件的啟停對整個系統(tǒng)影響最小化。與此同時,會將從對應(yīng)的上摘除。 作者:吳葉磊 一直以來我對優(yōu)雅地停止 Pod 這件事理解得很單純:不就利用是?PreStop hook?做優(yōu)雅退出嗎?但最近發(fā)現(xiàn)很多場景下 PreStop Hook 并不能很好地完成需求,這篇文章就簡單...

    liaosilzu2007 評論0 收藏0
  • 優(yōu)雅關(guān)閉kubernetes的nginx

    摘要:被設(shè)計為這樣一種方式,父進(jìn)程必須明確地等待子進(jìn)程終止,以便收集它的退出狀態(tài)。會完成的刪除,將優(yōu)雅退出的時間設(shè)置為表示立即刪除。 SIGINT SIGTERM SIGKILL區(qū)別 三者都是結(jié)束/終止進(jìn)程運行。 1.SIGINT SIGTERM區(qū)別 前者與字符ctrl+c關(guān)聯(lián),后者沒有任何控制字符關(guān)聯(lián)。前者只能結(jié)束前臺進(jìn)程,后者則不是。 2.SIGTERM SIGKILL的區(qū)別 前者可以被...

    Noodles 評論0 收藏0
  • 優(yōu)雅關(guān)閉kubernetes的nginx

    摘要:被設(shè)計為這樣一種方式,父進(jìn)程必須明確地等待子進(jìn)程終止,以便收集它的退出狀態(tài)。會完成的刪除,將優(yōu)雅退出的時間設(shè)置為表示立即刪除。 SIGINT SIGTERM SIGKILL區(qū)別 三者都是結(jié)束/終止進(jìn)程運行。 1.SIGINT SIGTERM區(qū)別 前者與字符ctrl+c關(guān)聯(lián),后者沒有任何控制字符關(guān)聯(lián)。前者只能結(jié)束前臺進(jìn)程,后者則不是。 2.SIGTERM SIGKILL的區(qū)別 前者可以被...

    余學(xué)文 評論0 收藏0
  • 使用kubernetes的deployment進(jìn)行RollingUpdate

    摘要:序,可以使得服務(wù)近乎無縫地平滑升級,即在不停止對外服務(wù)的前提下完成應(yīng)用的更新。如果少于指定數(shù)量的,會創(chuàng)建新的,反之則會刪除掉多余的以保證數(shù)量不變。對于應(yīng)用,默認(rèn)的帶有接口,可以用來進(jìn)行啟動成功的判斷。 序 rolling update,可以使得服務(wù)近乎無縫地平滑升級,即在不停止對外服務(wù)的前提下完成應(yīng)用的更新。 replication controller與deployment的區(qū)別 r...

    Lycheeee 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<