1. 程式人生 > >Kubernetes(k8s)中文文件 kubernetes設計理念_Kubernetes中文社群

Kubernetes(k8s)中文文件 kubernetes設計理念_Kubernetes中文社群

Kubernetes設計理念與分散式系統

分析和理解Kubernetes的設計理念可以使我們更深入地瞭解Kubernetes系統,更好地利用它管理分散式部署的雲原生應用,另一方面也可以讓我們借鑑其在分散式系統設計方面的經驗。

API設計原則

對於雲端計算系統,系統API實際上處於系統設計的統領地位,正如本文前面所說,K8s集群系統每支援一項新功能,引入一項新技術,一定會新引入對應的API物件,支援對該功能的管理操作,理解掌握的API,就好比抓住了K8s系統的牛鼻子。K8s系統API的設計有以下幾條原則:

  1. 所有API應該是宣告式的。正如前文所說,宣告式的操作,相對於命令式操作,對於重複操作的效果是穩定的,這對於容易出現數據丟失或重複的分散式環境來說是很重要的。另外,宣告式操作更容易被使用者使用,可以使系統向用戶隱藏實現的細節,隱藏實現的細節的同時,也就保留了系統未來持續優化的可能性。此外,宣告式的API,同時隱含了所有的API物件都是名詞性質的,例如Service、Volume這些API都是名詞,這些名詞描述了使用者所期望得到的一個目標分散式物件。
  2. API物件是彼此互補而且可組合的。這裡面實際是鼓勵API物件儘量實現面向物件設計時的要求,即“高內聚,鬆耦合”,對業務相關的概念有一個合適的分解,提高分解出來的物件的可重用性。事實上,K8s這種分散式系統管理平臺,也是一種業務系統,只不過它的業務就是排程和管理容器服務。
  3. 高層API以操作意圖為基礎設計。如何能夠設計好API,跟如何能用面向物件的方法設計好應用系統有相通的地方,高層設計一定是從業務出發,而不是過早的從技術實現出發。因此,針對K8s的高層API設計,一定是以K8s的業務為基礎出發,也就是以系統排程管理容器的操作意圖為基礎設計。
  4. 低層API根據高層API的控制需要設計。設計實現低層API的目的,是為了被高層API使用,考慮減少冗餘、提高重用性的目的,低層API的設計也要以需求為基礎,要儘量抵抗受技術實現影響的誘惑。
  5. 儘量避免簡單封裝,不要有在外部API無法顯式知道的內部隱藏的機制。簡單的封裝,實際沒有提供新的功能,反而增加了對所封裝API的依賴性。內部隱藏的機制也是非常不利於系統維護的設計方式,例如PetSet和ReplicaSet,本來就是兩種Pod集合,那麼K8s就用不同API物件來定義它們,而不會說只用同一個ReplicaSet,內部通過特殊的演算法再來區分這個ReplicaSet是有狀態的還是無狀態。
  6. API操作複雜度與物件數量成正比。這一條主要是從系統性能角度考慮,要保證整個系統隨著系統規模的擴大,效能不會迅速變慢到無法使用,那麼最低的限定就是API的操作複雜度不能超過O(N),N是物件的數量,否則系統就不具備水平伸縮性了。
  7. API物件狀態不能依賴於網路連線狀態。由於眾所周知,在分散式環境下,網路連線斷開是經常發生的事情,因此要保證API物件狀態能應對網路的不穩定,API物件的狀態就不能依賴於網路連線狀態。
  8. 儘量避免讓操作機制依賴於全域性狀態,因為在分散式系統中要保證全域性狀態的同步是非常困難的

