Kubernetes在華為全球 IT系統中的實踐

錢曙光發表於2016-08-26

宣告:本文為CSDN原創投稿文章,未經許可,禁止任何形式的轉載。
作者:王澤鋒,華為PaaS開發部高階工程師,多年電信領域系統軟體開發和效能調優經驗,對深度報文解析,協議識別頗有研究。華為PaaS平臺核心開發者,主導負責最初版本的運維繫統設計和開發,華為公有云 PaaS CCE服務的多AZ支援、親和/反親和性排程設計和開發。目前擔任Kubernetes社群的Committer,主導 Kubernetes社群親和性排程相關特性開發工作,對社群化的專案開源運作模式有濃厚的興趣。
責編:錢曙光,關注架構和演算法領域,尋求報導或者投稿請發郵件qianshg@csdn.net,另有「CSDN 高階架構師群」,內有諸多知名網際網路公司的大牛架構師,歡迎架構師加微信qshuguang2008申請入群,備註姓名+公司+職位。

Kubernetes是Google開源的Docker容器叢集管理系統,是Docker生態圈中的重要一員。隨著Kubernetes社群及各大廠商的不斷改進、發展,Kuberentes已經成為容器管理領域的領導者之一。華為高階工程師王澤鋒日前為大家帶來了「Kubernetes在華為全球IT系統中的實踐」的分享,以下是對其演講內容的整理:

簡單介紹

圖片描述

圖 1

華為作為一個全球性的公司,其IT系統規模非常龐大,部署在全球各個地區,業務量大,應用數量急劇增長。隨著規模的愈發龐大,問題也越來越突出:

  • 資源利用率低,虛擬機器增長過快,成本激增;
  • 跨區域多 DC 的部署維護無法拉伸,運維投入巨大;
  • 系統過載、應用伸縮週期長;
  • 難以支援快速迭代,業務上線效率低。

圖片描述

圖 2

面對業務壓力,Cloud Container Engine(以下簡稱CCE)的出現很好的解決了以上問題。華為的CCE可以理解為是華為的Kubernetes商業版本(圖2),整體上是希望通過一套核心技術,去打通IaaS和PaaS平臺,一套技術支撐多種業務場景。

圖片描述

圖 3

2015年底CCE在華為IT系統上線(圖3),資源利用率提升了3到4倍,當前的規模達到2000多個虛機。下面重點講下近期的兩大重點技術實踐:Kubernetes多叢集聯邦、應用間的親和/反親和排程。

Kubernetes 多叢集聯邦

圖片描述

圖 4

圖4是Kubernetes非常簡單的架構圖,圖中可以看到如下元件:

  • Pod(容器組)
  • Container(容器)
  • Label(標籤)
  • Replication Controller(副本控制器)
  • Service(服務)
  • Node(節點)
  • Kubernetes Master(Kubernetes主節點)

其基本排程單位是Pod,具體應用下面例項會提到。

圖片描述

圖 5

從華為內部IT系統的某項業務估算(圖5),其機器數量在2016年整體容器化之後,會達到3萬虛機和10萬容器。華為IT系統的典型場景是通過多資料中心統一管理部署應用,提供跨域的應用服務發現。但是跨域的網路限制與差異較大,難以通過k8s單叢集支援。所以如何提供跨域應用的大規模集中部署成為目前需要解決的問題。

圖片描述

圖 6

圖6是一個簡單的多叢集聯邦架構示意圖,圖中的3個黑塊的是在叢集聯邦層面加出來的三個關鍵元件,底下白色對應的是一個叢集,這個架構跟Kubernetes原來的架構非常相似。原來Kubernetes的架構也是一個Master管理多個節點,這裡是一個Master管理多個叢集。如果把圖中的叢集換成節點,其實就是Kubernetes的架構。使用者通過統一的API入口建立應用,叢集聯邦所做的就是把聯邦級別物件應用拆分到各個子叢集分別部署。Cluster controller會維護各個叢集的健康狀態、監控負載情況,而Service controller做一個提供跨叢集的服務發現的打通。

圖片描述

圖 7

總的來說,現在叢集管理架構都比較相似,都有Controller、Scheduler和Agent(Kubelet),這裡是利用 List-Watch(圖7)機制實現的元件間互動的解耦,體現了Everything talk to API的設計理念。比較類似SOA架構中的訊息匯流排,但跟訊息匯流排的差別在於對資料的儲存和事件通知做了統一化處理,當叢集收到新的事件的同時,可以拿到這一份新的資料,包括資料的變化是新增還是更新了某個物件。

