官术网_书友最值得收藏!

2.2.3 OpenShift的部署架構(gòu)規(guī)劃

介紹邏輯架構(gòu)、技術(shù)架構(gòu)的主要目的是幫助讀者了解產(chǎn)品本身使用的技術(shù)和理念,但是在企業(yè)實施落地私有云時,面臨的第一個問題就是部署架構(gòu),這部分也是企業(yè)最關(guān)心的。我們通常會從集群資源、網(wǎng)絡(luò)、存儲、高可用要求等幾個方面綜合考慮,本節(jié)我們就針對OpenShift在私有云中的高可用部署架構(gòu)設(shè)計需要考慮的點進行逐一說明,非高可用部署架構(gòu)較為簡單,我們也不推薦生產(chǎn)使用,所以本書不會介紹。關(guān)于公有云的部分將在第6章介紹。

1.OpenShift計算資源規(guī)劃

計算資源主要包括CPU和內(nèi)存,計算資源規(guī)劃主要包含兩部分內(nèi)容:第一個是選擇部署OpenShift的基礎(chǔ)設(shè)施,這里主要指物理機部署還是虛擬機部署;第二個是OpenShift計算資源容量的規(guī)劃。

(1)基礎(chǔ)設(shè)施的選擇

在前面的邏輯架構(gòu)中,我們就介紹了OpenShift支持運行在所有的基礎(chǔ)設(shè)施上,包含物理機、虛擬機甚至公有云,那么在私有云建設(shè)中選擇物理機好還是虛擬機好呢?理論上物理機部署能獲得最大的性能需求,但是不易擴展、運維困難;而選擇虛擬機部署則網(wǎng)絡(luò)以及計算資源的損耗較大。我們通常會從以下維度進行對比:

·集群性能:通常裸物理機運行在性能上占據(jù)優(yōu)勢,主要是由于虛擬化層帶來的資源損耗。

·運維管理:使用虛擬機可以利用IaaS層提供的運維便利性,而物理機運維相對復(fù)雜。

·環(huán)境屬性:根據(jù)環(huán)境屬性通常會有多套OpenShift,如生產(chǎn)環(huán)境、開發(fā)測試環(huán)境等,通常開發(fā)測試環(huán)境采用虛擬機部署,生產(chǎn)環(huán)境對性能要求高,則采用物理機部署。

·資源利用率:物理機部署如果僅部署OpenShift運行應(yīng)用,在業(yè)務(wù)不飽和的情況下物理機資源利用率低,而虛擬機則可以將物理機資源統(tǒng)一調(diào)度分配,資源利用率高。

·虛擬化成熟度:企業(yè)是否已經(jīng)有成熟的IaaS管理系統(tǒng)。

·IaaS與PaaS的聯(lián)動集成:企業(yè)是否考慮實現(xiàn)IaaS與PaaS的聯(lián)動,主要表現(xiàn)在OpenShift自動擴容集群節(jié)點或?qū)?jié)點做縱向擴展。

·成本:分別計算虛擬機和物理機所需要的成本,理論上虛擬機的成本更低,物理機可能涉及很多額外的硬件采購。

針對上面給出的幾點選型參考,每個企業(yè)的實際情況不同,需要結(jié)合企業(yè)的具體情況進行選擇,必要時可以進行對比測試。當然,這也不是非此即彼的選擇,目前實施落地的客戶中有完全運行在物理機環(huán)境的,也有完全運行在虛擬機環(huán)境的,還有客戶選擇Master節(jié)點使用虛擬機,Worker節(jié)點使用物理機。

如果選擇使用虛擬機部署,OpenShift認證的虛擬化平臺有紅帽O(jiān)penStack、紅帽KVM、紅帽RHV以及vSphere,在具體的項目實踐中選擇vSphere虛擬化和紅帽O(jiān)penStack的情況較多。

(2)計算資源容量的規(guī)劃

在確定了部署使用的基礎(chǔ)設(shè)施以后,就需要對資源容量進行規(guī)劃,通常1個物理服務(wù)器的CPU Core相當于2個虛擬機的vCPU,這在容量規(guī)劃中至關(guān)重要。

在考慮計算資源容量規(guī)劃的時候,一般會從集群限制、業(yè)務(wù)預(yù)期資源等入手。當然,在建設(shè)初期可以最小化建設(shè)規(guī)模,后續(xù)對集群采取擴容即可,但容量規(guī)劃的算法依然是適用的。

在官方文檔中會給出每個版本集群的限制,下面給出我們整理的一些關(guān)鍵指標,如表2-4所示。

表2-4 OpenShift集群規(guī)模限制說明

我們在部署OpenShift時,如何根據(jù)表2-4中的指標進行容量規(guī)劃呢?這里所說的容量規(guī)劃主要是指計算節(jié)點,有很多種可用的估算方法,這里我們介紹其中兩種常用方法:

·從集群規(guī)模出發(fā)估算。

·從業(yè)務(wù)需求出發(fā)估算。

1)從集群規(guī)模出發(fā)估算

這種估算方法適用于大型企業(yè)要建設(shè)一個大而統(tǒng)一的PaaS平臺的情況。在這種情況下,也意味著建設(shè)時并不清楚具體哪些業(yè)務(wù)會運行到OpenShift集群中,這時就需要對整個集群的規(guī)模進行大致的估算,由于Master節(jié)點和Infra節(jié)點相對固化,我們這里僅說明用于運行業(yè)務(wù)容器的計算節(jié)點。

OpenShift的計算資源總數(shù)主要由單個節(jié)點配置和集群最大節(jié)點數(shù)兩方面決定,而這兩方面還要受不同版本集群的限制以及網(wǎng)絡(luò)規(guī)劃上的限制,最終是取所有限制中最小的。下面我們就對這些約束條件進行說明。

·單個節(jié)點配置:表示每個計算節(jié)點的CPU和內(nèi)存配置。

計算節(jié)點配置的CPU和內(nèi)存通常需要滿足一定比例,比例可以根據(jù)運行應(yīng)用的類型靈活配置,如Java應(yīng)用居多,則需要配置較高的內(nèi)存,常用的比例有1:2、1:4、1:8、1:16等。在估算資源的時候,建議以一個標準規(guī)格為基準,本示例以每個節(jié)點配置8vCPU、32G內(nèi)存為基準。當然,如果考慮使用混合比例部署,則每種類型添加權(quán)重比例計算即可。

單個計算節(jié)點可運行的Pod數(shù)受計算節(jié)點CPU、單個節(jié)點最大Pod數(shù)以及網(wǎng)絡(luò)規(guī)劃每個節(jié)點可分配IP數(shù)三部分約束,節(jié)點真實可運行的Pod數(shù)為三者的最小值。計算公式如下:

Min[節(jié)點CPU核心數(shù)×每個核心可運行Pod數(shù),單個節(jié)點最大Pod數(shù),網(wǎng)絡(luò)規(guī)劃允許的最大Pod數(shù)]

對于公式中的每個核心可運行Pod數(shù)可以通過參數(shù)設(shè)置,在OpenShift 3中默認設(shè)置每個核心最多運行10個Pod,新版本OpenShift默認無任何限制,需要用戶自行添加限制。

為了簡化說明,暫時假設(shè)網(wǎng)絡(luò)規(guī)劃允許的最大Pod數(shù)為256,這部分將在后續(xù)網(wǎng)絡(luò)規(guī)劃部分詳細說明。我們以前面確定基準配置為例,每個節(jié)點配置8個vCPU,相當于4個Core。如果默認不設(shè)置每個核心可運行的Pod數(shù),那么單個節(jié)點可運行的最大Pod數(shù)為250;如果設(shè)置每個核心可運行的Pod數(shù)為10,那么就會取最小值40,也就是單個節(jié)點最多只能運行40個Pod。

在實際情況下,由于系統(tǒng)資源保留、其他進程消耗以及配額限制等,真實允許運行的最大Pod數(shù)會小于上述公式計算的理論值。

·集群最大節(jié)點數(shù):表示單個集群所能納管的最大節(jié)點數(shù),包含Master節(jié)點和所有類型的計算節(jié)點。

集群最大節(jié)點數(shù)受OpenShift不同版本的限制以及網(wǎng)絡(luò)劃分的限制,同樣取最小值。計算公式如下:

Min[OpenShift版本節(jié)點數(shù)限制,網(wǎng)絡(luò)規(guī)劃所允許的最大節(jié)點數(shù)]

假設(shè)使用OpenShift 4.6版本,該版本節(jié)點數(shù)限制為2000,網(wǎng)絡(luò)規(guī)劃所允許的最大節(jié)點數(shù)為512,那么集群最大規(guī)模為512個節(jié)點。

在確定了集群最大節(jié)點數(shù)和單個節(jié)點的配置之后,集群所需要的總資源就可以計算出來了,同時根據(jù)每個節(jié)點允許的最大Pod數(shù),也可以計算出整個集群允許運行的最大Pod數(shù),計算公式如下:

集群最大Pod數(shù)量=每個節(jié)點允許的最大Pod數(shù)×集群最大節(jié)點數(shù)

當然上述計算的值同樣要與不同版本集群所允許的最大Pod總數(shù)取最小值。我們以O(shè)penShift 4.3版本為例,計算公式如下:

Min[集群最大Pod數(shù)量,150000]

到此為止,就可以確定出集群最大允許運行的Pod總數(shù),也就評估出單個集群的最大規(guī)模了。在計算出這些數(shù)據(jù)之后,就可以評估是否滿足企業(yè)對PaaS的規(guī)劃。如果不滿足,則考慮采用增加單個節(jié)點資源和重新規(guī)劃網(wǎng)絡(luò)等手段增加集群可運行Pod的數(shù)目;如果滿足,則可以根據(jù)第一期允許集群運行最大Pod數(shù),反向推算出第一期建設(shè)所需要的計算節(jié)點數(shù)和計算資源總數(shù)。除了計算節(jié)點之外,額外還要加上Master節(jié)點以及其他外部組件。

2)從業(yè)務(wù)需求出發(fā)估算

這種估算方法適用于為某個項目組或某個業(yè)務(wù)系統(tǒng)建設(shè)PaaS平臺的情況。在這種情況下,我們很明確地知道會有哪些業(yè)務(wù)系統(tǒng)甚至組件運行到OpenShift集群中,這時就可以根據(jù)業(yè)務(wù)對資源的需求大致估算集群的規(guī)模。同樣,僅估算計算節(jié)點,其他類型節(jié)點數(shù)相對固定。

這種方法通常需要明確業(yè)務(wù)系統(tǒng)所使用的中間件或開發(fā)語言,并提供每個容器需要的資源以及啟動容器的個數(shù)來計算。但是在實際項目中,每個容器需要的資源往往是不容易估算的,簡單的方法是按以往運行的經(jīng)驗或者運行在虛擬機上的資源配置進行計算。當然,也可以根據(jù)應(yīng)用在虛擬機上運行的資源使用率進行計算。

我們以一套在OpenShift集群中運行如下類型的應(yīng)用為例,Pod類型、Pod數(shù)量、每個Pod最大內(nèi)存、每個Pod使用的CPU資源和存儲資源如表2-5所示。

表2-5 業(yè)務(wù)資源評估

那么,OpenShift集群提供的應(yīng)用計算資源至少為125個CPU Core、845GB內(nèi)存,這種資源估算方法通常還需要考慮為集群預(yù)留一定比例的空閑資源用于系統(tǒng)進程以及滿足故障遷移,最后再通過標準規(guī)格的節(jié)點配置計算出所需要的節(jié)點數(shù),取CPU和內(nèi)存計算的最大值,并向上取整。計算公式如下:

Max[業(yè)務(wù)所需要的總CPU×(1+資源預(yù)留百分比)/標準規(guī)格節(jié)點CPU,業(yè)務(wù)所需要的總內(nèi)存×(1+資源預(yù)留百分比)/標準規(guī)格節(jié)點內(nèi)存]

以前面定義的標準規(guī)格8vCPU、32G內(nèi)存,資源預(yù)留百分比為30%來計算上述場景,根據(jù)CPU計算為40.625(注意Core和vCPU的換算),根據(jù)內(nèi)存計算為34.328,然后取最大值并向上取整為41。那么該場景下需要41個計算節(jié)點。

上述計算方法比較粗糙,未考慮很多因素的影響。比如由于CPU可以超量使用而內(nèi)存不可以超量使用,這種情況下可以將計算結(jié)果取最小值,也就是需要35個計算節(jié)點。

另外需要注意的是,在集群計算節(jié)點規(guī)模較小時,如四到五個計算節(jié)點,需要在上述公式計算結(jié)果的基礎(chǔ)上至少增加1個節(jié)點才能滿足當一個節(jié)點故障時集群中仍有足夠的資源接受故障遷移的應(yīng)用容器。當集群計算節(jié)點規(guī)模較大時,計算公式中可以預(yù)留一定百分比的資源以承載一個節(jié)點故障后應(yīng)用容器遷移的需求,無須額外添加,當然這不是一定成立的,可以根據(jù)實際情況決定是否需要添加。

(3)計算節(jié)點配置類型選擇

無論采用哪種估算方法,都能得到集群所需要的總資源數(shù)。在總資源數(shù)一致的前提下,我們可以選擇計算節(jié)點數(shù)量多、每個計算節(jié)點配置相對較低的方案,也可以選擇計算節(jié)點數(shù)量少、每個計算節(jié)點配置較高的方案。我們有以下三種方案:第一種方案就是節(jié)點低配、數(shù)量多的方案;第三種就是節(jié)點高配、數(shù)量少的方案;第二種為折中的方案。示例如表2-6所示。

表2-6 節(jié)點配置類型選擇

在虛擬化環(huán)境中我們傾向于選擇第一種方案,因為更多的計算節(jié)點有助于實現(xiàn)Pod的高可用。在物理服務(wù)器上部署OpenShift時我們傾向于選擇第二種方案,原因之一是物理服務(wù)器增加CPU/內(nèi)存資源相對比較麻煩。

當然,這也不是絕對的必須二選一,比如在虛擬化環(huán)境中每個Pod需要4vCPU和8G內(nèi)存,而每個節(jié)點剛好配置了4vCPU和8G內(nèi)存,這樣導(dǎo)致每個節(jié)點只能運行一個Pod,不利于集群資源的合理利用,所以計算節(jié)點的配置高還是低是相對于平均每個Pod需要的資源而言的。總體的原則是保證Pod盡可能分散在不同節(jié)點,同時同一個節(jié)點資源可以被共享使用,提高利用率。