控制機制設計原則

  • 控制邏輯應該只依賴於當前狀態。這是為了保證分散式系統的穩定可靠,對於經常出現區域性錯誤的分散式系統,如果控制邏輯只依賴當前狀態,那麼就非常容易將一個暫時出現故障的系統恢復到正常狀態,因為你只要將該系統重置到某個穩定狀態,就可以自信的知道系統的所有控制邏輯會開始按照正常方式執行。
  • 假設任何錯誤的可能,並做容錯處理。在一個分散式系統中出現區域性和臨時錯誤是大概率事件。錯誤可能來自於物理系統故障,外部系統故障也可能來自於系統自身的程式碼錯誤,依靠自己實現的程式碼不會出錯來保證系統穩定其實也是難以實現的,因此要設計對任何可能錯誤的容錯處理。
  • 儘量避免複雜狀態機,控制邏輯不要依賴無法監控的內部狀態。因為分散式系統各個子系統都是不能嚴格通過程式內部保持同步的,所以如果兩個子系統的控制邏輯如果互相有影響,那麼子系統就一定要能互相訪問到影響控制邏輯的狀態,否則,就等同於系統裡存在不確定的控制邏輯。
  • 假設任何操作都可能被任何操作物件拒絕,甚至被錯誤解析。由於分散式系統的複雜性以及各子系統的相對獨立性,不同子系統經常來自不同的開發團隊,所以不能奢望任何操作被另一個子系統以正確的方式處理,要保證出現錯誤的時候,操作級別的錯誤不會影響到系統穩定性。
  • 每個模組都可以在出錯後自動恢復。由於分散式系統中無法保證系統各個模組是始終連線的,因此每個模組要有自我修復的能力,保證不會因為連線不到其他模組而自我崩潰。
  • 每個模組都可以在必要時優雅地降級服務。所謂優雅地降級服務,是對系統魯棒性的要求,即要求在設計實現模組時劃分清楚基本功能和高階功能,保證基本功能不會依賴高階功能,這樣同時就保證了不會因為高階功能出現故障而導致整個模組崩潰。根據這種理念實現的系統,也更容易快速地增加新的高階功能,以為不必擔心引入高階功能影響原有的基本功能。

Kubernetes的核心技術概念和API物件

API物件是K8s叢集中的管理操作單元。K8s集群系統每支援一項新功能,引入一項新技術,一定會新引入對應的API物件,支援對該功能的管理操作。例如副本集Replica Set對應的API物件是RS。

每個API物件都有3大類屬性:元資料metadata、規範spec和狀態status。元資料是用來標識API物件的,每個物件都至少有3個元資料:namespace,name和uid;除此以外還有各種各樣的標籤labels用來標識和匹配不同的物件,例如使用者可以用標籤env來標識區分不同的服務部署環境,分別用env=dev、env=testing、env=production來標識開發、測試、生產的不同服務。規範描述了使用者期望K8s叢集中的分散式系統達到的理想狀態(Desired State),例如使用者可以通過複製控制器Replication Controller設定期望的Pod副本數為3;status描述了系統實際當前達到的狀態(Status),例如系統當前實際的Pod副本數為2;那麼複製控制器當前的程式邏輯就是自動啟動新的Pod,爭取達到副本數為3。

K8s中所有的配置都是通過API物件的spec去設定的,也就是使用者通過配置系統的理想狀態來改變系統,這是k8s重要設計理念之一,即所有的操作都是宣告式(Declarative)的而不是命令式(Imperative)的。宣告式操作在分散式系統中的好處是穩定,不怕丟操作或執行多次,例如設定副本數為3的操作執行多次也還是一個結果,而給副本數加1的操作就不是宣告式的,執行多次結果就錯了。

Pod

K8s有很多技術概念,同時對應很多API物件,最重要的也是最基礎的是微服務Pod。Pod是在K8s叢集中執行部署應用或服務的最小單元,它是可以支援多容器的。Pod的設計理念是支援多個容器在一個Pod中共享網路地址和檔案系統,可以通過程序間通訊和檔案共享這種簡單高效的方式組合完成服務。Pod對多容器的支援是K8s最基礎的設計理念。比如你執行一個作業系統發行版的軟體倉庫,一個Nginx容器用來發布軟體,另一個容器專門用來從源倉庫做同步,這兩個容器的映象不太可能是一個團隊開發的,但是他們一塊兒工作才能提供一個微服務;這種情況下,不同的團隊各自開發構建自己的容器映象,在部署的時候組合成一個微服務對外提供服務。

