
在前面以Docker項目為例,一步步剖析了Linux容器的具體實作方式。
通過這些應該明白:一個“容器”,實際上是一個由Linux Namespace、Linux Cgroups和rootfs三種技術建構出來的程序的隔離環境。
一個正在運作的Linux容器,其實可以被看做
- 一組聯合挂載在 /var/lib/docker/aufs/mnt 上的rootfs,這部分稱為“容器鏡像”(Container Image),是容器的靜态視圖
- 一個由Namespace+Cgroups構成的隔離環境,這部分稱為“容器運作時”(Container Runtime),是容器的動态視圖。
作為一名開發者,我并不關心容器運作時的差異。
因為,在整個“開發-測試-釋出”的流程中,真正承載着容器資訊進行傳遞的,是容器鏡像,而不是容器運作時。
這個重要假設,正是容器技術圈在Docker項目成功後不久,就迅速走向了“容器編排”這個“上層建築”的主要原因:
作為雲基礎設施提供商,隻要能夠将使用者送出的Docker鏡像以容器的方式運作,就能成為容器生态圖上的一個承載點,進而将整個容器技術棧上的價值,沉澱在這個節點上。
更重要的是,隻要從我這個承載點向Docker鏡像制作者和使用者方向回溯,整條路徑上的各個服務節點
比如CI/CD、監控、安全、網絡、存儲等,都有我可以發揮和盈利的餘地。
這個邏輯,正是所有雲計算提供商如此熱衷于容器技術的重要原因:通過容器鏡像,它們可以和開發者關聯起來。
從一個開發者和單一的容器鏡像,到無數開發者和龐大容器叢集,容器技術實作了從“容器”到“容器雲”的飛躍,标志着它真正得到了市場和生态的認可。
**`容器從一個開發者手裡的小工具,一躍成為了雲計算領域的絕對主角
而能夠定義容器組織和管理規範的“容器編排”技術,坐上了容器技術領域的“頭把交椅”`**
最具代表性的容器編排工具,當屬
- Docker公司的Compose+Swarm組合
- Google與RedHat公司共同主導的Kubernetes項目
在前面介紹容器技術發展曆史中,已經對這兩個開源項目做了詳細地剖析和評述。是以,在今天專注于主角Kubernetes項目,談一談它的設計與架構。
跟很多基礎設施領域先有工程實踐、後有方法論的發展路線不同,Kubernetes項目的理論基礎則要比工程實踐走得靠前得多,這當然要歸功于Google公司在2015年4月釋出的Borg論文了。
Borg系統,一直以來都被譽為Google公司内部最強大的“秘密武器”。
相比于Spanner、BigTable等相對上層的項目,Borg要承擔的責任,是承載Google整個基礎設施的核心依賴。
在Google已經公開發表的基礎設施體系論文中,Borg項目當仁不讓地位居整個基礎設施技術棧的最底層。
這幅圖,來自于Google Omega論文的第一作者的博士畢業論文。它描繪了當時Google已經公開發表的整個基礎設施棧。在這個圖裡,你既可以找到MapReduce、BigTable等知名項目,也能看到Borg和它的繼任者Omega位于整個技術棧的最底層。
正是由于這樣的定位,Borg可以說是Google最不可能開源的一個項目。
得益于Docker項目和容器技術的風靡,它卻終于得以以另一種方式與開源社群見面,就是Kubernetes項目。
相比于“小打小鬧”的Docker公司、“舊瓶裝新酒”的Mesos社群,Kubernetes項目從一開始就比較幸運地站上了一個他人難以企及的高度:
在它的成長階段,這個項目每一個核心特性的提出,幾乎都脫胎于Borg/Omega系統的設計與經驗。
更重要的是,這些特性在開源社群落地的過程中,又在整個社群的合力之下得到了極大的改進,修複了很多當年遺留在Borg體系中的缺陷和問題。
盡管在釋出之初被批“曲高和寡”,但在逐漸覺察到Docker技術棧的“稚嫩”和Mesos社群的“老邁”,社群很快就明白了:Kubernetes項目在Borg體系的指導下,展現出了一種獨有的先進與完備性,這些才是一個基礎設施領域開源項目的核心價值。
從Kubernetes的頂層設計說起。
Kubernetes要解決什麼?
編排?排程?容器雲?還是叢集管理?
至今其實都沒有标準答案。在不同的發展階段,Kubernetes需要着力的問題是不同的。
但對于大多數使用者,他們希望Kubernetes項目帶來的體驗是确定的:
現在我有應用的容器鏡像,請幫我在一個給定的叢集上把應用運作起來
更進一步說,還希望Kubernetes能給我提供路由網關、水準擴充、監控、備份、災難恢複等一系列運維能力。
這不就是經典PaaS(eg. Cloud Foundry)項目的能力嗎!
而且,有了Docker後,根本不需要什麼Kubernetes、PaaS,隻要使用Docker公司的Compose+Swarm項目,就完全可以很友善DIY出這些功能!
是以說,如果Kubernetes項目隻是停留在拉取使用者鏡像、運作容器,以及提供常見的運維功能的話,那别說跟嫡系的Swarm競争,哪怕跟經典的PaaS項目相比也難有優勢
而實際上,在定義核心功能過程中,Kubernetes項目正是依托着Borg項目的理論優勢
才在短短幾個月内迅速站穩了腳跟
- 進而确定了一個如下所示的全局架構
可以看到,Kubernetes項目的架構,跟它的原型項目Borg類似,都由Master和Node兩種節點組成,分别對應着控制節點和計算節點。
其中,控制節點 --- 即Master節點,由三個緊密協作的獨立元件組合而成,它們分别是
- 負責API服務的kube-apiserver
- 負責排程的kube-scheduler
- 負責容器編排的kube-controller-manager
整個叢集的持久化資料,由kube-apiserver處理後儲存在Ectd
而計算節點上最核心的是
kubelet元件
在Kubernetes中,kubelet主要負責同容器運作時(比如Docker項目)打交道。
而這個互動所依賴的,是一個稱作CRI(Container Runtime Interface) 的遠端調用接口,這個接口定義了容器運作時的各項核心操作,比如:啟動一個容器需要的所有參數。
這也是為何,Kubernetes項目并不關心你部署的是什麼容器運作時、使用的什麼技術實作,隻要你的這個容器運作時能夠運作标準的容器鏡像,它就可以通過實作CRI接入到Kubernetes項目當中。
而具體的容器運作時,比如Docker項目,則一般通過OCI這個容器運作時規範同底層的Linux作業系統進行互動,即:把CRI請求翻譯成對Linux作業系統的調用(操作Linux Namespace和Cgroups等)。
此外,kubelet還通過gRPC協定同一個叫作Device Plugin的插件進行互動。
這個插件,是Kubernetes項目用來管理GPU等主控端實體裝置的主要元件,也是基于Kubernetes項目進行機器學習訓練、高性能作業支援等工作必須關注的功能。
而kubelet的另一個重要功能,則是調用網絡插件和存儲插件為容器配置網絡和持久化存儲。
這兩個插件與kubelet進行互動的接口,分别是
- CNI(Container Networking Interface)
- CSI(Container Storage Interface)。
實際上,kubelet這個奇怪的名字,來自于Borg項目裡的同源元件Borglet。
不過,如果你浏覽過Borg論文的話,就會發現,這個命名方式可能是kubelet元件與Borglet元件的唯一相似之處。因為Borg項目,并不支援我們這裡所講的容器技術,而隻是簡單地使用了Linux Cgroups對程序進行限制。
這就意味着,像Docker這樣的“容器鏡像”在Borg中是不存在的,Borglet元件也自然不需要像kubelet這樣考慮如何同Docker進行互動、如何對容器鏡像進行管理的問題,也不需要支援CRI、CNI、CSI等諸多容器技術接口。
kubelet完全就是為了實作Kubernetes項目對容器的管理能力而重新實作的一個元件,與Borg之間并沒有直接的傳承關系。
雖然不使用Docker,但Google内部确實在使用一個包管理工具,名叫Midas Package Manager (MPM),其實它可以部分取代Docker鏡像的角色。
Borg對于Kubernetes項目的指導作用又展現在哪裡呢?
Master節點!
雖然在Master節點的實作細節上Borg與Kubernetes不盡相同,但出發點高度一緻
即:
如何編排、管理、排程使用者送出的作業
是以,Borg項目完全可以把Docker鏡像看做是一種新的應用打包方式。
這樣,Borg團隊過去在大規模作業管理與編排上的經驗就可以直接“套”在Kubernetes項目。
這些經驗最主要的表現就是,從一開始,Kubernetes就沒有像同期的各種“容器雲”項目,把Docker作為整個架構的核心,而是另辟蹊徑,
僅僅把它作為最底層的一個容器運作時實作
而Kubernetes着重解決的問題,則來自于Borg的研究人員在論文中提到的一個非常重要的觀點:
運作在大規模叢集中的各種任務之間,實際上存在着各種各樣的關系。這些關系的處理,才是作業編排和管理系統最困難的地方。
這種任務 <=>任務之間的關系随處可見
比如
- 一個Web應用與資料庫之間的通路關系
- 一個負載均衡器和它的後端服務之間的代理關系
- 一個門戶應用與授權元件之間的調用關系
而且同屬于一個服務機關的不同功能之間,也完全可能存在這樣的關系
- 一個Web應用與日志搜集元件之間的檔案交換關系。
而在容器技術普及之前,傳統虛拟機對這種關系的處理方法都比較“粗粒度”
- 很多功能并不相關應用被一鍋部署在同台虛拟機,隻是因為偶爾會互相發幾個HTTP請求!
- 更常見的情況則是,一個應用被部署在虛拟機裡之後,你還得手動維護很多跟它協作的守護程序(Daemon),用來處理它的日志搜集、災難恢複、資料備份等輔助工作。
在“功能機關”劃分上,容器卻有着獨到的“細粒度”優勢:
畢竟容器的本質,隻是一個程序而已
就是說,隻要你願意,那些原擠在同一VM裡的各個應用、元件、守護程序,都可被分别做成鏡像!
然後運作在一個一個專屬的容器中。
它們之間互不幹涉,擁有各自的資源配額,可以被排程在整個叢集裡的任何一台機器上。
而這,正是一個PaaS系統最理想的工作狀态,也是所謂
微服務思想得以落地的先決條件
。
如果隻做到 封裝微服務、排程單容器 這層次,Docker Swarm 就已經綽綽有餘了。
如果再加上Compose項目,甚至還具備了處理一些簡單依賴關系的能力
- 一個“Web容器”和它要通路的資料庫“DB容器”
在Compose項目中,你可以為這樣的兩個容器定義一個“link”,而Docker項目則會負責維護這個“link”關系
其具體做法是:Docker會在Web容器中,将DB容器的IP位址、端口等資訊以環境變量的方式注入進去,供應用程序使用,比如:
DB_NAME=/web/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
-
平台項目自動地處理容器間關系的典型例子
當DB容器發生變化時(比如鏡像更新,被遷移至其他主控端),這些環境變量的值會由Docker項目自動更新
可是,如果需求是,要求這個項目能夠處理前面提到的所有類型的關系,甚至能
支援未來可能的更多關系
這時,“link”這針對一種案例設計的解決方案就太過簡單了
一旦要追求項目的普适性,那就一定要從頂層開始做好設計
Kubernetes最主要的設計思想是,從更宏觀的角度,以統一的方式來定義任務之間的各種關系,并且為将來支援更多種類的關系留有餘地。
比如,Kubernetes對容器間的“通路”進行了分類,首先總結出了一類常見的“緊密互動”的關系,即:
- 應用之間需要非常頻繁的互動和通路
- 會直接通過本地檔案進行資訊交換
正常環境下的應用往往會被直接部在同一台機器,通過Localhost通信,通過本地磁盤目錄交換檔案
而在Kubernetes,這些容器會被劃分為一個“Pod”
Pod裡的容器共享同一個Network Namespace、同一組資料卷,進而達到高效率交換資訊的目的。
Pod是Kubernetes中最基礎的一個對象,源自于Google Borg論文中一個名叫Alloc的設計
Borg配置設定(配置設定的縮寫)是一台機器上可以運作一個或多個任務的資源的保留集。無論是否使用資源,資源都會保持配置設定狀态。 Alloc可用于為将來的任務留出資源,在停止任務和重新啟動任務之間保留資源,以及将來自不同作業的任務收集到同一台計算機上–例如,一個Web伺服器執行個體和一個關聯的logaver任務,用于複制伺服器的URL日志從本地磁盤記錄到分布式檔案系統。配置設定資源與機器資源的處理方式相似。在一個内部運作的多個任務共享其資源。如果必須将配置設定重定位到另一台計算機,則其任務将随之重新安排。
配置設定集就像一項工作:它是一組在多台機器上保留資源的配置設定。建立配置設定集後,可以送出一個或多個作業以在其中運作。為簡便起見,我們通常使用“任務”來指代配置設定或頂級任務(在配置設定外的一個),使用“作業”來指代作業或配置設定集。
而對于另外一種更為常見的需求,比如
Web應用與資料庫之間的通路關系
Kubernetes則提供了一種叫作“Service”的服務。像這樣的兩個應用,往往故意不部署在同一機器,即使Web應用所在的機器當機了,資料庫也不受影響。
可對于一個容器來說,它的IP位址等資訊是不固定的,Web應用又怎麼找到資料庫容器的Pod呢?
是以,Kubernetes的做法是給Pod綁定一個Service服務
Service服務聲明的IP位址等資訊是“終生不變”的。Service主要就是作為Pod的代理入口(Portal),進而代替Pod對外暴露一個固定的網絡位址。
這樣,對于Web應用的Pod來說,它需要關心的就是資料庫Pod的Service資訊。不難想象,Service後端真正代理的Pod的IP位址、端口等資訊的自動更新、維護,則是Kubernetes的職責。
圍繞着容器和Pod不斷向真實的技術場景擴充,我們就能夠摸索出一幅如下所示
- Kubernetes核心功能“全景圖”
Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考 - 從容器這個最基礎的概念出發,首先遇到了容器間“緊密協作”關系的難題,于是就擴充到了Pod
- 有了Pod之後,我們希望能一次啟動多個應用的執行個體,這樣就需要Deployment這個Pod的多執行個體管理器
- 而有了這樣一組相同的Pod後,我們又需要通過一個固定的IP位址和端口以負載均衡的方式通路它,于是就有了Service
如果現在
兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊
最典型的例子就是Web應用對資料庫通路時需要Credential(資料庫的使用者名和密碼)資訊。
那麼,在Kubernetes中這樣的關系又如何處理呢?
Kubernetes項目提供了一種叫作Secret的對象,是一個儲存在Etcd裡的鍵值對。
這樣,你把Credential資訊以Secret的方式存在Etcd裡,Kubernetes就會在你指定的Pod(比如,Web應用Pod)啟動時,自動把Secret裡的資料以Volume的方式挂載到容器裡。這樣,這個Web應用就可以通路資料庫了。
除了應用與應用之間的關系外,應用運作的形态是影響“如何容器化這個應用”的第二個重要因素。
為此,Kubernetes定義了基于Pod改進後的對象。比如
-
Job
描述一次性運作的Pod(比如,大資料任務)
-
DaemonSet
描述每個主控端上必須且隻能運作一個副本的守護程序服務
-
CronJob
描述定時任務
如此種種,正是Kubernetes定義容器間關系和形态的主要方法。
Kubernetes并沒有像其他項目那樣,為每一個管理功能建立一個指令,然後在項目中實作其中的邏輯
這種做法,的确可以解決目前的問題,但是在更多的問題來臨之後,往往會力不從心
相比之下,在Kubernetes中,我們推崇
- 首先,通過一個“編排對象”,比如Pod/Job/CronJob等描述你試圖管理的應用
- 然後,再為它定義一些“服務對象”,比如Service/Secret/Horizontal Pod Autoscaler(自動水準擴充器)等。這些對象,會負責具體的平台級功能。
這種使用方法,就是所謂的“聲明式API”。這種API對應的“編排對象”和“服務對象”,都是Kubernetes中的API對象(API Object)。
這就是Kubernetes最核心的設計理念,也是接下來我會重點剖析的關鍵技術點。
Kubernetes如何啟動一個容器化任務
現在已經制作好了一個Nginx容器鏡像,希望讓平台幫我啟動這個鏡像。并且,我要求平台幫我運作兩個完全相同的Nginx副本,以負載均衡的方式共同對外提供服務。
如果DIY,可能需要啟動兩台虛拟機,分别安裝兩個Nginx,然後使用keepalived為這兩個虛拟機做一個虛拟IP。
而如果使用Kubernetes呢?要做的則是編寫如下這樣一個YAML檔案(比如名叫nginx-deployment.yaml):
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
在上面這個YAML檔案中,定義了一個Deployment對象,它的主體部分(spec.template部分)是一個使用Nginx鏡像的Pod,而這個Pod的副本數是2(replicas=2)。
然後執行:
$ kubectl create -f nginx-deployment.yaml
這樣,兩個完全相同的Nginx容器副本就被啟動了。
不過,這麼看來,做同樣一件事情,Kubernetes使用者要做的工作也不少啊
别急,在後續會陸續介紹Kubernetes這種“聲明式API”的好處,以及基于它實作的強大的編排能力。
總結
首先,一起回顧了容器的核心知識,說明了容器其實可以分為兩個部分
- 容器運作時
- 容器鏡像
然後,重點介紹了Kubernetes的架構,詳細講解了它如何使用“聲明式API”來描述容器化業務和容器間關系的設計思想。
排程
過去很多的叢集管理項目(比如Yarn、Mesos,以及Swarm)所擅長的,都是把一個容器,按照某種規則,放置在某個最佳節點上運作起來。這種功能,稱為“排程”。
編排
而Kubernetes所擅長的,是按照使用者的意願和整個系統的規則,完全自動化地處理好容器之間的各種關系。
這種功能,就是我們經常聽到的一個概念:編排。
是以說,Kubernetes的本質,是為使用者提供一個具有普遍意義的容器編排工具。
Kubernetes為使用者提供的不僅限于一個工具。它真正的價值,還是在于提供了一套基于容器建構分布式系統的基礎依賴
參考
- 深入剖析Kubernetes
- Large-scale cluster management at Google with Borg