這篇文章已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Bitmovin 如何在雲端和地端透過 Kubernetes 進行多階段 Canary 部署

在多個公有雲上運行大規模視訊編碼基礎架構非常困難。在 Bitmovin,我們在過去幾年中成功地做到了,但從工程的角度來看,這既不令人愉快,也特別不好玩。

因此,顯然地,真正讓我們決定使用 Kubernetes 的主要原因之一是它對不同支援的雲端供應商的通用抽象,以及它提供的經過深思熟慮的程式設計介面。更重要的是,Kubernetes 專案並未滿足於最低公分母的方法。相反地,他們添加了在雲端中運行容器化工作負載所需且有用的必要抽象概念,然後完成了所有艱苦的工作,將這些概念映射到不同的雲端供應商及其產品。

我們在 2016 年年中早期測試中看到的出色穩定性、速度和操作可靠性,使得遷移到 Kubernetes 成為必然的選擇。

而且,Kubernetes 專案一直追求的擴展願景與我們公司自身的目標密切相關,這也沒有壞處。以 >1,000 個節點叢集為目標可能是一個崇高的目標,但對於像我們這樣快速成長的視訊公司來說,讓您的基礎架構旨在支援未來成長至關重要。此外,在為我們的新基礎架構進行初步腦力激盪後,我們立即知道我們將運行大量的容器,並且擁有一個以在全球範圍內運行為明確目標的系統,對我們來說是完美的選擇。現在,隨著最近 Kubernetes 1.6 版本的發布及其 對 5,000 個節點叢集的支援,我們感覺到我們對容器編排系統的選擇更加有道理。

在測試和遷移階段,為了讓我們的基礎架構在 Kubernetes 上運行,我們非常熟悉 Kubernetes API 以及圍繞它的整個生態系統。因此,當我們考慮擴展我們的雲端視訊編碼產品,供客戶在其自己的資料中心或雲端環境中使用時,我們很快決定利用 Kubernetes 作為我們無所不在的雲端作業系統,作為解決方案的基礎。

僅僅幾個月後,這項努力已成為我們最新的服務產品:Bitmovin 受管理的內部部署編碼。由於所有 Kubernetes 叢集都共享相同的 API,因此調整我們的雲端編碼服務以在 Kubernetes 上運行,使我們能夠部署到客戶的資料中心,而無需考慮底層運行的硬體基礎架構。借助社群提供的出色工具,例如 kube-up 和統包解決方案,例如 Google Container Engine,任何人都可以輕鬆地在其自己的基礎架構或其自己的雲端帳戶中配置新的 Kubernetes 叢集。

為了為部署到裸機的客戶提供最大的靈活性,並且可能尚未對 Kubernetes 進行任何自訂雲端整合,我們決定僅基於任何 Kubernetes 安裝中可用的設施來構建我們的解決方案,並且不需要與周圍基礎架構進行任何整合(它甚至可以在 Minikube 內部運行!)。我們不依賴 LoadBalancer 類型的 Services,主要是因為企業 IT 通常不願意向開放網際網路開放埠口 - 而且並非每個裸機 Kubernetes 安裝都開箱即用地支援外部配置的負載平衡器。為了避免這些問題,我們部署了一個 BitmovinAgent,它在叢集內部運行並輪詢我們的 API 以獲取新的編碼作業,而無需任何網路設定。然後,此代理程式使用本地可用的 Kubernetes 憑證來啟動新的部署,這些部署透過 Kubernetes API 在可用硬體上運行編碼器。

即使在沒有完整雲端整合可用的情況下,我們從使用 Kubernetes API 獲得的一致排程、健康檢查和監控,確實使我們能夠專注於使編碼器在容器內部工作,而不是花費寶貴的工程資源來整合一堆不同的虛擬機器監管程式、機器配置器和監控系統。

多階段 Canary 部署