Pod是K8s叢集中所有業務型別的基礎,可以看作執行在K8s叢集中的小機器人,不同型別的業務就需要不同型別的小機器人去執行。目前K8s中的業務主要可以分為長期伺服型(long-running)、批處理型(batch)、節點後臺支撐型(node-daemon)和有狀態應用型(stateful application);分別對應的小機器人控制器為Deployment、Job、DaemonSet和PetSet,本文後面會一一介紹。

複製控制器(Replication Controller,RC)

RC是K8s叢集中最早的保證Pod高可用的API物件。通過監控執行中的Pod來保證叢集中執行指定數目的Pod副本。指定的數目可以是多個也可以是1個;少於指定數目,RC就會啟動執行新的Pod副本;多於指定數目,RC就會殺死多餘的Pod副本。即使在指定數目為1的情況下,通過RC執行Pod也比直接執行Pod更明智,因為RC也可以發揮它高可用的能力,保證永遠有1個Pod在執行。RC是K8s較早期的技術概念,只適用於長期伺服型的業務型別,比如控制小機器人提供高可用的Web服務。

副本集(Replica Set,RS)

RS是新一代RC,提供同樣的高可用能力,區別主要在於RS後來居上,能支援更多種類的匹配模式。副本集物件一般不單獨使用,而是作為Deployment的理想狀態引數使用。

部署(Deployment)

部署表示使用者對K8s叢集的一次更新操作。部署是一個比RS應用模式更廣的API物件,可以是建立一個新的服務,更新一個新的服務,也可以是滾動升級一個服務。滾動升級一個服務,實際是建立一個新的RS,然後逐漸將新RS中副本數增加到理想狀態,將舊RS中的副本數減小到0的複合操作;這樣一個複合操作用一個RS是不太好描述的,所以用一個更通用的Deployment來描述。以K8s的發展方向,未來對所有長期伺服型的的業務的管理,都會通過Deployment來管理。

服務(Service)

RC、RS和Deployment只是保證了支撐服務的微服務Pod的數量,但是沒有解決如何訪問這些服務的問題。一個Pod只是一個執行服務的例項,隨時可能在一個節點上停止,在另一個節點以一個新的IP啟動一個新的Pod,因此不能以確定的IP和埠號提供服務。要穩定地提供服務需要服務發現和負載均衡能力。服務發現完成的工作,是針對客戶端訪問的服務,找到對應的的後端服務例項。在K8s叢集中,客戶端需要訪問的服務就是Service物件。每個Service會對應一個叢集內部有效的虛擬IP,叢集內部通過虛擬IP訪問一個服務。在K8s叢集中微服務的負載均衡是由Kube-proxy實現的。Kube-proxy是K8s叢集內部的負載均衡器。它是一個分散式代理伺服器,在K8s的每個節點上都有一個;這一設計體現了它的伸縮性優勢,需要訪問服務的節點越多,提供負載均衡能力的Kube-proxy就越多,高可用節點也隨之增多。與之相比,我們平時在伺服器端做個反向代理做負載均衡,還要進一步解決反向代理的負載均衡和高可用問題。

任務(Job)

Job是K8s用來控制批處理型任務的API物件。批處理業務與長期伺服業務的主要區別是批處理業務的執行有頭有尾,而長期伺服業務在使用者不停止的情況下永遠執行。Job管理的Pod根據使用者的設定把任務成功完成就自動退出了。成功完成的標誌根據不同的spec.completions策略而不同:單Pod型任務有一個Pod成功就標誌完成;定數成功型任務保證有N個任務全部成功;工作佇列型任務根據應用確認的全域性成功而標誌成功。

後臺支撐服務集(DaemonSet)

長期伺服型和批處理型服務的核心在業務應用,可能有些節點執行多個同類業務的Pod,有些節點上又沒有這類Pod執行;而後臺支撐型服務的核心關注點在K8s叢集中的節點(物理機或虛擬機器),要保證每個節點上都有一個此類Pod執行。節點可能是所有叢集節點也可能是通過nodeSelector選定的一些特定節點。典型的後臺支撐型服務包括,儲存,日誌和監控等在每個節點上支援K8s叢集執行的服務。

