1. 程式人生 > >將Hypernetes整合至Kubernetes帶來安全性與多租戶機制_Kubernetes中文社群

將Hypernetes整合至Kubernetes帶來安全性與多租戶機制_Kubernetes中文社群

儘管很多開發者與安全專家樂於強調Linux容器所擁有的明確邊界,但大多數使用者仍然希望進一步提升其隔離性水平,特別是在多租戶執行環境當中。遺憾的是,目前這部分使用者仍不得不將容器執行在虛擬機器當中,有些甚至採取了容器與虛擬機器一對一的包裝方式。

這種作法直接影響到了雲原生應用的諸多固有優勢:包裝在容器外面的虛擬機器拖慢了服務的啟動速度,帶來更多的記憶體消耗,叢集的資源利用率也無法得到有的效保證。

在今天的文章中,我們將介紹HyperContainer這種基於虛擬化技術的容器實現,瞭解它如何契合於Kubernetes的設計理念並幫助使用者直接為使用者提供一套虛擬化技術加持的容器——而不是將它們打包進虛擬機器當中。

HyperContainer

HyperContainer是一款基於虛擬化技術的容器方案,它允許大家利用現有標準虛擬化管理程式(包括KVM與Xen等)來啟動Docker映象。作為開源專案,HyperContainer由一套名為runV的OCI標準容器執行時和一個名為hyperd的守護程式共同構成。HyperContainer的設計思路非常明確:它要將虛擬化與容器兩項技術的優勢結合起來。

其實,我們可以將所有容器都看作由兩大部分組成(Kubernetes正是這麼設計的)。第一部分稱為容器執行時,這裡HyperContainer利用虛擬化技術(而非namespaces與cgroups)實現隔離與資源限制。另一部分稱為應用資料,HyperContainer使用的是Docker映象。因此在HyperContainer當中,虛擬化技術能夠構建出完全隔離的沙箱環境和獨立的使用者kernel(這也意味著’top’指令及/proc檔案系統在Hyper容器中都是正常工作的)。但從開發者的角度來看,其使用感受及可移植性則與標準容器保持一致。

HyperContainer原生支援Pod

HyperContainer的有趣之處在於,它不僅能夠為多租戶環境(例如公有云)帶來理想的安全保障,同時也能夠很好地契合Kubernetes的設計理念。

Kubernetes當中最為重要的概念就是Pod,而Pod的設計思想則源自Google公司著名的Borg系統對真實世界工作負載進行抽象得出的結論(見Borg論文8.1節),即在大多數情況下使用者都希望能夠將多個緊密協作的容器打包稱為一個原子的排程單元來進行管理。當使用Linux容器時,Kubernetes Pod負責將多個容器打包並封裝成為一個邏輯組。而在HyperContainer當中,虛擬機器則被用來充當這組容器的天然邊界,從而使得Pod能夠則作為HyperContainer的頂級物件:

20161013162310

HyperContainer將多個輕量化應用容器打包成一個Pod,並在Pod級別來暴露容器操作的介面。在Pod當中,一個名為HyperKernel的極小Linux核心會被啟動。而這個HyperKernel責由HyperStart這個輕量的Init服務來負責構建,HyperStart會作為PID 1的程序存在並建立Pod本體、設定Mount namespace、根據載入的映象啟動各應用。

這套模式能夠與Kubernetes十分順暢地對接。一旦將HyperContainer與Kubernetes相結合,就構成了我們在標題中提到的“Hypernetes”專案。

Hypernetes

Kubernetes的一個最大優勢在於它是目前唯一支援多容器執行時的編排管理框架,這意味著使用者不會被鎖定在單一的容器技術提供商上。在這裡我們很高興地宣佈,我們已經與Kubernetes團隊達成合作關係,共同將HyperContainter整合至Kubernetes主幹當中。這些合作工作包括了:

  1. 容器執行時優化與重構
  2. 新的客戶端-伺服器模式執行時介面
  3. 整合containerd並支援runV

儘管HyperContainer並非基於Linux容器技術堆疊所構建,但OCI標準和Kubernetes的多執行時架構使得這一整合過程非常順利。

在另一方面,為了在多租戶環境下執行HyperContainer,我們還建立了一款新的Kubernetes網路外掛,並對現有的儲存外掛做了修改。由於Hypernetes直接使用虛擬機器來作為Pod,它能夠直接利用現有的成熟IaaS層技術來提供多租戶網路及持久化卷儲存。目前Hypernetes實現方案採用了標準的OpenStack元件。

接下來,我們將進一步探討具體實現細節。

身份驗證與授權

在Hypernetes當中,我們選擇了Keystone來管理不同租戶並在管理操作流程中執行身份驗證任務。由於Keystone源自OpenStack生態系統,因此它能夠同Hypernetes中使用的網路與儲存外掛進行無縫協作。

多租戶網路模型

對於多租戶容器叢集,每位租戶都擁有與其他租戶完全隔離的自有網路環境。在Hypernetes中,每位租戶亦擁有自己的Network。相較於利用OpenStack配置網路的複雜作法,Hypernetes只需要建立一個Network物件即可,具體如下:

apiVersion: v1
kind: Network
metadata:
name: net1
spec:
tenantID: 065f210a2ca9442aad898ab129426350
subnets:
subnet1:
 cidr: 192.168.0.0/24
 gateway: 192.168.0.1

