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

Bitmovin 如何在雲端和本地使用 Kubernetes 進行多階段金絲雀部署

在多個公共雲上執行大規模影片編碼基礎設施是一項艱鉅的任務。在 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 的服務,主要是因為企業 IT 通常不願意向公共網際網路開放埠,而且並非每個裸機 Kubernetes 安裝都開箱即用地支援外部預置的負載均衡器。為了避免這些問題,我們部署了一個 BitmovinAgent,它在叢集內部執行,並輪詢我們的 API 以獲取新的編碼作業,而無需任何網路設定。然後,該代理使用本地可用的 Kubernetes 憑據啟動新的部署,這些部署透過 Kubernetes API 在可用硬體上執行編碼器。

即使沒有完整的雲集成,我們從使用 Kubernetes API 中獲得的持續排程、健康檢查和監控也確實使我們能夠專注於讓編碼器在容器中工作,而不是將寶貴的工程資源花費在整合一系列不同的虛擬機器管理器、機器提供者和監控系統上。

多階段金絲雀部署

我們與 Kubernetes API 的首次接觸並非為了本地編碼產品。將我們的容器化編碼工作流構建在 Kubernetes 上,是因為我們在 Bitmovin API 基礎設施的開發和部署過程中,看到了 Kubernetes 平臺令人難以置信的簡便性和強大功能後,才做出的決定。大約四個月前,我們遷移到了 Kubernetes,它使我們能夠快速迭代我們的服務,同時滿足我們零停機部署和穩定開發到生產流水線的需求。為了實現這一目標,我們提出了一種架構,該架構執行近千個容器,並滿足我們在第一天就設定的以下要求

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

顯然,如果每個合併的功能都立即部署到生產環境,那麼 #2 和 #3 之間就存在衝突——我們如何確保這些釋出沒有錯誤,並且不會對我們的客戶產生負面影響?

為了克服這個矛盾,我們為每個微服務設計了一個四階段的金絲雀管道,同時部署到生產環境,並隔離對客戶的更改,直到新構建在生產環境中被證明可以可靠且正確地執行。

一旦新的構建被推送,我們將其部署到一個只有內部測試和整合測試套件才能訪問的內部階段。一旦內部測試套件透過,QA 報告沒有問題,並且我們沒有檢測到任何異常行為,我們就會將新的構建推送到我們的免費階段。這意味著 5% 的免費使用者將被隨機分配到這個新構建。在這個階段執行一段時間後,構建會被提升到下一個階段,該階段將有 5% 的付費使用者路由到它。只有當構建成功透過這三個障礙後,它才會被部署到生產層,在那裡它將接收來自我們剩餘使用者以及我們的企業客戶的所有流量,這些客戶不屬於付費使用者組,並且他們的流量永遠不會被路由到金絲雀軌道。

此設定預設使我們的 Kubernetes 安裝規模相當大,因為我們所有的金絲雀層都以最少 2 個副本可用。由於我們目前正在叢集中部署大約 30 個微服務(並且還在增長),因此每個服務至少有 10 個 Pod(8 個應用程式 Pod + 最少 2 個執行金絲雀路由的 HAProxy Pod)。儘管在現實中,我們首選的標準配置通常是執行 2 個內部 Pod、4 個免費 Pod、4 個其他 Pod 和 10 個生產 Pod,以及 4 個 HAProxy Pod——總共大約 700 個 Pod。這也意味著我們正在執行至少 150 個服務,這些服務為其底層的微服務金絲雀層提供靜態 ClusterIP。

一個典型的部署看起來是這樣的:

| 服務 (ClusterIP) | 部署 | #Pod | | 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 服務之前,負責負載均衡不同金絲雀版本服務的,是一個小型 HAProxy Pod 叢集,它們的 haproxy.conf 來自 Kubernetes ConfigMaps,看起來像這樣:

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 的標頭,該標頭決定此請求屬於哪個客戶桶。基於此,決定是命中金絲雀部署還是生產部署。

顯然,在這種規模下,kubectl(儘管仍然是我們日常在叢集上工作的主要工具)並不能很好地概述一切是否都按預期執行,以及哪些可能複製過多或過少。

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

因此,擁有像 Kubernetes 這樣以 API 驅動的容器編排器,對我們來說簡直是天賜之物,因為它使我們能夠編寫處理這些問題的工具。

我們構建的工具可以直接在 kubectl(例如 bash 指令碼)上執行,或者直接與 API 互動並理解我們的特殊架構,從而為我們提供系統概覽。這些工具主要是使用 client-go 庫用 Go 語言構建的。

其中一個工具值得特別強調,因為它基本上是我們一眼就能真正看到服務健康狀況的唯一方式。它會遍歷所有帶有 tier: service 選擇器的 Kubernetes 服務,並檢查伴隨的 HAProxy 部署是否可用,以及所有 Pod 是否以 4 個副本執行。它還會檢查 HAProxys 後面的 4 個服務(內部、免費、其他和生產)是否至少有 2 個端點正在執行。如果這些條件中的任何一個不滿足,我們就會立即在 Slack 和透過電子郵件收到通知。

用我們之前的編排器管理如此多的 Pod 證明非常不可靠,並且覆蓋網路經常導致問題。但 Kubernetes 則不然——即使將我們當前的測試工作量翻倍也能完美無缺地執行,而且總的來說,自從我們安裝它以來,叢集一直執行得像發條一樣。

切換到 Kubernetes 的另一個優點是除了 API(我們用來編寫一些內部部署工具)之外,還可以使用 Kubernetes 資源規範。這使我們能夠擁有一個包含所有 Kubernetes 規範的 Git 儲存庫,其中每個軌道都是從一個通用模板生成的,並且只包含金絲雀軌道和名稱等可變事物的佔位符。

所有對叢集的更改都必須透過修改這些資源規範的工具進行,並自動提交到 Git,因此,無論何時出現問題,我們都可以除錯基礎設施隨時間發生的變化!

總結一下這篇文章——透過將我們的基礎設施遷移到 Kubernetes,Bitmovin 能夠實現:

  • 零停機部署,讓我們的客戶可以 24/7 不間斷地編碼
  • 快速的開發到生產週期,使我們能夠更快地推出新功能
  • 多層質量保證和對生產部署的高度信心
  • 跨雲架構和本地部署的普遍抽象
  • 穩定可靠的服務健康檢查和排程
  • 圍繞我們的基礎設施定製工具以檢查和驗證系統
  • 部署歷史(Git 中的資源規範 + 自定義工具)

我們感謝 Kubernetes 社群在專案中做出的傑出貢獻。專案推進的速度令人驚歎!在如此多樣化的環境中保持如此高水平的質量和健壯性,著實令人震驚。