這一篇咱們來說網易爲支撐大規模公有云對於Kubernetes的定製化。前端
1、整體架構node
網易的Kubernetes集羣是基於網易雲IaaS平臺OpenStack上面進行部署的,在外面封裝了一個容器平臺的管理層,負責統一的帳號,計費等。算法
Kubernetes集羣固然是要高可用的,於是會有多個Master節點。編程
其中APIServer前端有負載均衡器haproxy,須要有一個VIP,在兩臺haproxy之間進行漂移,保證一臺啊掛了,另一臺可以使用同一個VIP接上。後端
這種漂移使用的協議爲VRRP。api
Scheduler和Controller也是有多份的,可是隻能有一個Leader,須要經過etcd進行選舉。緩存
另外網易自身實現的部分放在單獨的進程裏NeteaseController,用於處理和IaaS層聯動的功能。安全
2、一個仍是多個Kubernetes集羣?網絡
通常有IaaS平臺的公有云或者私有云部署Kubernetes採用的是左面的樣子。也即經過調用IaaS層的API,自動化部署多個Kubernetes平臺,每個Kubernetes平臺僅僅屬於一個租戶。架構
這樣的好處是不一樣的租戶的Kubernetes之間是徹底隔離的。
壞處也不少:
當租戶很是多的時候,Kubernetes集羣很是多,很是難以維護
每一個Kubernetes集羣的規模很是小,徹底不能發揮容器平臺的優點,容器平臺相對於IaaS平臺比較輕量級,按最佳實踐是可以管理比IaaS更大的集羣的,例如幾萬或者幾十萬個節點,然而這種方式會將Kubernetes集羣限制在很小的規模。
雖然不少雲平臺提供Kubernetes集羣的自動化部署工具,可是僅僅自動化的是安裝過程,一旦這個集羣交給客戶了,雲平臺就無論了,這樣Kubernetes的升級,修復,運維所有要客戶本身來,大大提高了運維成本。
Kubernetes集羣規模須要提早規劃階段數目,添加新的節點數目須要手動進行。
網易採用的是右面的方式,整個IaaS層上面只有一個Kubernetes平臺,Kubernetes平臺的運維,升級,修復所有由雲平臺運維人員進行,運維成本較低,並且不須要用戶本身操心。
對於用戶來說,看到的就只是容器,不用關心容器平臺。當用戶建立容器的時候,當Kubernetes集羣資源不足的時候,Netease Controller會自動調用IaaS層API建立虛擬機,而後在虛擬機上部署容器,一切自動進行。
固然這種方式的一個缺點是一個Kubernetes的不一樣租戶之間的隔離問題。
採起的方式是不一樣的租戶不共享虛擬機,不一樣的租戶不共享虛擬網絡,這樣租戶之間就隔離了,容器的內核也隔離了,二層網絡也隔離了。
固然這個方式的另外一個問題是Kubernetes的集羣數目很是大,這在下面會詳細說這個事情。
還有一個問題是容器放在虛擬機裏面,虛擬機的啓動速度就成了很大的問題,若是啓動速度很慢,則會拖慢容器的敏捷性。
3、APIServer認證模塊接Keystone解決複雜的租戶管理問題
Kubernetes有基於keystone的用戶名和密碼進行認證的默認機制,然而每次用用戶名和密碼進行認證是效率很低的。
網易經過定製化認證流程,使得apiserver也採用相似nova的認證方式。
APIServer在keystone裏面註冊一個service角色的帳號,並從keystone獲取臨時管理員的Token。
客戶請求到來的時候,經過用戶名密碼到Keystone進行認證,獲取一個token,而且帶着這個token到APIServer進行認證,APIServer帶着這個token,連同本身的臨時管理員的Token進行聯合認證,若是驗證經過則獲取用戶信息,而且緩存到etcd裏面。
當客戶經過獲取的token來請求的時候,先從etcd裏面讀取這個token進行驗證,爲了使用本地緩存,加一個listwatch實時同步etcd裏面的內容到本地緩存。
若是token過時,則重新到keystone裏面revoke。
4、從APIServer看集羣的規模問題
隨着集羣規模的擴大,apiserver的壓力愈來愈大。
由於全部的其餘組件,例如Controller,Scheduler,客戶端,Kubelet等都須要監聽apiserver,來查看etcd裏面的變化,從而執行必定的操做。
不少人都將容器和微服務聯繫起來,從Kubernetes的設計能夠看出,Kubernetes的模塊設計時很是的微服務化的,每一個進程都僅僅幹本身的事情,而經過apiserver鬆耦合的關聯起來。
而apiserver則很像微服務中的api網關,是一個無狀態的服務,能夠很好的彈性伸縮。
爲了應對listwatch,apiserver用了watchcache來緩解壓力,然而最終的瓶頸仍是在etcd上。
最初用的是etcd2,這個時候listwatch每次只能接受一個事件,因此壓力很大。爲了繼續使用etcd2,則須要使用多個etcd2的集羣來解決這個問題,經過不一樣的租戶分配到不一樣的etcd2集羣來分擔壓力。
未來會遷移到etcd3有了事件的批量推送,可是從etcd2到etcd3須要必定的遷移工做。
5、經過優化Scheduler解決並行調度的問題
對於大的資源池的調度是一個很大的問題,由於一樣一個資源只能被一個任務使用,若是並行調度,則存在兩個並行的調度器同時認爲某個資源空閒,因而同時將兩個任務調度到同一臺機器,結果出現競爭的狀況。
當OpenStack遇到這種問題,採起的方法是從新調度的方式進行。
而Mesos則採起了更爲聰明的兩層調度的算法。
詳情見文章號稱瞭解mesos雙層調度的你,先來回答下面這五個問題!
Mesos首先經過第一層的調度Allocator,將不一樣的節點分給不一樣的Framework,則不一樣的Framework就能看到不一樣的節點了,不一樣Framework裏面的調度器是第二層調度,是可以並行調度的,而且即使並行調度,不一樣的Framework也是不會調度到衝突的節點的。
Mesos的雙層調度策略,使得Mesos可以管理大規模的集羣,例如tweeter宣稱的幾十萬的節點,由於對於某一個Framework來說,不會同時在幾十萬個節點中選擇運行任務的節點,而是僅僅在其中mesos分配給他的一部分中進行調度,不一樣的framework能夠並行調度。
還記得前面咱們提到的,爲了租戶隔離,不一樣的租戶是不共享虛擬機的,這樣不一樣的租戶是能夠參考Mesos的機制進行並行調度的。由於不一樣的租戶即使進行並行調度,也不會出現衝突的現象,每一個租戶不是在幾萬個節點中進行調度,而僅僅在屬於這個租戶的有限的節點中進行調度,大大提升了調度策略。
而且經過預過濾無空閒資源的Node,調整predicate算法進行預過濾,進一步減小調度規模。
6、經過優化Controller加快新任務的調度速度
Kubernetes採用的是微服務常使用的基於事件的編程模型。
當有增量事件產生的時候,則controller根據事件進行添加,刪除,更新等操做。
可是基於事件的模型的一個缺點是,老是經過delta進行事件觸發,過了一段時間,就不知道是否同步了,於是須要週期性的Resync一下,保證全量的同步以後,而後再進行增量的事件處理。
然而問題來了,當Resync的時候,正好遇到一個新容器的建立,則全部的事件在一個隊列裏面,拖慢了新建立容器的速度。
經過保持多個隊列,而且隊列的優先級ADD優於Update優於Delete優於Sync,保證相應的實時性。
7、經過優化虛擬機啓動速度加速容器啓動速度
前面說了,爲了保證安全性,容器是啓動在虛擬機裏面的,然而若是虛擬機的啓動速度慢了,容器的啓動速度就會被拖慢。
那麼虛擬機的啓動速度爲何會慢呢?
比較慢的一個是網卡的初始化速度比較慢,在OpenStack裏面,每每須要經過訪問DHCP Sever獲取IP地址和路由信息,把IP靜態化能夠解決這個問題。
另一個比較慢的是Cloud-init,這個是在虛擬機啓動以後作初始化的一個工具,是爲了可以在虛擬機啓動後作靈活的配置,好比配置key,或者執行一些腳本。
在AWS裏面,cloudformation編排工具在虛擬機啓動後的一些編排工做是經過cloud-init來作的,彈性伸縮機制經過虛擬機鏡像複製多份,複製後作的少許的配置工做也是cloud-init來作的。
OpenStack繼承了AWS的這個機制,是經過Metadata Server來對虛擬機進行靈活的配置,包括OpenStack編排機制Heat也是經過Cloud-init進行配置。
對於Metadata Server的機制,能夠看這篇文章當發現你的OpenStack虛擬機網絡有問題,不妨先試一下這16個步驟
然而在虛擬機裏面跑容器的方式,是不須要cloud-init的,由於靈活定製化的事情由裏面的容器來作,用戶使用的也是裏面的容器,而不是虛擬機,於是虛擬機能夠作的十分的精簡,將不須要的服務所有刪除。
8、經過使用IaaS層的高性能網絡提供給容器高速互聯能力
上一節咱們講過,使用Flannel和Calico都僅僅適用於裸機容器,並且僅僅用於容器之間的互通。
一旦有IaaS層,就會存在網絡二次虛擬化的問題。
虛擬機之間的互聯是須要經過一個虛擬網絡的,例如vxlan的實現,而使用Flannel或者Calico至關於在虛擬機網絡虛擬化的上面再作一次虛擬化,使得網絡性能大幅度下降。
並且若是使用Flannel或者Calico,那容器內的應用和虛擬機上的應用相互通訊的時候,則須要出容器平臺,多使用node port,經過NAT的方式訪問,或者經過外部負載均衡器的方式進行訪問。在現實應用中,不可能一會兒將全部的應用所有容器化,部分應用容器化,部分應用部署在虛擬機裏面是常有的現象。然而經過NAT或者外部負載均衡器的方式,對應用的相互調用有侵入,是的應用不能像原來同樣相互調用,尤爲是當應用之間使用Dubbo或者SpringCloud這種服務發現機制的時候尤爲如此。
網易開發了本身的NeteaseController,在監聽到有新的Pod建立的時候,調用IaaS的API建立IaaS層的虛擬網卡,而後在虛擬機內部,經過調用Netease CNI插件將虛擬網卡添加到容器裏面。添加的技術用的就是上一節提到的setns命令。
經過這個圖咱們能夠看出,容器的網卡是直接鏈接到虛擬私有網絡的OVS上的,和虛擬機是一個平的二層網絡,在OVS來看,容器和虛擬機是在同一個網絡裏面的。
這樣一方面沒有了二次虛擬化,只有OVS一層虛擬化。另外容器和虛擬機網絡打平的好處是,當部分應用部署容器,部分應用部署虛擬機的時候,對應用沒有侵入,應用原來如何相互訪問,如今仍是如何訪問,有利於應用逐步容器化。
容器還能夠有一個網卡直接鏈接到公網,這一點可以保持公網IP在容器內部可見,容易使用,並且能夠保證在容器的生命週期內,公網IP能夠保持。
另外這個網卡在負載均衡可使用,下面咱們詳細說。
使用OVS的二層網絡,能夠很好的實現多租戶,高性能互聯,QoS,安全過濾,放ARP和IP欺騙等。
9、經過優化kube-proxy優化內部相互訪問
默認的容器之間的相互訪問經過服務進行。
一旦建立一個服務,就會建立一個Endpoint,kube-proxy可以在api-server監聽到這個服務的建立,則須要監聽服務的端口,而且設置iptables將對這個服務對應的VIP的訪問轉發到kube-proxy的這個端口上。
當一個客戶端要訪問這個服務的時候,首先經過DNS,將服務名轉化爲VIP,而後VIP經過iptables規則轉發到kube-proxy的監聽端口,kube-proxy將這個包隨機轉發給後端的一個Pod。
當Pod不管IP如何變,如何彈性伸縮,只要有服務名,都可以訪問到。
僅僅經過DNS,是可以作的服務名和IP的對應,也是能夠基於DNS進行負載均衡的,這就是基本的基於DNS的服務發現。然而這種方式的缺點是不可以流控,因此最好中間有一個proxy,這就是kube-proxy。
就像Mesos中原來的服務發現是經過Mesos-DNS進行,後來改用了minuteman作這件事情,minuteman的實現機制和kube-proxy很像很像。
然而這種方式的問題是,當集羣規模很大的時候,服務建立的不少的時候,kube-proxy會牽扯到大量的iptables和監聽端口。
然而在咱們的設計中,租戶之間的容器網絡應該是徹底隔離的,在某個租戶的虛擬機上的Kube-proxy是無需包含另外一個租戶的轉發規則的。
因此這裏作的一個優化是隻監聽本租戶的service,而且建立轉發規則就好,由於每一個租戶的節點數目有限,轉發規則也不會不少。
10、經過LVS和haproxy進行外部負載均衡器優化
Kubernetes默認的外部負載均衡是用ingress作的,性能不是很高。
通常公有云要求負載均衡可以承載的吞吐量很是的大,不能用純軟的方式。
在網易的方案中,最外層有兩個LVS,部署在物理機上,經過多個萬兆上行口進行轉發,能夠承載很是大的吞吐量。
對於不一樣的租戶,能夠建立不一樣的軟負載均衡,經過建立多個haproxy,構成一個集羣,因爲haproxy是基於虛擬機的,能夠彈性伸縮,使得他也不會成爲瓶頸。
haproxy是後端和容器進行二層互聯,是經過虛擬網絡進行的,然而haproxy鏈接LVS是須要經過物理網絡的,這就須要haproxy經過上面的機制,經過兩張網卡,一張卡鏈接到物理網絡,做爲前端,一張卡鏈接到虛擬網絡,做爲後端鏈接容器。
最後給一張優化總圖。