例項說明:這時要建立一個ReplicaSet,第一步是叢集起來後,Controller-manager(包含多個Controller)、Scheduler和Kubelet都會去發起watch,但是watch的物件都是不一樣的。ReplicaSet Controller會watch ReplicaSet,Scheduler watch的是一個Pod(也可以理解為一個應用的例項)。這裡的watch是帶一個條件的,destNode為空,就是未排程的Pod;而Kubelet watch的Pod只是destNode為Node自身的Pod,也就是這個Pod排程到相應的節點才會做相應的處理。在建立應用的時候,k8s會先將物件儲存到etcd中,同時存完之後會上報已建立的事件。與此同時ReplicaSet Controller已經事先watch這個事件,它就會收到通知。ReplicaSet Controller發現有一個新的RS被建立,這個時候它會去做拆分,因為RS對應的就是一個多例項無狀態的應用,所以RS有多少個副本,Controller就建立多少個相同的Pod。

這樣第一階段建立的過程已經完成。因為Scheduler watch了Pod,新建立的Pod是沒有被排程過的,destNode是空的,這個時候它會收到Pod建立的通知,並執行排程演算法(把這個叢集中所有可用節點的資料根據Pod的排程需求做一個綜合計算,選出一個Node),然後將Pod繫結到這個Node上(更新這個Pod的destNode),這樣Scheduler的流程就結束了。然後在相對應被繫結的Node上,Kubelet會發現有新的Pod排程過來,就會按照Pod的定義建立並啟動容器。

經過上述的流程可知,各個元件通過List-Watch不同的物件(即便是相同的物件,狀態也是不一樣的),實現天然的解耦,各個元件處理一個應用生命週期的不同階段,同時保證了在一個非同步的分散式系統裡面多元件間處理流程的先後順序問題。同時List-Watch每次只是做事件的監控,所以每次watch比如建立一個Pod獲取的是增量資訊,獲取的數量互動是非常少的。我們知道通過訊息匯流排傳一個訊息/事件,是有可能丟失的,這就需要有一個容錯機制,來保證最終一致性。Kubernetes內部是通過List來解決的。比如ReplicaSet Controller會週期性地對ReplicaSet做全量List,拿到的資料就是當前系統需要起的應用資料,它會檢查當前各個應用的例項執行情況,做相應處理,多餘的刪除,不足的補齊。

圖片描述

圖 8

圖8是叢集聯邦下的應用建立流程。如圖所示,使用者向聯邦API Server發請求建立應用,它會首先將物件儲存起來。同時Controller會週期性獲取各叢集的監控資料,同步到API Server。在Scheduler watch到建立應用之後,它會根據排程演算法,按照聯邦裡面所有的叢集監控資訊包括健康情況等指標篩選出合適的叢集,對應用做拆分(當前主要是各個叢集的容量和監控負載情況)。這個例子裡面應用被拆分到了兩個叢集,cluster A是2個例項、cluster B是3個例項,拆分後仍是把這個物件儲存到API Server,跟Kubernetes原生在單叢集下建立的流程是一致的。這個時候Controller會watch到sub RS建立(實際上是RS物件的一個屬性更新),它會去對應的叢集建立實際的應用實體,例項數量為前面應用拆分時的運算值。

圖片描述

圖 9

圖9呈現了聯邦排程器的關鍵機制,它watch的是兩類物件,一個是副本集,一個是Cluster(聯邦裡面有多少可用叢集,它們負載的情況等)。當RC/RS有變化時,會將它們儲存到一個本地佇列,worker會從這個本地佇列中逐個讀取RC/RS並處理。處理時根據載入的排程策略篩選出合適的目標叢集,然後去計算出分別應該在這幾個叢集建立多少個例項。最後寫回到API Server的資料格式如圖所示,是更新RS的一個欄位destClusters,未排程時是空值,現在變成[{cluster: “clusterA”, replicas: 6}, … ]這樣的內容。

圖片描述

圖 10

叢集聯邦中包含多個Controller,這個例子(圖 10)裡主要涉及兩個,一個是Cluster Controller,一個是Replication Controller。Cluster Controller watch的是cluster物件,維護聯邦中所有Cluster狀態資訊,包括週期性的健康檢查、負載情況等。如果配置了安全訪問,還需要確保配置(比如訪問叢集所用的證書檔案)是正確可用的。Replication Controller則watchRS,剛才提到聯邦排程器也會watch RS,這裡watch的是已經過排程器拆分處理的RS,並負責到各個叢集建立指定數量的例項。