我們第一次接觸 Kubernetes API 並不是為了內部部署編碼產品。在 Kubernetes 上建構我們的容器化編碼工作流程,而是在看到 Kubernetes 平台在 Bitmovin API 基礎架構的開發和推出過程中被證明是多麼容易和強大之後做出的決定。我們在大約四個月前遷移到 Kubernetes,它使我們能夠為我們的服務提供快速的開發迭代,同時滿足我們對無停機部署和從開發到生產的穩定管道的要求。為了實現這一點,我們提出了一個架構,該架構運行了近一千個容器,並滿足了我們在第一天提出的以下要求

  1. 1.為我們的客戶提供零停機部署
  2. 2.每次 git 主線推送都持續部署到生產環境
  3. 3.已部署服務對客戶的高度穩定性

顯然,如果每個合併的功能都立即部署到生產環境,則 #2 和 #3 是相互矛盾的 - 我們如何確保這些版本沒有錯誤,並且不會對我們的客戶產生不利的副作用?

為了克服這個矛盾修辭,我們為每個微服務設計了一個四階段 Canary 管道,我們同時部署到生產環境,並讓變更遠離客戶,直到新的建置版本被證明在生產環境中可靠且正確地工作。

一旦新的建置版本被推送,我們會將其部署到一個僅供我們內部測試和整合測試套件訪問的內部階段。一旦內部測試套件通過,QA 報告沒有問題,並且我們沒有檢測到任何異常行為,我們就會將新的建置版本推送到我們的免費階段。這表示我們 5% 的免費使用者將隨機分配到這個新的建置版本。在這個階段經過一段時間後,建置版本會晉升到下一個階段,該階段會將 5% 的付費用戶路由到它。只有在建置版本成功通過所有這 3 個障礙之後,它才會部署到生產層級,在那裡它將接收我們剩餘使用者的所有流量以及我們企業客戶的流量,這些企業客戶不屬於付費群組,並且永遠不會看到他們的流量被路由到 Canary 軌道。

預設情況下,這種設定使我們成為一個相當大的 Kubernetes 安裝,因為我們所有的 Canary 層級都以至少 2 個複寫可用。由於我們目前正在將大約 30 個微服務(並且還在增長)部署到我們的叢集,因此每個服務至少需要 10 個 Pods(8 個應用程式 Pods + 最少 2 個執行 Canary 路由的 HAProxy Pods)。雖然實際上,我們首選的標準配置通常是運行 2 個內部 Pods、4 個免費 Pods、4 個其他 Pods 和 10 個生產 Pods,以及 4 個 HAProxy Pods - 總共約 700 個 Pods。這也表示我們至少運行 150 個服務,這些服務為其底層微服務 Canary 層級提供靜態 ClusterIP。

典型的部署看起來像這樣

| 服務 (ClusterIP) | 部署 | #Pods | | account-service | account-service-haproxy | 4 | | account-service-internal | account-service-internal-v1.18.0 | 2 | | account-service-canary | account-service-canary-v1.17.0 | 4 | | account-service-paid | account-service-paid-v1.15.0 | 4 | | account-service-production | account-service-production-v1.15.0 | 10 |

生產軌道的範例服務定義將具有以下標籤選擇器

apiVersion: v1

kind: Service

metadata:

 name: account-service-production

 labels:

 app: account-service-production

 tier: service

 lb: private

spec:

 ports:

 - port: 8080

 name: http

 targetPort: 8080

 protocol: TCP

 selector:

 app: account-service

 tier: service

 track: production

在 Kubernetes 服務之前,對服務的不同 Canary 版本進行負載平衡的是一小組 HAProxy Pods 叢集,它們從 Kubernetes ConfigMaps 取得它們的 haproxy.conf,看起來像這樣

frontend http-in

 bind \*:80

 log 127.0.0.1 local2 debug


 acl traffic\_internal hdr(X-Traffic-Group) -m str -i INTERNAL

 acl traffic\_free  hdr(X-Traffic-Group) -m str -i FREE

 acl traffic\_enterprise hdr(X-Traffic-Group) -m str -i ENTERPRISE


 use\_backend internal if traffic\_internal

 use\_backend canary if traffic\_free

 use\_backend enterprise if traffic\_enterprise


 default\_backend paid


