歷時3年,美圖全面容器化踩過的坑
本文圍繞美圖業務和大家分享美圖容器基礎平臺建設中的探索經驗以及在業務落地過程中的具體問題和相應的方案。
美圖從 2016 年開始了容器相關的探索到 2018 年業務基本實現容器化,今天主要會圍繞美圖的業務情況,聊一聊在容器基礎平臺建設探索過程中遇見的一些問題,以及具體如何落地的方案,希望可以給大家一些參考。
美圖公司成立于 2008 年 10 月,懷揣著“成為全球懂美的科技公司”的愿景,創造了一系列軟硬件產品,如美圖秀秀、美顏相機、短視頻社區美拍以及美圖拍照手機。
美圖產品的多樣化也催生了復雜多樣的服務端技術,億級 MAU 對服務端的技術要求也越加嚴苛。
2016 年我們開始調研容器化相關技術,2017 年我們開始擁抱 Kubernetes,2018 年容器平臺基本落成并推進業務的整體容器化。
我們期望通過容器化可以提升公司研發人員的線上支撐能力,提升持續開發和集成的能力,提升整體資源利用率和服務的可用性。
美圖容器化建設實踐
容器化之前
在業務容器化之前,我們業務是以物理機的方式部署到北京、寧波等多個 IDC,部分服務部署到公有云。
其中大部分業務是單 IDC 部署,部分業務存在跨 IDC 間的調用,然后 IDC 之間通過專線打通。
當時存在的幾個重要的問題:
- 服務部署沒有進行隔離,業務混部需要控制得非常小心,資源的利用率非常低。
- 業務類型較多,缺乏完全統一和完善的自動化運維手段,業務的增長會伴隨著維護人力的增加。
- 測試環境與生產環境存在較大差異,這也導致一些生產環境問題不能在測試期間發現。
- 開發人員線上意識較薄弱,線上故障率持續較高。
- 面對機房級故障時業務遷移非常困難,出問題時只能尷尬地等機房恢復。
對此,我們希望通過積極的調整來解決掉存在的種種問題,而容器化是一個非常好的機會,可行性也比較高。
同時我們希望借著這個機會對我們的技術架構以及相關人員做一次從意識到技能的全面提升,為未來的技術演進鋪平部分道路。
選擇 Kubernetes
2017 年容器編排的“戰爭”打完,Kubernetes 取得領先并趨于成熟。我們也徹底投入到 Kubernetes 當中,容器系統的大規模落地離不開成熟的容器編排系統。
Kubernetes 對容器的編排、資源的調度以及強大的擴展能力極大地方便了我們平臺的構建。
單體容器如集裝箱,它統一的標準方便了調度運輸。Kubernetes 提供了對集裝進行集中調度的碼頭和輪渡,讓一切井然有序并且易于實施。
容器基礎平臺則好比基于容器和 Kubernetes 之上的完整的運輸系統,它需要集裝箱,碼頭輪渡,高速公路等整套體系,實際服務容器化的過程不是一個簡單的打包裝箱和調度規劃過程。
大部分的服務與外界是有割不開聯系的,需要保持與外界的聯通,服務進駐容器更像是用戶住進集裝箱房子,她需要相應的基礎配套設施,像水,電,燃氣等等。
所以我們首先是提供各種基礎設施,讓服務能進駐容器而不會水土不服。比如需要做好資源隔離,打通底層網絡,做好負載均衡,處理好應用日志等等。
容器平臺建設
首先我們對多地機房及云端資源進行梳理,成為我們計算及存儲的資源池。
同時我們構建起基礎的容器網絡,日志系統,存儲服務等底層基礎,然后依托 Kubernetes 完成了基于多租戶的容器管理平臺建設,提供完善的項目管理,服務編排,資源調度,負載均衡等能力。
我們提供同集群多租戶的模式,所以對集群內的業務隔離,資源調度,彈性伸縮等都會有很高的要求。
同時我們也存在多集群混合云的應用場景,因此存在著對跨集群跨機房的容器網絡互通,多集群的負載均衡等的特定需求。
①基礎建設之網絡
先來看基礎建設中網絡這一層。網絡屬于底層建設,解決的問題非常關鍵。
容器網絡解決的問題主要包括:
- Pod 內部容器間的通信
- Pod 與 Pod 的通信
- Pod 與 Service 的通信
- Service 與集群外部的通信
- 跨集群跨網段通信
容器網絡除了要解決上述的 5 個問題的同時也需要考慮如何將網絡層的性能損失最小化。接下來先來看看在網絡方案選擇上我們的一些考慮。
Kubernetes 通過 CNI 提供了非常強的擴展能力,同時社區的活躍也讓網絡插件有了更多選擇的空間。
CNI 是 CNCF 旗下的一個項目,作為容器網絡標準,由一組用于配置網絡接口的規范和庫組成,同時還包含了一些插件。
CNI 僅關心容器創建時的網絡分配和當容器被刪除時釋放網絡資源。CNI 具有廣泛的支持,而且規范易于實現,社區支持非常豐富,網絡可選方案也比較多。
網絡方案的選型方面,我們會比較看重性能、穩定性、可維護性相關。在對 Flannel、Opencontrail、Contiv、Weave、Calico、Romana 等開源方案進行詳細的對比和分析之后,最終選擇了 Calico 方案。
經過我們實際的壓測驗證,Calico 的性能比較接近 Host 的性能。從社區活躍度、方案成熟程度和穩定性方面考慮 Calico 也是較為良好,同時其基于 BGP 的方案也為我們后繼的網絡擴展提供了可能性。
那么在 Calico 方案里面,Kubernetes 創建 Pod 時的過程是怎樣的呢?Kubernetes 在創建 Pod 時,會先創建 Sandbox 的虛擬網絡,然后 Pod 中的容器直接繼承 Sandbox 網絡。
在創建網絡時,Kubelet 服務會通過標準的 CNI 接口調用 Calico CNI 插件,為容器創建一個 veth-pair 類型的網卡,并寫入路由表信息。
節點上的路由表通過 Calico Bird 組件以 BGP 的形式廣播到其他鄰居上,其他節點在收到路由條目后再進一步聚合路由寫入到自身節點上。
Calico 在同子網內直接使用 BGP、跨子網時使用 IPIP。 而 IPIP 因為其單隊列的設計存在著性能瓶頸,這嚴重限制了節點的吞吐能力,特別是作為 LB 時影響更為嚴重,所以我們需要規避 IPIP 的問題。
另外因為我們多機房建設需要打通不同機房、不同集群、不同網段的網絡,所以我們需要進一步推進網絡的優化。
圖一
進一步的網絡建設主要是三方面內容:
- 多集群的容器網絡與物理網絡打通
- 去掉 IPIP,關閉 NAT 優化性能
- 增加限速,對節點網絡進行保護
圖一中是簡化后的一個網絡拓撲圖,集群的 Calico-RR(反射器) 與物理網關通過 BGP 進行打通,實現機房物理網絡與容器網絡拉平,解決了多集群網絡互通的問題,同時因為網絡已拉到同一平面,也直接規避 IPIP 的性能問題。
從上圖可以看出,每個機房作為一個 AS 部署一個 Kubernetes 集群,機房內部冗余有多個 RR(反射器),RR 分別與機房內的網關建立 iBGP 連接,機房間的路由器通過 OSPF 同步彼此之間的路由。
除了私有云我們也需要解決混合云的場景,實現集群網絡跨云打通。受限于協議的支持,我們并沒有采用與私有云一樣的打通方式。
因為云端網段相對固定,在規劃完成后變動較少,因此我們采用靜態路由的方式,機房網關上配置相應網段的靜態路由規則,同時在云端路由上也配置上相應路由規則,最終打通路由路徑。
我們在實施的過程中遇到了不少細節上的問題,比如舊集群單個集群跨了三機房,在打通網絡時存在環路的情況需要通過靜態路由來規避問題。
在做網絡限速時,插件存在 Bug 并且社區沒有解決(目前新版本已解決了)需要手動修復。不過問題一一解決后,網絡基礎也完成了生產落地和打通。
②基礎建設之 LB
Kubernetes 在設計上其實是充分考慮了負載均衡和服務發現的,它提供了 Service 資源,并通過 kube-proxy 配合 Cloud Provider 來適應不同的應用場景。
此外還有一些其他負載均衡機制,包括 Service,Ingress Controller,Service Load Balancer,Custom Load Balancer。
不過 Kuernetes 的設計有它實際適用的場景和局限性,并不能完全滿足我們復雜場景落地,同時也考慮到社區方案成熟度問題,最終我們使用了自定義開發的 Custom Load Balancer。
七層負載的選型上,我們是使用了較為成熟的 Nginx Custom Controller 的方案。
我們也對 Envoy 等方案進行了仔細對比,但是考慮到我們公司對于 Nginx 有非常成熟的運維經驗,以及我們很多業務依賴于 Nginx 的一些第三方擴展的功能。
所以從推動業務容器快速落地角度、維護穩定性角度,我們最終選擇了Nginx 作為早期的落地方案。
不過在與 Kubernetes 結合方面,Nginx 還是存在著不少的細節問題,我們也一直在推動解決,同時我們也在考慮 Envoy 等后續的優化方案。
Custom Load Balancer 由 Nginx、 Kubenernet Controller 以及管理組件組成。
Kubenernet Controller 負責監聽 Kubernetes 資源,并根據資源情況動態更新 Nginx 配置。
Nginx Upstream 直接配置對應的 Service Endpoints,并增加相應的存活檢測機制。
因為在網絡層面上物理網與容器網絡已拉平,所以 Nginx 與各集群的 Service 的 Endpoint 是完全鏈路可達的,因此也可直接支撐多集群的負載均衡。
LB 提供了友好的 UI 界面,提高了發布的效率、減少人為故障。 同時,LB 也具備灰度升級,流量控制,故障降級等相關基礎功能,并且提供了豐富的指標,讓運維監控可視化。
③基礎建設之日志
我們再來看一下另外一個比較重要的基礎設施——日志。日志其實是較為關鍵的基礎設施,它是審計,排障,監控報警等所必需的。
日志標準化一直是比較難推進的一件事情,特別是存在大量舊系統的情況下。
一方面面臨業務代碼的改造,另一方面面臨著開發及運維習慣的改造。 而容器化恰好是推進日志標準化很好的一個機會。
圖二
日志架構上我們選用的是 Cluster-Level 的方式,使用 Fluentd 作為節點采集 Agent。
Docker 日志驅動使用 Json log-driver,業務容器日志輸出到標準輸出,最終落盤到容器所屬的目錄中。
Fluentd 采集 Docker 輸出日志,并寫入 Kafka 隊列。Logstash 負責消費隊列數據并入 Elasticsearch,同時由 Kibana 提供統一的日志查詢界面。
在業務容器化落地的過程中,日志也暴露了很多的問題。如:兼容問題,標準輸出的日志可能經過多層封裝導致日志被截斷或者添加了額外內容,如 PHP-FPM ,Nginx 等。
又比如日志格式不統一,不同類型業務日志格式各不相同,比較難完全統一。再比如業務日志可靠性要求,有些允許極端情況下丟失,有些不允許丟失。
為了讓業務能更快速將舊業務遷移至容器平臺,我們為每種特性的業務類型做了定制化方案,輔助業務快速接入。
比如對 PHP,業務將日志輸出至 pipe 再由 tail 容器讀取 pipe 數據輸至標準輸出。
再如大數據業務,因為統計日志與事件日志是分割開的,一起輸到標準輸出會需要較大改造量,改造時間較長,所以對采集方式進行適配調整,業務直接輸出日志到 rootfs,并由宿主機 agent 直接采集 rootfs 約定目錄的日志數據。
總之日志因為與其他系統以及人員習慣耦合度太高,所以要完成標準化,完成系統解耦和人員依賴改變是比較消耗時間精力的一件事情。
④基礎建設之彈性調度
再來看關于調度的一些建設。容器調度,其實是為了解決資源利用率的問題,本質上是一個整數規劃問題。
Kubernetes 的調度策略源自 Borg, 但是為了更好的適應新一代的容器應用,以及各種規模的部署,Kubernetes 的調度策略相應做的更加靈活,也更加容易理解和使用。
Kubernetes 對 Pod 的調度通過兩個階段來實現。Predicates 階段用于過濾出符合基本要求的節點,Priorities 階段用于得到更優節點。
不過由于調度是根據分配量來進行而不是實際使用率,所以業務需要準確評估出自己的資源使用量,如果評估不準有可能會造成資源的浪費或者影響業務質量。
圖三
比如我們看圖三的實例,左邊為空閑服務器,中間每個 Pod 都申請了內存的 Request 及 Limit,調度器根據 Request 計算,服務器能放得下這幾個 Pod,于是調度到了服務器上面。
可以看到實際 Limit 是機器可用資源的兩倍。那么假如這時 Pod1 內存使用超過了 Request,但是遠沒有達到 Limit,這時服務器有可能會出現 Swap。
而更進一步,機器資源不足后,有可能會出現 OOM,內存最多且 Request/Limit 比值最小的那個 Pod 中的進程將會被 OOM Kill。
而這種 Kill 將會造成業務的抖動。同時如果出現大量 Swap 也會使硬盤出現 IO 瓶頸,影響同機器上的其他業務。
在這樣的場景下,當前 Kubernetes 的調度器實現會面臨一些問題,因為它是根據配額來調度的,而業務用戶不合理的配額需求導致了很多預期之外的場景,所以它無法簡單解決。
針對這種場景,我們總結出了以下幾個優化點:
- 優化業務 Request 值,根據業務歷史數據調節 Request。
- 增加運行時指標,將節點當前利用率考慮進去。
- 對于特殊質量保障的業務設置 Guaranteed 級別。
- 規避 Pod 內存進行 Swap。
- 完善 IO 及網絡等資源的隔離機制。
實際上我們在業務的開發測試集群中就遇到了資源緊張同時調度不均衡導致大量 OOM 的場景,并且一度影響到了業務的接入。
本質這還是資源利用率過高時調度的不合理造成。后面經過優化改進才從這種困境中逃離。
再看另外一個場景,我們在將集群分配率擠壓到高于 50% 以上時,很容易出現資源碎片。這時一些資源需求較大的 Pod 會出現無法調度的情況。
在這種場景下則需要通過一些人工干預來進行調度調整。針對這種場景,我們其實是需要通過一些策略調優來優化我們的調度,包括:Reschedule、 MostRequestedPriority、以及優先級來優化集群的調度。
圖四
圖四是簡化后一個單資源的示例。實際應用中我們更希望集群有足夠的冗余度來做資源調度。
同時在混合云場景,我們更希望是盡量使用完部分節點再擴容新的節點。比如圖四所示,希望存在一些大塊的空白節點,盡量減少碎片空間。
不過提高容器利用率,則會遇到上一場景提到的利用率過高時 Pod 資源不足的問題。所以必需首先解決好資源使用的預估及調度優化。
而且也需要在利用率及冗余度上做平衡,設定對相應的策略權重,通過一定水位限制確保節點仍有一定冗余度,如 MostRequestedPriority 的水位限制。水位控制與我們希望的集群利用率直接相關。
圖五
前面考慮很多時候是先從單個維度出發來考慮問題,實際場景中我們是多維度的。
多維度下會復雜得多,并且碎片的情況更容易出現,在這種情況下很多時候得到的只是一個局部更優調度而不是全局更優。
有時候為了更接近全局更優分配需要進行一定重調度。這需要我們自定義控制器做特定的調度策略,同時考慮到大量調動 Pod 可能會帶來的業務抖動,特別是對于部分優雅關閉沒有做很好的業務,所以需要較嚴謹的保護規則和時機控制。如只在機器資源較為緊張時對優先級較低的服務進行調整。
在實際業務容器化過程中,業務對于資源的依賴復雜多樣,根據業務的實際需求,我們進一步引入了 IO,網絡,內存帶寬等一些資源需求的調度,我們在調度策略中也新增了對應的擴展資源。
⑤基礎建設之彈性伸縮
調度解決了業務資源合理分配,彈性伸縮組(HPA)則是在提升資源利用率的同時對業務進行資源保障的重要手段。
它保證在業務量級上來時能及時的進行擴容,在業務量級下降后又能及時回收資源,HPA 是根據特定的指標以及業務設定的目標值來增加或減少 Pod 的數量。
這部分需要考慮三方面:
- 伸縮指標的擴展
- 錯峰調度的實現
- 伸縮時的業務抖動的優化
圖六
圖六左側是我們擴展指標的架構圖,從圖中我們可以看到,通過擴展采集模塊及 CME 構建了擴展指標的采集面,通過預測器輸出預測指標,通過 custom-metrics 提供 HPA 所需要的擴展指標。
伸縮指標我們主要是擴展出了 4 個指標,包括 QPS,網絡入帶寬,網絡出帶寬,消息隊列積壓長度。錯峰調度則是通過定時策略實現。
我們這邊有一個云處理的業務,會對于視頻做 H.265 轉碼、編碼優化等 CPU 密集型操作,經常會有突峰的轉碼需求導致該業務需要大量的 CPU 資源。
在這種情況下,HPA 的擴縮容有時會跟不上節奏造成業務處理延時變長,主要是伸縮組算法在計算伸縮值時算法較為簡單,容易在業務量變小后馬上過量縮量。
這就會導致業務量波動時反復進行伸縮影響業務穩定,所以我們引入了慢縮容機制同時增加收縮滑動窗口以達到消峰的作用。
圖七
圖七是一個錯峰調度的案例。我們某個處理服務,白天需要大量資源占用,高峰時需要 2500 核心,而低峰期間所需求的資源則非常少,最小時僅需要 60 核。
而另外一個服務的一些離線計算任務沒有太強時間急迫性要求,可以放在夜間處理,還有一些統計類定時任務也是可以放置在夜間,這樣整體集群資源可以被充分利用。
⑥基礎建設之監控
監控是我們生產穩定的一個重要的保障手段。在容器化之前,我們運維體系已經有一套成熟的監控機制,容器化之后相應的監控并不需要完全推倒重做,部分體系可以復用比如物理機監控,在這之上引入新的容器監控系統。
容器平臺主要監控是幾方面的內容:
- 物理機基礎監控,主要是像硬盤,IO,內存,網絡等
- 業務指標監控,包括異常監控以及性能監控
- 容器行為的監控,監控 Pod 資源,容器事情等
- 容器組件的監控
實際上監控指標是持續在豐富及優化的過程,我們最初只監控了主要的四方面的指標,而最終進行匯總分析時則是從集群,服務,Pod,業務等等視角進行多維度的匯總分析輸出。
圖八
監控報表對于我們問題分析及排障會提供巨大的便利。圖八是一個 CPU 監控圖,圖中可以看出有兩個高峰期 Pod 的 CPU 是跑滿了。
實際對應到的正是線上的某一次業務異常,當時業務的 SLA 在流量高峰時部分處理延時較高并導致報警。
基礎平臺的建設涉及到的還有很多內容,比如多集群的管理、多租戶管理、DNS 的優化,鏡像服務的優化,混合云落地等等,限于篇幅不一一展開。
業務落地
前面講了比較多是基礎平臺構建的一些內容,下面我們聊一下業務接入的一些事情。
我們知道容器化給業務帶來的收益是非常多的,但是我們也是需要考慮可能會給業務帶來的困難。
考慮各種遷移和改造的問題,我們需要將平臺更進一步優化,讓業務接入成本盡量低。
提供更簡單方便的 CI/CD 流程,我們需要提供友好的統一的操作界面,提供完整的接入指導,快速排障工具,定期的培訓等。
圖九
比如我們優化 CI/CD 流程,整個構建和發布過程對開發盡量透明,圖九右邊是新的流程,開發提交完代碼之后,Gitlab-CI 會自動促發測試及構建過程,并將鏡像推送到倉庫。
開發同學僅需要在統一門戶網上面操作對應的版本進行發布,平臺會自動生成 Deployment 并發布至相應的集群。
再比如,我們提供一個友好的管理平臺,它可以減少業務學習成本以及出錯的概率。
同時也提供了靈活的軟件階段定制支持。可以使用簡單的方式定制多個階段,包括:Dev、Test、Pre、 Beta、Canary、Realse … …
綜上,其實我們僅基礎平臺建設是遠不夠,實際業務接入過程中需要考慮很多其他因素。同時業務的需求也不斷地優化我們的平臺架構,最終實現整體的落地。
實際上我們做的也確實還遠遠不夠,業務在接入過程還是需要面臨著眾多的問題。
所以我們需要在各方面都進一步完善,業務其實一直在教導我們如何做好一個平臺,我們也在不斷地學習吸收。這也是我們不斷提升的源動力之一。
展望未來
未來我們將長期運行多集群混合云的架構,會逐步引入多家公有云,優化調度系統,更進一步提高我們的資源利用率,同時也會保持著對 ServiceMesh、Serverless、邊緣計算等方向的關注,會結合著業務的需求,更進一步優化容器基礎平臺。
目前就職于美圖技術保障部架構平臺,主要從事容器基礎平臺建設,流媒體體系相關建設。負責容器平臺基礎組件建設、調度系統研發、多機房及混合云建設、流媒體基礎服務建設及用戶體驗優化等。在容器化技術及流媒體方向有著多年的積累及豐富的實戰經驗。









































