阿里云k8s服務(wù)springboot項目應(yīng)用升級時出現(xiàn)502錯誤
隨著小步快跑、快速迭代的開發(fā)模式被越來越多的互聯(lián)網(wǎng)企業(yè)認同和采用,應(yīng)用的變更、升級頻率變得越來越頻繁。為了應(yīng)對不同的升級需求,保證升級過程平穩(wěn)順利地進行,誕生了一系列的部署發(fā)布模式。
- 停機發(fā)布 - 把老版的應(yīng)用實例完全停止,再發(fā)布新的版本。這種發(fā)布模式主要為了解決新老版本互不兼容、無法共存的問題,缺點是一段時間內(nèi)服務(wù)完全不可用。
- 藍綠發(fā)布 - 在線上同時部署相同數(shù)量的新老版本應(yīng)用實例。待新版本測試通過后,將流量一次性地切到新的服務(wù)實例上來。這種發(fā)布模式解決了停機發(fā)布中存在的服務(wù)完全不可用問題,但會造成比較大的資源消耗。
- 滾動發(fā)布 - 分批次逐步替換應(yīng)用實例。這種發(fā)布模式不會中斷服務(wù),同時也不會消耗過多額外的資源,但由于新老版本實例同時在線,可能導(dǎo)致來自相同客戶端的請求在新老版中切換而產(chǎn)生兼容性問題。
- 金絲雀發(fā)布 - 逐漸將流量從老版本切換到新版本上。如果觀察一段時間后沒有發(fā)現(xiàn)問題,就進一步擴大新版本流量,同時減少老版本上流量。
- A/B 測試 - 同時上線兩個或多個版本,收集用戶對這些版本的反饋,分析評估出最好版本正式采用。
隨著越來越多的應(yīng)用被容器化,如何方便地讓容器應(yīng)用平穩(wěn)順利升級受到了廣泛關(guān)注。本文將介紹 k8s 中不同部署形式下應(yīng)用的升級方法,并重點介紹如何對 Deployment 中的應(yīng)用實施滾動發(fā)布(本文所作的調(diào)研基于k8s 1.13
)。
K8s 應(yīng)用升級
在 k8s 中,pod 是部署和升級的基本單位。一般來說,一個 pod 代表一個應(yīng)用實例,而 pod 又會以Deployment、StatefulSet、DaemonSet、Job等形式部署運行,下面依次介紹在這些部署形式下 pod 的升級方法。
Deployment
Deployment 是 pod 最常見的部署形式,這里將以基于 spring boot 的 java 應(yīng)用為例進行介紹。該應(yīng)用是基于真實應(yīng)用抽象出來的簡單版本,非常具有代表性,它有如下特點:
- 應(yīng)用啟動后,需要花費一定的時間加載配置,在這段時間內(nèi),無法對外提供服務(wù)。
- 應(yīng)用能夠啟動并不意味著它能夠正常提供服務(wù)。
- 應(yīng)用如果無法提供服務(wù)不一定能自動退出。
- 在升級過程中需要保證即將下線的應(yīng)用實例不會接收到新的請求且有足夠時間處理完當前請求。
參數(shù)配置
為了讓具有上述特點的應(yīng)用實現(xiàn)零宕機時間和無生產(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,默認為 RollingUpdate。- Recreate - K8s 會先刪掉全部原有 pod 再創(chuàng)建新的 pod。該方式適用于新老版本互不兼容、無法共存的場景。但由于該方式會造成一段時間內(nèi)服務(wù)完全不可用,在上述場景之外須慎用。
- RollingUpdate - K8s 會將 pod 分批次逐步替換掉,可用來實現(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)定性的前提下盡可能快地進行升級,可以將 maxUnavailable 設(shè)置為 0,同時為 maxSurge 賦予一個較大值。
- 如果系統(tǒng)資源比較緊張,pod 負載又比較低,為了加快升級速度,可以將 maxSurge 設(shè)置為 0,同時為 maxUnavailable 賦予一個較大值。需要注意的是,如果 maxSurge 為 0,maxUnavailable 為 DESIRED,可能造成整個服務(wù)的不可用,此時 RollingUpdate 將退化成停機發(fā)布。
樣例選擇了一個折中方案,將 maxSurge 設(shè)置為 3,將 maxUnavailable 設(shè)置為 2,平衡了穩(wěn)定性、資源消耗和升級速度。
配置探針
K8s 提供以下兩類探針:
- ReadinessProbe - 默認情況下,一旦某個 pod 中的所有容器全部啟動,k8s 就會認為該 pod 處于就緒狀態(tài),從而將流量發(fā)往該 pod。但某些應(yīng)用啟動后,還需要完成數(shù)據(jù)或配置文件的加載工作才能對外提供服務(wù),因此通過容器是否啟動來判斷其是否就緒并不嚴謹。通過為容器配置就緒探針,能讓 k8s 更準確地判斷容器是否就緒,從而構(gòu)建出更健壯的應(yīng)用。K8s 保證只有 pod 中的所有容器全部通過了就緒探測,才允許 service 將流量發(fā)往該 pod。一旦就緒探測失敗,k8s 會停止將流量發(fā)往該 pod。
- LivenessProbe - 默認情況下,k8s 會認為處于運行狀態(tài)下的容器是可用的。但如果應(yīng)用在出現(xiàn)問題或不健康時無法自動退出(例如發(fā)生嚴重死鎖),這種判斷就會出現(xiàn)問題。通過為容器配置活性探針,能讓 k8s 更準確地判斷容器是否正常運行。如果容器沒有通過活性探測,kubelet 會將其停止,并根據(jù)重啟策略決定下一步的動作。
探針的配置非常靈活,用戶可以指定探針的探測頻率、探測成功閾值、探測失敗閾值等。各參數(shù)的含義和配置方法可參考文檔Configure Liveness and Readiness Probes。
樣例為目標容器配置了就緒探針和活性探針:
- 就緒探針的 initialDelaySeconds 設(shè)置成 30,這是因為應(yīng)用平均需要 30 秒時間完成初始化工作。
- 在配置活性探針時,需要保證容器有足夠時間到達就緒狀態(tài)。如果參數(shù) initialDelaySeconds、periodSeconds、failureThreshold 設(shè)置得過小,可能造成容器還未就緒就被重啟,以至于永遠無法達到就緒狀態(tài)。樣例中的配置保證如果容器能在啟動后的 80 秒內(nèi)就緒就不會被重啟,相對 30 秒的平均初始化時間有足夠的緩沖。
- 就緒探針的 periodSeconds 設(shè)置成 10,failureThreshold 設(shè)置成 1。這樣當容器異常時,大約 10 秒后就不會有流量發(fā)往它。
- 活性探針的 periodSeconds 設(shè)置成 20,failureThreshold 設(shè)置成 3。這樣當容器異常時,大約 60 秒后就不會被重啟。
配置 minReadySeconds
默認情況下,一旦新創(chuàng)建的 pod 變成就緒狀態(tài) k8s 就會認為該 pod 是可用的,從而將老的 pod 刪除掉。但有時問題可能會在新 pod 真正處理用戶請求時才暴露,因此一個更穩(wěn)健的做法是當某個新 pod 就緒后對其觀察一段時間再刪掉老的 pod。
參數(shù) minReadySeconds 可以控制 pod 處于就緒狀態(tài)的觀察時間。如果 pod 中的容器在這段時間內(nèi)都能正常運行,k8s 才會認為新 pod 可用,從而將老的 pod 刪除掉。在配置該參數(shù)時,需要仔細權(quán)衡,如果設(shè)置得過小,可能造成觀察不充分,如果設(shè)置得過大,又會拖慢升級進度。樣例將 minReadySeconds 設(shè)置成了 120 秒,這樣能保證處于就緒狀態(tài)的 pod 能經(jīng)歷一個完整的活性探測周期。
配置 terminationGracePeriodSeconds
當 k8s 準備刪除一個 pod 時,會向該 pod 中的容器發(fā)送 TERM 信號并同時將 pod 從 service 的 endpoint 列表中移除。如果容器無法在規(guī)定時間(默認 30 秒)內(nèi)終止,k8s 會向容器發(fā)送 SIGKILL 信號強制終止進程。Pod 終止的詳細流程可參考文檔Termination of Pods。
由于應(yīng)用處理請求最長耗時 40 秒,為了讓其在關(guān)閉前能夠處理完已到達服務(wù)端的請求,樣例設(shè)置了 60 秒的優(yōu)雅關(guān)閉時間。針對不同的應(yīng)用,您可以根據(jù)實際情況調(diào)整 terminationGracePeriodSeconds 的取值。
觀察升級行為
上述配置能夠保證目標應(yīng)用的平滑升級。我們可以通過更改 Deployment 中 PodTemplateSpec 的任意字段觸發(fā) pod 升級,并通過運行命令kubectl get rs -w
觀察升級行為。這里觀察到的新老版本的 pod 副本數(shù)的變化情況如下:
- 創(chuàng)建 maxSurge 個新 pod。這時 pod 總數(shù)達到了允許的上限,即 DESIRED + maxSurge。
- 不等新 pod 就緒或可用,立刻啟動 maxUnavailable 個老 pod 的刪除流程。這時可用 pod 數(shù)為 DESIRED - maxUnavailable。
- 某個老 pod 被完全刪除,這時會立刻補充一個新 pod。
- 某個新 pod 通過了就緒探測變成了就緒態(tài),k8s 會將流量發(fā)往該 pod。但由于未達到規(guī)定的觀察時間,該 pod 并不會被視作可用。
- 某個就緒 pod 在觀察期內(nèi)運行正常被視作可用,這時可以再次啟動某個老 pod 的刪除流程。
- 重復(fù)步驟 3、4、5 直到所有老 pod 被刪除,并且可用的新 pod 達到目標副本數(shù)。
失敗回滾
應(yīng)用的升級并不總會一帆風(fēng)順,在升級過程中或升級完成后都有可能遇到新版本行為不符合預(yù)期需要回滾到穩(wěn)定版本的情況。K8s 會將 PodTemplateSpec 的每一次變更(如果更新模板標簽或容器鏡像)都記錄下來。這樣,如果新版本出現(xiàn)問題,就可以根據(jù)版本號方便地回滾到穩(wěn)定版本。回滾 Deployment 的詳細操作步驟可參考文檔Rolling Back a Deployment。
StatefulSet
StatefulSet 是針對有狀態(tài) pod 常用的部署形式。針對這類 pod,k8s 同樣提供了許多參數(shù)用于靈活地控制它們的升級行為。好消息是這些參數(shù)大部分都和升級 Deployment 中的 pod 相同。這里重點介紹兩者存在差異的地方。
策略類型
在 k8s 1.7 及之后的版本中,StatefulSet 支持 OnDelete 和 RollingUpdate 兩種策略類型。
- OnDelete - 當更新了 StatefulSet 中的 PodTemplateSpec 后,只有手動刪除舊的 pod 后才會創(chuàng)建新版本 pod。這是默認的更新策略,一方面是為了兼容 k8s 1.6 及之前的版本,另一方面也是為了支持升級過程中新老版本 pod 互不兼容、無法共存的場景。
- RollingUpdate - K8s 會將 StatefulSet 管理的 pod 分批次逐步替換掉。它與 Deployment 中 RollingUpdate 的區(qū)別在于 pod 的替換是有序的。例如一個 StatefulSet 中包含 N 個 pod,在部署的時候這些 pod 被分配了從 0 開始單調(diào)遞增的序號,而在滾動更新時,它們會按逆序依次被替換。
Partition
可以通過參數(shù).spec.updateStrategy.rollingUpdate.partition
實現(xiàn)只升級部分 pod 的目的。在配置了 partition 后,只有序號大于或等于 partition 的 pod 才會進行滾動升級,其余 pod 將保持不變。
Partition 的另一個應(yīng)用是可以通過不斷減少 partition 的取值實現(xiàn)金絲雀升級。具體操作方法可參考文檔Rolling Out a Canary。
DaemonSet
DaemonSet 保證在全部(或者一些)k8s 工作節(jié)點上運行一個 pod 的副本,常用來運行監(jiān)控或日志收集程序。對于 DaemonSet 中的 pod,用于控制它們升級行為的參數(shù)與 Deployment 幾乎一致,只是在策略類型方面略有差異。DaemonSet 支持 OnDelete 和 RollingUpdate 兩種策略類型。
- OnDelete - 當更新了 DaemonSet 中的 PodTemplateSpec 后,只有手動刪除舊的 pod 后才會創(chuàng)建新版本 pod。這是默認的更新策略,一方面是為了兼容 k8s 1.5 及之前的版本,另一方面也是為了支持升級過程中新老版本 pod 互不兼容、無法共存的場景。
- RollingUpdate - 其含義和可配參數(shù)與 Deployment 的 RollingUpdate 一致。
滾動更新 DaemonSet 的具體操作步驟可參考文檔Perform a Rolling Update on a DaemonSet。
Job
Deployment、StatefulSet、DaemonSet 一般用于部署運行常駐進程,而 Job 中的 pod 在執(zhí)行完特定任務(wù)后就會退出,因此不存在滾動更新的概念。當您更改了一個 Job 中的 PodTemplateSpec 后,需要手動刪掉老的 Job 和 pod,并以新的配置重新運行該 job。
總結(jié)
K8s 提供的功能可以讓大部分應(yīng)用實現(xiàn)零宕機時間和無生產(chǎn)中斷的升級,但也存在一些沒有解決的問題,主要包括以下幾點:
- 目前 k8s 原生僅支持停機發(fā)布、滾動發(fā)布兩類部署升級策略。如果應(yīng)用有藍綠發(fā)布、金絲雀發(fā)布、A/B 測試等需求,需要進行二次開發(fā)或使用一些第三方工具。
- K8s 雖然提供了回滾功能,但回滾操作必須手動完成,無法根據(jù)條件自動回滾。
- 有些應(yīng)用在擴容或縮容時同樣需要分批逐步執(zhí)行,k8s 還未提供類似的功能。
實例配置:
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)用進行更新時, 訪問不再出現(xiàn)502的報錯。
更多關(guān)于阿里云k8s服務(wù)springboot項目應(yīng)用升級時出現(xiàn)502錯誤技術(shù)文章請查看下面的相關(guān)鏈接
版權(quán)聲明:本站文章來源標注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。