請注意,其中的tenantID由Keystone負責提供。在個yaml將建立一個新的Neutron網路,並使用預設路由與192.168.0.0/24子網。

對於由使用者建立的任意Network物件,都將由我們所新增的Kubernetes Network控制器(Network Controller)來負責其生命週期管理工作。該Network可被分配至一個或多個Kubernetes Namespaces,而任意歸屬於同一Network的Pod都能夠直接通過IP地址彼此通訊。

apiVersion: v1
kind: Namespace
metadata:
name: ns1
spec:
network: net1

如果Namespace中不存在Network的欄位,它會預設使用Kubernetes自帶的網路模型,其中包括了預設的kube-proxy。因此如果使用者在Namespace中利用相關Network建立一個Pod,Hypernetes將根據Kubernetes網路外掛模型設定一套Neutron網路。下圖是一個粗略的流程示例:

20161013162320

Hypernetes使用了一個名叫kubestack的獨立程序來將Kubernetes Pod請求翻譯至Neutron網路API。另外,kubestack還負責處理另一項重要的網路功能:多租戶Service代理。

在多租戶環境下,基於iptables的預設kube-proxy無法訪問到所有Pod,因為這些Pod會被隔離在不同網路當中。面對這一需求,Hypernetes利用了內建的HAproxy作為門戶(Portal)。HAproxy將負責代理該Pod namespace中的全部Service例項。Kube-proxy則根據標準的OnServiceUpdate和OnEndpointsUpdate流程對這些後端伺服器進行更新,使用者在使用中將不會有任何異樣的感覺。不過這種作法的弊端在於,HAproxy必須監聽部分可能與使用者容器相沖突的埠。正因為如此,我們才計劃在下一版本中利用LVS取代目前的代理機制。

在上述基於Neutron的網路外掛的幫助下,Hypernetes Service能夠提供一套OpenStack負載均衡器,其效果與GCE上的“外部負載均衡器”基本一致。當用戶利用外部IP建立一項Service時,系統會同時為其建立一套OpenStack負載均衡器,被代理的各後端服務則通過之前提到的kubestack工作流進行自動更新。

持久化儲存

所謂的持久化儲存,我們實際上指的是如何在Kubernetes當中構建起一套多租戶的持久化資料卷。我們之所以沒有選擇現有Kubernetes Cinder資料卷外掛,是因為該模式並不適用於基於虛擬化的容器上。比如:

  • Cinder資料卷外掛要求使用完整的OpenStack叢集來作為Kubernetes的下層IaaS。
  • OpenStack將會負責找到需要掛載資料卷的Pod執行在哪個虛擬機器上。 然後Cinder資料卷外掛會將Cinder卷掛載到Kubernetes所在的虛擬機器上的指定目錄。
  • Kubelet會繫結此目錄以作為目標Pod內各容器的資料卷(host:path模式)。

不過在Hypernetes中,整個流程則要簡單得多。歸功於Pod的物理邊界,HyperContainer能夠直接將Cinder資料卷作為塊儲存裝置直接新增至Pod當中——正如同我們對普通虛擬機器所做的那樣。這項機制消除了前面提到的OpenStack通過Nova來查詢目標Pod對應的虛擬機器所帶來的時間浪費。

目前,我們的Cinder外掛使用的是Ceph RBD後端,其工作方式同其它Kubernetes標準資料卷外掛一致,使用者只需要建立Cinder資料卷(比如以下示例中volumeID所標示的內容)即可。

apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: nginx-persistent-storage
 mountPath: /var/lib/nginx
volumes:
- name: nginx-persistent-storage
cinder:
 volumeID: 651b2a7b-683e-47e1-bdd6-e3c62e8f91c0
 fsType: ext4

因此當用戶在Pod yaml中聲明瞭Cinder資料卷後,Hypernetes會檢查kubelet是否正在使用HyperContainer容器執行時。如果是,則Cinder資料卷可在無需進行額外路徑對映的前提下直接與Pod進行掛載。然後,該資料卷的元資料將作為HyperContainer描述的一部分被交給至Kubelet RunPod程序處用來啟動Pod。大功告成!

受益於Kubernetes網路與資料卷的外掛模式,我們能夠輕鬆的使用上述解決方案來支撐HyperContainer在Kubernetes中執行,儘管它採用了與Linux容器完全不同的隔離機制。我們還計劃在容器執行時整合工作完成後通過滿足CNI網路介面與Kubernetes資料卷外掛標準從而將這些解決方案合併到Kubernetes主幹當中。

我們堅信這些開源專案都是容器生態系統中的重要組成部分,而它們的成長很大程度上歸功於開源軟體精神和Kubernetes團隊的技術視野。

總結

在今天的文章中,我們介紹了HyperContainer以及Hypernetes專案的部分技術細節。我們希望大家會對這一全新的、安全的容器方案和它與Kubernetes的整合抱有興趣。如果大家想試用Hypernetes以及HyperContainer,我們正好剛剛釋出了基於上述技術的、全新的、安全的容器雲服務的beta測試版本(Hyper_)。不過如果大家更傾向於選擇私有環境,那麼Hypernetes與HyperContainter專案絕對能夠幫助大家獲得更安全的、更適合生產環境的Kubernetes解決方案。