到此為止,我們已經(jīng)介紹完計算資源容量規(guī)劃。在OpenShift高可用架構(gòu)下可以很容易地通過添加節(jié)點對集群資源實現(xiàn)橫向擴容。如果在虛擬機環(huán)境安裝,那么縱向擴容也是很容易的。

2.OpenShift的網(wǎng)絡(luò)介紹與規(guī)劃

在上一節(jié)中提到在規(guī)劃每個節(jié)點允許運行的最大Pod數(shù)以及集群最大節(jié)點數(shù)時都與網(wǎng)絡(luò)規(guī)劃有關(guān)。本節(jié)介紹OpenShift的網(wǎng)絡(luò)是如何規(guī)劃的。在開始介紹網(wǎng)絡(luò)規(guī)劃之前,需要先弄清楚OpenShift中的網(wǎng)絡(luò)模型,這樣才能明確要規(guī)劃的網(wǎng)絡(luò)代表的含義。

(1)OpenShift內(nèi)部通信網(wǎng)絡(luò)整體概述

由于OpenShift網(wǎng)絡(luò)通信的概念較多,理解起來相對困難,為了方便讀者深入內(nèi)部,在開始講解網(wǎng)絡(luò)模型之前,我們先嘗試從OpenShift的用戶角度提出5個問題,并解答這5個問題,從而讓讀者先對OpenShift網(wǎng)絡(luò)通信有個大致的概念。然后讀者可以帶著對這5個問題的理解,閱讀本章更為詳細的內(nèi)容。

·問題1:OpenShift中Pod之間的通信是否一定需要Service?

答案:Pod之間的通信不需要Service。

在同一個Namespace中,Pod A開放了某個端口,在一個Pod B中curl pod_A_IP:port,就能與之通信,如圖2-45所示。

圖2-45 查看兩個Pod的IP地址

所以說,Pod之間能不能通信和兩個Pod經(jīng)不經(jīng)過Service、有沒有Service無關(guān)。

·問題2:從一個Pod訪問另外一個Pod,Pod之間可以通信,其數(shù)據(jù)鏈路是什么?

答案:兩個Pod如果在一個OpenShift節(jié)點上,其通信流量沒有繞出本宿主機的OVS;如果兩個Pod在不同節(jié)點上,Pod之間的通信經(jīng)過了Vxlan。具體內(nèi)容后文將展開說明。

·問題3:既然Pod之間通信不需要Service,為何Kubernetes要引入Service?

答案:我們首先要明確Service是對一組提供相同功能的Pod的抽象,并為它們提供一個統(tǒng)一的內(nèi)部訪問入口。它主要解決以下兩方面問題:

①負載均衡:Service提供其所屬多個Pod的負載均衡。

②服務(wù)注冊與發(fā)現(xiàn):解決不同服務(wù)之間的通信問題。在OpenShift中創(chuàng)建應(yīng)用后,需要提供訪問應(yīng)用的地址供其他服務(wù)調(diào)用,這個地址就是由Service提供的。

在OpenShift中,我們每創(chuàng)建一個Service,就會分配一個IP地址,稱為ClusterIP,這個IP地址是一個虛擬地址(OpenShift外部不可達),這樣內(nèi)部DNS就可以通過Service名稱(FQDN)解析成ClusterIP地址。這就完成了服務(wù)注冊,DNS解析需要的信息全部保存在Etcd中。

那么,Service注冊到Etcd的內(nèi)容是什么呢?實際上是Service資源對象的Yaml包含的相關(guān)內(nèi)容。也就是說,Etcd中記錄的Services注冊信息里有Namespace、Service Name、ClusterIP,通過這三個信息就可組成DNS A記錄,也就是,Service的FQDN和Service ip之間的對應(yīng)關(guān)系。需要說明的是,Etcd不是DNS,DNS A記錄是通過查詢生成的。OpenShift的DNS是由SkyDNS/CoreDNS實現(xiàn)的(后面內(nèi)容會詳細介紹)。

服務(wù)發(fā)現(xiàn)這個詞經(jīng)常被妖魔化。它的作用是讓OpenShift某個服務(wù)發(fā)現(xiàn)另外一個服務(wù)。也就是說,Service A要和Service B通信,我需要知道Service B是誰、在哪,Cluster IP、對應(yīng)的Pod IP都是什么,這就叫作服務(wù)發(fā)現(xiàn)。

·問題4:有了Service以后,Pod之間的通信和沒有Service有何區(qū)別?

答案:在數(shù)據(jù)通信層,沒區(qū)別,因為Service只是邏輯層面的東西。但是,沒有Service,多個Pod無法實現(xiàn)統(tǒng)一入口,也無法實現(xiàn)負載均衡。也就是說,沒有Service,多個Pod之間的負載均衡就要依賴第三方實現(xiàn)。

那么,有了Service以后,Pod之間怎么尋址?回答這個問題,我們要站在開發(fā)者角度。如果一個程序員要寫微服務(wù),微服務(wù)之間要相互調(diào)用,應(yīng)該怎么寫?寫Pod IP和ClusterIP是不現(xiàn)實的,因為這兩個IP可能發(fā)生變化。

如果程序員決定用Kubernetes做服務(wù)發(fā)現(xiàn)的,要實現(xiàn)不同服務(wù)之間的調(diào)用,就需要使用Kubernetes的Service名稱,因為我們可以固定Service名稱。(若使用微服務(wù)框架中的服務(wù)注冊中心做服務(wù)注冊發(fā)現(xiàn),可以不使用Kubernetes的Service。)

OpenShift/Kubernetes中Service有短名和長名。以圖2-46為例,jws-app就是Service的短名,Service長名的格式是<sevrvice_name>.<namespace>.svc.cluser.local,也就是jws-app.web.svc.cluser.local。Service短名可以自動補充成長名,這是OpenShift中的DNS做的,這個后面介紹。

圖2-46 查看Service的名稱

那么,如果在兩個不同的Namespace中有兩個相同的Service短名,微服務(wù)調(diào)用是不是會出現(xiàn)混亂?程序員的代碼里是不是要寫Service全名?

首先,站在OpenShift集群管理員的角度,我們看到所有的項目有幾十個或者更多,會覺得在不同Namespaces中存在相同的Service短名是可能的(比如Namespace A中有個acat的Service,Namespace B中也有個acat的Service)。但站在程序員角度,他只是OpenShift的使用者、擁有自己的Namespace的管理權(quán),其他Namespace不能訪問。而且絕大多數(shù)情況下,同一個業(yè)務(wù)項目的微服務(wù)一般會運行在同一個Namespace中,默認如果使用短名稱(只寫Service Name),則會自動補全成當前Namespace的FQDN,只有在跨Namespace調(diào)用的時候才必須寫全名FQDN。

所以,程序員寫的程序用到了Service Name。那么,真正運行應(yīng)用的Pod之間的通信也必然會以Service Name去找。通過Service名稱解析為Service ClusterIP,然后經(jīng)過kube-proxy(默認Iptables模式)的負載均衡最終選擇一個實際的Pod IP。找到Pod IP以后,接下來就會進行實際的數(shù)據(jù)交換,這就和Service沒有關(guān)系了。

·問題5:ClusterIP到Pod IP這部分的負載均衡是怎么實現(xiàn)的?

答案:目前版本的OpenShift中是通過kube-proxy(iptables模式)實現(xiàn)的。具體內(nèi)容后面詳細介紹。

通過以上5個問題的問答,相信很多讀者大致了解了OpenShift的通信網(wǎng)絡(luò)。接下來,我們講述OpenShift的網(wǎng)絡(luò)模型。

(2)OpenShift的網(wǎng)絡(luò)模型

OpenShift的網(wǎng)絡(luò)模型繼承自Kubernetes,從內(nèi)到外共包含以下四個部分:

·Pod內(nèi)部容器通信的網(wǎng)絡(luò)。

·Pod與Pod通信的網(wǎng)絡(luò)。

·Pod和Service之間通信的網(wǎng)絡(luò)。

·集群外部與Service或Pod通信的網(wǎng)絡(luò)。

這四部分構(gòu)成了整個OpenShift的網(wǎng)絡(luò)模型,下面分別進行說明。

Pod內(nèi)部容器通信的網(wǎng)絡(luò)

我們都知道Pod是一組容器的組合,意味著每個Pod中可以有多個容器,那么多個容器之間如何通信呢?這就是這部分要解決的問題。

Kubernetes通過為Pod分配統(tǒng)一的網(wǎng)絡(luò)空間,實現(xiàn)了多個容器之間的網(wǎng)絡(luò)共享,也就是同一個Pod中的容器之間通過Localhost相互通信。

Pod與Pod通信的網(wǎng)絡(luò)

關(guān)于這部分Kubernetes在設(shè)計之時的目標就是Pod之間可以不經(jīng)過NAT直接通信,即使Pod跨主機也是如此。而這部分Kubernetes早期并未提供統(tǒng)一的標準方案,需要用戶提前完成節(jié)點網(wǎng)絡(luò)配置,各個廠商提供了不同的解決方案,諸如Flannel、OVS等。隨著Kubernetes的發(fā)展,在網(wǎng)絡(luò)方向上希望通過統(tǒng)一的方式來集成不同的網(wǎng)絡(luò)方案,這就有了現(xiàn)在的容器網(wǎng)絡(luò)開放接口(Container Network Interface,CNI)。

CNI項目是由多個公司和項目創(chuàng)建的,包括CoreOS、紅帽、Apache Mesos、Cloud Foundry、Kubernetes、Kurma和rkt。CoreOS首先提出定義網(wǎng)絡(luò)插件和容器之間的通用接口,CNI被設(shè)計為規(guī)范,它僅關(guān)注容器的網(wǎng)絡(luò)連接并在刪除容器時刪除分配的網(wǎng)絡(luò)資源。

CNI有三個主要組成部分:

·CNI規(guī)范:定義容器運行時和網(wǎng)絡(luò)插件之間的API。

·插件:與各種SDN對接的組件。

·庫:提供CNI規(guī)范的Go實現(xiàn),容器運行時可以利用它來便捷地使用CNI。

各廠商遵守規(guī)范來開發(fā)網(wǎng)絡(luò)組件,在技術(shù)實現(xiàn)上共分為兩大陣營:

·基于二層實現(xiàn):通過將Pod放在一個大二層網(wǎng)絡(luò)中,跨節(jié)點通信通常使用Vxlan或UDP封包實現(xiàn),常用的此類插件有Flannel(UDP或Vxlan封包模式)、OVS、Contiv、OVN等。

·基于三層實現(xiàn):將Pod放在一個互聯(lián)互通的網(wǎng)絡(luò)中,通常使用路由實現(xiàn),常用的此類插件有Calico、Flannel-GW、Contiv、OVN等。

可以看到網(wǎng)絡(luò)插件的種類繁多,OpenShift默認使用的是基于OVS的二層網(wǎng)絡(luò)實現(xiàn)Pod與Pod之間的通信,后面我們將詳細介紹。

Pod和Service之間通信的網(wǎng)絡(luò)

Pod與Service之間的通信主要是指在Pod中訪問Service的地址。在OpenShift中,Service是對一組提供相同功能的Pod的抽象,并為它們提供一個統(tǒng)一的內(nèi)部訪問入口。主要解決以下兩個問題:

·服務(wù)注冊與發(fā)現(xiàn):服務(wù)注冊與發(fā)現(xiàn)在微服務(wù)架構(gòu)中用來解決不同服務(wù)之間的通信問題。在OpenShift中創(chuàng)建應(yīng)用后,需要提供訪問應(yīng)用的地址供其他服務(wù)調(diào)用,這個地址就是由Service提供的。

每創(chuàng)建一個Service,就會分配一個Service IP地址,稱為ClusterIP,這個IP地址是一個虛擬地址,無法執(zhí)行ping操作。同時自動在內(nèi)部DNS注冊一條對應(yīng)的A記錄,這就完成了服務(wù)注冊,注冊信息全部保存在Etcd中。服務(wù)發(fā)現(xiàn)支持環(huán)境變量和DNS兩種方式,其中DNS的方式最為常用,關(guān)于DNS的部分我們將在后面章節(jié)詳細說明。

·負載均衡:每個Service后端可能對應(yīng)多個Pod示例,在訪問Service的時候需要選擇一個合適的后端處理請求。

Service的負載均衡可以有很多的實現(xiàn)方式,目前Kubernetes官方提供了三種代理模式:userspace、iptables、ipvs。當前版本的OpenShift默認的代理模式是iptables,本節(jié)主要介紹這種模式。有興趣的讀者可參考Kubernetes官網(wǎng)中對Service的介紹自行了解其他模式。

Iptables模式如圖2-47所示。

圖2-47 Iptables模式

從圖2-47中可以看出,當客戶端訪問Servcie的ClusterIP時,由Iptables實現(xiàn)負載均衡,選擇一個后端處理請求,默認的負載均衡策略是輪詢。在這種模式下,每創(chuàng)建一個Service,會自動匹配后端實例Pod記錄在Endpoints對象中,并在所有Node節(jié)點上添加相應(yīng)的iptables規(guī)則,將訪問該Service的ClusterIP與Port的連接重定向到Endpoints中的某一個后端Pod。本書由于篇幅所限,不再贅述關(guān)于負載均衡實現(xiàn)的細節(jié)。

這種模式有兩個缺點:第一,不支持復(fù)雜的負載均衡算法;第二,當選擇的某個后端Pod沒有響應(yīng)時無法自動重新連接到另一個Pod,用戶必須利用Pod的健康監(jiān)測來保證Endpoints列表中Pod都是存活的。

集群外部與Service或Pod通信的網(wǎng)絡(luò)

前面我們說過,創(chuàng)建Service分配的ClusterIP是一個虛擬IP地址,外部是無法訪問的,那么該如何實現(xiàn)集群外部訪問部署在集群中的應(yīng)用呢?

目前OpenShift共有以下五種對外暴露服務(wù)的方式:

·Hostport

·Nodeport

·Hostnetwork

·LoadBalancer

·Ingress/Router

不同方式的使用場景各不相同,關(guān)于每種方式的具體細節(jié)我們將在后面小節(jié)中進行說明。

多租戶的隔離

