天天看點

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

作者 | 驕龍

導讀:本篇是《SpringCloud 應用在 Kubernetes 上的最佳實踐》系列文章的第八篇,主要介紹了如何做到流量的無損上/下線。更多相關文章閱讀可檢視文末。

前言

上篇我們講的是釋出復原過程,尤其是在 Kubernetes 的復原過程中,原生有提供 Rollout 到上一個版本的能力,能保證我們在釋出過程中遇到問題時快速回退的能力。然而在每一次上線的過程中,我們最難處理的就是正在運作中的流量,如何做到流量的無損上/下線,是一個系統能保證 SLA 的關鍵。

介紹

什麼是優雅上線?就如下面這個房子一樣,未建好的房子,人住進去會有危險,房子應該建好,裝修好,人才能住進去。

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

那麼如何做到優雅上線呢?我們先來看一個 WEB 應用的加載過程,就像上面造房子一樣,是個漫長的過程:

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

應用的加載是

漫長

的,在加載過程,服務是不可預期的;如過早地打開 Socket 監聽,則用戶端可能感受到漫長的等待;如果資料庫、消息隊列、REDIS 用戶端未完成初始化,則服務可能因缺少關鍵的底層服務而異常。

是以在應用準備完成後,才接入服務,即做到優雅上線。當然應用上線後,也可能因如資料庫斷連等情況引起服務不可用;或是準備完成了,但在上線前又發生資料庫斷連,導緻服務異常。為了簡化問題,後面兩種情況作為一個應用自愈的問題來看待。

什麼是優雅下線?與建房子相反就像下面的危房一樣,人住在裡面很危險,人應該先從房子出來,然後推掉房子。

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

那麼如何做到優雅下線呢?我們先來看一個 WEB 應用的停止過程:

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

是以關閉服務接入(轉移服務接入),完成正在處理的服務,清理自身占用的資源後退出即做到優雅下線。

如何實作優雅下線

從上面介紹看,似乎不難,但事實上,很少有系統真正實作了優雅上下線。因為軟體本身由無數各種各樣互相依賴的結構組成,每個結構都使用一些資源,污染一些資源;通常在設計之初優雅上下線也不被作為優先考慮的需求,是以對于下線的過程,通常都沒被充分考慮,在設計上通常要求:

  • 結構(元件)應形成層次關系;
  • 使用者線程需能收到停止信号并響應退出;否則使用 daemon 線程;
  • 結構應按依賴關系自下向上建構:就像建房子一樣,自内向外建構而成;
  • 結構應按依賴關系自上向下銷毀:就像拆房子一樣,自外向内拆解。

優雅下線實作路徑

大緻分為一個完整的過程,需要經曆一下四個關鍵的節點,如下圖:

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)
  • 接收信号:停止信号可能從程序内部觸發(比如 Crash 場景),如果自退出的話基本上無法保證優雅下線;是以能保證優雅下線的前提就是需要正确處理來自程序外部的信号;
  • 停止流量接收:由于在停止之前,我們會有一些正在處理的請求,貿然退出會對這些請求産生損耗。但是在這段時間之内我們絕不能再接收新的業務請求,如果這是一個背景任務型(消息消費型或任務排程型)的程式,也要停止接收新的消息和任務。對于一個普通的 WEB 場景,這一塊不同的場景實作的方式也會不一樣,下面的 Srping Cloud 應用的下線流程會詳細講解;
  • 銷毀資源:常見的是一些系統資源,也包括一些緩存、鎖的清理、同時也包括線程池、關閉阻塞中的的 IO 操作,等到我們這些伺服器資源銷毀之後,就可以通知主線程退出。

Spring Cloud 應用

