微博K8S實戰:如何應對春晚等突發峰值流量?
本文通過圍繞微博業務需求和大家分享如何使用 Kubernetes 來解決具體的業務問題和相應的解決方案。
文中分享了基于 Kubernetes 的 PaaS 層彈性混合部署方案,其中有 Kubernetes 的優點,也有部分 Kubernetes 在企業落地的缺陷,希望本文對大家有一定的借鑒意義。
微博容器平臺簡介
2016 年微博平臺實現基于混合云的彈性平臺 DCP,提升了 Feed、手機微博、廣告、搜索、話題、視頻、直播等多個核心業務熱點應對能力。
2017 年微博平臺率先探索基于 Kubernetes 的 PaaS 層彈性混合部署解決方案,并且積極的和社區保持同步。
2018 年實現 CI/CD 與生產環境混合部署,2019 年春晚實現部分核心業務混合部署與彈性伸縮。
本文主要介紹微博平臺落地 Kubernetes 過程中的一些經驗教訓。
為什么選擇 Kubernetes
因為歷史原因,微博 Docker 容器治理是采用獨占物理機(虛擬機)模式,直接使用物理機的網絡協議棧,服務治理采用服務池方式。
隨著設備計算能力的提升,這種治理方式有幾個問題急需解決:
- 利用率問題:一個服務池內新、老設備共存,因為業務容器需要兼容老設備規格,導致服務無法充分發揮出新設備應有的計算能力。
- 容器網絡問題:因為直接采用物理機網絡棧,導致業務混合部署只能采用調整業務監聽端口的方式,造成接入成本、管理成本高。
- 調度治理問題:因為默認采用獨占策略,服務池之間資源相互隔離,不同類型的業務類型無法共享資源,導致資源總是相對緊缺。
Kubernetes 提供標準化的容器管理,CNI 網絡虛擬化,自定義彈性調度策略,能夠很好的解決上述問題。
但是 Kubernetes 面向公有 PaaS 的實現方案在內網環境下有些方面不適用,主要有如下幾個方面:
- 網絡虛擬化:在 BGP 的虛擬網絡方案和隧道的虛擬網絡方案中都會引入 iptables 來完成流量牽引,和防火墻的相關功能。
在企業內網使用的過程中并沒有很強烈的防火墻需求,引入 iptables 往往會造成性能下降(nf_conntrack 表被打滿,NAT 性能太低)。
所以微博平臺現階段沒有使用 BGP 虛擬化網絡方案和隧道虛擬化網絡方案。
- 滾動發布:目前的 Kubernetes 的滾動發布(Deployment)不支持 In-place rolling updates ,每次一個 Pod 都有可能被分配不同的 IP 地址。
在企業內部使用容器的時候,固定 IP 的需求往往很強烈,所以我們拋棄了 Kubernetes 而選擇了整合了公司內部的滾動發布系統。
- 資源隔離:原生的內存隔離策略中不支持 Swap 的限制,容器會占用物理機的 Swap,我們修改了內存隔離限制。
- 負載均衡:原生的 Service 模式會引入 iptables 來做 NAT,同時 Service 的負載是硬負載,沒法調整流量權重。
我們基于 Kubernetes 搭建了一套 PaaS 平臺,對 Kubernetes 進行了改進,提供了以下功能:
- 網絡虛擬化:基于 CNI,提供了隔離內網和公有云網絡差異的虛擬化網絡方案。
- 調度管理:基于 kube-scheduler,提供了鎖定 IP 的調度系統,該系統支持帶寬初篩,硬盤初篩,機房就近調度,返回庫存狀態,提前鎖定 IP 功能等功能。
- CI/CD:一鍵發布代碼,替代 Kubernetes 的 Deployment 進行滾動發布。
- 資源隔離:在原有的隔離策略上,擴展出計算資源隔離,網絡資源隔離,存儲資源隔離。
- 負載均衡:整合已有的調度系統,利用微服務快速部署+彈性調度提前鎖定 IP,減少服務抖動耗時。
- 模塊化運維:把已有的物理機運維工具整合到容器中,在 Pod 里面共享存儲,共享網絡。
- 彈性擴縮容:通過對 DCP 的整合,使其具有了容器彈性擴縮容的功能。
- 監控:通過模塊化的運維體系,整合了監控所需日志,無縫連接已有功能。
圖一
整體方案如圖一,微博容器平臺劃分出如下幾層:
- 服務層:平臺的主要入口提供容器擴縮容、上下線、維護服務池、負載均衡,監控管理等功能。
- PaaS 層:提供容器管理和調度管理等相關功能,負責將服務層的請求轉化成對應容器的操作。
- IaaS 層:提高機器資源、網絡資源、存儲資源供 PaaS 生成的容器使用,負責對容器使用資源進行管理。
容器彈性化擴縮容平臺建設
微博容器彈性擴縮容平臺,是在 Kubernetes 基礎上進行了改進,充分利用了微博平臺已有的資源,避免重復造輪子。具體的工作如下:
基礎建設之網絡虛擬化
之前已經說過了,微博的容器會獨占物理機的網絡協議棧,雖然能夠大幅度提升網絡效率,但是會導致多容器部署時出現端口沖突。
為了解決端口沖突需要使用虛擬化網絡技術提供容器獨立的 IP 地址。多個容器獨立 IP 需要解決以下的三個問題:
- 容器的 IP 地址分配問題。
- 容器的 IP 路由問題。
- 虛擬化網絡對網絡的性能損失要最小化。
因為采用 Kubernetes IP 分配都是通過記錄在 etcd 中,所以不會出現分配重復或者分配錯誤的問題,而第二個問題社區里面通常會采用隧道類型方案和 BGP 方案。
以下是隧道模式和 BGP 模式的優缺點對比如表一, 性能測試如表二(BGP 主要工作是路由交換,轉發等不受影響,等同于物理機性能。)
表一
表二
在測試結果中顯示 vxlan 因為需要封裝和解封隧道導致帶寬損耗過 5%,所以隧道方案不適用于內網的網絡環境。
而 BGP 的方案 Calico 會引入 Iptables 去做 ACL,不僅在業務峰值流量的情況下會觸發 nf_conntrack 表被打滿丟包的風險。
而且 BGP 方案在公有云和內網落地的時候也存在問題:
- 公有云方面:從公有云虛擬機發出的報文必須是 Mac 地址和 IP 地址匹配的,所以導致在公有云機器上 BGP 虛擬網絡的容器根本無法通信。
- 內網方面:內網機器的上聯交換機上做了 Vlan 和 IP 的綁定,如果在內網機器上起了一個其他網段的 IP 地址,報文發送不出本機。
接下來先來看看在網絡方案上我們做的一些工作,見圖二:
圖二
微博虛擬網絡主要是四方面內容:
- 對機房網絡進行改造,修改機器的上聯交換機為 trunk 模式,支持多 Vlantag 的網絡通信。
- 在物理機層面通過創建網卡子接口(如圖一左側),通過對網卡子接口做 MacVlan 虛擬網卡插入 Kubernetes 的 Pause 容器中,把容器網絡與物理網絡打通。
- 公有云方面通過創建彈性網卡,讓一個機器上有多個網卡,且每塊網卡帶獨立 IP 地址,然后對新加的網卡做 host-device,將網卡的所屬 network namespace 修改為 Kubernetes 的 Pause 容器,把容器和物理網絡打通。
- 對 CNI 插件進行修改,能夠給容器分配指定 IP 地址 。
圖二左側是簡化后的內網網絡拓撲,容器的虛擬網卡通過 MacVlan 與物理網卡的網卡子接口相連,發出的報文會帶上網卡子接口的 Vlantag。
而這部分的流量上到上聯交換機之后就和物理機發出的沒有任何區別,之后的都是交換機和網關去解決掉路由的問題。
這個方案的設計對現有的環境依賴最小,同時改動量少。實現機房物理網絡與容器網絡的扁平化,解決了容器網絡和物理網絡互聯互通的問題。
由于沒有隧道解封性能問題,性能基本上持平物理機性能。本質上這是一個私有云的網絡解決方案,但是很好的解決了問題。
圖二右側是簡化后的公有云網絡拓撲,通過把物理機上的網卡遷移到容器里面來間接的實現多 IP。由于是虛擬機的彈性網卡,等同于虛擬機上的物理網卡,性能沒有問題。
虛擬網絡后續的演近:對 Calico 進行改進取消 Iptables 依賴。利用 Calico 去解決內網網絡方案中 IP 浪費的問題。同時可以對 Calico 做進一步的研究,如動態遷移容器如何保持 IP 漂移。
基礎建設之調度管理
容器調度,其實是為了提高資源利用率,同時減少資源碎片化。
Kubernetes 的調度策略做的相對靈活,對 Pod 的調度通過三個階段來實現,初篩階段用于篩選出符合基本要求的物理機節點,優選階段用于得到在初篩的節點里面根據策略來完成選擇最優節點。
在優選完畢之后,還有一個綁定過程,用于把 Pod 和物理機進行綁定,鎖定機器上的資源。這三步完成之后,位于節點上的 kubelet 才能開始真正的創建 Pod。
在實際的接入過程中,Kubernetes 的基礎調度策略不能滿足平臺的業務需求,主要有如下兩點:
- 因為沒有規格的概念,所以無法給出庫存狀態。
- 初篩的緯度太少,目前只支持 CPU,內存的初篩,優選不支持同機房就近調度。
整體方案
圖三
整體的調度管理分成如下幾層:
- 接口層:用于接收請求返回特定規格,特定調度需求下的庫存數量,同時返回鎖定好的虛擬 IP 地址。也用于接收請求釋放虛擬 IP 地址。
- 初篩層:對緩存中的節點信息進行初篩,返回能部署規格的全部物理機節點信息。
- 優選層:根據優選結果,模擬部署 Pod,統計整體庫存。
- 部署策略層:按照部署策略挑選物理機,并且鎖定物理機上的虛擬 IP 地址,返回庫存信息。
調度管理之接口層
鎖定庫存接口層的邏輯:
- 把請求參數轉化為 Kubernetes 的 Pod 對象 -> v1.Pod。
- 把 Scheduler 的 Cache 進行一次深拷貝,后續的動作都會在這個深拷貝的 Cache 中完成。
- 請求監控返回物理機的實時硬盤信息,實時帶寬信息。整合到深拷貝 Cache 中。
- 連同請求參數都傳遞給初篩層。
釋放庫存接口層的邏輯:
- 調用 Kubernetes 接口,把物理機節點上虛擬 IP 的 label 改成 unusing。
調度管理
圖四
初篩層:對上述的 Cache 部分里面的 Node 節點進行 CPU,內存,硬盤和帶寬的初步篩選,返回通過篩選的所有物理機節點。
優選層:對上述的物理機節點信息進行打分,對節點進行排序。然后根據請求所需部署的容器數量,按照物理機節點的進行模擬部署(挑選物理機按照分數從高到低排列),直到全部節點上可部署容器數量為 0,統計每個節點能部署的容器個數。
部署策略層:根據請求參數的不同(目前只支持集中/平鋪),鎖定物理機上的 IP 地址(調用 Kubernetes 的 API 把物理機上虛擬 IP 的 Label 置為 Using 狀態),并且返回這些 IP 地址。
整體流程圖
圖五
后續演進:支持調度優先級,且能根據實際的資源使用情況進行調度而不是 Kubernetes 預分配的資源進行調度。
基礎建設之資源隔離
Kubernetes 支持 CPU、內存的隔離。在宿主機層面支持驅逐模式。通過虛擬化網絡的方案,主容器網絡和混部容器的網絡分割開來。整體的資源隔離目標是為了隔離開主容器和混部容器對資源的使用。
以下是我們做的一些改進:
- 計算資源隔離:K8S 提供了內存的限制能力,是通過 OOM 來限制內存的使用。在此基礎上,我們還增加了限制容器使用物理機的 Swap。
- 存儲資源隔離:K8S 沒有提供基于物理機的存儲資源配額方案,但是提供了相關的框架接口。在此基礎上,開發了有配額大小限制的物理機靜態存儲解決方案。
- 網絡資源隔離:針對容器的網絡限速方案,已經在內部測試通過,同時幫助社區完善了相關的限速代碼。
后續的演進:資源隔離的后續方向很多,首先要解決的是 Kubernetes 如何能動態設置資源閾值。其后需要能夠設置內存 OOM 優先級,以及滿足資源超賣的需求。
基礎建設之 CI/CD
平臺于 2018 年基于 Gitlab 開發了 CI/CD,通過 CI/CD 和 Kubernetes 的配合來完成從代碼提交到上線的完整流程。
其中使用 Kubernetes 的上線流程(如 Deployment)的滾動發布存在著容器 IP 不固定,動態化的問題是因為 Kubernetes 的設計原則中對集群的管理尤其是服務升級過程中需要保持“無損”升級(升級過程中提供服務的副本數一直符合預期)。
如果對一個 Deployment 進行滾動升級,那么這個 Deployment 里面的 IP 地址和滾動升級之前的 IP 地址是不會相同的。
而如果集群夠大,一次滾動發布就會導致負載均衡變更 (集群副本數/滾動發布步長)次。
對于微博服務來說,頻繁變更會導致這個負載均衡轄下,所以后端實例的接口不穩定。
而平臺內部的之前的上線系統是根據業務冗余度及業務實際需要來調整上線的步長,減少在上線過程中業務的抖動,也就是通常說的 In-place rolling updates。保證在上線過程中容器的 IP 不變。
整體流程的核心思路為:
- 切斷容器的流量
- 進行流量檢測,確保已經沒有線上流量
- 清理舊容器
- 部署新容器
- 檢測服務是否正常啟動(端口檢測,接口驗證)
- 接收線上流量,提供服務
針對該問題,容器平臺沒有使用 Kubernetes 的原生滾動發布而是做了以下幾個改進和適配:
- 首先不使用 DP,RC 的概念來完成滾動發布,只使用 Pod 的概念。
- 集成已有的上線系統,來完成滾動發布,回滾功能。
- 流量引入/流量拒絕 利用 Kubernetes 容器生命周期管理的 lifecycle(修改了其中的 postStar 的原生實現,因為原生里面只調用一次,不管成功與否都會殺掉容器。改進成了如果不成功會按照指定的次數或時間進行重試),服務檢查利用 liveness probe、readiness probe 來完成。
主要流程有:
- 提前給每個機器上劃分虛擬 IP 段,并給機器打上虛擬 IP 的 Label。
- 在原有的上線系統中增加對 Kubernetes 管理容器的上線流程,上線過程中通過服務池中已有 IP 反查 Pod Name,然后刪掉舊 Pod,然后用新的鏡像 tag 生成 Pod 的 json 字符串(其中 nodeSelect=${IP}),然后提交 Kubernetes 去創建新 tag 版本的 Pod。
- Kubelet 接到創建請求之后,提取其中的 IP 地址發給 CNI,創建指定 IP 的新 tag 版本 Pod。
上線回滾的流程變成了刪除 Pod,創建 Pod 的固定化操作,見圖六:
圖六
由于給機器打好了虛擬 IP 標簽,所以 Pod 的創建會被分配給固定的物理機去執行,配合修改之后的 CNI 就能創建指定新 tag +固定 IP 的 Pod 來提供服務。滾動發布和回滾變成了刪除 Pod,創建 Pod 的固定化操作。
基礎建設之模塊化運維
由于之前的容器是獨占物理機的模式,所以對于容器的運維也是在物理機上進行的,一些功能如日志處理、域名解析、時鐘同步、運維 Agent 管理以及定時任務等都是在物理機層面操作。
如果開始多容器混合部署,以上的功能都兼容改動工作量大。再加上平臺面向的業務方眾多,需求各不相同。
例如日志推送的方式已經出現 scribe、flume、filebeat 等不同的方式,業務運維需要根據自身去定制運維容器,由此可見模塊化運維的必要性。
圖七
我們基于 Kubernetes 的 Pod 概念做了如下的工作,整體架構見圖七:
- 單獨做了運維容器,把運維相關的工具集成在容器里面。
- 運維容器和業務容器共享網絡協議棧,共享日志存儲。
- 在容器里面共享存儲是帶配額的靜態存儲。
模塊化運維之定時任務
物理機上的定時任務依賴于 Crontab,而 Crontab 會由 Systemd 來啟動。
在容器中使用 Systemd 啟動會涉及到提權問題,在實踐過程中發現用 Systemd 如果權限控制不當會造成容器被 Kill 的情況。
所以單獨開發了兼容 Linux Crontab 語法的定時任務工具 -gorun,把這個工具集成在了運維容器里面,替代了 Crontab 來完成定時任務。
模塊化運維之日志處理
圖八
日志的處理主要包括監控采集、日志推送、日志查詢、日志壓縮清理四方面:
- 日志推送:通過 scribe,flume 等方式接收業務日志,推送給信息系統部等數據處理部門,如圖八。
- 日志查詢:容器產生的日志需要能夠靜態存儲三天左右,方便故障定位。所以日志會存儲基于 Kubernetes 的 PVC 概念開發的本地帶配額的靜態存儲里面。
- 日志壓縮清理:磁盤空間有限,打印的日志需要定期的清理和壓縮。
- 監控采集:通過監聽文件變化或者監聽端口來采集需要的監控數據。
通過上述手段,能夠利用現有的日志體系,同時開發的工作量最小,通過這樣的操作,以后的容器想要接入,只需要在 Pod 的配置文件里面多寫一個 Container 的配置即可。
后續的演進:后續的運維容器將會進一步的拆分,做成標準化的服務,例如域名解析容器,日志推送容器。讓業務的接入變得更加的容易。
基礎建設之彈性擴縮容
彈性伸縮在微博的應用很廣,作為支持了全公司春晚擴容的 DCP 系統其主要能力就是進行 IaaS 層虛擬機的彈性伸縮。它是對業務進行保障的重要手段之一。
彈性擴縮容保證在峰值流量來臨時通過擴容提高接口的可用性,在業務量級下降后回收資源節省了成本,而 PaaS 層的擴縮容比 IaaS 層的擴縮容更具有優勢。
一來是因為 PaaS 的啟動更輕,沒有虛擬機的初始化階段,所以啟動更快;二來是因為我們的彈性調度系統能提前鎖定 IP,可以做到業務容器和變更 Nginx 同步進行。
所以在 PaaS 層的彈性擴縮容,我們目前做到了如下幾點工作:
- 定時擴縮容實現
- 自動化擴縮容的實現
定時擴縮容是復用了 DCP 系統的定時擴縮容功能,并做了一定的適配,目前可以選擇使用原生擴容模式(IaaS 層)和 Kubernetes 擴容模式。
選擇好了模式之后需要填的就是 Pod 的調度參數和本身 Pod 的標準化 Json 文件,之后就是常規功能。
自動化擴縮容的實現,是基于 CI/CD 系統中的放量系統完成的,CI/CD 會在上線過程中有單機放量的步驟,其中會采集放量容器的監控數據,業務數據和日志數據。
橫向對比當前服務池的整體接口耗時和縱向對比歷史七天內單機的接口耗時。
通過這套系統,把相關的閾值指標導出之后,集成到監控系統中,如果判斷接口的 QPS 和耗時產生了惡化,會相應的觸發擴容操作,不同于 IaaS 層的擴容,PaaS 的擴容在存量機器上是免費的。
例如前端某接口的 QPS 過高,處理不過來了可以在機器學習服務池的機器上進行容器擴縮容去先扛住量。
基礎建設之負載均衡
微博平臺的 7 層負載均衡方案是使用 Nginx。目前仍使用的是靜態文件的進行路由信息管理。
Nginx 變更系統實現了一套動態的解決方案,將 upstream 與對應的服務池進行關聯,確保對應的接口由對應的服務池來提供服務,當服務池內有節點變更時,自動觸發配置下發進行 Nginx 的變更。
Kubernetes 從最開始的 Service 上就開始了負載均衡和服務發現的嘗試,例如在 Service 上利用 Iptables 進行請求的隨機負載(問題在于后端某個實例的壓力已經很高了,由于是 Iptables 的硬負載不能感知到,依然把請求傳遞過來)。
而且和網絡虛擬化一樣引入 Iptables 會有 nf_conntrack 打滿的風險,考慮到公司內部已經有成熟的負載均衡系統,這塊進行了一個適配。
由于彈性調度的功能,我們會在創建 Pod 之前就可以鎖定 IP 地址。當 IP 地址鎖定后,我們可以同步的變更我們的負載均衡系統+啟動我們的業務容器,能夠更快的響應業務請求,見圖九:
圖九
基礎建設之監控系統
監控和日志都是平臺內部成熟的組件,通過模塊化運維中日志信息的采集推送,監控數據采集推送已經兼容原有方式推送到監控平臺。而物理機監控已經通過運維物理機部署 Agent 來采集,不需要重新引入別的工具去完成。
整體的監控總共有如下的幾個部分:
①物理機信息監控:物理機 CPU、內存、IOPS、帶寬、負載、網絡等基礎監控信息。
②業務容器的業務監控:包括接口 QPS、耗時、返回狀態碼占比、err/warn 日志記數、鏈接資源耗時等,見圖十:
圖十
③容器使用物理資源監控:CPU、內存、IOPS、帶寬、負載、網絡等基礎監控信息,由于已有一套監控體系,修改了的實現把原先需要集中處理的部分落在了計算節點本地,然后通過已有的物理機監控數據推送到遠端監控上。
一來避免了之前 Heapster 結構中的單點問題;二來可以復用現有的日志推送架構,見圖十一:
圖十一
④Kubernetes 組件監控:包括單機的 Kubelet 接口耗時、成功率監控、日志中 err、warn 監控,同樣包括 Master 節點的同類數據監控。
基礎平臺的建設不限于上述的部分,還做了鑒權,DNS 優化等相關服務。限于篇幅不便展開,于此同時微博平臺 Kubernetes 也在積極的與社區保持迭代,爭取做到能回饋社區。
春晚紅包飛業務落地
2019 年春晚期間,后端服務支持紅包飛業務,如果按照傳統方式需要近百臺的公有云設備按需成本。
我們通過把垂直業務從大容器中抽離形成微服務,利用 Kubernetes PaaS 整合能力,在未增加資源成本的情況下完成春晚紅包飛保障。
目前有 6 大服務池接入 Kubernetes PaaS,共管理數千核 CPU,數 TB 內存的計算資源。
通過 Kubernetes 整合閑散資源與合理的混合部署,可提供近整體的 30% 的彈性伸縮能力。
綜上,是微博平臺在 Kubernetes 與業務更好結合道路上的一些體會,實際業務接入過程中依然會有很多的挑戰。
但是在團隊成員的努力下,最終實現了方案的落地并保障了春晚的線上業務的穩定。
通過這次的平臺建設充分體會到只有和業務結合,并且服務好業務才能更好的促進自身架構的成長,很多時候看似很理想的方案,面對真實的業務需求還是會出現紕漏。
我們會吸取歷史的教訓,總結經驗。爭取利用先進的技術來為公司創造更大的價值。
展望未來
未來我們將長期運行混合部署的微服務架構,優化調度系統,對接更多的 IaasS 層提供商。
更進一步提高接口可用性和資源利用率,也會對服務的穩定性和資源的利用率做進一步探索,利用技術提升研發效率也是我們后續的方向。
在探索的同時我們也會對 ServiceMesh、Serverless 持續關注,結合著業務需求,更進一步優化容器基礎平臺。
作者:彭濤、王琨
簡介:彭濤,主要負責微博容器平臺的開發工作。Kubernetes 代碼貢獻者。曾就職于百度基礎架構部,百度公有云事業部。長期從事云計算行業。熟悉網絡虛擬化,SDN,OpenStack,Kubernetes。致力于推動 Kubernetes 在微博的應用落地,構建高效的 PaaS 平臺。
王琨,微博平臺高級產品運維工程師,主要負責微博 Feed、用戶關系、架構業務的運維支撐及改造工作。擅長大規模分布式系統集群的管理與運維,疑難問題分析,故障定位與處理。致力于推進運維自動化,構建微博平臺高效的運維平臺。