對于OpenShift來說,一個Namespace就是一個租戶,實現(xiàn)多租戶隔離主要表現(xiàn)在網(wǎng)絡(luò)上,即每個租戶都擁有與其他租戶完全隔離的自有網(wǎng)絡(luò)環(huán)境。而OpenShift的網(wǎng)絡(luò)可以由多種第三方插件實現(xiàn),是否支持多租戶隔離要看選擇的Pod網(wǎng)絡(luò)插件。目前廣泛使用的是通過網(wǎng)絡(luò)策略控制網(wǎng)絡(luò)隔離,網(wǎng)絡(luò)策略采用了比較嚴格的單向流控制,最小粒度可控制到Pod與Pod,而不僅僅是Namespace級別的隔離。

在了解了OpenShift網(wǎng)絡(luò)模型之后,可以看到OpenShift網(wǎng)絡(luò)涉及的范圍大而復(fù)雜,除了Pod內(nèi)部容器通信比較簡單、無須管理之外,其余的三部分都是可以配置管理的,如替換不同的插件或者通過不同的方式實現(xiàn)。下面我們就針對這三部分分別進行說明。

(3)OpenShift Pod網(wǎng)絡(luò)的實現(xiàn)

OpenShift使用軟件定義網(wǎng)絡(luò)的方法提供統(tǒng)一的Pod網(wǎng)絡(luò),使得集群中Pod可以跨主機通信。理論上,OpenShift兼容所有符合CNI規(guī)范的網(wǎng)絡(luò)插件,但目前受Network Operator管理的僅支持OpenShift SDN、OVN Kubernetes、Kuryr三種網(wǎng)絡(luò)插件,如果使用第三方網(wǎng)絡(luò)插件,將不安裝Network Operator。官方默認使用OpenShift SDN,這也是我們推薦使用的網(wǎng)絡(luò)插件,下面我們就主要介紹這個網(wǎng)絡(luò)插件的實現(xiàn)。

OpenShift SDN

在OpenShift中Pod的網(wǎng)絡(luò)默認由OpenShift SDN實現(xiàn)和維護,底層是使用OpenvSwitch實現(xiàn)的二層覆蓋網(wǎng)絡(luò),跨節(jié)點通信使用Vxlan封包。用戶對網(wǎng)絡(luò)的需求往往是復(fù)雜的,有些用戶需要一個平面網(wǎng)絡(luò),而有些用戶則需要基于網(wǎng)絡(luò)隔離。為了滿足客戶的不同需求場景,OpenShift SDN提供了三種模式:subnet、multitenant、networkpolicy。

·subnet:提供扁平的Pod網(wǎng)絡(luò)。集群中每個Pod都可以與其他服務(wù)的Pod(本項目或其他項目)進行通信。

·multitenant:提供項目級別的隔離,這種模式下,除default項目外,默認所有項目之間隔離。

·networkpolicy:OpenShift默認的OVS插件模式,提供Pod粒度級別的隔離,這種模式的隔離完全由NetworkPolicy對象控制。項目管理員可以創(chuàng)建網(wǎng)絡(luò)策略,例如配置項目的入口規(guī)則以保護服務(wù)免受攻擊。

模式切換通過修改Network Operator的配置network.config/cluster來實現(xiàn),細節(jié)請參考官網(wǎng)文檔。

無論使用OpenShift SDN的哪種模式,Pod之間網(wǎng)絡(luò)通信如圖2-48所示。

圖2-48 Pod之間網(wǎng)絡(luò)通信

從圖2-48可以看出Pod之間通信有三條鏈路:

·Pod 1(10.128.0.2/23)和Pod 2(10.128.0.3/23)在同一個節(jié)點上,從Pod 1到Pod 2的數(shù)據(jù)流如下:

Pod 1的eth0→vethxx→br0→vethyy→Pod 2的eth0

這條鏈路通信方式來源于Docker的網(wǎng)絡(luò)模型,不熟悉的讀者請自行查閱學(xué)習(xí),本書不再贅述。

·Pod 1(10.128.0.2/23)和Pod 3(10.129.0.2/23)在不同的節(jié)點上,從Pod 1到Pod 3的數(shù)據(jù)流如下:

Pod 1的eth0→vethxx→br0→vxlan0→hostA eth0(192.168.1.101)→network→hostB eth0(192.168.1.102)→vxlan0→br0→vethmm→Pod 3的eth0

·Pod 1(10.128.0.2/23)訪問外部網(wǎng)絡(luò),數(shù)據(jù)流如下:

Pod 1的eth0→vethxx→br0→tun0→(NAT)→eth0(physical device)→Internet

在介紹OpenShift SDN的三種模式時提到兩種模式可以提供多租戶網(wǎng)絡(luò)隔離,那么網(wǎng)絡(luò)隔離的實現(xiàn)原理是什么呢?

網(wǎng)絡(luò)隔離的實現(xiàn)

由于Multitenant和NetworkPolicy模式的隔離機制不同,我們分別說明。

·Multitenant模式

在Multitenant模式下,每個項目都會收到唯一的虛擬網(wǎng)絡(luò)ID(VNID),用于標識分配給項目的Pod的流量。默認一個項目中的Pod無法向不同項目中的Pod發(fā)送數(shù)據(jù)包或從其接收數(shù)據(jù)包,也就是說,不同VNID項目中的Pod之間是無法互相通信的。

但有一個項目是例外,這就是VNID為0的項目default,該項目中Pod能夠被所有項目的Pod訪問,稱為全局項目。這主要是因為該項目中運行了一些全局組件(如Router和Registry),這些全局組件需要滿足與其他任意項目的網(wǎng)絡(luò)互通。

在這種模式下,在可以滿足網(wǎng)絡(luò)隔離的前提下又提供了靈活的打通網(wǎng)絡(luò)操作,可以隨時打通兩個項目的網(wǎng)絡(luò)或隔離兩個項目的網(wǎng)絡(luò),為項目的隔離提供了極大的靈活性。

·NetworkPolicy模式

在NetworkPolicy模式下,支持按Namespace和按Pod級別進行網(wǎng)絡(luò)訪問控制,通過管理員配置網(wǎng)絡(luò)策略來實現(xiàn)隔離。

在OpenShift中,OpenShift SDN插件默認使用了NetworkPolicy模式。在這種模式的集群中,網(wǎng)絡(luò)隔離完全由NetworkPolicy對象控制,目前在OpenShift中僅支持Ingress類型的網(wǎng)絡(luò)策略。默認情況下,所有項目中沒有任何的NetworkPolicy對象,也就是說,所有項目的Pod可以相互通信。項目管理員需要在項目中創(chuàng)建NetworkPolicy對象以指定允許的傳入連接。我們可以看到NetworkPolicy起著至關(guān)重要的作用,接下來就說明NetworkPolicy如何工作。

NetworkPolicy的架構(gòu)和最佳實踐

NetworkPolicy描述一組Pod之間是如何被允許相互通信,以及如何與其他網(wǎng)絡(luò)端點進行通信。NetworkPolicy底層使用Iptables規(guī)則實現(xiàn),所以注意策略不宜作用于大量獨立Pod,否則會導(dǎo)致Iptables規(guī)則太多而性能下降。

NetworkPolicy具有如下特點:

·項目管理員可以創(chuàng)建網(wǎng)絡(luò)策略,而不僅僅是集群管理員才能創(chuàng)建網(wǎng)絡(luò)策略。

·NetworkPolicy通過網(wǎng)絡(luò)插件來實現(xiàn),所以必須使用一種支持NetworkPolicy的網(wǎng)絡(luò)方案。

·沒有NetworkPolicy的Namespace,默認無任何訪問限制。

NetworkPolicy的配置文件字段結(jié)構(gòu)如下:


apiVersion: networking.k8s.io/v1 
kind: NetworkPolicy 
metadata:
  name: test-network-policy
spec:
  podSelector: 
    matchLabels:
      role: db 
  policyTypes: 
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchLabels: 
            project: myproject
      - podSelector: 
          matchLabels:
            role: frontend 
      ports:
        - protocol: TCP 
          port: 6379

在上述配置中,Spec下描述了NetworkPolicy對象的主要屬性:

·podSelector:通過標簽選擇被控制訪問的Pod,也就是這個網(wǎng)絡(luò)策略要作用于哪些Pod。如果為空,則表示所有Pod。

·policyTypes:定義策略的類型,有Ingress和Egress兩種,OpenShift中目前僅支持Ingress。

·ingress:通過標簽選擇允許訪問的Pod,也就是這個網(wǎng)絡(luò)策略允許哪些Pod訪問podSelector中設(shè)定的Pod。支持通過namespaceSelector基于namespace級別選擇和通過podSelector基于Pod級別選擇,同時可以通過ports屬性限定允許訪問的協(xié)議和端口。如果為空,則表示不允許任何Pod執(zhí)行訪問。

在介紹NetworkPolicy的概念后,介紹其最佳實踐的五條規(guī)則:

·Policy目的明確規(guī)則:每個NetworkPolicy資源只包含單一的source和destination。

·關(guān)聯(lián)應(yīng)用相近規(guī)則:強相關(guān)的Service放置在相同的Namespace中,一般同一個Namespace中的Pod允許相互訪問。

·允許openshift-ingress Namespace訪問規(guī)則:這樣OpenShift ingress routers才能訪問到應(yīng)用Pod。

·允許相關(guān)的Namespaces的特定通信:例如允許Web應(yīng)用訪問后端應(yīng)用。

·默認全拒絕:沒有被NetworkPolicy允許的通信將被全部拒絕。

需要指出的是,NetworkPolicy是Namespace資源。也就是說,我們限制這個Namespace的networkpolicy.ingress策略(OpenShift NetworkPolicy目前不支持networkpolicy.egress)。此外,NetworkPolicy對象的作用是疊加的,這意味著我們可以將多個NetworkPolicy對象組合在一起以滿足復(fù)雜的網(wǎng)絡(luò)隔離要求。

接下來,我們通過幾個示例展現(xiàn)NetworkPolicy的最佳實踐。

示例1:允許同一個Namespace內(nèi)的Pod之間相互通信。

·spec.podSelector為空,表示匹配項目中所有Pod。

·spec.ingress.from.podSelector為空,表示匹配項目中所有Pod。

下面配置的含義是允許項目內(nèi)所有Pod的訪問。


kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-same-namespace
spec:
  podSelector:
  ingress:
  - from:
    - podSelector: {}

如圖2-49所示,my-backend-prod項目中的兩個Pod允許相互通信。

圖2-49 一個項目中兩個Pod的相互通信

示例2:僅允許來自O(shè)penShift的Ingress Controller的通信。

·通過label允許openshift-ingress Namespace過來的流量。

·spec.podSelector為空,表示匹配本Namespace中的所有Pod。

·spec.ingress[0].from[0].namespaceSelector為空,表示匹配包含network.openshift.io/policy-group=ingress標簽項目中的所有Pod。

配置如下所示,作用是該Namespace中的所有Pod都允許openshift-ingress Namespace中所有Pod訪問。


kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-from-openshift-ingress-namespace
spec:
  podSelector:
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: openshift-ingress

效果如圖2-50所示。

圖2-50 允許openshift-ingress Namespace中pod的訪問

示例3:允許其他Namespace的流量訪問。

·spec.podSelector.matchLabels.component:backend匹配Namespace中l(wèi)abel為backend的Pod。

·spec.ingress[0].from[0].namespaceSelector通過name label匹配Namespace。

·spec.ingress[0].ports[0]定義允許的targetports。

配置如下所示,作用是backend label的Namespace中的backend label Pod允許Namespace label為myapp-frontend-prod的所有Pod訪問TCP 8443端口。


kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
 name: allow-frontend-to-backend-ports
spec:
 podSelector:
   matchLabels:
     component: backend
 ingress:
  - from:
    -namespaceSelector:
       matchLabels:
         name: myapp-frontend-prod
   ports:
    -protocol: TCP
     port: 8443

效果如圖2-51所示。

圖2-51 允許其他Namespace的流量訪問

示例4:允許其他項目的特定Pod端口訪問。

下面配置的含義為允許Namespace label為myapp-frontend-prod中的、Pod label為component:api-gateway的Pod訪問本項目中Pod label為component:backend的TCP 8443端口。


kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
 name: allow-frontend-api-to-backend-ports
spec:
 podSelector:
    matchLabels:
     component: backend
 ingress:
  - from:
    -namespaceSelector:
       matchLabels:
         name: myapp-frontend-prod
     podSelector:
       component: api-gateway
   ports:
    -protocol: TCP
     port: 8443

效果如圖2-52所示。

圖2-52 允許其他項目的特定Pod端口訪問

示例5:默認拒絕。

只要存在NetworkPolicy,就表示拒絕。也就是除了策略允許的通信,其余全都拒絕。

“deny-by-default”NetworkPolicy用于限制所有的ingress流量。

spec.podSelector為空,表示匹配所有Pod。

沒有spec.ingress rules,表示所有的ingress流量都被拒絕。


kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: deny-by-default
spec:
  podSelector:
  ingress: []

效果如圖2-53所示。

Pod訪問外部網(wǎng)絡(luò)的控制

前文我們提到,Pod訪問外部網(wǎng)絡(luò)(集群外部)時,通過SNAT做地址轉(zhuǎn)換,最終以Pod所在的Worker節(jié)點的IP訪問外部網(wǎng)絡(luò)。

默認情況下,OpenShift不限制容器的出口流量。也就是說,可以從任意的OpenShift Worker節(jié)點對外發(fā)起訪問請求。但是,如果出口訪問需要經(jīng)過防火墻,就會有一個問題,我們需要在防火墻上配置容器出口的IP,由于不同的Pod在不同的OpenShift Worker節(jié)點上,并且Pod還可以在其他節(jié)點上重啟,因此防火墻上就需要配置很多策略,甚至防火墻必須接受來自所有這些節(jié)點的流量。針對這樣的需求,可以通過配置Pod的Egress IP地址實現(xiàn)Pod訪問外部網(wǎng)絡(luò)的控制。主要有兩種實現(xiàn)方式:

·配置Namespace級別的Egress IP:通過為Namespace指定Egress IP,并將Egress IP分配到指定的節(jié)點實現(xiàn)。支持自動配置和手動配置兩種模式。