一個 Spring boot 應用通常由應用本身和一系列的 Starter 組成,對于 Spring boot 體系,需要了解如下核心概念:

  • Starter:提供一系列的子產品,由 Spring boot 核心通過 auto-configuration 機制加載;
  • Bean:一切皆 Bean,starter 子產品的加載産生各種 Bean;
  • Context:Bean 的容器,容器擁有生命周期,Bean 需要感覺生命周期事件;
  • LifeCycle:生命周期管理接口;
  • ApplicationEvent:子產品之間,子產品與容器之間,通過發送或監聽事件來達到互相通訊的目的。

是以對于應用上下線這個主題,我們應盡可能利用其豐富的原生事件機制,Spring Cloud 中内置的 Starter 機制針對整個生命周期管理的過程有了很好的封裝。

Spring Cloud 應用的優雅上線

Spring Cloud 啟動過程觸發回調及事件如下,詳細介紹見

application-events-and-listeners

,簡單羅列如下:

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

Spring 自身及其元件大量基于這些事件建構,如響應 WebServerInitializedEvent 事件向服務注冊中心注冊服務,對于應用一般可利用:

  • InitializingBean or @PostConstruct:在 Bean 裝配完後,被回調,如完成資料源初始化連接配接;
  • ApplicationReadyEvent、ApplicationRunner、CommandLineRunner:如開始監聽消息隊列,處理消息;注冊到SLB等;先通過配置禁用服務的自動注冊,在這裡做手動服務注冊。

Spring Cloud 應用的優雅下線

Spring Cloud 本身可以作為一個應用單獨存在,也可以是依附在一個微服務叢集中,同時還能作為反向代理架構中的一個網關。不同的場景,需要用到的方法也不一樣,我們就常用的三種場景針對性的加以說明。

場景一:直接通路 WEB 服務

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

用戶端直接通路 WEB 應用,在這個用例下,優雅下線需要做的事情有:

  • 正在處理的請求完成處理
  • 應用自身完成安全下線并正常退出
  • 用戶端感覺到連接配接異常

Spring-boot 從 2.3 開始内置了 WEB 應用優雅下線的能力,需配置如下,具體介紹參見

graceful-shutdown

server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=20s           

其實作方式:

  • 首先關閉 socket 監聽,等待正在處理的所有請求完成:具體可見 WebServerGracefulShutdownLifecycle,通過 getPhase 傳回最大值,達到早于 WEB 容器關閉執行的目的;
  • 然後觸發 WEB 容器關閉:具體可見 WebServerStartStopLifecycle。

但其實,對于未被 WEB 容器完全接收的請求,用戶端仍會收到連接配接被重置的異常,隻是這個時間視窗極小。該需求從提出到實作的時間跨度較長,感興趣的可參見

github 上的讨論

場景二:經由反向代理的服務優雅下線

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

因為執行個體前面還有反向代理,相比上個場景,需要新增“反向代理下線”這個處理流程。即若應用已經下線,但反向代理未摘除該應用執行個體時用戶端将感覺到失敗。一般采取的政策有:

  • 反向代理支援失敗轉移到其它應用執行個體;
  • 在關閉應用前,如将健康探測接口傳回不健康以及等待足夠的逾時,讓反向代理感覺并摘除執行個體的路由資訊。

對于仍在使用 2.3 以前版本的 Spring Cloud 應用,可參見

一個方案

,實作方式:

  • 使用自身的 shutdownHook 替換 Spring 的 shutdownHook;
  • 先改變 health 狀态,等待一段時間,讓反向代理感覺并摘除執行個體的路由資訊。

場景三:在微服務叢集中下線單個服務

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

在優雅關閉 Spring Cloud 應用自身之前,我們除了完成場景一之中的目标之外,還需要将自身節點從注冊中心中下線。目前在 Spring Cloud 中針對注冊中心下線的場景暫未提供開箱即用的方法,下面介紹兩種可能的實作方案:

方案 1:先通過腳本、或通過監聽 ContextClosedEvent 反注冊服務摘除流量;等待足夠時間,如使用 ribbon 負載均衡器,需要長于配置的重新整理時間;對于基于 HTTP 的服務,若 Spring Cloud 版本小于 2.3,則時間需加上預期的請求處理時間;

方案 2:用戶端支援連接配接感覺重試,如重試,實作方案可參考

Spring-retry

,針對連接配接異常 RemoteConnectFailureException 做重試。

針對 Eureka 中的場景,有一個很好的參考的例子,請參見:

https://home1-oss.github.io/home1-oss-gitbook/release/docs/oss-eureka/GRACEFUL_SHUTDOWN.html

Kubernetes 下的機制

Kubernetes 中針對應用的的管控提供了豐富的手段,正常的情況它提供了應用生命周期中的靈活擴充點,同時也支援自己擴充它的 Operator 自定義上下線的流程。

SpringCloud 應用在 Kubernetes 上的最佳實踐 — 線上釋出(優雅上下線)

抛開實作成本,以下線的情況來說,一個 Kubernetes 應用執行個體下線之前,管控程式會向 POD 發送一個 SIGTERM 的信号,應用響應時除了額外響應這一個信号之外,還能觸發一段自定義的 PreStop 的挂在腳本,代碼樣例如下:

yaml
lifecycle:                   
      preStop:                   
        exec:                    
          command:               
          - sh
          - -c
          - "sleep 5"           

上面的例子一點特殊說明:因服務控制面重新整理與 POD 收到 SIGTERM 同時發生,是以這裡通過 sleep 5 讓服務控制面先完成重新整理,應用程序再響應 SIGTERM 信号。

Spring Cloud 與 Kubernetes 的結合

Kubernetes 會根據健康檢查的情況來更新服務(Service)清單,其中如果 Liveness 失敗,則會觸發容器重建,這是一個相對很重的操作;若 Readiness 失敗,則 Kubenetes 則預設不會将路由服務流量到相應的容器;基于這一機理,Spring Cloud 2.3 開始,也做了原生的的支援,具體參見

liveness-and-readiness-probes-with-Spring-boot

,這些健康檢查端點可對接 Kubnetes 相應的 probe:

  • /actuator/health/liveness
  • /actuator/health/readiness

同時,Spring Boot 内置了相應的 API、事件、Health Check 監控,部分代碼/配置片段如下:

java
// Available as a component in the application context
ApplicationAvailability availability;
LivenessState livenessState = availabilityProvider.getLivenessState();
ReadinessState readinessState = availabilityProvider.getReadinessState();
....
// 對于應用,也可以通過API,釋出相應的事件,來改變應用的狀态
AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
// 同時,應用監控也可影響這健康狀态,将監控與健康關聯,在K8S體系下,可以實作如離群摘除,應用自愈的能力
// application.properties
management.endpoint.health.group.liveness.include=livenessProbe,cacheCheck           

回到 Spring Cloud 應用 在微服務叢集中下線單個服務 的章節中,我們的應用如果跑在 Kuberntes 中,如果我們使用了原生的 Kubernetes 機制去管理應用生命周期的話,隻需要釋出一個應用事件 (LivenessState.BROKEN) 即可實作優雅下線的能力。

EDAS提供内置的優雅上下線能力

通過上面兩部分了解了 Spring Cloud 和 K8s 中的機制,EDAS 基于原生的機制,衍生出來了自己的方法,除了最大化利用這些能力:主動更新 Liveness、Readiness、Ribbon 服務清單之外,我們還提供了無代碼侵入的開箱即用的能力,列舉如下:

後續

這一章節之後,和釋出相關的内容都已經更新完畢,下一章節我們要開始高可用部分的能力,高可用也是系統保障 SLA 的關鍵部分,簡單的了解是流量洪峰到來如何保證系統不會受到影響?當然我們還有一部分要達成的是洪峰退去之後資源是否存在浪費?敬請期待 ...

相關文章推薦:

阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公衆号。”