backend internal

 balance roundrobin

 server internal-lb  user-resource-service-internal:8080 resolvers dns check inter 2000

backend canary

 balance roundrobin

 server canary-lb    user-resource-service-canary:8080 resolvers dns check inter 2000 weight 5

 server production-lb user-resource-service-production:8080 resolvers dns check inter 2000 weight 95

backend paid

 balance roundrobin

 server canary-paid-lb user-resource-service-paid:8080 resolvers dns check inter 2000 weight 5

 server production-lb user-resource-service-production:8080 resolvers dns check inter 2000 weight 95

backend enterprise

 balance roundrobin

 server production-lb user-resource-service-production:8080 resolvers dns check inter 2000 weight 100

每個 HAProxy 都會檢查由我們的 API 閘道分配的標頭,稱為 X-Traffic-Group,它決定了此請求屬於哪個客戶群組。基於此,將決定是訪問 Canary 部署還是生產部署。

顯然,在這個規模下,kubectl(雖然仍然是我們處理叢集的主要日常工具)並沒有真正讓我們很好地了解一切是否按預期運行,以及哪些可能過度或複寫不足。

由於我們進行藍綠部署,我們有時會忘記在新版本啟動後關閉舊版本,因此某些服務可能過度複寫,並且在 kubectl 中列出的 25 個部署的雜亂中找到這些問題並非易事,至少可以這麼說。

因此,擁有像 Kubernetes 這樣非常 API 驅動的容器編排器,對我們來說真是一個天賜之物,因為它允許我們編寫工具來處理這個問題。

我們建構的工具可以直接從 kubectl 運行(例如 bash 腳本),也可以直接與 API 交互並了解我們的特殊架構,以便快速了解系統的概況。這些工具主要是在 Go 中使用 client-go 庫建構的。

其中一個工具值得強調,因為它基本上是我們真正一目瞭然地查看服務健康狀況的唯一方法。它會遍歷我們所有具有 tier: service 選擇器的 Kubernetes 服務,並檢查隨附的 HAProxy 部署是否可用,以及所有 Pods 是否以 4 個複本運行。它還檢查 HAProxy 後面的 4 個服務(內部、免費、其他和生產)是否至少有 2 個端點在運行。如果未滿足任何這些條件,我們會立即在 Slack 和電子郵件中收到通知。

使用我們之前的編排器管理這麼多 Pods 被證明非常不可靠,並且覆蓋網路經常導致問題。Kubernetes 則不然 - 即使將我們目前的工作負載增加一倍以進行測試,也能完美運行,總體而言,自從我們安裝它以來,叢集一直像時鐘一樣運作。

切換到 Kubernetes 的另一個優勢是 Kubernetes 資源規格的可用性,除了 API(我們用它來編寫一些用於部署的內部工具)。這使我們能夠擁有一個包含我們所有 Kubernetes 規格的 Git 儲存庫,其中每個軌道都從通用範本生成,並且僅包含用於 Canary 軌道和名稱等可變事物的佔位符。

對叢集的所有變更都必須透過修改這些資源規格的工具進行,並自動簽入到 git 中,因此,每當我們看到問題時,我們都可以除錯基礎架構隨著時間推移經歷的變更!

總結這篇文章 - 透過將我們的基礎架構遷移到 Kubernetes,Bitmovin 能夠擁有

  • 零停機部署,讓我們的客戶可以 24/7 不間斷地進行編碼
  • 快速的開發到生產週期,使我們能夠更快地交付新功能
  • 多個品質保證層級以及對生產部署的高度信心
  • 跨雲端架構和內部部署的無所不在的抽象
  • 穩定可靠的服務健康檢查和排程
  • 圍繞我們基礎架構的自訂工具,用於檢查和驗證系統
  • 部署歷史記錄(git 中的資源規格 + 自訂工具)

我們要感謝 Kubernetes 社群在該專案上所做的令人難以置信的工作。該專案的推進速度簡直令人驚嘆!在如此多樣化的環境中保持如此高水準的品質和穩健性真是令人驚訝。