·配置Egress防火墻:通過在集群中創(chuàng)建EgressNetworkPolicy對象實現(xiàn)對外訪問的控制。該策略只能由集群管理員定義,而且每個項目只能定義一個EgressNetworkPolicy對象,支持multitenant和networkpolicy網(wǎng)絡(luò)模式。

在以上兩種方式中,配置Egress IP的方式是比較簡單易行的。具體配置步驟不再贅述。具體操作見Repo中“Egress IP的配置與刪除”。

圖2-53 默認拒絕

OpenShift中的多網(wǎng)絡(luò)平面

在OpenShift中通過Multus-CNI可以實現(xiàn)Pod多網(wǎng)絡(luò)平面,讓一個Pod同時配置多個網(wǎng)卡連接到多個網(wǎng)絡(luò),使一個Pod沿多個不同的網(wǎng)絡(luò)鏈路發(fā)送流量成為可能。例如OpenShift集群的網(wǎng)絡(luò)流量使用OVS網(wǎng)絡(luò),而對性能要求較高的業(yè)務(wù)數(shù)據(jù),則連接其他類型的CNI插件,這在OpenShift 3中是無法實現(xiàn)的。在Multus CNI模式下,每個Pod都有一個eth0接口,該接口連接到集群范圍的Pod網(wǎng)絡(luò)。使用Multus CNI添加其他網(wǎng)絡(luò)接口,將其命名為net1、net2等。同時,Multus-CNI作為一個CNI插件,可以調(diào)用其他CNI插件,它支持:

·CNI規(guī)范的參考插件(例如Flannel、DHCP、Macvlan、Pvlan)。

·第三方插件(例如Calico、Weave、Cilium、Contiv)。

·Kubernetes中的SRIOV、SRIOV-DPDK、OVS-DPDK和VPP工作負載以及Kubernetes中的基于云原生和基于NFV的應(yīng)用程序。

我們可以根據(jù)業(yè)務(wù)需要,對插件進行選擇。例如對網(wǎng)絡(luò)性能要求高的應(yīng)用,可以使用帶有Multus CNI的Macvlan等方案。針對Macvlan多網(wǎng)絡(luò)平面的配置方法,我們將在第5章詳細介紹。

(4)OpenShift中DNS的實現(xiàn)

OpenShift為每個Pod分配來自Pod網(wǎng)絡(luò)的IP地址,但是這個IP地址會動態(tài)變化,無法滿足業(yè)務(wù)連續(xù)通信的需求,于是有了Service來解決這個問題,也就是我們前面提到的服務(wù)發(fā)現(xiàn)與注冊。

每個Service會有ClusterIP地址和名稱,默認情況下ClusterIP會在Service刪除重建之后變化,而Service名稱可以保持重建也不變化。這樣在服務(wù)之間通信就可以選擇使用Service名稱,那么Service名稱如何解析到IP地址呢,這就是我們本節(jié)要說明的內(nèi)——penShift內(nèi)置DNS。

OpenShift使用CoreDNS,提供OpenShift內(nèi)部的域名解析服務(wù)。我們僅對關(guān)鍵的部分進行說明,關(guān)于CoreDNS更多的信息,感興趣的讀者請自行閱讀官網(wǎng)文檔。

CoreDNS會監(jiān)聽Kubernetes API,當新創(chuàng)建一個Service時,CoreDNS中就會提供<service-name>.<project-name>.svc.cluster.local域名的解析。除了解析Service,還可以通過<service-name>.<project-name>.endpoints.cluster.local解析Endpoints。

例如,如果myproject服務(wù)中存在myapi服務(wù),則整個OpenShift集群中的所有Pod都可以解析myapi.myproject.svc.cluster.local主機名以獲取Service ClusterIP地址。除此之外,OpenShift DNS還提供以下兩種短域名:

·來自同一項目的Pod可以直接使用Service名稱作為短域名,不帶任何域后綴,如myapi。

·來自不同項目的Pod可以使用Service名稱和項目名稱作為短域名,不帶任何域后綴,如myapi.myproject。

在OpenShift中通過名為dns的Cluster Operator創(chuàng)建整個DNS堆棧,DNS Operator容器運行在項目openshift-dns-operator中,由該Operator在openshift-dns項目下創(chuàng)建出DaemonSet部署CoreDNS,也就是在每個節(jié)點會啟動一個CoreDNS容器。在Kubelet將--cluster-dns設(shè)定為CoreDNS的Service ClusterIP,這樣Pod中就可以使用CoreDNS進行域名解析。

Cluster Domain定義了集群中Pod和Service域名的基本DNS域,默認為cluster.local。CoreDNS的ClusterIP是集群Service Network網(wǎng)段中的第10個地址,默認網(wǎng)段為172.30.0.0/16,第10個地址為172.30.0.10。DNS解析流程如圖2-54所示。

圖2-54表示了OpenShift的DNS解析流程:

·宿主機上應(yīng)用的DNS解析直接通過宿主機上/etc/resolv.conf中配置的上游DNS服務(wù)器解析,也表明在宿主機上默認無法解析Kubernetes的Service域名。

·Pod中的應(yīng)用直接通過Pod中配置的DNS Server 173.30.0.10解析所有域名,該域名會將解析查詢分配到具體的CoreDNS實例中。

·在CoreDNS實例中,如果有Cache緩存,則直接返回,如果沒有Cache緩存,則判斷,若解析域名屬于cluster.local、in-addr.arpa或ip6.arpa,則通過CoreDNS的Kubernetes插件去查詢,本質(zhì)上是通過Kubernetes API查詢Etcd中保存的數(shù)據(jù)實現(xiàn)域名解析IP地址的返回,否則轉(zhuǎn)到宿主機/etc/resolv.conf中配置的上游DNS服務(wù)器。

圖2-54 OpenShift DNS解析流程

簡單而言,在OpenShift中創(chuàng)建一個應(yīng)用Pod,這個Pod中的nameserver會指向到CoreDNS的ClusterIP地址(172.30.0.10)。我們查看prometheus-k8s-0這個Pod的DNS配置,如圖2-55所示。

圖2-55 prometheus-k8s-0 Pod設(shè)置的DNS

查看宿主機的DNS配置,如圖2-56所示。

圖2-56 查看宿主機的DNS配置

OpenShift宿主機的nameserver通常是數(shù)據(jù)中心自建的內(nèi)部DNS服務(wù)器。

為了方便讀者的理解,我們舉個例子,如果我們要在Pod中nslookup baidu.com,其流程如下:

1)根據(jù)Pod DNS配置,請求被轉(zhuǎn)到對應(yīng)CoreDNS Pod,如果CoreDNS Pod中有記錄的緩存,則直接返回。

2)如果CoreDNS Pod中沒有緩存,CoreDNS查看這是外部域名,它就會轉(zhuǎn)到宿主機指向的192.168.91.8去解析,如果這個192.168.91.8地址也解析不了,那就看這個DNS是否還有上級的DNS能夠解析baidu.com。

(5)OpenShift上OVN-Kubernetes的實現(xiàn)

OVN(Open Virtual Network)是一款支持虛擬網(wǎng)絡(luò)抽象的軟件系統(tǒng)。OVN在OVS現(xiàn)有功能的基礎(chǔ)上原生支持虛擬網(wǎng)絡(luò)抽象,OVN為OVS提供了一個控制平面。OVN-Kubernetes是一個開源項目,致力于將OVN應(yīng)用到Kubernetes上。OCP 4.6正式支持OVN-Kubernetes。

OpenShift 4.6默認支持OpenShift-SDN(OVS)和OVN-Kubernetes兩種模式,兩者實現(xiàn)功能對比如表2-7所示。

表2-7 OpenShift-SDN(OVS)與OVN-Kubernetes功能對比

整體上看,OVN-Kubernetes在實現(xiàn)Overlay、service、NAT方面,其效率和性能高于OpenShift-SDN。因此對性能有一定要求的客戶,我們推薦使用OVN-Kubernetes模式。

我們可以在安裝OpenShift的時候,指定使用OVN-Kubernetes模式,也可以在安裝OpenShift后通過修改Network Operator模式實現(xiàn),建議使用第一種模式。因為Geneve tunnels模式的實現(xiàn)必須在安裝時指定。

使用OVN-Kubernetes模式,OpenShift將不再需要kube-proxy,因此也就不再需要Iptables實現(xiàn)。我們查看用OVN-Kubernetes模式在OpenShift上的組件,如圖2-57所示,查看openshift-ovn-kubernetes namespaces中的Pod。

圖2-57 查看OVN-Kubernetes的實現(xiàn)

從圖2-57我們看出,ovn-kubernetes的相關(guān)Pod分為三部分:ovnkube-master-*(運行在3個master上)、ovnkube-node-*(運行在所有OCP節(jié)點上)、ovs-node-*(運行在所有OCP節(jié)點上)。OVN-Kubernetes的組件是以daemonset方式部署的,如圖2-58所示。

圖2-58 OVN-Kubernetes的daemonset

我們對比OVN社區(qū)的架構(gòu)圖,如圖2-59所示。

圖2-59 OVN架構(gòu)圖

我們查看ovnkube-master-*pod(運行在3個master上)包含的容器,這幾個容器對應(yīng)OVN架構(gòu)圖CMS部分,如圖2-60所示。

圖2-60 ovnkube-master-*pod包含的容器

我們查看ovnkube-node-*pod(運行在所有OCP節(jié)點上)包含的容器,這幾個容器對應(yīng)OVN架構(gòu)圖中的ovn controller,如圖2-61所示。

圖2-61 ovnkube-node-*pod包含的容器

我們查看ovs-node-*pod(運行在所有OCP節(jié)點上)包含的容器,它們負責(zé)ovs的實現(xiàn),如圖2-62所示。

圖2-62 ovs-node-*pod對應(yīng)的容器

登錄ovnkube-node pod的ovn-controller容器,可以查看和OVB相關(guān)的信息,如查看OVN LBs(僅列出部分內(nèi)容):


#ovn-nbctl list load-balancer
witch e751ee40-d944-435c-a541-e4b378a404fc (ext_worker-2.weixinyucluster.bluecat.ltd)
    port etor-GR_worker-2.weixinyucluster.bluecat.ltd
        type: router
        addresses: ["52:54:17:8a:f3:00"]
        router-port: rtoe-GR_worker-2.weixinyucluster.bluecat.ltd
    port br-ex_worker-2.weixinyucluster.bluecat.ltd
        type: localnet
        addresses: ["unknown"]
switch 7acf5030-9cbe-4b52-97c9-2e9ad9231ed5 (worker-2.weixinyucluster.bluecat.ltd)
    port openshift-marketplace_certified-operators-766bcd6f65-6mjvz
        addresses: ["0a:58:0a:82:02:07 10.130.2.7"]
    port openshift-marketplace_community-operators-7c895d7b67-crzb4
        addresses: ["0a:58:0a:82:02:09 10.130.2.9"]
    port openshift-monitoring_grafana-5d7b5b575b-qwch2
        addresses: ["0a:58:0a:82:02:0c 10.130.2.12"]
    port k8s-worker-2.weixinyucluster.bluecat.ltd
        addresses: ["a6:69:cf:72:11:13 10.130.2.2"]

我們可以在安裝OpenShift時設(shè)置OVN-Kubernetes模式,或者在OpenShift安裝后修改Network operator,將其修改為OVN-Kubernetes模式,具體的方法,請參照“大魏分享”公眾號文章,如圖2-63所示。

圖2-63 設(shè)置OpenShift OVNKubernetes模式的方法

(6)OpenShift外部訪問的實現(xiàn)

如前文所述,在OpenShift網(wǎng)絡(luò)模型中,有5種方式可以實現(xiàn)集群外部訪問OpenShift中的Pod。這么多方式可以實現(xiàn)對外暴露服務(wù),那么它們之間有什么區(qū)別,適用于什么場景?下面將通過實際的示例演示分別說明。

Hostport方式

Hostport方式指的是在一個宿主機上運行的容器,為了外部能夠訪問這個容器,將容器的端口與宿主機進行端口映射,可以直接通過Docker實現(xiàn)。為了避免宿主機上的端口占用,在容器和宿主機做端口映射的時候,通常會映射一個比較大的端口號(小端口被系統(tǒng)服務(wù)占用)。如圖2-64所示。

圖2-64 Hostport方式

下面我們在宿主機上啟動一個apache的容器,將容器的端口80映射成宿主機的端口10080,如圖2-65所示。

然后,查看這個容器的網(wǎng)絡(luò)模式,如圖2-66所示。

可以看到,該容器使用的是Hostport的模式,占用宿主機的端口號是10080。我們查看容器的IP,地址為172.17.0.2,如圖2-67所示。

圖2-65 端口映射啟動apache

圖2-66 Hostport網(wǎng)絡(luò)模式

圖2-67 容器IP地址

接下來,我們驗證apache服務(wù)。首先,圖形化登錄宿主機,訪問宿主機的80端口(確保宿主機的httpd服務(wù)是停止的),無法訪問,如圖2-68所示。

圖2-68 訪問宿主機的80端口

接下來,訪問宿主機的10080端口,可以訪問容器中的apache網(wǎng)頁,如圖2-69所示。

圖2-69 訪問宿主機的10080端口

Hostport將容器與宿主機的端口進行映射。這種方案的優(yōu)勢是易操作,缺點是無法支持復(fù)雜業(yè)務(wù)場景,并且容器間的相互訪問比較困難。

接下來,我們看Nodeport的訪問方式。

Nodeport方式

NodePort是Servcie的一種類型,本質(zhì)上是通過在集群的每個節(jié)點上暴露一個端口,然后將這個端口映射到Service的端口來實現(xiàn)的。將Service IP和端口映射到OpenShift集群所有節(jié)點的節(jié)點IP和隨機分配的大端口號,默認的取值范圍是30000~32767。

為什么將Service IP和OpenShift中所有節(jié)點做映射?這是因為Service是整個集群范圍的,是跨單個節(jié)點的。

我們看一個Service的yaml配置文件。


apiVersion: v1
kind: Service
metadata:
...
spec:
  ports:
  - name: 3306-tcp
    port: 3306
    protocol: TCP
    targetPort: 3306
    nodePort: 30306
selector:
  app: mysqldb
  deploymentconfig: mysqldb
  sessionAffinity: None
type: NodePort

這個配置的含義是采用Nodeport的方式,將mysql server的IP和節(jié)點IP做映射,Serivce的源端口是3306,映射到節(jié)點的端口是30306。