有狀態服務集(PetSet)

K8s在1.3版本里釋出了Alpha版的PetSet功能。在雲原生應用的體系裡,有下面兩組近義詞;第一組是無狀態(stateless)、牲畜(cattle)、無名(nameless)、可丟棄(disposable);第二組是有狀態(stateful)、寵物(pet)、有名(having name)、不可丟棄(non-disposable)。RC和RS主要是控制提供無狀態服務的,其所控制的Pod的名字是隨機設定的,一個Pod出故障了就被丟棄掉,在另一個地方重啟一個新的Pod,名字變了、名字和啟動在哪兒都不重要,重要的只是Pod總數;而PetSet是用來控制有狀態服務,PetSet中的每個Pod的名字都是事先確定的,不能更改。PetSet中Pod的名字的作用,並不是《千與千尋》的人性原因,而是關聯與該Pod對應的狀態。

對於RC和RS中的Pod,一般不掛載儲存或者掛載共享儲存,儲存的是所有Pod共享的狀態,Pod像牲畜一樣沒有分別(這似乎也確實意味著失去了人性特徵);對於PetSet中的Pod,每個Pod掛載自己獨立的儲存,如果一個Pod出現故障,從其他節點啟動一個同樣名字的Pod,要掛載上原來Pod的儲存繼續以它的狀態提供服務。

適合於PetSet的業務包括資料庫服務MySQL和PostgreSQL,叢集化管理服務Zookeeper、etcd等有狀態服務。PetSet的另一種典型應用場景是作為一種比普通容器更穩定可靠的模擬虛擬機器的機制。傳統的虛擬機器正是一種有狀態的寵物,運維人員需要不斷地維護它,容器剛開始流行時,我們用容器來模擬虛擬機器使用,所有狀態都儲存在容器裡,而這已被證明是非常不安全、不可靠的。使用PetSet,Pod仍然可以通過漂移到不同節點提供高可用,而儲存也可以通過外掛的儲存來提供高可靠性,PetSet做的只是將確定的Pod與確定的儲存關聯起來保證狀態的連續性。PetSet還只在Alpha階段,後面的設計如何演變,我們還要繼續觀察。

叢集聯邦(Federation)

K8s在1.3版本里釋出了beta版的Federation功能。在雲端計算環境中,服務的作用距離範圍從近到遠一般可以有:同主機(Host,Node)、跨主機同可用區(Available Zone)、跨可用區同地區(Region)、跨地區同服務商(Cloud Service Provider)、跨雲平臺。K8s的設計定位是單一叢集在同一個地域內,因為同一個地區的網路效能才能滿足K8s的排程和計算儲存連線要求。而聯合叢集服務就是為提供跨Region跨服務商K8s叢集服務而設計的。

每個K8s Federation有自己的分散式儲存、API Server和Controller Manager。使用者可以通過Federation的API Server註冊該Federation的成員K8s Cluster。當用戶通過Federation的API Server建立、更改API物件時,Federation API Server會在自己所有註冊的子K8s Cluster都建立一份對應的API物件。在提供業務請求服務時,K8s Federation會先在自己的各個子Cluster之間做負載均衡,而對於傳送到某個具體K8s Cluster的業務請求,會依照這個K8s Cluster獨立提供服務時一樣的排程模式去做K8s Cluster內部的負載均衡。而Cluster之間的負載均衡是通過域名服務的負載均衡來實現的。

所有的設計都儘量不影響K8s Cluster現有的工作機制,這樣對於每個子K8s叢集來說,並不需要更外層的有一個K8s Federation,也就是意味著所有現有的K8s程式碼和機制不需要因為Federation功能有任何變化。

儲存卷(Volume)

