Zero downtime migrate helm 2 to 3 with the sample violent way

這篇文章主要來分享,漸強實驗室是如何利用簡單暴力的方式,安靜無感的把 helm 2 升級到 helm 3!

前情提要,漸強實驗室的眾多服務主要都部署在一個大型共享的 Kubernetes 叢集,這個決定當然主要是跟人力與成本有關,有篇文章分析還不錯,提供大家參考;而這個叢集上面的應用主要使用 helm 來做部署的管理,這個叢集目前維護了兩年多,起初使用 helm 2 來部署大量的應用,直到 helm 3 在一年多前問世…

當時評估了一下,考量總有一天還是要上 helm 3,於是決定,新的服務使用 helm 3 來部署,然後舊的服務續用 helm 2,然後再找時間來慢慢升級,於是這場升級之旅延續了一年多,終於在最近全部升級完成,雖然 2020 年的 11 月 helm 2 就已經不再更新 😅 ,但你知道的… 本來就用的好好的東西,處理的優先次序一直不是太高,就拖了一陣子。

當時講到是否一定要升級到 helm 3 跟好處,我們其實也是觀望了一陣子,但光是之後社群不再維護,我想除非自己或公司 fork 來維護,不然使用已經不維護的工具,怎麼樣都不會是最好的選擇,所以尬廣跟上新版本,也是很樸實無華的工程師日常才對!?

前前後後升級了幾個社群開發的 helm chart (ex. Jenkins, Prometheus Operator…),然後是我們自己開發的 chart,用暴力替換的主要是我們自己開發的 chart,分成 HTTP API server 以及 Async task worker 兩種,都是屬於 stateless 的應用。

因為 helm 2 與 helm 3 是完全獨立管理,我們在兩個 helm 版本的 release 命名規則上有做調整,chart 的寫法也稍微更新了一下,來跟上 best practices 的寫法,也鋪陳了日後暴力升級的種子。

以下進入 migrate 主題

首先身為一個工程師,不知道大家是不是跟我一樣,聽到 migrate 就有一種麻煩來了的感覺,舉凡系統、功能、資料表… 等等各種不同的 migrations 通常都很麻煩,所以時常我都會問,有沒有簡單暴力的方法,可以直接 migrate?

內心戲(🙋🏼‍♂:老闆讓我停機 3 小。 老闆: 供 3 小?)

稍微簡介一下我們兩種不同 type 的 pod 的部署情況:

  • HTTP API server
    簡單的示意圖來表示,首先外部流量透過 ingress 進入,一律會先進入 nginx 的 deployment,用來做後面一群 API server 的一些全局設定配置( rate limit,或一些特別的頁面),然後才是導入後方的 API server deployment,當然中間還有一些 service 跟一些 hpa 的設定,但並不影響 migrate 的進行。
  • Async task worker
    簡化的示意圖,就是生活在 Message Queue 後面的 Consumer 們,而他們就是用 helm 2 來 release 的一群 deployment,不同的 deployment 下的 workers 就負責消費不同的 Queue。

怎麼做到暴力更換呢?

Canary Deployment

金絲雀部署簡單的解釋,就是先把一小部分的流量,往新版本導流,如果新版本沒問題,就逐漸地把全部流量導流過去,只是這邊要部署的不是新的版本的 Container Image,而是同一版 Container Image 的 helm 3 release。

Migrate HTTP API server

簡單的 Canary Deployment 示意圖(箭頭代表 requests 流向):

  • 部署開始
Canary deployment started
  • 部署完成
Canary deployment finished

這邊幾個關鍵的點是,因為我們在 helm 2 跟 helm 3 使用了不同的命名規則,所以新的 helm 3 release 所創建包含 Deployment & Service 在 Kubernetes 內都是新的名稱,於是 Nginx 就可以透過 loadbalancer 控制流量的設定,來對兩組 Deployment 的 Service 做分流,好處就是因為 migrate 的過程,需要保留舊的版本,來提供 rollback 以備不時之需,而這個方法也是可以保留舊的部署版本(scale to 0),直到 helm 3 的 release 也迭代了幾版後,就可以把 helm 2 的 release 移除。

Migrate async task worker

簡單的 Canary Deployment 示意圖(箭頭代表 messages 流向):

  • 部署開始
Canary deployment started
  • 部署完成
Canary deployment finished

部署 task workers 的部分也是類似,只是 task worker 是主動去 Message Queue 消費訊息,但概念是一樣的,先用 helm 3 release 一個新的 worker 加入消費者的行列,理論上同一個版本的 Container Image 應該是沒什麼問題,看一下 worker 狀態,如果都沒有錯誤發生,就可以把 helm 3 release 的 worker scale out,然後把 helm 2 release 的 worker scale to 0,來完成 helm 2 to 3 的 release 的升級。

Notice !

在這個過程中,雖然說很簡單,但是還是有遇到問題,是跟 Prometheus 有關的問題,我們使用 Prometheus 來抓取系統服務的 Metrics,雖然 API 會透過 Nginx 這個 reverse proxy 來存取後方的 API server,但是 Prometheus 抓取 Metrics 是直接訪問 API server 的 /metrics endpoint,所以我在換第一組服務的時候,就發現 Grafana 上的某一些 dashborad ,資料消失了😅 ,因為他是透過舊的 Service 去直接訪問 API server,當流量導向新的 Service 後,舊的 API server 先是沒有流量導致沒有新的 Metrics,scale to 0 之後,更是直接讓 Prometheus 訪問不了,於是趕緊把 /metrics endpoint 改到新的 Service…

結論

我當然也是有去看 Migrating Helm v2 to v3 ,其實也不會非常麻煩,但是在早期轉換的過程中,對於同時使用兩個版本共生,以及改變命名規則的決定,導致了我對於這些 Stateless 的服務遷移有了粗暴的想法,透過 Canary deployment 的方式,的確是一個簡單又安全的操作方式。

p.s. 移除 tiller 的當下,真的有一種舒坦的感覺,終於不用再把指令改成 helm2helm3 然後每次下指令的時候,都要先看一下,到底要用 2 還 3 …

工商時間

漸強實驗室目前在招募:

  1. 後端工程師:https://www.yourator.co/companies/CrescendoLab/jobs/8249
  2. 前端工程師:https://www.yourator.co/companies/CrescendoLab/jobs/8595

誠摯歡迎各路好手來加入我們!

--

--