這樣配置完畢以后,外部要訪問Pod,訪問的是nodeip:30306。然后訪問請求通過iptables的NAT將nodeip和端口轉(zhuǎn)化為Service ip和3306端口,最終請求通過Service負載均衡到Pod,如圖2-70所示。

圖2-70 Nodeport訪問示意圖

Nodeport方式與Hostport方式最重要的一個區(qū)別是Hostport是針對一個單宿主機的一個容器,而Nodeport是針對Kubernetes集群而言的。

Nodeport方式的缺點很明顯,宿主機端口浪費和安全隱患,并且數(shù)據(jù)轉(zhuǎn)發(fā)次數(shù)較多。

Hostnetwork方式

Hostnetwork是Pod運行的一種模式,在Hostnetwork方式下,Pod的IP和端口會直接綁定到宿主機的IP和端口。應(yīng)用訪問的時候,訪問宿主機的IP和端口號后,這個請求直接轉(zhuǎn)到Pod和相同的端口(不經(jīng)過iptables和Service負載)。也就是說,這種情況下,Pod的IP就是宿主機的IP,Pod暴露哪個端口,宿主機就對外暴露哪個端口。

例如,在數(shù)據(jù)中心的OpenShift中,Router就是以Hostnetwork模式運行(在公有云環(huán)境中Router是通過LoadBalancer類型Service對外暴露的)。如圖2-71中的worker-0.weixinyucluster.bluecat.ltd是Worker節(jié)點,IP是192.168.91.20,這個節(jié)點上運行了router-default-f698c8675-pkvgt。

圖2-71 Router在Worker節(jié)點上的運行

Router Pod的IP也是192.168.91.20,如圖2-72所示。

圖2-72 Router Pod信息

我們查看Router Pod暴露的端口有三個:80、443、1936,如圖2-73所示。

圖2-73 router暴露端口

Pod中ports定義的端口和Node監(jiān)聽的端口也是一致的,如圖2-74所示。

圖2-74 端口定義

Hostnetwork方式相比于Nodeport方式,其優(yōu)勢在于可以直接使用宿主機網(wǎng)絡(luò),轉(zhuǎn)發(fā)路徑短,性能好,缺點是占用節(jié)點的實際端口,無法在用一個節(jié)點同時運行相同端口的兩個Pod。

LoadBalancer方式

LoadBalancer方式也是Service的一種類型,用于和云平臺負載均衡器結(jié)合。當使用LoadBalancer類型Service暴露服務(wù)時,實際上是通過向底層云平臺申請創(chuàng)建一個負載均衡器來向外暴露服務(wù)。目前LoadBalancer Service可以支持大部分的云平臺,比如國外的AWS、GCE、DigitalOcean,國內(nèi)的阿里云、私有云OpenStack等,因為這種模式深度結(jié)合了云平臺負載均衡器,所以只能在一些云平臺上使用。當然,一些軟/硬件負載均衡器(如MetalLB)也可以為OpenShift提供LoadBalancer Service的IP地址。

Ingress/Router方式

Ingress是一種負載的實現(xiàn)方式,如常用的Nginx、HAproxy等開源的反向代理負載均衡器實現(xiàn)對外暴露服務(wù)。本質(zhì)上Ingress就是用于配置域名轉(zhuǎn)發(fā),并實時監(jiān)控應(yīng)用Pod的變化,動態(tài)地更新負載均衡的配置文件。Ingress包含兩大組件Ingress Controller和Ingress。Ingress是一種資源對象,聲明域名和Service對應(yīng)的問題;Ingress Controller是負載均衡器,加載Ingress動態(tài)生成負載均衡配置,如圖2-75所示。

圖2-75 Ingress負載邏輯圖

在OpenShift中通過Router實現(xiàn)Ingress的功能,提供集群外訪問,那么Router的本質(zhì)是什么?

OpenShift默認的Router本質(zhì)上是一個以Hostnetwork方式運行在節(jié)點上的容器化HAproxy,可提供HTTP、HTTPS、WebSockets、TLS with SNI協(xié)議的訪問。Router相當于Ingress Controller,Route相當于Ingress對象。OpenShift使用社區(qū)提供的HAproxy Ingress Controller,通過Ingress Operator實現(xiàn)部署。在OpenShift中可以同時使用Router或Ingress對象對外暴露服務(wù)。Router的轉(zhuǎn)發(fā)邏輯如圖2-76所示。

圖2-76 Router轉(zhuǎn)發(fā)邏輯

可以看到在圖2-76中有兩個服務(wù),分別為app1和app2,通過Route對象分別暴露域名為app1.example.com和app2.cloud.com,這樣在Router中就會加載這兩個應(yīng)用的負載規(guī)則。當訪問app1.example.com時會將請求直接轉(zhuǎn)發(fā)到app1所對應(yīng)的Pod IP上,而不經(jīng)過Service負載。

值得說明的是,Router提供集群外部的訪問,暴露的域名是用于外部訪問的,需要外部DNS解析,與前面介紹的OpenShift內(nèi)部DNS沒有關(guān)系。

客戶端要訪問某一個應(yīng)用,例如在瀏覽器中輸入http://cakephp-ex-test.apps.example.com,首先外部DNS將這個域名解析成Router所在OpesnShift節(jié)點的IP,假設(shè)為192.168.137.102。然后,請求到達Router后會根據(jù)配置文件中該域名所對應(yīng)的后端Pod以及負載均衡策略進行請求分發(fā)。如圖2-77所示。

可以看到圖2-77中的規(guī)則就是HAproxy的配置文件,負載均衡使用最少連接,該服務(wù)有三個后端Pod,將請求直接負載到三個Pod IP上。

圖2-77 Router中的配置

由于Router使用Hostnetwork運行,因此每個節(jié)點只能運行一個Pod實例。在實際使用中通常需要使用多個OpenShift節(jié)點運行多個Router,然后再使用集群外部的負載均衡將請求負載到多個Router上。

外部訪問方式的使用建議

通過前面介紹,相信讀者已經(jīng)了解了每種方式的實現(xiàn)機制和使用方法。選擇哪種方式實現(xiàn)對外訪問,可以參考以下原則:

·對于HTTP、HTTPS類的七層應(yīng)用,往往通過Router暴露FQDN的方式訪問。

·對于非HTTP、HTTPS類的四層應(yīng)用(如mysql),存在兩種情況:

 ·單個節(jié)點運行一個副本:如果應(yīng)用無須在一個節(jié)點運行多個Pod實例,優(yōu)先使用Hostnetwork方式。

 ·單個節(jié)點運行多個副本:如果應(yīng)用需要在一個節(jié)點運行多個Pod實例,則使用Nodeport方式。

理論上,Hostnetwork方式轉(zhuǎn)發(fā)路徑短,性能比Nodeport方式好。

(7)OpenShift四層Ingress的實現(xiàn)

上文我們提到,OpenShift的Ingress請求通過容器化的HAproxy實現(xiàn)。HAproxy是一個性能非常好的軟負載,穩(wěn)定性強。OpenShift最初設(shè)計是OpenShift中運行的前端的應(yīng)用才需要對外暴露。前端對后端應(yīng)用的訪問,如果在同一個OpenShift集群中,則通過Servcie實現(xiàn)訪問;如果在集群外部(如虛擬化環(huán)境),則通過NAT方式實現(xiàn)外部訪問。因此理論上OpenShift上應(yīng)用入口請求絕大多數(shù)是七層的。

但隨著OpenShift承載的應(yīng)用類型越來越多,會有這樣的需求:OpenShift上部署了mysql,需要給另一個OpenShift集群中的Web應(yīng)用提供服務(wù),這就需要四層Ingress。

關(guān)于OpenShift實現(xiàn)四層Ingress的方式,我們可以參照表2-8。

表2-8 OpenShift Ingress四層的實現(xiàn)

幾種實現(xiàn)方式的具體配置步驟,請參照“大魏分享”公眾號文章,鏈接如圖2-78二維碼所示。

圖2-78 OpenShift Ingress四層的具體實現(xiàn)步驟

總結(jié)起來:

1)OpenShift上,如果Ingress大多數(shù)是七層請求,采用默認的Router方式即可。

2)OpenShift上,如果有少量的四層Ingress需求,采用默認的HAproxy+Nodeport就可以,使用時注意把端口號設(shè)置在Nodeport允許的范圍內(nèi)。此外,為了規(guī)避OpenShift Node出現(xiàn)故障造成Nodeport不能訪問的情況,建議使用硬負載或軟負載為OpenShift Node配置VIP,這樣客戶端直接訪問VIP:Port即可。這種方式是紅帽官方推薦的四層Ingress實現(xiàn)方法。

3)OpenShift上的HAproxy默認支持七層,如果要支持四層,需要定制模板支持四層訪問。

4)通過OpenShift上的Nginx Ingress Operator(Loadbalancer模式)可以實現(xiàn)四層Ingress,但前端需要能夠提供Loadbalancer Service IP的硬件負載均衡器。此外,這種方式實現(xiàn)四層配置,需要通過全局的(nginx-ingress命名空間)Configmap實現(xiàn),還需要手工寫要暴露的應(yīng)用的Service全名,這有一定工作量。

5)如果OpenShift部署在裸機上,又不想引入類似F5的硬件負載均衡器,那么使用MetalLB為Nginx Ingress controller提供IP。中小規(guī)模使用Layer 2,規(guī)模大了則需要打開BGP以保證性能。這種方式性價比較高,適合在開發(fā)測試環(huán)境使用。但MetalLB這個開源項目目前沒有廠商提供企業(yè)級技術(shù)支持。

(8)OpenShift的網(wǎng)絡(luò)規(guī)劃

經(jīng)過前面對OpenShift網(wǎng)絡(luò)的介紹,我們已經(jīng)清楚地知道各部分網(wǎng)絡(luò)如何實現(xiàn)以及有哪些方式。接下來就需要對集群的網(wǎng)絡(luò)進行規(guī)劃,網(wǎng)絡(luò)的規(guī)劃需要在部署OpenShift之前完成,主要是因為某些網(wǎng)絡(luò)插件或參數(shù)在安裝之后無法修改。網(wǎng)絡(luò)規(guī)劃主要有以下兩部分內(nèi)容:

·網(wǎng)絡(luò)插件選型。

·網(wǎng)絡(luò)地址段規(guī)劃。

網(wǎng)絡(luò)插件選型

網(wǎng)絡(luò)插件選型主要指對實現(xiàn)Pod網(wǎng)絡(luò)的插件進行選型,也就是選擇合適的CNI網(wǎng)絡(luò)插件。雖然目前默認的OpenShift SDN已經(jīng)可以滿足基本的網(wǎng)絡(luò)需求,也是我們優(yōu)先推薦的網(wǎng)絡(luò)實現(xiàn)模式,但是OpenShift SDN仍無法實現(xiàn)有些特殊的需求,比如性能上的考慮、外部直接訪問Pod IP等,幸運的是,CNI的出現(xiàn)使得各個插件都遵循統(tǒng)一的規(guī)范實現(xiàn),這樣就可以使用受支持的CNI插件替換默認的OpenShift SDN。

在前面的介紹中就可以看到目前有很多CNI插件,在技術(shù)實現(xiàn)以及功能上千差萬別,我們該如何選擇合適的插件呢?通常可以參考以下指標進行衡量:

·網(wǎng)絡(luò)性能:考慮不同網(wǎng)絡(luò)插件的帶寬、延遲等網(wǎng)絡(luò)指標。粗略估計的話,可以通過調(diào)研網(wǎng)絡(luò)插件的技術(shù)實現(xiàn),從理論上對不同插件網(wǎng)絡(luò)性能進行排序;如果需要精確的評估性能,最好進行專門的對比測試。

·多租戶隔離:是否需要支持多租戶隔離將決定選取的網(wǎng)絡(luò)插件。

·直接訪問Pod:是否需要從集群外部直接訪問Pod IP地址。

·網(wǎng)絡(luò)插件成熟性:網(wǎng)絡(luò)插件的成熟性直接決定使用過程中是否會出現(xiàn)重大問題。

·網(wǎng)絡(luò)插件可維護性:網(wǎng)絡(luò)插件在使用過程中是否易于運維,出現(xiàn)問題是否容易排查。

·平臺支持性:是否受OpenShift官方支持,雖然理論上兼容所有的CNI插件,但不受支持的插件在安裝和使用時可能會出現(xiàn)問題。

讀者結(jié)合企業(yè)的具體需求并參考上面列出的這些衡量指標,就基本可以完成網(wǎng)絡(luò)插件的選型。

網(wǎng)絡(luò)地址段規(guī)劃

網(wǎng)絡(luò)地址段規(guī)劃是指針對OpenShift相關(guān)的網(wǎng)絡(luò)地址進行規(guī)劃,OpenShift涉及的網(wǎng)絡(luò)地址主要有三類:Pod IP地址、Service ClusterIP地址以及集群節(jié)點IP地址,這都在我們的規(guī)范范圍內(nèi)。

另外,在計算資源容量規(guī)劃中我們提到網(wǎng)絡(luò)規(guī)劃會影響集群最大節(jié)點數(shù)和單節(jié)點最大Pod數(shù),這主要是子網(wǎng)劃分導(dǎo)致的,所以有效的規(guī)劃網(wǎng)絡(luò)至關(guān)重要。

為了更好地理解網(wǎng)絡(luò)規(guī)劃,這里先解釋一下OpenShift SDN的子網(wǎng)劃分策略。

·OpenShift SDN的子網(wǎng)劃分

子網(wǎng)劃分是通過借用IP地址的若干主機位來充當子網(wǎng)地址,從而將原來的網(wǎng)絡(luò)分為若干個彼此隔離的子網(wǎng)。由子網(wǎng)劃分的概念知道,只有在CNI是基于二層實現(xiàn)的時候才需要子網(wǎng)劃分,如OpenShift SDN或Flannel,像Calico這樣基于三層路由實現(xiàn)不存在子網(wǎng)劃分問題。默認集群在安裝時需要配置一個統(tǒng)一的網(wǎng)段(Cluster Network),每個計算節(jié)點在加入集群后會分配一個子網(wǎng)(hostsubnet)為運行在節(jié)點的容器使用。Cluster Network默認定義為10.128.0.0/14,分配hostsubnet子網(wǎng)的掩碼長度為9,那么允許分配的最大子網(wǎng)為29=512個,也就是說,默認情況下集群最多允許有512個節(jié)點。這樣分配到每個節(jié)點的子網(wǎng)掩碼為/23,如10.128.2.0/23,每個子網(wǎng)中可容納的Pod個數(shù)為29–2=510個。

