做者| 趙明山(立衡)
git
OpenKruise 是阿里雲開源的雲原生應用自動化管理套件,也是當前託管在 Cloud Native Computing Foundation ( CNCF ) 下的 Sandbox 項目。它來自阿里巴巴多年來容器化、雲原生的技術沉澱,是阿里內部生產環境大規模應用的基於 Kubernetes 之上的標準擴展組件,也是緊貼上游社區標準、適應互聯網規模化場景的技術理念與最佳實踐。
OpenKruise 在 2021.5.20 發佈了最新的 v0.9.0版本( ChangeLog ),上一篇文章咱們介紹了新增 Pod 重啓、刪除防禦等重磅功能,今天向你們介紹另外一個核心特性,即 SidecarSet 基於上一個版本擴展了特別針對 Service Mesh 場景的支持。
github
SidecarSet 是 Kruise 提供的獨立管理 Sidecar 容器的 workload。用戶經過 SidecarSet 可以便利的完成對 Sidecar 容器的自動注入和獨立升級,詳情請參考:OpenKruise 官網
api
默認狀況下,Sidecar 的獨立升級順序是先中止舊版本的容器,而後再建立新版本的容器。這種方式尤爲適合不影響 Pod 服務可用性的 Sidecar 容器,例如日誌收集 agent ,可是對於不少代理或運行時的 Sidecar 容器,如 Istio Envoy,這種升級方法就有問題了。Envoy 做爲 Pod 中的一個 Proxy 容器代理了全部的流量,這種場景下若是直接重啓升級,Pod 服務的可用性必然會受到影響,所以須要考慮應用自身的發佈和容量狀況,沒法徹底獨立於應用作 Sidecar 的發佈。
app
阿里巴巴集團內部擁有上萬的 Pod 都是基於 Service Mesh 來實現相互間的通訊,因爲 Mesh 容器升級會致使業務 Pod 的不可用,於是 Mesh 容器的升級將會極大阻礙 Service Mesh 的迭代。針對這種場景,咱們同集團內部的 Service Mesh 團隊一塊兒合做實現了 Mesh 容器的熱升級能力。本文將重點介紹在實現 mesh 容器熱升級能力的過程當中 SidecarSet 是扮演了怎樣的重要角色。
tcp
Mesh 容器不能像日誌採集類容器直接原地升級,其緣由在於:Mesh 容器必需要不間斷地對外提供服務,而獨立升級方式會致使 Mesh 服務存在一段不可用時間。雖然社區中已有一些知名的 Mesh 服務如 Envoy 、Mosn 等默認可以提供平滑升級的能力,可是這些升級方式沒法與雲原生進行恰當地結合,且 kubernetes 自己也缺少對此類 Sidecar 容器的升級方案。
ide
OpenKruise SidecarSet 爲此類 Mesh 容器提供了 Sidecar 熱升級機制,可以經過雲原生的方式助力 Mesh 容器實現無損熱升級。post
apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: hotupgrade-sidecarset spec: selector: matchLabels: app: hotupgrade containers: - name: sidecar image: openkruise/hotupgrade-sample:sidecarv1 imagePullPolicy: Always lifecycle: postStart: exec: command: - /bin/sh - /migrate.sh upgradeStrategy: upgradeType: HotUpgrade hotUpgradeEmptyImage: openkruise/hotupgrade-sample:empty
SidecarSet 熱升級機制主要包含注入熱升級 Sidecar 容器和 Mesh 容器平滑升級兩個過程。
性能
針對熱升級類型的 Sidecar 容器,在 Pod 建立時 SidecarSet Webhook 將會注入兩個容器:
ui
上述 Empty 容器在 Mesh 容器運行過程當中,並無作任何實際的工做。
阿里雲
熱升級流程主要分爲一下三個步驟:
僅需上述三個步驟便可完成熱升級中的所有流程,若對 Pod 執行屢次熱升級,則重複執行上述三個步驟便可。
SidecarSet 熱升級機制不只完成了 Mesh 容器的切換,而且提供了新老版本的協調機制( PostStartHook ),可是至此還只是萬里長征的第一步,Mesh 容器同時還須要提供 PostSartHook 腳原本完成 Mesh 服務自身的平滑升級(上述 Migration 過程),如:Envoy 熱重啓、Mosn 無損重啓。
Mesh 容器通常都是經過監聽固定端口來對外提供服務,此類 Mesh 容器的migration 過程能夠歸納爲:經過 UDS 傳遞 ListenFD 和中止 Accpet 、開始排水。針對不支持熱重啓的 Mesh 容器能夠參考此過程完成改造,邏輯圖以下:
不一樣 Mesh 容器對外提供的服務以及內部實現邏輯各有差別,進而具體的 Migration也有所不一樣,上述邏輯只是對其中一些要點作了一些總結,但願能對有須要的各位有所裨益,同時在 Github 上面咱們也提供了一個熱升級 Migration Demo 以供參考,下面將對其中的一些關鍵代碼進行介紹。
1. 協商機制
Mesh 容器啓動邏輯首先就須要判斷第一次啓動仍是熱升級平滑遷移過程,爲了減小Mesh 容器溝通成本,Kruise 在兩個 sidecar 容器中注入了兩個環境變量 SIDECARSET_VERSION 和 SIDECARSET_VERSION_ALT ,經過判斷兩個環境變量的值來判斷是不是熱升級過程以及當前 sidecar 容器是新版本仍是老版本。
// return two parameters: // 1. (bool) indicates whether it is hot upgrade process // 2. (bool ) when isHotUpgrading=true, the current sidecar is newer or older func isHotUpgradeProcess() (bool, bool) { // 當前sidecar容器的版本 version := os.Getenv("SIDECARSET_VERSION") // 對端sidecar容器的版本 versionAlt := os.Getenv("SIDECARSET_VERSION_ALT") // 當對端sidecar容器version是"0"時,代表當前沒有在熱升級過程 if versionAlt == "0" { return false, false } // 在熱升級過程當中 versionInt, _ := strconv.Atoi(version) versionAltInt, _ := strconv.Atoi(versionAlt) // version是單調遞增的int類型,新版本的version值會更大 return true, versionInt > versionAltInt }
2. ListenFD 遷移
經過 Unix Domain Socket 實現 ListenFD 在不一樣容器間的遷移,此步一樣也是熱升級中很是關鍵的一步,代碼示例以下:
// 爲了代碼的簡潔,全部的失敗都將不捕獲 /* 老版本sidecar經過Unix Domain Socket遷移ListenFD到新版本sidecar */ // tcpLn *net.TCPListener f, _ := tcpLn.File() fdnum := f.Fd() data := syscall.UnixRights(int(fdnum)) // 與新版本sidecar容器經過 Unix Domain Socket創建連接 raddr, _ := net.ResolveUnixAddr("unix", "/dev/shm/migrate.sock") uds, _ := net.DialUnix("unix", nil, raddr) // 經過UDS,發送ListenFD到新版本sidecar容器 uds.WriteMsgUnix(nil, data, nil) // 中止接收新的request,而且開始排水階段,例如:http2 GOAWAY tcpLn.Close() /* 新版本sidecar接收ListenFD,而且開始對外服務 */ // 監聽 UDS addr, _ := net.ResolveUnixAddr("unix", "/dev/shm/migrate.sock") unixLn, _ := net.ListenUnix("unix", addr) conn, _ := unixLn.AcceptUnix() buf := make([]byte, 32) oob := make([]byte, 32) // 接收 ListenFD _, oobn, _, _, _ := conn.ReadMsgUnix(buf, oob) scms, _ := syscall.ParseSocketControlMessage(oob[:oobn]) if len(scms) > 0 { // 解析FD,並轉化爲 *net.TCPListener fds, _ := syscall.ParseUnixRights(&(scms[0])) f := os.NewFile(uintptr(fds[0]), "") ln, _ := net.FileListener(f) tcpLn, _ := ln.(*net.TCPListener) // 基於接收到的Listener開始對外提供服務,以http服務爲例 http.Serve(tcpLn, serveMux) }
阿里雲服務網格( Alibaba Cloud Service Mesh,簡稱 ASM)提供了一個全託管式的服務網格平臺,兼容社區 Istio 開源服務網格。當前,基於 OpenKruise SidecarSet 的熱升級能力,ASM 實現了數據平面 Sidecar 熱升級能力( Beta ),用戶能夠在應用無感的狀況下完成服務網格的數據平面版本升級,正式版也將於近期上線。除熱升級能力外,ASM 還支持配置診斷、操做審計、訪問日誌、監控、服務註冊接入等能力,全方位提高服務網格使用體驗,歡迎您前往試用。
總結
雲原生中 Mesh 容器的熱升級一直都是迫切卻又棘手的問題,本文中的方案也只是阿里巴巴集團在此問題上的一次探索,在反饋社區的同時也但願可以拋磚引玉,引起各位對此中場景的思考。同時,咱們也歡迎更多的同窗參與到 OpenKruise 社區來,共同建設一個場景更加豐富、完善的 K8s 應用管理、交付擴展能力,可以面向更加規模化、複雜化、極致性能的場景。