阿里云k8s服務(wù)springboot項(xiàng)目應(yīng)用升級時(shí)出現(xiàn)502錯誤
隨著小步快跑、快速迭代的開發(fā)模式被越來越多的互聯(lián)網(wǎng)企業(yè)認(rèn)同和采用,應(yīng)用的變更、升級頻率變得越來越頻繁。為了應(yīng)對不同的升級需求,保證升級過程平穩(wěn)順利地進(jìn)行,誕生了一系列的部署發(fā)布模式。
- 停機(jī)發(fā)布 - 把老版的應(yīng)用實(shí)例完全停止,再發(fā)布新的版本。這種發(fā)布模式主要為了解決新老版本互不兼容、無法共存的問題,缺點(diǎn)是一段時(shí)間內(nèi)服務(wù)完全不可用。
- 藍(lán)綠發(fā)布 - 在線上同時(shí)部署相同數(shù)量的新老版本應(yīng)用實(shí)例。待新版本測試通過后,將流量一次性地切到新的服務(wù)實(shí)例上來。這種發(fā)布模式解決了停機(jī)發(fā)布中存在的服務(wù)完全不可用問題,但會造成比較大的資源消耗。
- 滾動發(fā)布 - 分批次逐步替換應(yīng)用實(shí)例。這種發(fā)布模式不會中斷服務(wù),同時(shí)也不會消耗過多額外的資源,但由于新老版本實(shí)例同時(shí)在線,可能導(dǎo)致來自相同客戶端的請求在新老版中切換而產(chǎn)生兼容性問題。
- 金絲雀發(fā)布 - 逐漸將流量從老版本切換到新版本上。如果觀察一段時(shí)間后沒有發(fā)現(xiàn)問題,就進(jìn)一步擴(kuò)大新版本流量,同時(shí)減少老版本上流量。
- A/B 測試 - 同時(shí)上線兩個或多個版本,收集用戶對這些版本的反饋,分析評估出最好版本正式采用。
隨著越來越多的應(yīng)用被容器化,如何方便地讓容器應(yīng)用平穩(wěn)順利升級受到了廣泛關(guān)注。本文將介紹 k8s 中不同部署形式下應(yīng)用的升級方法,并重點(diǎn)介紹如何對 Deployment 中的應(yīng)用實(shí)施滾動發(fā)布(本文所作的調(diào)研基于k8s 1.13
)。
K8s 應(yīng)用升級
在 k8s 中,pod 是部署和升級的基本單位。一般來說,一個 pod 代表一個應(yīng)用實(shí)例,而 pod 又會以 Deployment、StatefulSet、DaemonSet、Job 等形式部署運(yùn)行,下面依次介紹在這些部署形式下 pod 的升級方法。
Deployment
Deployment 是 pod 最常見的部署形式,這里將以基于 spring boot 的 java 應(yīng)用為例進(jìn)行介紹。該應(yīng)用是基于真實(shí)應(yīng)用抽象出來的簡單版本,非常具有代表性,它有如下特點(diǎn):
- 應(yīng)用啟動后,需要花費(fèi)一定的時(shí)間加載配置,在這段時(shí)間內(nèi),無法對外提供服務(wù)。
- 應(yīng)用能夠啟動并不意味著它能夠正常提供服務(wù)。
- 應(yīng)用如果無法提供服務(wù)不一定能自動退出。
- 在升級過程中需要保證即將下線的應(yīng)用實(shí)例不會接收到新的請求且有足夠時(shí)間處理完當(dāng)前請求。
參數(shù)配置
為了讓具有上述特點(diǎn)的應(yīng)用實(shí)現(xiàn)零宕機(jī)時(shí)間和無生產(chǎn)中斷的升級,需要精心地配置 Deployment 中的相關(guān)參數(shù)。這里和升級有關(guān)的配置如下(完整配置參見 spring-boot-probes-v1.yaml)。
kind: Deployment ... spec: replicas: 8 strategy: type: RollingUpdate rollingUpdate: maxSurge: 3 maxUnavailable: 2 minReadySeconds: 120 ... template: ... spec: containers: - name: spring-boot-probes image: registry.cn-hangzhou.aliyuncs.com/log-service/spring-boot-probes:1.0.0 ports: - containerPort: 8080 terminationGracePeriodSeconds: 60 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 successThreshold: 1 failureThreshold: 1 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 40 periodSeconds: 20 successThreshold: 1 failureThreshold: 3 ...
配置 strategy
通過 strategy 可以配置 pod 的替換策略,主要參數(shù)如下。
.spec.strategy.type
- 用于指定替換 pod 的策略類型。該參數(shù)可取值 Recreate 或 RollingUpdate,默認(rèn)為 RollingUpdate。- Recreate - K8s 會先刪掉全部原有 pod 再創(chuàng)建新的 pod。該方式適用于新老版本互不兼容、無法共存的場景。但由于該方式會造成一段時(shí)間內(nèi)服務(wù)完全不可用,在上述場景之外須慎用。
- RollingUpdate - K8s 會將 pod 分批次逐步替換掉,可用來實(shí)現(xiàn)服務(wù)熱升級。
.spec.strategy.rollingUpdate.maxSurge
- 指定在滾動更新過程中最多可創(chuàng)建多少個額外的 pod,可以是數(shù)字或百分比。該值設(shè)置得越大、升級速度越快,但會消耗更多的系統(tǒng)資源。.spec.strategy.rollingUpdate.maxUnavailable
- 指定在滾動更新過程中最多允許多少個 pod 不可用, 可以是數(shù)字或百分比。該值設(shè)置得越大、升級速度越快,但服務(wù)會越不穩(wěn)定。
通過調(diào)節(jié) maxSurge 和 maxUnavailable,可以滿足不同場景下的升級需求。
- 如果您希望在保證系統(tǒng)可用性和穩(wěn)定性的前提下盡可能快地進(jìn)行升級,可以將 maxUnavailable 設(shè)置為 0,同時(shí)為 maxSurge 賦予一個較大值。
- 如果系統(tǒng)資源比較緊張,pod 負(fù)載又比較低,為了加快升級速度,可以將 maxSurge 設(shè)置為 0,同時(shí)為 maxUnavailable 賦予一個較大值。需要注意的是,如果 maxSurge 為 0,maxUnavailable 為 DESIRED,可能造成整個服務(wù)的不可用,此時(shí) RollingUpdate 將退化成停機(jī)發(fā)布。
樣例選擇了一個折中方案,將 maxSurge 設(shè)置為 3,將 maxUnavailable 設(shè)置為 2,平衡了穩(wěn)定性、資源消耗和升級速度。
配置探針
K8s 提供以下兩類探針:
- ReadinessProbe - 默認(rèn)情況下,一旦某個 pod 中的所有容器全部啟動,k8s 就會認(rèn)為該 pod 處于就緒狀態(tài),從而將流量發(fā)往該 pod。但某些應(yīng)用啟動后,還需要完成數(shù)據(jù)或配置文件的加載工作才能對外提供服務(wù),因此通過容器是否啟動來判斷其是否就緒并不嚴(yán)謹(jǐn)。通過為容器配置就緒探針,能讓 k8s 更準(zhǔn)確地判斷容器是否就緒,從而構(gòu)建出更健壯的應(yīng)用。K8s 保證只有 pod 中的所有容器全部通過了就緒探測,才允許 service 將流量發(fā)往該 pod。一旦就緒探測失敗,k8s 會停止將流量發(fā)往該 pod。
- LivenessProbe - 默認(rèn)情況下,k8s 會認(rèn)為處于運(yùn)行狀態(tài)下的容器是可用的。但如果應(yīng)用在出現(xiàn)問題或不健康時(shí)無法自動退出(例如發(fā)生嚴(yán)重死鎖),這種判斷就會出現(xiàn)問題。通過為容器配置活性探針,能讓 k8s 更準(zhǔn)確地判斷容器是否正常運(yùn)行。如果容器沒有通過活性探測,kubelet 會將其停止,并根據(jù)重啟策略決定下一步的動作。
探針的配置非常靈活,用戶可以指定探針的探測頻率、探測成功閾值、探測失敗閾值等。各參數(shù)的含義和配置方法可參考文檔 Configure Liveness and Readiness Probes。
樣例為目標(biāo)容器配置了就緒探針和活性探針:
- 就緒探針的 initialDelaySeconds 設(shè)置成 30,這是因?yàn)閼?yīng)用平均需要 30 秒時(shí)間完成初始化工作。
- 在配置活性探針時(shí),需要保證容器有足夠時(shí)間到達(dá)就緒狀態(tài)。如果參數(shù) initialDelaySeconds、periodSeconds、failureThreshold 設(shè)置得過小,可能造成容器還未就緒就被重啟,以至于永遠(yuǎn)無法達(dá)到就緒狀態(tài)。樣例中的配置保證如果容器能在啟動后的 80 秒內(nèi)就緒就不會被重啟,相對 30 秒的平均初始化時(shí)間有足夠的緩沖。
- 就緒探針的 periodSeconds 設(shè)置成 10,failureThreshold 設(shè)置成 1。這樣當(dāng)容器異常時(shí),大約 10 秒后就不會有流量發(fā)往它。
- 活性探針的 periodSeconds 設(shè)置成 20,failureThreshold 設(shè)置成 3。這樣當(dāng)容器異常時(shí),大約 60 秒后就不會被重啟。
配置 minReadySeconds
默認(rèn)情況下,一旦新創(chuàng)建的 pod 變成就緒狀態(tài) k8s 就會認(rèn)為該 pod 是可用的,從而將老的 pod 刪除掉。但有時(shí)問題可能會在新 pod 真正處理用戶請求時(shí)才暴露,因此一個更穩(wěn)健的做法是當(dāng)某個新 pod 就緒后對其觀察一段時(shí)間再刪掉老的 pod。
參數(shù) minReadySeconds 可以控制 pod 處于就緒狀態(tài)的觀察時(shí)間。如果 pod 中的容器在這段時(shí)間內(nèi)都能正常運(yùn)行,k8s 才會認(rèn)為新 pod 可用,從而將老的 pod 刪除掉。在配置該參數(shù)時(shí),需要仔細(xì)權(quán)衡,如果設(shè)置得過小,可能造成觀察不充分,如果設(shè)置得過大,又會拖慢升級進(jìn)度。樣例將 minReadySeconds 設(shè)置成了 120 秒,這樣能保證處于就緒狀態(tài)的 pod 能經(jīng)歷一個完整的活性探測周期。
配置 terminationGracePeriodSeconds
當(dāng) k8s 準(zhǔn)備刪除一個 pod 時(shí),會向該 pod 中的容器發(fā)送 TERM 信號并同時(shí)將 pod 從 service 的 endpoint 列表中移除。如果容器無法在規(guī)定時(shí)間(默認(rèn) 30 秒)內(nèi)終止,k8s 會向容器發(fā)送 SIGKILL 信號強(qiáng)制終止進(jìn)程。Pod 終止的詳細(xì)流程可參考文檔 Termination of Pods。
由于應(yīng)用處理請求最長耗時(shí) 40 秒,為了讓其在關(guān)閉前能夠處理完已到達(dá)服務(wù)端的請求,樣例設(shè)置了 60 秒的優(yōu)雅關(guān)閉時(shí)間。針對不同的應(yīng)用,您可以根據(jù)實(shí)際情況調(diào)整 terminationGracePeriodSeconds 的取值。
觀察升級行為
上述配置能夠保證目標(biāo)應(yīng)用的平滑升級。我們可以通過更改 Deployment 中 PodTemplateSpec 的任意字段觸發(fā) pod 升級,并通過運(yùn)行命令kubectl get rs -w
觀察升級行為。這里觀察到的新老版本的 pod 副本數(shù)的變化情況如下:
- 創(chuàng)建 maxSurge 個新 pod。這時(shí) pod 總數(shù)達(dá)到了允許的上限,即 DESIRED + maxSurge。
- 不等新 pod 就緒或可用,立刻啟動 maxUnavailable 個老 pod 的刪除流程。這時(shí)可用 pod 數(shù)為 DESIRED - maxUnavailable。
- 某個老 pod 被完全刪除,這時(shí)會立刻補(bǔ)充一個新 pod。
- 某個新 pod 通過了就緒探測變成了就緒態(tài),k8s 會將流量發(fā)往該 pod。但由于未達(dá)到規(guī)定的觀察時(shí)間,該 pod 并不會被視作可用。
- 某個就緒 pod 在觀察期內(nèi)運(yùn)行正常被視作可用,這時(shí)可以再次啟動某個老 pod 的刪除流程。
- 重復(fù)步驟 3、4、5 直到所有老 pod 被刪除,并且可用的新 pod 達(dá)到目標(biāo)副本數(shù)。
失敗回滾
應(yīng)用的升級并不總會一帆風(fēng)順,在升級過程中或升級完成后都有可能遇到新版本行為不符合預(yù)期需要回滾到穩(wěn)定版本的情況。K8s 會將 PodTemplateSpec 的每一次變更(如果更新模板標(biāo)簽或容器鏡像)都記錄下來。這樣,如果新版本出現(xiàn)問題,就可以根據(jù)版本號方便地回滾到穩(wěn)定版本?;貪L Deployment 的詳細(xì)操作步驟可參考文檔 Rolling Back a Deployment。
StatefulSet
StatefulSet 是針對有狀態(tài) pod 常用的部署形式。針對這類 pod,k8s 同樣提供了許多參數(shù)用于靈活地控制它們的升級行為。好消息是這些參數(shù)大部分都和升級 Deployment 中的 pod 相同。這里重點(diǎn)介紹兩者存在差異的地方。
策略類型
在 k8s 1.7 及之后的版本中,StatefulSet 支持 OnDelete 和 RollingUpdate 兩種策略類型。
- OnDelete - 當(dāng)更新了 StatefulSet 中的 PodTemplateSpec 后,只有手動刪除舊的 pod 后才會創(chuàng)建新版本 pod。這是默認(rèn)的更新策略,一方面是為了兼容 k8s 1.6 及之前的版本,另一方面也是為了支持升級過程中新老版本 pod 互不兼容、無法共存的場景。
- RollingUpdate - K8s 會將 StatefulSet 管理的 pod 分批次逐步替換掉。它與 Deployment 中 RollingUpdate 的區(qū)別在于 pod 的替換是有序的。例如一個 StatefulSet 中包含 N 個 pod,在部署的時(shí)候這些 pod 被分配了從 0 開始單調(diào)遞增的序號,而在滾動更新時(shí),它們會按逆序依次被替換。
Partition
可以通過參數(shù).spec.updateStrategy.rollingUpdate.partition
實(shí)現(xiàn)只升級部分 pod 的目的。在配置了 partition 后,只有序號大于或等于 partition 的 pod 才會進(jìn)行滾動升級,其余 pod 將保持不變。
Partition 的另一個應(yīng)用是可以通過不斷減少 partition 的取值實(shí)現(xiàn)金絲雀升級。具體操作方法可參考文檔 Rolling Out a Canary。
DaemonSet
DaemonSet 保證在全部(或者一些)k8s 工作節(jié)點(diǎn)上運(yùn)行一個 pod 的副本,常用來運(yùn)行監(jiān)控或日志收集程序。對于 DaemonSet 中的 pod,用于控制它們升級行為的參數(shù)與 Deployment 幾乎一致,只是在策略類型方面略有差異。DaemonSet 支持 OnDelete 和 RollingUpdate 兩種策略類型。
- OnDelete - 當(dāng)更新了 DaemonSet 中的 PodTemplateSpec 后,只有手動刪除舊的 pod 后才會創(chuàng)建新版本 pod。這是默認(rèn)的更新策略,一方面是為了兼容 k8s 1.5 及之前的版本,另一方面也是為了支持升級過程中新老版本 pod 互不兼容、無法共存的場景。
- RollingUpdate - 其含義和可配參數(shù)與 Deployment 的 RollingUpdate 一致。
滾動更新 DaemonSet 的具體操作步驟可參考文檔 Perform a Rolling Update on a DaemonSet。
Job
Deployment、StatefulSet、DaemonSet 一般用于部署運(yùn)行常駐進(jìn)程,而 Job 中的 pod 在執(zhí)行完特定任務(wù)后就會退出,因此不存在滾動更新的概念。當(dāng)您更改了一個 Job 中的 PodTemplateSpec 后,需要手動刪掉老的 Job 和 pod,并以新的配置重新運(yùn)行該 job。
總結(jié)
K8s 提供的功能可以讓大部分應(yīng)用實(shí)現(xiàn)零宕機(jī)時(shí)間和無生產(chǎn)中斷的升級,但也存在一些沒有解決的問題,主要包括以下幾點(diǎn):
- 目前 k8s 原生僅支持停機(jī)發(fā)布、滾動發(fā)布兩類部署升級策略。如果應(yīng)用有藍(lán)綠發(fā)布、金絲雀發(fā)布、A/B 測試等需求,需要進(jìn)行二次開發(fā)或使用一些第三方工具。
- K8s 雖然提供了回滾功能,但回滾操作必須手動完成,無法根據(jù)條件自動回滾。
- 有些應(yīng)用在擴(kuò)容或縮容時(shí)同樣需要分批逐步執(zhí)行,k8s 還未提供類似的功能。
實(shí)例配置:
livenessProbe: failureThreshold: 3 httpGet: path: /user/service/test port: 8080 scheme: HTTP initialDelaySeconds: 40 periodSeconds: 20 successThreshold: 1 timeoutSeconds: 1 name: dataline-dev ports: - containerPort: 8080 protocol: TCP readinessProbe: failureThreshold: 1 httpGet: path: /user/service/test port: 8080 scheme: HTTP initialDelaySeconds: 30 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1
經(jīng)測試 , 再對sprintboot 應(yīng)用進(jìn)行更新時(shí), 訪問不再出現(xiàn)502的報(bào)錯。
更多關(guān)于阿里云k8s服務(wù)springboot項(xiàng)目應(yīng)用升級時(shí)出現(xiàn)502錯誤技術(shù)文章請查看下面的相關(guān)鏈接
相關(guān)文章
Dell R710 服務(wù)器做Raid0與Raid5磁盤陣列的圖文教程
這篇文章主要介紹了Dell R710 服務(wù)器做Raid0與Raid5磁盤陣列的圖文教程,需要的朋友可以參考下2018-05-05windows服務(wù)器如何創(chuàng)建.開頭(.well-known)的文件夾
國內(nèi)的云服務(wù)商騰訊云提供免費(fèi)有效期一年的SSL證書,申請過程中需要身份驗(yàn)證,需要dns域名解析或者服務(wù)器文件驗(yàn)證。如果想使用文件驗(yàn)證,必須創(chuàng)建一個以.開頭的文件,使用windows服務(wù)器如何創(chuàng)建.開頭的文件夾。2023-04-04ubuntu服務(wù)器部署gitlab docker并配置nginx反向代理https訪問的過程解析
這篇文章主要介紹了ubuntu服務(wù)器部署gitlab docker并配置nginx反向代理https訪問的過程,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-02-02Apache?Hudi靈活的Payload機(jī)制硬核解析
這篇文章主要為大家介紹了Apache?Hudi靈活的Payload機(jī)制超硬核解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03TortoiseSVN使用方法_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了TortoiseSVN的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08