可以看到集群默認安裝集群節(jié)點最大只能到512個節(jié)點,如果集群要支持最大集群規(guī)模2000個節(jié)點,需要將Cluster Network擴展為10.128.0.0/13,分配hostsubnet子網(wǎng)的掩碼長度為11,這樣允許分配的最大子網(wǎng)為211=2048個,每個節(jié)點上可運行的Pod總數(shù)為28–2=254個。

·網(wǎng)絡(luò)地址段規(guī)劃

了解了子網(wǎng)劃分之后,對需要的三個網(wǎng)絡(luò)地址進行規(guī)劃。

集群節(jié)點IP地址:在OpenShift中集群外部訪問和Pod跨節(jié)點通信都需要經(jīng)過節(jié)點IP訪問,這個地址段是一個真實能在集群外部訪問的地址段,不能與任何現(xiàn)有地址沖突。OpenShift集群運行僅需要一塊網(wǎng)卡,管理流量和業(yè)務(wù)流量都在一張網(wǎng)卡上,目前版本暫時無法實現(xiàn)拆分,但是用戶可以添加存儲網(wǎng)絡(luò),專門用于讀寫后端的存儲設(shè)備。另外,如果通過軟負載均衡實現(xiàn)某些組件的高可用,還需要額外多申請幾個與節(jié)點同網(wǎng)段的IP地址,用作負載均衡的VIP。

Service ClusterIP地址:該地址段僅在集群內(nèi)部可訪問,不需要分配真實的外部可訪問的網(wǎng)段,默認地址段為172.30.0.0/16。但需要保證與OpenShift中應(yīng)用交互的系統(tǒng)與該地址段不沖突,假設(shè)存在OpenShift集群內(nèi)的應(yīng)用需要與OpenShift集群外部業(yè)務(wù)系統(tǒng)通信,這時候如果外部應(yīng)用也是172.30.0.0/16網(wǎng)段,那么OpenShift內(nèi)應(yīng)用的流量就會被攔截在集群內(nèi)部。針對不同的集群,該地址段可以使用相同的地址段。

Pod IP地址:該地址段是否可以對外訪問取決于CNI插件的類型。如果選擇基于二層路由覆蓋網(wǎng)絡(luò)實現(xiàn)的CNI,那么該地址段僅在集群內(nèi)可訪問;如果選擇基于三層路由實現(xiàn)的CNI,那么該地址段在集群外也可訪問。OpenShift SDN的該地址是一個內(nèi)部可訪問的地址段,默認設(shè)置為10.128.0.0/14,我們需要根據(jù)對集群規(guī)模的需求來規(guī)劃這個網(wǎng)段,針對不同的集群,該地址段也可以使用相同的地址段。

網(wǎng)段規(guī)劃范例

客戶使用10臺物理服務(wù)器構(gòu)建OpenShift集群(SDN使用默認的OVS):3臺作為Master,4臺作為Node、3臺作為Infra Node,存儲使用NAS。

針對這套環(huán)境,一共需要配置三個網(wǎng)絡(luò)。

網(wǎng)絡(luò)1:OpenShift集群內(nèi)部使用的網(wǎng)絡(luò)(不與數(shù)據(jù)中心網(wǎng)絡(luò)沖突)。

有兩個網(wǎng)段:Service IP網(wǎng)段和Pod IP網(wǎng)段(在OpenShift安裝時設(shè)置,安裝以后不能進行修改)

·Service IP默認網(wǎng)段是172.30.0.0/16。

·Pod IP默認網(wǎng)段是10.128.0.0/14。

Pod IP和Service IP這兩個網(wǎng)段都不需要分配數(shù)據(jù)中心IP。如果OpenShift內(nèi)的應(yīng)用只和同一個OpenShift集群的應(yīng)用通信,那么將使用Service IP,沒有發(fā)生IP沖突的問題。但如果存在OpenShift集群內(nèi)的應(yīng)用與OpenShift集群外部通信(需要在OpenShift中為外部應(yīng)用配置Service Endpoint),這時候如果外部應(yīng)用也是172.30.0.0/16網(wǎng)段,那么就會出現(xiàn)IP沖突。根據(jù)我們的項目經(jīng)驗,一定要規(guī)劃好網(wǎng)段,OpenShift的網(wǎng)段不要與數(shù)據(jù)中心現(xiàn)在和未來可能使用的網(wǎng)段沖突。

網(wǎng)絡(luò)2:生產(chǎn)環(huán)境業(yè)務(wù)網(wǎng)絡(luò),共需要13個IP。

其中,10臺物理服務(wù)器,每個都需要1個IP。此外,OpenShift安裝還需要一臺Bootstrap主機,該主機在OpenShift安裝成功后可以關(guān)閉,因此在部署過程中需要多一個IP地址。由于有3個Master節(jié)點,使用軟負載實現(xiàn)高可用,因此需要一個VIP。此外,為了保證Router的高可用,在3個Infra節(jié)點上分別部署Router,然后使用軟負載實現(xiàn)高可用,因此還需要一個VIP。

網(wǎng)絡(luò)3:NAS網(wǎng)絡(luò)。

需要保證10臺物理服務(wù)器都可以與NAS網(wǎng)絡(luò)正常通信,因此需要配置與NAS網(wǎng)絡(luò)可通信的IP地址,每個服務(wù)器需要一個IP地址。

因此,使用物理服務(wù)器部署,建議每個服務(wù)器至少配置兩個雙口網(wǎng)卡。不同網(wǎng)卡的兩個網(wǎng)口綁定,配置網(wǎng)絡(luò)2,負責(zé)OpenShift節(jié)點IP。另外的兩個網(wǎng)口綁定后,配置網(wǎng)絡(luò)3,負責(zé)與NAS通信。

3.OpenShift的存儲介紹與規(guī)劃

(1)OpenShift的存儲介紹

在OpenShift中Pod會被經(jīng)常性地創(chuàng)建和銷毀,也會在不同的主機之間快速遷移。為了保證容器在重啟或者遷移以后能夠使用原來的數(shù)據(jù),就必須使用持久化存儲。所以,持久化存儲的管理對于PaaS平臺來說就顯得非常重要。

OpenShift存儲PV和PVC

OpenShift利用Kubernetes Persistent Volume(持久卷,簡稱PV)概念來管理存儲。管理員可以快速劃分卷提供給容器使用。開發(fā)人員通過命令行和界面申請使用存儲,而不必關(guān)心后端存儲的具體類型和工作機制。

PV是一個開放的存儲管理框架,提供對各種不同類型存儲的支持。OpenShift默認支持NFS、GlusterFS、Cinder、Ceph、EBS、iSCSI和Fibre Channel等存儲,用戶還可以根據(jù)需求對PV框架進行擴展,從而使其支持更多類型的存儲。

Persistent Volume Claim(持久卷聲明,簡稱PVC)是用戶的一個Volume請求。用戶通過創(chuàng)建PVC消費PV的資源。

PV只有被PVC綁定后才能被Pod掛載使用,PV和PVC的生命周期如圖2-79所示。

圖2-79 PV和PVC的生命周期

從圖2-79中可以看到,生命周期包含5個階段:

·Avaliable:這個階段表示PV創(chuàng)建完成,處于可用狀態(tài)。創(chuàng)建PV可以通過手動創(chuàng)建或動態(tài)創(chuàng)建。

·Pending:這個階段表示PV和PVC處于匹配狀態(tài),匹配的策略有訪問模式和卷大小以及支持通過label匹配。如果無法匹配,則PVC會一直處于Pending狀態(tài),如果可以匹配,但是后端存儲置備卷失敗,則會轉(zhuǎn)為Failure狀態(tài)。

·Bound:這個階段表示PV和PVC已經(jīng)處于綁定狀態(tài),這個狀態(tài)的PVC才能被Pod掛載使用。

·Released:這個階段表示掛載PVC的Pod被刪除,PVC處于釋放狀態(tài),也就是未被任何Pod掛載,但這個狀態(tài)的PV無法被PVC再次綁定。

·Failure:這個階段表示刪除PVC,PV轉(zhuǎn)變?yōu)榛厥諣顟B(tài),該狀態(tài)下的PV無法直接被新的PVC綁定。回收狀態(tài)下PV是否保留數(shù)據(jù)取決于PV的回收策略定義,默認會保留。如果想要將該狀態(tài)的PV轉(zhuǎn)變?yōu)锳vailable,必須刪除PV然后重新創(chuàng)建。

在PV和PVC的生命周明中,最關(guān)鍵的兩個階段是Available和Bound。PV按創(chuàng)建方式的不同可分為動態(tài)PV和靜態(tài)PV。靜態(tài)PV是指通過手動創(chuàng)建PV,而動態(tài)PV是指由StorageClass(簡稱SC)動態(tài)創(chuàng)建PV。

靜態(tài)PV需要手動編輯Yaml文件并應(yīng)用到集群中,不同的存儲后端,PV的配置參數(shù)也不同,如NFS后端的PV示例內(nèi)容如下。


apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv0001
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  nfs:
    path: /data/mydb
    server: xxx.xxx.xxx.xxx
  persistentVolumeReclaimPolicy: Retain

其中訪問模式和PV容量對能否和PVC綁定至關(guān)重要。PV支持的訪問模式共有三種,如表2-9所示。

表2-9 PV訪問模式

不同后端存儲對訪問模式的支持是不同的。接下來介紹常見后端存儲支持的PV訪問模式,如表2-10所示。

表2-10 不同后端存儲支持的PV訪問模式

從表2-10中可以看到,Azure File和NFS支持的讀寫類型是最全的。我們可以使用NAS或者配置NFS Server。當然,企業(yè)級NAS的性能要比NFS Server好得多。在OpenShift中,除了表2-10中列出的常見存儲類型之外,還可以選擇軟件定義存儲(如Ceph),Ceph可以同時提供塊存儲RBD、對象存儲RADOSGW、文件系統(tǒng)存儲CephFS。

除了靜態(tài)PV之外,OpenShift還可以使用StorageClass來管理動態(tài)PV。每個StorageClass都定義一個Provisioner屬性,也就是后端存儲類型。OpenShift安裝后會內(nèi)嵌一些Provisioner,它們的StorageClass會被自動創(chuàng)建,如表2-11所示。

表2-11 不同后端存儲的Provisioner屬性

如果要創(chuàng)建一個沒有對應(yīng)Provisioner的StorageClass,也稱為靜態(tài)StorageClass,可以使用kubernetes.io/no-provisioner,示例如下。


apiVersion: storage.k8s.io/v1 
kind: StorageClass 
metadata:
   name: static-provisioner
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

創(chuàng)建StorageClass之后就可以通過創(chuàng)建PVC觸發(fā)StorageClass完成PV的創(chuàng)建,但是靜態(tài)StorageClass除外,因為靜態(tài)StorageClass沒有真實的后端存儲,依然需要手動創(chuàng)建PV并明確指定storageClassName為靜態(tài)StorageClass的名稱,詳細的使用案例參見第3章3.2.4節(jié)的第2小節(jié)。

無論是通過靜態(tài)還是動態(tài)創(chuàng)建PV,只有PVC和PV綁定之后才能被Pod使用。尤其在集群中有多個不同后端的PV時,PVC如何能綁定到滿足預(yù)期的PV將成為關(guān)鍵,下面我們就進行詳細說明。

PV和PVC綁定邏輯

在上一小節(jié)中,我們介紹了PV的創(chuàng)建方式和支持的類型,那么如果一個集群中既有多種類型的StorageClass,又有多種不同后端的靜態(tài)PV,PVC與PV的匹配需要遵循一定的邏輯,如圖2-80所示。

圖2-80 PV和PVC匹配邏輯

從圖2-80中可以看出動態(tài)PV優(yōu)先,如果動態(tài)PV無法滿足PVC需求,才會匹配靜態(tài)PV。而且能否匹配成功是根據(jù)PV、PVC、集群中StorageClass的配置等多方面決定的,匹配大致邏輯如下:

1)創(chuàng)建PVC后,首先會判定PVC中是否指定了storageClassName字段,例如下面PVC定義會觸發(fā)StorageClass gp2創(chuàng)建的PV并綁定(靜態(tài)StorageClass需要手動創(chuàng)建PV,后文不再重復(fù)強調(diào)),如果無法找到指定的StorageClass,則PVC處于Pending狀態(tài)。


kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-claim
spec:
  storageClassName: gp2
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

2)如果PVC中沒有指定storageClassName參數(shù),則會判定集群中是否有默認Storage-Class,如果存在,則會直接使用默認StorageClass創(chuàng)建PV。一個集群最多只能有一個默認StorageClass,表示如果PVC中未指定明確的storageClassName,則使用默認StorageClass創(chuàng)建PV。使用如下命令將集群中一個SC設(shè)置為默認StorageClass。


# oc annotate storageclass <SC_NAME>
"storageclass.kubernetes.io/is-default-class=true"

建議不要設(shè)置靜態(tài)StorageClass為默認StorageClass,因為靜態(tài)StorageClass不會自動創(chuàng)建PV,即使設(shè)定為默認StorageClass,還是要手動創(chuàng)建設(shè)定storageClassName的PV,導(dǎo)致之前設(shè)定為默認StorageClass沒有價值。

3)如果集群未定義默認StorageClass,則會進入靜態(tài)PV匹配。首先會判定在PVC是否定義了selector用于匹配特定標簽的PV。通常在PV上設(shè)定標簽主要用于對PV分級,比如根據(jù)存儲性能、存儲地理位置等。例如,下面的PVC就只能匹配包含storage-tier=gold且volume-type=ssd的PV,如果無法找到符合標簽的PV,則PVC處于Pending狀態(tài)。


apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: high-performance-volume
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
  selector:
    matchLabels:
      storage-tier: gold
      volume-type: ssd

4)如果PVC中未定義selector,或者有滿足selector的PV,則根據(jù)PVC和PV兩者中定義的訪問模式和容量大小匹配。其中訪問模式必須完全相同,而容量大小是只要PV定義的容量大小大于等于PVC定義的容量大小就可以匹配成功。如果訪問模式或者容量大小無法滿足需要,則PVC處于Pending狀態(tài)。