K8s叢集中的儲存卷跟Docker的儲存卷有些類似,只不過Docker的儲存卷作用範圍為一個容器,而K8s的儲存卷的生命週期和作用範圍是一個Pod。每個Pod中宣告的儲存卷由Pod中的所有容器共享。K8s支援非常多的儲存卷型別,特別的,支援多種公有云平臺的儲存,包括AWS,Google和Azure雲;支援多種分散式儲存包括GlusterFS和Ceph;也支援較容易使用的主機本地目錄hostPath和NFS。K8s還支援使用Persistent Volume Claim即PVC這種邏輯儲存,使用這種儲存,使得儲存的使用者可以忽略後臺的實際儲存技術(例如AWS,Google或GlusterFS和Ceph),而將有關儲存實際技術的配置交給儲存管理員通過Persistent Volume來配置。

持久儲存卷(Persistent Volume,PV)和持久儲存卷宣告(Persistent Volume Claim,PVC)

PV和PVC使得K8s叢集具備了儲存的邏輯抽象能力,使得在配置Pod的邏輯裡可以忽略對實際後臺儲存技術的配置,而把這項配置的工作交給PV的配置者,即叢集的管理者。儲存的PV和PVC的這種關係,跟計算的Node和Pod的關係是非常類似的;PV和Node是資源的提供者,根據叢集的基礎設施變化而變化,由K8s叢集管理員配置;而PVC和Pod是資源的使用者,根據業務服務的需求變化而變化,有K8s叢集的使用者即服務的管理員來配置。

節點(Node)

K8s叢集中的計算能力由Node提供,最初Node稱為服務節點Minion,後來改名為Node。K8s叢集中的Node也就等同於Mesos叢集中的Slave節點,是所有Pod執行所在的工作主機,可以是物理機也可以是虛擬機器。不論是物理機還是虛擬機器,工作主機的統一特徵是上面要執行kubelet管理節點上執行的容器。

金鑰物件(Secret)

Secret是用來儲存和傳遞密碼、金鑰、認證憑證這些敏感資訊的物件。使用Secret的好處是可以避免把敏感資訊明文寫在配置檔案裡。在K8s叢集中配置和使用服務不可避免的要用到各種敏感資訊實現登入、認證等功能,例如訪問AWS儲存的使用者名稱密碼。為了避免將類似的敏感資訊明文寫在所有需要使用的配置檔案中,可以將這些資訊存入一個Secret物件,而在配置檔案中通過Secret物件引用這些敏感資訊。這種方式的好處包括:意圖明確,避免重複,減少暴漏機會。

使用者帳戶(User Account)和服務帳戶(Service Account)

顧名思義,使用者帳戶為人提供賬戶標識,而服務賬戶為計算機程序和K8s叢集中執行的Pod提供賬戶標識。使用者帳戶和服務帳戶的一個區別是作用範圍;使用者帳戶對應的是人的身份,人的身份與服務的namespace無關,所以使用者賬戶是跨namespace的;而服務帳戶對應的是一個執行中程式的身份,與特定namespace是相關的。

名字空間(Namespace)

名字空間為K8s叢集提供虛擬的隔離作用,K8s叢集初始有兩個名字空間,分別是預設名字空間default和系統名字空間kube-system,除此以外,管理員可以可以建立新的名字空間滿足需要。

RBAC訪問授權

K8s在1.3版本中釋出了alpha版的基於角色的訪問控制(Role-based Access Control,RBAC)的授權模式。相對於基於屬性的訪問控制(Attribute-based Access Control,ABAC),RBAC主要是引入了角色(Role)和角色繫結(RoleBinding)的抽象概念。在ABAC中,K8s叢集中的訪問策略只能跟使用者直接關聯;而在RBAC中,訪問策略可以跟某個角色關聯,具體的使用者在跟一個或多個角色相關聯。顯然,RBAC像其他新功能一樣,每次引入新功能,都會引入新的API物件,從而引入新的概念抽象,而這一新的概念抽象一定會使叢集服務管理和使用更容易擴充套件和重用。

總結

從K8s的系統架構、技術概念和設計理念,我們可以看到K8s系統最核心的兩個設計理念:一個是容錯性,一個是易擴充套件性。容錯性實際是保證K8s系統穩定性和安全性的基礎,易擴充套件性是保證K8s對變更友好,可以快速迭代增加新功能的基礎。

參考文件

參考:https://feisky.gitbooks.io/kubernetes/architecture/concepts.html

K8S中文社群微信公眾號