天天看點

将Hypernetes整合至Kubernetes帶來安全性與多租戶機制

盡管很多開發者與安全專家樂于強調Linux容器所擁有的明确邊界,但大多數使用者仍然希望進一步提升其隔離性水準,特别是在多租戶運作環境當中。遺憾的是,目前這部分使用者仍不得不将容器運作在虛拟機當中,有些甚至 采取了容器與虛拟機一對一的包裝方式。

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

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

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的頂級對象:

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網絡。下圖是一個粗略的流程示例:

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解決方案。

本文轉自中文社群-

将Hypernetes整合至Kubernetes帶來安全性與多租戶機制