可以發(fā)現(xiàn),在動態(tài)PV綁定時只判斷storageClassName,而在靜態(tài)PV綁定時才會判斷selector、訪問模式、容量大小。

另外,需要注意的是,訪問模式和容量大小的匹配只是邏輯上的,并不會校驗后端存儲是否支持這種訪問模式或后端存儲的真實空間大小。例如我們完全可以通過多讀寫訪問模式掛載iSCSI卷,只不過由于鎖機制,無法同時啟動多個實例。

容器云原生存儲

OpenShift目前主推OpenShift Container Storage(簡稱OCS)實現(xiàn)存儲層。OCS主要是通過Rook+Ceph實現(xiàn)的。

Rook(https://rook.io/)使Ceph部署、引導(dǎo)、配置、供應(yīng)、擴展、升級、遷移、災(zāi)難恢復(fù)、監(jiān)視和資源管理自動化。Operator將啟動和監(jiān)視Ceph Monitor容器,提供RADOS存儲的Ceph OSD守護程序,以及啟動和管理其他Ceph守護程序。通過初始化Pod和運行服務(wù)所需的其他工件來管理存儲池、對象存儲(S3/Swift)和文件系統(tǒng)的CRD。

Rook的功能如下:

·高可用性和彈性:Ceph沒有單點故障(SPOF),并且其所有組件都以高可用性的方式本地工作。

·數(shù)據(jù)保護:Ceph會定期清理不一致的對象,并在必要時進行修復(fù),以確保副本始終保持一致。

·跨混合云的一致存儲平臺:Ceph可以部署在任何位置(內(nèi)部部署或裸機),因此無論用戶身在何處,都能提供類似的體驗。

·塊、文件和對象存儲服務(wù):Ceph可以通過多個存儲接口公開你的數(shù)據(jù),從而解決所有應(yīng)用程序用例。

·放大/縮小:Operator完全負責(zé)添加和刪除存儲。

·儀表板:Operator部署了一個儀表板,用于監(jiān)視和自檢集群。

OCS存儲架構(gòu)如圖2-81所示。

圖2-81 OCS存儲架構(gòu)

OCS通過Operator方式進行安裝。目前支持在OpenShift物理節(jié)點上離線安裝。

OCS的安裝很簡單,大致步驟如圖2-82所示,安裝OCS的Operator。

接下來,利用OCS Operator部署的API創(chuàng)建Ceph集群,選擇加入OCS的節(jié)點。此處我們選擇新添加三個節(jié)點,如圖2-83所示。

圖2-82 安裝OCS的Operator

圖2-83 選擇OCS節(jié)點

當OCS相關(guān)所有Pod都創(chuàng)建成功并處于Running狀態(tài),代表OCS部署成功。OCS部署成功后,我們查看OpenShift中的StorageClass,增加了Ceph相關(guān)的內(nèi)容。


# oc get sc
NAME                          PROVISIONER                             AGE
localblock                    kubernetes.io/no-provisioner            51m
ocs-storagecluster-ceph-rbd   openshift-storage.rbd.csi.ceph.com      51m
ocs-storagecluster-cephfs     openshift-storage.cephfs.csi.ceph.com   51m
openshift-storage.noobaa.io   openshift-storage.noobaa.io/obc         45m

部署成功后,就可以在OpenShift中通過CSI的方式調(diào)用OCS。

我們使用配置文件創(chuàng)建一個PVC(調(diào)用storageClassName:ocs-storagecluster-ceph-rbd)。


# cat create_ns_ocs_pvc.yaml
---
kind: Namespace
apiVersion: v1
metadata:
  name: "e-library"
  labels:
    name: "e-library"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ocs-pv-claim
  labels:
    name: "e-library"
  namespace: "e-library"
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: ocs-storagecluster-ceph-rbd

查看PVC創(chuàng)建成功,并且OCS自動創(chuàng)建PV與之綁定。


# oc get pvc
NAME           STATUS   VOLUME        CAPACITY   ACCESS MODES   STORAGECLASS  AGE
ocs-pv-claim   Bound    pvc-f06484c8-abd7-11ea-b311-0242ac110022   10Gi  RWO            
    ocs-storagecluster-ceph-rbd  3m52s

接下來,我們就可以創(chuàng)建Pod來消費這個PVC了。

OCS早期版本只支持內(nèi)置模式,也就是說,必須把OCS裝在OpenShift上,利用OpenShift的Worker節(jié)點的本地存儲空間作為存儲空間。這種模式部署和使用都很方便。唯一的問題是存儲服務(wù)器無法與OpenShift集群解耦。

OCS從4.5版本開始支持外部的存儲模式。也就是說,通過OpenShift上安裝的OCS Operator,可以對接在外部物理機上安裝的Ceph。然后以O(shè)penShift中Rook的方式管理外部物理機上的Ceph,實現(xiàn)存儲服務(wù)器與OpenShift集群解耦。

我們在OpenShift上部署OCS Operator后,可以選擇連接外部的集群,然后提示下載Python腳本。將這個腳本在外置的Ceph集群的任意一個Monitor節(jié)點上執(zhí)行,獲取Ceph集群信息,輸入到OCS對接外置存儲的位置,如圖2-84所示。

圖2-84 OCS對接外置存儲

我們將這個腳本在外置的Ceph集群的Monitor節(jié)點上執(zhí)行。

首先查看腳本的使用幫助。


#python ceph-external-cluster-details-exporter.py --help

在下面的命令中,rbd-data-pool-name指定要創(chuàng)建的pool的名稱,rgw-endpoint指定Ceph集群對象網(wǎng)關(guān)地址。


#python ceph-external-cluster-details-exporter.py --rbd-data-pool-name abc --rgw-
    endpoint 192.168.18.203:8080 

命令執(zhí)行后,會以json的方式返回一大串輸出結(jié)果,將結(jié)果粘貼到如圖2-84所示的空白處,即可完成添加。由于后續(xù)的操作步驟與內(nèi)置模式類似,因此不再展開說明。

OCS對接外置Ceph存儲的后續(xù)步驟,請參考Repo中“ocs外置存儲方式”。

OpenShift/Kubernetes存儲趨勢

在OpenShift的網(wǎng)絡(luò)部分,我們提到了一個開源項目CNI,它定義網(wǎng)絡(luò)插件和容器之間的通用接口,實現(xiàn)容器運行時與SDN的松耦合。那么,在容器存儲方面,有沒有類似的開源項目呢?

開源項目Container Storage Interface(CSI)正是為了實現(xiàn)這個目的而創(chuàng)建的。CSI旨在提供一種標準,使任意塊存儲和文件存儲在符合這種標準的情況下為Kubernetes上的容器化提供持久存儲。隨著CSI的采用,Kubernetes存儲層變得真正可擴展。使用CSI,第三方存儲提供商可以編寫和部署插件,在Kubernetes中公開新的存儲系統(tǒng),而無須觸及核心Kubernetes代碼。CSI為Kubernetes用戶提供了更多存儲選項,使系統(tǒng)更加安全可靠。目前在OpenShift中的CSI正式GA。

CSI是通過External CSI Controllers實現(xiàn)的,它是一個運行在Infra節(jié)點包含三個容器的Pod(如圖2-85所示)。

·External CSI Attacher Container:它將從OpenShift發(fā)過來的attach和detach調(diào)用轉(zhuǎn)換為對CSI Driver的ControllerPublish和ControllerUnpublish調(diào)用。

·External CSI Provisioner Container:它將從OpenShift發(fā)過來的provision和delete的調(diào)用轉(zhuǎn)化為對CSI Driver的CreateVolume和DeleteVolume的調(diào)用。

·CSI Driver Container。

圖2-85 OpenShift CSI邏輯圖

通過一個CSI Driver DaemonSet,在每個OpenShift節(jié)點上啟動一個Driver Container。它允許OpenShift將CSI driver提供的存儲掛載到OpenShift節(jié)點,并將其映射掛載到Pod中。

需要指出的是,從Ceph社區(qū)版本v14開始,OpenShift訪問Ceph必須要有CSI Driver,無法繞開CSI直接訪問Ceph存儲。

(2)OpenShift存儲規(guī)劃

OpenShift使用存儲類型選擇

選擇合適的存儲有助于最大限度地減少所有資源的存儲使用。通過優(yōu)化存儲,管理員可以確保現(xiàn)有存儲資源以高效的方式工作。在OpenShift上可用的存儲類型如表2-12所示。

表2-12 OpenShift上可用的存儲類型

表2-12按目前的三種存儲類型整理了OpenShift支持的存儲,主要是幫助讀者厘清三種存儲的區(qū)別和分類,我們可以根據(jù)不同的需求選擇合適類型的存儲。除了公有云存儲外,OpenShift在私有云上可以使用的主流存儲包括NAS、Ceph以及基于Linux實現(xiàn)的NFS。表2-13展示了基于不同維度對這幾類存儲進行的對比。

表2-13 OpenShift常用后端存儲對比

如表2-13所示,基于Linux的NFS方案生產(chǎn)不推薦,因為數(shù)據(jù)高、可用性難保證,且有性能瓶頸;企業(yè)NAS看似是最好的選擇,但是也存在成本較高、擴展難等問題;而OCS由于與OpenShift完美集成,并且支持外置Ceph的模式,因此會越來越成為OpenShift持久化存儲的理想選擇。

OpenShift存儲容量規(guī)劃

OpenShift存儲容量規(guī)劃包括OpenShift節(jié)點、OpenShift附加組件、OpenShift上運行的應(yīng)用。由于OpenShift上運行的應(yīng)用沒有通用的存儲容量規(guī)劃方法,需要根據(jù)具體的業(yè)務(wù)需求規(guī)劃,在這里我們就不討論。下面我們將分別說明OpenShift節(jié)點和OpenShift附加組件這兩部分的存儲容量規(guī)劃方法。

OpenShift節(jié)點所需要的存儲主要是節(jié)點文件系統(tǒng)上的一些特殊的目錄,通常消費本地存儲。

·Etcd數(shù)據(jù)存儲

Etcd用于保存OpenShift所有的元數(shù)據(jù)和資源對象,官方建議將Master和Etcd部署在相同的節(jié)點,也就是Etcd數(shù)據(jù)保存在Master節(jié)點的本地磁盤,默認在/var/lib/etcd/目錄下,該目錄最小需要20 GB的存儲。

·Docker/CRI-O本地存儲

Docker/CRI-O作為容器運行時,在每個節(jié)點都會運行,在運行過程中會保存鏡像到本地以及為容器運行分配根空間都需要消耗本地磁盤,官方建議在生產(chǎn)環(huán)境中專門為運行時配置一塊裸磁盤。這部分存儲的大小取決于容器工作負載、容器的數(shù)量、正在運行的容器的大小以及容器的存儲要求,通常建議配置100G甚至更大的存儲。另外,建議最好定期清理本地?zé)o用的鏡像和容器,一方面是為了釋放磁盤空間,另一方面是為了提升運行時性能。

·OpenShift節(jié)點本地日志存儲

OpenShift節(jié)點運行的進程的日志默認存放在/var/log目錄下,該目錄最小需要15G的存儲。

除了這三個對于OpenShift相對關(guān)鍵的目錄之外,其余操作系統(tǒng)分區(qū)規(guī)劃遵循企業(yè)操作系統(tǒng)安裝規(guī)范即可。

在清楚了OpenShift節(jié)點存儲規(guī)劃之后,下面看看OpenShift附加組件的存儲規(guī)劃。OpenShift包含的一些附件組件是需要掛載持久化存儲的,如鏡像倉庫、日志系統(tǒng)等,這部分存儲是掛載到容器中消費,通常使用的是非本地存儲。它主要包含如下幾部分:

·鏡像倉庫

鏡像倉庫可以選擇的存儲類型有塊存儲、文件系統(tǒng)存儲、對象存儲,我們推薦優(yōu)先使用對象存儲,其次是文件系統(tǒng)存儲,最后才是塊存儲。如果選擇塊存儲就只能用一個實例讀寫,不利于鏡像倉庫高可用的實現(xiàn)。

OpenShift中的鏡像倉庫包括OpenShift內(nèi)部鏡像倉庫和外部鏡像倉庫。OpenShift內(nèi)部鏡像倉庫主要用于存放在開發(fā)過程中生成的應(yīng)用鏡像,存儲空間增長主要取決于構(gòu)建生成應(yīng)用的二進制文件的數(shù)量和大小;OpenShift外部鏡像倉庫在開發(fā)測試環(huán)境用于存儲應(yīng)用所需要的基礎(chǔ)鏡像,如Tomcat鏡像,存儲空間增長主要取決于保存的基礎(chǔ)鏡像的數(shù)量和大小,對于一個企業(yè)來說,基礎(chǔ)鏡像相對是固定的,存儲空間增長不會很大;鏡像倉庫在生產(chǎn)環(huán)境用于存放發(fā)布生產(chǎn)的鏡像,存儲空間增長取決于保存的應(yīng)用鏡像的大小和數(shù)量。

經(jīng)過上述描述,可以發(fā)現(xiàn),開發(fā)測試環(huán)境的內(nèi)部鏡像倉庫的存儲空間增長是最快的,因為頻繁的構(gòu)建每天會產(chǎn)生大量的鏡像上傳到內(nèi)部鏡像倉庫。我們可以根據(jù)每天構(gòu)建應(yīng)用的次數(shù)以及每次構(gòu)建生成應(yīng)用的二進制文件的大小粗略估計出該倉庫所需要的存儲空間,計算公式如下:

開發(fā)測試環(huán)境內(nèi)部鏡像倉庫存儲空間=平均每天構(gòu)建應(yīng)用的次數(shù)×平均每天構(gòu)建應(yīng)用的二進制文件的大小×保留鏡像的天數(shù)+基礎(chǔ)鏡像總大小

其中,基礎(chǔ)鏡像總大小可以在開發(fā)測試環(huán)境的外部鏡像倉庫拿到這個數(shù)據(jù),當然也可以給一個適當足夠大的值。

開發(fā)測試環(huán)境的外部鏡像倉庫用于存放基礎(chǔ)鏡像,相對固定,每個企業(yè)對該倉庫存儲空間的需求是不一樣的,按以往經(jīng)驗來說,通常配置100G或200G是足夠的。

生產(chǎn)環(huán)境的鏡像倉庫可以通過平均每天發(fā)布應(yīng)用的次數(shù)、平均鏡像大小以及保留的天數(shù)來估計所需要的存儲空間,計算公式如下:

生產(chǎn)環(huán)境鏡像倉庫存儲空間=平均每天發(fā)布應(yīng)用的次數(shù)×平均鏡像大小×保留的天數(shù)

到此為止,所有的鏡像倉庫存儲容量就規(guī)劃完了,如果在使用過程中出現(xiàn)了存儲不足的情況,優(yōu)先考慮清理無用鏡像來釋放空間,如果確實無法釋放,再考慮擴容空間。

·日志系統(tǒng)

日志系統(tǒng)默認使用容器化的EFK套件,唯一需要掛載存儲的是ElasticSearch,可以選擇的存儲類型有塊存儲和文件系統(tǒng)存儲。出于性能上的考慮,推薦優(yōu)先使用塊存儲,其次選擇文件系統(tǒng)存儲。如果使用文件系統(tǒng)存儲,則必須每個ElasticSearch實例分配一個卷。

ElasticSearch存儲大小可以使用以下方法進行粗略估算:

統(tǒng)計應(yīng)用輸出日志每行的平均字節(jié)數(shù),如每行256字節(jié);統(tǒng)計每秒輸出的行數(shù),如每秒輸出10行。那么一天一個Pod輸出的日志量為256字節(jié)×10×60×60×24,大約為216MB。

再根據(jù)運行的Pod數(shù)目計算出每天大約需要的日志存儲量,隨后根據(jù)需要保留的日志的天數(shù)計算出總?cè)罩敬鎯臻g需求,建議多附加20%的額外存儲量。

如在生產(chǎn)環(huán)境200個容器,24小時積累日志43G左右。如果保留一周,則需要300G的存儲空間。

上述計算只是估算了保存一份日志的存儲空間,我們都知道ElasticSearch是通過副本機制實現(xiàn)數(shù)據(jù)的高可用,因此為高可用ElasticSearch規(guī)劃空間時還需要考慮副本數(shù)的影響,通常是根據(jù)一份日志的存儲空間直接乘以保留的副本數(shù)。

以上方法只是一個粗略估計,如果需要更為精確的估算,則最好在應(yīng)用穩(wěn)定上線之后通過ElasticSearch每天增加的存儲空間推算每天的日志增長量。

·OpenShift監(jiān)控系統(tǒng)

OpenShift監(jiān)控系統(tǒng)使用Prometheus套件,需要掛載存儲的組件有Prometheus、AlertManager。可以使用的存儲類型有塊存儲和文件系統(tǒng)存儲,推薦優(yōu)先使用塊存儲,其次使用文件系統(tǒng)存儲。如果使用文件系統(tǒng)存儲,最好經(jīng)過測試后再使用。

OpenShift中的Prometheus默認使用Operator部署,配置存儲需要配置動態(tài)存儲類或提前創(chuàng)建好可用的PV。Prometheus有兩個實例,AlerManager有三個實例,總共需要5個PV。

AlertManager需要的存儲空間較小,按經(jīng)驗配置40G是足夠的。Prometheus需要的存儲空間與集群節(jié)點數(shù)、集群Pod數(shù)、保留數(shù)據(jù)天數(shù)(默認15天)等因素有關(guān)。官方在默認配置下給出四組Prometheus測試數(shù)據(jù)供參考,如表2-14所示。

表2-14 Prometheus存儲需求測試數(shù)據(jù)

根據(jù)上述測試數(shù)據(jù),在默認配置下,Prometheus在15天需要的存儲量基本與節(jié)點數(shù)和Pod總數(shù)呈線性增長,我們根據(jù)這個比例估算需要的存儲量即可,同樣建議在計算時多附加20%的額外存儲量以預(yù)防意外情況。

4.OpenShift高可用架構(gòu)設(shè)計

高可用性對于一個平臺級系統(tǒng)至關(guān)重要,必須保證系統(tǒng)能夠持續(xù)提供服務(wù)。對于OpenShift而言,要實現(xiàn)這一點,需要保證各組件都高可用,這對設(shè)計OpenShift部署架構(gòu)提出一些要求。由于篇幅有限,本章僅介紹一些核心組件的高可用實現(xiàn),日志和監(jiān)控系統(tǒng)的高可用實現(xiàn)我們在下一章介紹。在部署階段需要實現(xiàn)高可用的組件有:

·控制節(jié)點

·Router

·鏡像倉庫

·管理控制臺

下面我們分別說明上述組件的高可用實現(xiàn)。

(1)控制節(jié)點的高可用

在前面的架構(gòu)介紹中就提到控制節(jié)點作為整個集群的核心,負責(zé)整個集群的管理和調(diào)度等,由于計算節(jié)點有多個實例,一個甚至幾個節(jié)點發(fā)生故障時不會影響整個集群,也就是整個OpenShift平臺的高可用主要取決于控制節(jié)點。

控制節(jié)點通常包含Master進程和Etcd進程,OpenShift官方僅支持將Master與Etcd共用節(jié)點部署,這樣每個Master從運行在同一個節(jié)點的Etcd實例讀寫數(shù)據(jù),減少讀寫數(shù)據(jù)的網(wǎng)絡(luò)延遲,有利于提高集群性能。但這樣會導(dǎo)致Master節(jié)點的個數(shù)受Etcd節(jié)點個數(shù)約束,Etcd為分布式鍵值數(shù)據(jù)庫,集群內(nèi)部需要通過投票實現(xiàn)選舉,要求節(jié)點個數(shù)為奇數(shù)。在OpenShift中,我們固定將Master設(shè)置為三個(如果集群規(guī)模較大,可以為Master節(jié)點配置更多的資源,無須再增加Master節(jié)點數(shù)量至5個),控制節(jié)點的部署形態(tài)如圖2-86所示:

圖2-86 控制節(jié)點部署圖

通常導(dǎo)致控制節(jié)點故障有以下兩個因素:

·服務(wù)本身異常或服務(wù)器宕機。

·網(wǎng)絡(luò)原因?qū)е路?wù)不可用。

OpenShift為了應(yīng)對上述故障,控制節(jié)點高可用需要從存儲層、管理層、接入層三個方面實現(xiàn)。存儲層主要指Etcd集群,所有集群的元數(shù)據(jù)和資源對象全部保存在Etcd集群中;管理層主要指調(diào)度以及各種ControllerManager組件,也就是Controller-Manager服務(wù);接入層主要指集群API接口,這是集群組件間以及用戶交互的唯一入口。

存儲層高可用

Etcd是CoreOS開源的一個高可用、強一致性的分布式存儲服務(wù),使用Raft算法將一組主機組成集群,集群中的每個節(jié)點都可以根據(jù)集群運行的情況在三種狀態(tài)間切換:Follower、Candidate與Leader。Leader和Follower之間保持心跳,如果Follower在一段時間內(nèi)沒有收到來自Leader的心跳,就會轉(zhuǎn)為Candidate,發(fā)出新的選主請求。

在Etcd集群初始化的時候,內(nèi)部的節(jié)點都是Follower節(jié)點,之后會有一個節(jié)點因為沒有收到Leader的心跳轉(zhuǎn)為Candidate節(jié)點,發(fā)起選主請求。當這個節(jié)點獲得了大于半數(shù)節(jié)點的投票后會轉(zhuǎn)為Leader節(jié)點,如圖2-87所示。

當Leader節(jié)點服務(wù)異常后,其中的某個Follower節(jié)點因為沒有收到Leader的心跳轉(zhuǎn)為Candidate節(jié)點,發(fā)起選主請求。只要集群中剩余的正常節(jié)點數(shù)目大于集群內(nèi)主機數(shù)目的一半,Etcd集群就可以正常對外提供服務(wù),如圖2-88所示。

當集群內(nèi)部的網(wǎng)絡(luò)出現(xiàn)故障,集群可能會出現(xiàn)“腦裂”問題,這個時候集群會分為一大一小兩個集群(奇數(shù)節(jié)點的集群),較小的集群會處于異常狀態(tài),較大的集群可以正常對外提供服務(wù)。如圖2-89所示。

圖2-87 Etcd集群初始化選舉

圖2-88 Leader故障后的選舉

圖2-89 “腦裂”后選舉

Etcd集群每隔100ms會檢測心跳。如果OpenShift的環(huán)境網(wǎng)絡(luò)條件差,Master節(jié)點之間網(wǎng)絡(luò)延遲超過100ms,則可能導(dǎo)致集群中的不穩(wěn)定和頻繁的leader change(詳見https://access.redhat.com/solutions/4885601)。此外,存儲的超時也會對Etcd造成嚴重影響。要排除磁盤緩慢導(dǎo)致的Etcd警告,可以監(jiān)視指標backend_commit_duration_seconds(p99持續(xù)時間應(yīng)小于25ms)和wal_fsync_duration_seconds(p99持續(xù)時間應(yīng)小于10ms)以確認存儲速度正常(詳見https://access.redhat.com/solutions/4770281)。需要注意的是,如果存儲已經(jīng)出現(xiàn)明顯的性能問題,就不必再進行測試。

圖2-90 網(wǎng)絡(luò)引起的Etcd集群抖動問題處理

關(guān)于網(wǎng)絡(luò)引起的Etcd集群抖動問題的診斷過程,可以參照“大魏分享”公眾號的文章,如圖2-90二維碼所示。

管理層高可用

管理層主要是Controller-Manager服務(wù)。由于管理層的特殊性,在同一時刻只允許多個節(jié)點的一個服務(wù)處理任務(wù),也就是管理層通過一主多從實現(xiàn)高可用。為了簡化高可用實現(xiàn),并未引入復(fù)雜的算法,利用Etcd強一致性的特點實現(xiàn)了多個節(jié)點管理層的選舉。

多個節(jié)點在初始化時,Controller-Manager都會向Etcd注冊Leader,誰搶先注冊成功,Leader就是誰。利用Etcd的強一致性,保證在分布式高并發(fā)情況下Leader節(jié)點全局唯一。當Leader異常時,其他節(jié)點會嘗試更新為Leader。但是只有一個節(jié)點可以成功。選舉過程如圖2-91所示。

圖2-91 管理層實現(xiàn)選舉

接入層高可用

接入層主要是Apiserver服務(wù)。由于Apiserver本身是無狀態(tài)服務(wù),可以實現(xiàn)多活。通常采用在Apiserver前端加負載均衡實現(xiàn),負載均衡軟件由用戶任意選擇,可以選擇硬件的,也可以選擇軟件的。OpenShift在安裝部署的時候會要求在Master前面安裝HAproxy作為多個Master的負載均衡器,如圖2-92所示。

從圖2-92中可以看到通過負載均衡,HAproxy負載均衡到多個Master節(jié)點。

我們可以看到通過對三個層面高可用的實現(xiàn)保證了控制節(jié)點任何一個宕機都不會影響整個集群的可用性。當然,如果故障節(jié)點大于一半以上,集群就會進入只讀模式。

圖2-92 接入層的高可用實現(xiàn)

(2)Router的高可用

Router作為OpenShift中訪問應(yīng)用的入口,是保證應(yīng)用訪問高可用的必要一環(huán)。Router建議使用Hostnetwork模式運行,由于端口沖突,每個OpenShift節(jié)點只能運行一個Router。利用這種特性,我們通常在多個節(jié)點上運行多個Router來實現(xiàn)高可用,建議至少啟動三個,這樣才能保證在升級Router所在節(jié)點時業(yè)務(wù)不中斷。在多個Router情況下,該如何訪問應(yīng)用呢?與多個Master節(jié)點高可用類似,可以通過軟件/硬件負載均衡完成多個Router的負載均衡。

(3)鏡像倉庫的高可用

OpenShift的鏡像倉庫分為內(nèi)部鏡像倉庫和外部鏡像倉庫,用于保存應(yīng)用鏡像和基礎(chǔ)鏡像。鏡像倉庫服務(wù)的高可用也至關(guān)重要,尤其是倉庫中的鏡像數(shù)據(jù)的高可用,必須保證數(shù)據(jù)不丟失。

無論內(nèi)部倉庫還是外部倉庫,目前默認都是使用docker-distribution服務(wù)實現(xiàn),屬于無狀態(tài)應(yīng)用,實現(xiàn)高可用的方式與控制節(jié)點接入層類似,啟動多個實例,然后通過HAproxy實現(xiàn)負載。唯一的區(qū)別是鏡像倉庫的多個實例需要使用對象存儲或者掛載同一個共享存儲卷,如NAS。鏡像倉庫的高可用實現(xiàn)如圖2-93所示。

圖2-93 鏡像倉庫的高可用實現(xiàn)

當然,目前還有很多其他的鏡像倉庫的實現(xiàn),如Quay、Harbor等,關(guān)于這些產(chǎn)品實現(xiàn)高可用的方法,請參考具體產(chǎn)品的官方說明,本書不展開說明。

(4)管理控制臺的高可用

管理控制臺主要指用戶訪問的Web界面,這部分的高可用實現(xiàn)相對簡單。由于與管理控制臺相關(guān)的組件是以容器形式運行在OpenShift上的,而且這些組件都是無狀態(tài)組件,只需要啟動多個容器實例就可以實現(xiàn)管理控制臺的高可用,如下所示。


[root@lb.weixinyucluster ~]# oc get pods -n openshift-console |grep -i console
console-7c5f4f7b44-cqbbm    1/1     Running   0          2d4h
console-7c5f4f7b44-dt4sh    1/1     Running   0          2d4h

到此為止,關(guān)于OpenShift的技術(shù)解密和架構(gòu)設(shè)計就已介紹完畢,相信讀者已經(jīng)對OpenShift整體有了清晰的認識,這些內(nèi)容將成為構(gòu)建企業(yè)級PaaS平臺堅實的基礎(chǔ)知識。

主站蜘蛛池模板: 东丰县| 湘阴县| 陈巴尔虎旗| 东方市| 客服| 且末县| 阿克陶县| 泽库县| 中卫市| 新闻| 南平市| 天长市| 岑巩县| 通山县| 西平县| 马鞍山市| 北海市| 遵义县| 邻水| 建湖县| 万盛区| 得荣县| 弥勒县| 烟台市| 临城县| 高台县| 富锦市| 水城县| 威信县| 易门县| 遂溪县| 开平市| 疏附县| 开化县| 台中县| 利辛县| 肥城市| 周口市| 慈溪市| 宁津县| 黄梅县|