网易云容器服务基于Kubernetes的实践探索
Kubernetes的特點
近年來Docker容器作為一種輕量級虛擬化技術革新了整個IT領域軟件開發部署流程,如何高效自動管理容器和相關的計算、存儲等資源,將容器技術真正落地上線,則需要一套強大容器編排服務,當前大紅大紫的Kubernetes已經被公認為這個領域的領導者。Google基于內部Borg十多年大規模集群管理經驗在2014年親自傾心打造了Kubernetes這個開源項目,欲倚之與AWS在云計算2.0時代一爭高下,即便如此,Kubernetes的定位主要是面向私有云市場,它最典型的部署模式是在GCE或AWS平臺上基于云主機、云網絡、云硬盤及負載均衡等技術給用戶單獨部署一整套Kubernetes容器管理集群,其本質上是賣的IAAS服務,Kubernetes集群還是需要靠用戶自己維護,大家知道Kubernetes雖然功能強大但使用、管理、運維門檻也高,出問題了大多數用戶會束手無策。
這幾年國內容器云領域也是群雄割據,但多數還是以私有云為主,提供公有云容器服務的卻很少,主要是公有云要考慮的問題多、挑戰大。雖然如此,網易云的還是提供了公有云模式的容器服務。網易云從2015年開始做容器服務時也被Kubernetes強大的功能、插件化思想和背后強大的技術實力說吸引,至今已經跟隨Kubernetes一起走過兩年多,積累了不少經驗。因為我們特別希望以公有云的方式提供一種更易用容器服務,任何對容器感興趣的用戶都能快速上手,為此也遇到了很多私有云下不會出現的問題。
列舉幾個Kubernetes在公有云容器場景下的需要特別解決的關鍵問題。一是Kubernetes里沒有用戶(租戶)的概念,只有一個很弱的命名空間來做邏輯隔離。二是Kubernetes和Docker的安全問題很突出,API訪問控制較弱且沒有用戶流控機制,一些資源全局可見。而Docker容器與宿主機共享內核的輕量級隔離從根本上沒法做到徹底安全。三是Kubernetes集群所需要IAAS資源(如Node,PV)都要預先準備足夠,否則容器隨時會創建失敗,公有云這樣的話會造成嚴重的資源浪費,產生巨大的成本問題。四是Kubernetes單個集群能支撐的節點總數有限,最大安全規模只有5千個Node,公有云下擴展性將會有問題。
網易云容器如何解決Kubernetes在公有云上的問題
先看下網易云容器服務的架構圖(如圖1),這里的Kubernetes處于底層IAAS服務和上層容器平臺的中間,因為我們的容器服務不僅僅提供Kubernetes本身容器編排管理功能,更是為提供一整套專業的容器解決方案,還包括容器鏡像服務,負載均衡服務,通過使用DevOps 工具鏈高效管理微服務架構??紤]到Kubernetes概念較多、普通用戶使用復雜,也為了便于整合其他配套服務,我們并沒有直接暴露Kubernetes的API和所有概念給普通用戶。
公有云租戶概念
網易云容器服務基于Kubernetes已有的Namespace的邏輯隔離特性,虛擬出一個租戶的概念,并與Namespace進行永久綁定:一個Namespace只能屬于一個租戶,一個租戶則可以有多個Namespace。這樣Kubernetes里不同租戶之間的Pod、Service、Secret就能自然分割,而且可以直接在原生的Namespace/Resouce級別的認證授權上進行租戶級別的安全改造。
多租戶安全問題
關于Kubernetes的API的安全訪問控制,盡管網易云容器沒有直接暴露Kubernetes的API給用戶,但用戶容器所在的Node端也都要訪問API,Node本質就是用戶的資源。我們在最早基于Kubernetes 1.0開發的時候就專門增加了一套輕量級擴展授權控制插件:基于規則訪問控制,比如配置各租戶只能Get和Watch屬于自己Namespace下的Pod資源,解決對Kubernetes資源API權限控制粒度不夠精確且無法動態增減租戶的問題。值得欣慰的是幾個月前官方發布的1. 6新推出RBAC(基于角色訪問控制)功能,使得授權管理機制得以增強,但服務端對用戶Node端訪問的異常流量控制的缺乏依然是一個隱患,為此,我們也在apiserver端增加請求數來源分類統計和控制模塊,避免有不良用戶從容器里逃逸到Node上進行惡意攻擊。?
原生的kube-proxy提供的內部負載必須要List&Watch集群所有Service和Endpoint,導致就算在多租戶場景下Service和Endpoint也要全部暴露,同時導致iptables規則膨脹轉發效率極低;為此我們對kube-proxy也做了優化改造:每個租戶的Node上只會List&Watch自己的相關Namespace下資源即可,這樣既解決了安全問題又優化性能,一箭雙雕。
至于Docker的隔離不徹底的問題,我們則選擇了最徹底的做法:在容器外加了一層用戶看不見的虛擬機,通過IAAS層虛擬機的OS 內核隔離保證容器的安全。
容器的IAAS資源管理
容器云作為新一代的基礎設施云服務,資源管理必然也是非常關鍵的。私有云場景下整個集群資源都屬于企業自己,預留的所有資源都可以一起直接使用、釋放、重用;而公有云多租戶下的所有資源首先是要進行租戶劃分的,一旦加入kubernetes集群,Node、PV、Network的屬主租戶便已確定不變,如果給每個租戶都預留資源,海量租戶累計起來就非??植懒?#xff0c;沒法接受。當然,可以讓公有云用戶在創建容器前,提前把所有需要的資源都準備好,但這樣又會讓用戶用起來更復雜,與容器平臺易用性的初衷不符,我們更希望能幫用戶把精力花在業務本身。
于是我們需要改造kubernetes,以支持按需動態申請、釋放資源。既然要按需實時申請資源,那就先理下容器的創建流程,簡單起見,我們直接創建Pod來說明這個過程,如圖3所示。
Pod創建出來后,首先控制器會檢查是否有PV(網易云容器為支持網絡隔離還增加租戶Network資源),PV資源是否匹配,不匹配則等待。如果Pod不需要PV或者PV匹配后調度器才能正常調度Pod,然后scheduler從集群所有Ready的 Node列表找合適Node綁定到Pod上,沒有則調度失敗,并從1秒開始以2的指數倍回退(backoff)等待并重新加入調度隊列,直到調度成功。最后在調度的Node的kubelet上拉鏡像并把容器創建并運行起來。
通過分析上述流程可以發現,可以在控制器上匹配PV或Network時實時創建資源,然后在調度器因缺少Node而調度失敗時再實時創建Node(虛擬機VM),再等下次失敗backoff重新調度。但是仔細分析后會發現還有很多問題,首先是IAAS中間層提供的創建資源接口都是異步的,輪詢等待效率會很多,而且PV,Network,Node都串行申請會非常慢,容器本來就是秒級啟動,不能到云服務上就變成分鐘級別;其次Node從創建VM,初始化安裝Docker、kubelet、kube-proxy到啟動進程并注冊到Kubernetes上時間漫長,調度器backoff重新調度多次也不一定就緒,最后,基于Kubernetes的修改要考慮少侵入,Kubernetes社區極度活躍一直保持3個月發布一個大版本的節奏,要跟上社區發展可能需要不斷升級線上版本。
最終,我們通過增加獨立的ResourceController,借助watch機制采用全異步非阻塞、全事件驅動模式。資源不足就發起資源異步申請,并接著處理后面流程,而資源一旦就緒立馬觸發再調度,申請Node時中間層也提前準備虛擬機資源池,并將Node初始化、安裝步驟預先在虛擬機鏡像中準備好。于是,我們詳細的創建流程演變為圖4所示。(注:最新Kubernetes 已經通過StorageClass類型支持PV dynamic provisioning)?
與原生的Kubernetes相比,我們增加了一個獨立的 ResourceController管理所有IAAS資源相關的事情,具體的Pod創建步驟如下:
- 1、上層client請求apiserver創建一個Pod。
- 2、ResourceController watch到有新增Pod,檢查PV和Network是否已經創建;?
同時,另一邊的scheduler也發現有新Pod尚未調度,也嘗試對Pod進行調度。 - 3、因為資源都沒有提前準備,最初ResourceController檢查時發現沒有與Pod匹配的PV和Network,會向IAAS中間層請求創建云盤和網絡資源;?
scheduler 則也因為找不到可調度的Node也同時向IAAS中間層請求創建對應規格的VM資源(Node),這時Pod也不再重入調度隊列,后面一切準備就緒才會重調度。 - 4、因為IAAS中間層創建資源相對較慢,也只提供異步接口,待底層資源準備完畢,便立即通過apiserver注冊PV、Network、Node資源
- 5~6、ResourceController當發現PV和Network都滿足了,就將他們與Pod綁定;當發現Pod申請的Node注冊上來,且PV和Network均綁定,會把Pod設置為ResourceReady就緒狀態
- 7、Scheduler再次watch到Pod處于ResourceReady狀態,則重新觸發調度過程,
- 8、Pod調度成功與新動態創建Node進行綁定
- 9~10、對應Node的kubelet watch到新調度的Pod還沒有啟動,則會先拉取鏡像再啟動容器。
集群最大規模問題
從正式發布1.0版本至今最新的1.7,Kubernetes共經歷了2次大規模的性能優化,從1.0的200個node主要通過增加apiserver cache提升到1000個node,再到1.6通過升級etcdv3和json改protobuf最終提升到5000 node。但是官方稱后續不會再考慮繼續優化單集群規模了,已有的集群聯邦功能又太過簡陋。如果公有云場景下隨著已有用戶規模不斷增大,一旦快接近集群最大規模時,就只能將其中一些大用戶一批批遷移出去來騰空間給剩余用戶。
于是我們自己在社區版本基礎上又做了大量定制化的性能優化,目前單集群性能測試最大安全規模已經超過3萬,驗收測試包括集群高水位下,大并發創建速度deployment和快速重啟master端服務和所有node端kubelet等在內的多種極端異常操作,保證創建時間均值<5s,99值<15s,集群中心管控服務最差在3分鐘內快速恢復正常。
具體的優化措施包括:
- 1、 scheduler優化?
根據租戶之間資源完全隔離互補影響的特性,我們將原有的串行調度流程,改造為租戶間完全并行的調度模式,再配合協程池來爭奪可并行的調度任務。在調度算法上,還采用預先排除資源不足的node、優化過濾函數順序等策略進行局部優化。 - 2、 Controller優化?
熟悉Kubernetes的人都知道,Kubernetes有個核心特點就是事件驅動,實時性很好,但是有個Sync事件卻干擾了FIFO的順序,我們通過將Add、Update、Delete、Sync事件排序并增加多優先級隊列的方式解決這種異常干擾。?
增加Secret本地緩存 - 3、 apiserver優化?
apiserver的核心是提供類似CRUD的restful接口,優化方向無外乎降低響應時間,減少cpu、內存消耗以提高吞吐量,我們最主要的一個優化是增加以租戶ID為過濾條件的查詢索引,這樣就能實現在租戶內跨Namespace聚合查詢的效果。另外apiserver的客戶端原生的流控策略太暴力,客戶端默認在流控被限制后會反復重試,進一步加劇apiserver的壓力,我們增加了一種基于反饋的智能重試的策略抹平這種突發流量。 - 4、Node端優化?
kube-proxy本來需要控制整個集群負載轉發的,Apiserver有了租戶查詢索引后,我們就能只watch自己租戶內的Service/Endpoint,急劇縮小iptables規則數量,提高查找轉發效率。而且我們還精簡kubelet和kube-proxy內存占用和連接數。
網易云容器服務的其他實踐及總結
容器的網絡是非常復雜一塊,容器云服務至少要提供穩定、靈活、高效的跨主機網絡,雖然開源網絡實現很多,但是它們要么不支持多租戶、要么性能不好,且直接拿沒有經過大規模線上考驗的開源軟件問題總會很多。幸運的時網易云有自己專業的IAAS云網絡團隊,他們能提供專業級的VPC網絡解決方案,天生就支持多租戶、安全策略控制和高性能擴展,已經做到容器與虛擬主機的網絡是完全互通且地位對等的。
網易云容器服務還在Kubernetes社區版本基礎上結合產品需求新增了很多功能,包括支持特有的有狀態容器,及Node故障時容器系統目錄也能自動遷移以保持數據不變,多副本Pod可按Node的AvailableZone分布強制均衡調度(社區只盡力均衡)、容器垂直擴容、有狀態容器動態掛卸載外網IP等。
相比容器的輕量級虛擬化,虛擬機雖然安全級別更高,但是在cpu、磁盤、網絡等方面都存在一定的性能損耗,而有些業務卻又對性能要求非常高。針對這些特殊需求,最近我們也在開發基于Kubernetes的高性能裸機容器,繞過虛擬機將網絡、存儲等虛擬化技術直接對接到Docker容器里,在結合SR-IOV網絡技術、網易高性能云盤NBS(netease block storage)等技術將虛擬化的性能損耗降到最低。
最后,分享一些網易云容器服務上線近兩年來的遇到的比較典型的坑。
-
1、Apiserver作為集群hub中心本身是無狀態的可水平擴展,但是多apiserver讀寫會在Apiserver切換時可能會出現寫入的數據不能立馬讀到的問題,原因是etcd的raft協議不是所有節點強一致寫的。
-
2、haproxy連接的問題,多Apiserver前用haproxy做負載均衡,haproxy很容易出現客戶端端口不夠用和連接數過多的問題,可以通過擴大端口范圍、增加源ip地址等方式解決端口問題,通過增加client/service的心跳探活解決異常連接GC的問題。
-
3、用戶覆蓋更新已有tag的私有容器鏡像問題,強烈建議大家不要覆蓋已有tag的鏡像,也不要使用latest這樣模糊的鏡像標簽,否則RS多Pod副本或者同一個Node上同鏡像容器很容易出現版本不一致的詭異問題。
-
4、有些容器小文件非常多,很容易把inode用光而磁盤空間卻剩余很多的問題,建議把這種類型應用調度到inode配置多的node上,另外原生kubelet也存在不會檢查inode過多觸發鏡像回收的問題。
-
5、有些Pod刪除時銷毀過慢的問題,Pod支持graceful刪除,但是如果容器鏡像啟動命令寫得不好,可能會導致信號丟失不光沒法graceful刪除還會導致延遲30s的問題
總之,在公有云場景下,用戶來源廣泛,使用習慣千變萬化沒法控制,我們已經碰到過很多純私有云場景下很難出現的問題,如用戶鏡像跑起不來,Pod多容器端口沖突,日志直接輸出到標準輸出,或者日志寫太快沒有切割,甚至把容器磁盤100%寫滿等,因為篇幅有限,所以只能挑選幾個有代表性的專門說明。因為云上要考慮的問題太多,特別是這種基礎設施服務類的,使用場景又非常靈活,線上出現的一些問題之前完全想不到,包括很多還是用戶自己使用的問題,但為了要讓用戶有更好的體驗,也只能盡力而為,優先選擇一些通用的問題去解決。
作者:婁超,網易云容器編排技術負責人。曾經參與淘寶分布式文件系統tfs和阿里云緩存服務研發,2015年加入網易參與網易云容器服務研發,經歷網易云基礎服務(蜂巢)v1.0,v2.0的容器編排相關的設計和研發工作,并推動網易云內部Kubernetes版本不斷升級。?
本文為《程序員》原創文章,未經允許不得轉載,更多精彩文章請訂閱《程序員》,給我們投稿請聯系郵箱weiwei@csdn.net。(責編/魏偉)
歡迎掃描下方二維碼,關注CSDN云計算微信,獲取更多干貨原創內容。
總結
以上是生活随笔為你收集整理的网易云容器服务基于Kubernetes的实践探索的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zmbx是哪个保险公司
- 下一篇: [重磅] 如何更好地实现服务调用和消息推