圖片描述

圖 11

接著前面的例子,拆分到Cluster的A是2個例項,拆分到Cluster的B是3個例項,這個實際上可以理解為一個控制面的處理,解決了一個應用在聯邦下部署到多個叢集的問題。接下來要解決資料面的問題,部屬下去之後如何實現應用跨叢集的訪問(圖11)。我們知道在單叢集裡面是全通的網路,但是跨叢集的我們還希望適配混合雲的場景,這個時候底層的網路不一定是通的,因此我們直接按照叢集間網路不通的場景來設計。從外部流量來說,使用者請求會通過全球分散式路由被分攤到各個叢集,每個叢集有一個負載均衡,再把流量導到各個Node上去。跨叢集訪問的時候,叢集內的訪問仍可以按原來的服務發現機制處理。跨叢集的時候因為是跨網路訪問,網路是不通的,這裡有個約束是每個叢集的負載均衡器需要有public IP,可以被所有叢集內的所有Node訪問。當應用的訪問請求需要跨叢集的時候,流量會被轉發到對端叢集的負載均衡器上,由它再做反向代理,再轉到後端應用例項所在的Node上去。

圖片描述

圖 12

圖12是Service Controller的關鍵機制:一個是watch Service變化、一個是watch Cluster變化。如果某個Cluster掛了流量就不應該轉發過去,因此要watch它的變化,在服務裡面重新整理每個服務後端(Cluster)的聯通性,保證它的每一個後端都是通的。同時要 watch 服務的變化,可能是新建立服務,服務後端的例項被重新整理,或者說是服務後端可能有LB掛掉,對於這個服務來說都是有變化的,這個時候要去做相應處理。

Cluster處理的就是Cluster的狀態發生變化的情況。這裡簡單介紹下新增服務時的處理流程。在聯邦Service Controller中,每個叢集都有服務物件,服務包含了Endpoint,這裡有兩類,一個是當前叢集內服務的Pod例項所在的Node,另外跨叢集的時候,需要訪問到其它叢集所在的LB,它是把這兩組資訊都寫到kube-proxy裡面去。同時為了對外提供訪問支援,需要按叢集分別將本叢集中服務例項的Endpoint寫到對應叢集的LB中,做反向代理。這樣就支援了單叢集內的通訊和跨叢集通訊。對於外部流量,因為最前面是全球分散式路由,使用者在訪問的時候,可以根據使用者的地域或訪問時延等因素,選擇一個就近的叢集的LB,以獲得較為理想的訪問速度。

應用間的親和/反親和排程

圖片描述

圖 13

容器化和微服務化的改造會出現親和性和反親和性(圖13)的疑問,原來一個虛機上會裝多個元件,程式間會有通訊。但是在做容器化拆分的時候,往往直接按程式拆分容器,比如業務程式一個容器,監控日誌處理或者本地資料放在另一個容器,並且有獨立的生命週期。這時如果他們分佈在網路中兩個較遠的點,請求經過多次轉發,效能會很差。所以希望通過親和性實現就近部署,然後增強網路能力實現通訊上的就近路由,減少網路的損耗。反親和性主要是出於高可靠性考慮,儘量分散例項,某個節點故障的時候,對應用的影響只是N分之一或者只是一個例項。

圖片描述

圖 14

先來看一下單應用的情況(圖14)。在這個例子裡,我們希望所有的例項部署到一個AZ裡面(即在AZ級別互相親和),同時在Node級別互相反親和,每個Node部署一個例項。這樣某個Node掛的時候,只會影響一個例項。但是每個不同的企業,在不同的平臺上,對於這種AZ、Rack、Node等failure domain的理解和命名都不一樣。比如有人會希望應用在機框級別做反親和,因為有可能一個機框都會down掉。但本質上它們都是Node上的一個label,排程時只需要按這個特殊的label進行動態分組,處理親和反親和的關係即可。

圖片描述

圖 15

在親和性、反親和性的支援上面,會實現硬性和軟性兩種支援(圖15),因為如果是全硬性條件,很容易因為配置不恰當導致應用部署失敗。這裡實質上是兩類演算法,但是在實現邏輯上比較接近。硬性演算法做過濾,不滿足的節點都全部過濾掉,保證最後留下來的一定是滿足條件的。在軟性的情況下,不需要這麼苛刻地要求,只是best effort,條件不滿足時仍希望應用排程部署成功,這裡用的是評分排序,根據親和性和反親和性的符合情況,符合程度高的給高分,符合程度低的給低分,選Node時按分數從高到低進行選擇。

圖片描述

圖 16

親和性是相互的,這個時候就會考慮對稱性的問題(圖16)。兩個應用互相親和,在應用的定義裡面,比如應用B親和應用A,實際是在應用B裡面寫一條親和的規則去親和A,但是A不知情。如果A先建立B後建立,是沒有問題的,因為B會去找A,但是反過來A不會去找B。因此我們在做演算法實現的時候給它加了一個預設行為,在排程的時候,需要自己親和/反親和哪些Pod(前面的單向思路);同時又要去檢查哪些Pod親和/反親和自己,以此實現對稱性 。

另外一個常見的問題是被親和的應用發生遷移的情況。需要說明的有兩點,一是在演算法設計上做了對稱性的考慮,不管是先部署還是後部署,即便這個應用掛了,它在重建並被排程時,依然會檢查當前系統裡親和哪些Pod或者被哪些Pod親和,優先跟他們部到一起去。另外,目前RC/RS(副本集無狀態應用)只有Node掛掉的時候才會發生重建Pod的情況,Node沒有掛,異常退出是在原地自己重啟的。這個從兩個層面上,能夠保證親和的應用不在一起、反親和的應用分開這種需求。

圖片描述

圖 17

這時做了硬性和軟性兩種實現,在做軟性的時候沒問題,因為即便不滿足也能排程成功,按照完全對稱的思路就可以做了。這裡最主要的問題就是硬性親和的對稱性。親和其他應用的場景,如果被親和的應用不存在(未排程),排程失敗是合理的。但是如果因為不存在其他的Pod親和當前待排程的Pod而導致失敗就不合理。因此硬親和不是完全對稱的,它的反向是一個軟親和。

圖片描述

圖 18

儘管我們在親和性/反親和性的實現上做了對稱性考慮,但它只能在涉及排程的階段起作用,排程後,一個Pod的生命週期內,不會再根據實際的情況進行調整。因此後面我們會從兩個方面進一步解決前面的問題(圖18)。

第一個是限制異地重啟,即Forgiveness。假如Node故障或重啟導致Pod被遷移(Pod重建後被排程到其他Node),假如Pod原先儲存了一些本地資料,但是因為被遷移到了別的節點,這些資料相當於丟失。所以這個時候給它配一個Forgiveness策略,指定Pod繫結在它所執行的Node上,如果這個Node掛了,不遷移Pod,而是等待Node自動恢復的時候,把這個Pod原地拉起。這樣原來存在本地硬碟裡的資料,就可以被處理。對於有實效性的本地資料,我們還引入了超時機制。比如本地暫存的日誌,超過兩天三天之後,這個日誌再做後續處理也沒有意義。這個時候Pod需要在兩三天之內繫結在這個Node上,等到Node恢復再處理任務。如果超過兩三天Node還沒有恢復,但這些本地資料已經失去意義,這時候,我們希望Pod可以被遷移。在做驅逐檢查的時候,沒有超時的Forgiveness Pod就繼續繫結在Node上,超時的正常驅逐進行遷移就可以了。

第二個是執行時遷移。在執行的過程中,叢集負載、應用間親和性的滿足程度、Node的健康度或者服務質量時時刻刻都在變化,系統應該能夠根據這個當前的情況去動態地調整應用例項的分佈,這就是Rescheduling的意義。Rescheduling最終體現的行為是週期性地檢查叢集狀態,並做遷移的調整。但在遷移之前,有許多要考慮的因素,比如應用的可用性問題(不能因為遷移導致某個應用的所有例項在某個時刻不可用),遷移的單向性(被遷移的Pod如果回到原來的Node,就變成多此一舉)和收斂性(不能因為遷移某個Pod而引發大量其他Pod被遷移)。

2016年9月22日-23日,SDCC 2016大資料技術&架構實戰峰會將在杭州舉行,兩場峰會大牛講師來自阿里、京東、蘇寧、唯品會、美團點評、遊族、餓了麼、有贊、Echo等知名網際網路公司,共同探討海量資料下的應用監控系統建設、異常檢測的演算法和實現、大資料基礎架構實踐、敏捷型資料平臺的構建及應用、音訊分析的機器學習演算法應用,以及高可用/高併發/高效能系統架構設計、電商架構、分散式架構等話題與技術。
9月4日24點前仍處於最低六折優惠票價階段,單場峰會(含餐)門票只需499元,5人以上團購或者購買兩場峰會通票更有特惠,限時折扣,預購從速。(票務詳情連結)。

相關文章