在前面以Docker項目為例,一步步剖析了Linux容器的具體實現(xiàn)方式偶垮。
通過這些應該明白:一個“容器”铡俐,實際上是一個由Linux Namespace、Linux Cgroups和rootfs三種技術構建出來的進程的隔離環(huán)境脯倚。
一個正在運行的Linux容器,其實可以被看做
- 一組聯(lián)合掛載在 /var/lib/docker/aufs/mnt 上的rootfs嵌屎,這部分稱為“容器鏡像”(Container Image)推正,是容器的靜態(tài)視圖
- 一個由Namespace+Cgroups構成的隔離環(huán)境,這部分稱為“容器運行時”(Container Runtime)宝惰,是容器的動態(tài)視圖植榕。
作為一名開發(fā)者,我并不關心容器運行時的差異尼夺。
因為尊残,在整個“開發(fā)-測試-發(fā)布”的流程中,真正承載著容器信息進行傳遞的淤堵,是容器鏡像寝衫,而不是容器運行時。
這個重要假設拐邪,正是容器技術圈在Docker項目成功后不久慰毅,就迅速走向了“容器編排”這個“上層建筑”的主要原因:
作為云基礎設施提供商,只要能夠將用戶提交的Docker鏡像以容器的方式運行扎阶,就能成為容器生態(tài)圖上的一個承載點汹胃,從而將整個容器技術棧上的價值婶芭,沉淀在這個節(jié)點上。
更重要的是统台,只要從我這個承載點向Docker鏡像制作者和使用者方向回溯,整條路徑上的各個服務節(jié)點
比如CI/CD啡邑、監(jiān)控贱勃、安全、網(wǎng)絡谤逼、存儲等贵扰,都有我可以發(fā)揮和盈利的余地。
這個邏輯流部,正是所有云計算提供商如此熱衷于容器技術的重要原因:通過容器鏡像戚绕,它們可以和開發(fā)者關聯(lián)起來。
從一個開發(fā)者和單一的容器鏡像枝冀,到無數(shù)開發(fā)者和龐大容器集群舞丛,容器技術實現(xiàn)了從“容器”到“容器云”的飛躍,標志著它真正得到了市場和生態(tài)的認可果漾。
容器從一個開發(fā)者手里的小工具球切,一躍成為了云計算領域的絕對主角 而能夠定義容器組織和管理規(guī)范的“容器編排”技術,坐上了容器技術領域的“頭把交椅”
最具代表性的容器編排工具绒障,當屬
- Docker公司的Compose+Swarm組合
- Google與RedHat公司共同主導的Kubernetes項目
在前面介紹容器技術發(fā)展歷史中吨凑,已經(jīng)對這兩個開源項目做了詳細地剖析和評述。所以户辱,在今天專注于主角Kubernetes項目鸵钝,談一談它的設計與架構。
跟很多基礎設施領域先有工程實踐庐镐、后有方法論的發(fā)展路線不同恩商,Kubernetes項目的理論基礎則要比工程實踐走得靠前得多,這當然要歸功于Google公司在2015年4月發(fā)布的Borg論文了必逆。
Borg系統(tǒng)痕届,一直以來都被譽為Google公司內(nèi)部最強大的“秘密武器”。
相比于Spanner末患、BigTable等相對上層的項目研叫,Borg要承擔的責任,是承載Google整個基礎設施的核心依賴璧针。
在Google已經(jīng)公開發(fā)表的基礎設施體系論文中嚷炉,Borg項目當仁不讓地位居整個基礎設施技術棧的最底層。
這幅圖探橱,來自于Google Omega論文的第一作者的博士畢業(yè)論文申屹。它描繪了當時Google已經(jīng)公開發(fā)表的整個基礎設施棧绘证。在這個圖里,你既可以找到MapReduce哗讥、BigTable等知名項目嚷那,也能看到Borg和它的繼任者Omega位于整個技術棧的最底層。
正是由于這樣的定位杆煞,Borg可以說是Google最不可能開源的一個項目魏宽。
得益于Docker項目和容器技術的風靡,它卻終于得以以另一種方式與開源社區(qū)見面决乎,就是Kubernetes項目队询。
相比于“小打小鬧”的Docker公司、“舊瓶裝新酒”的Mesos社區(qū)构诚,Kubernetes項目從一開始就比較幸運地站上了一個他人難以企及的高度:
在它的成長階段蚌斩,這個項目每一個核心特性的提出,幾乎都脫胎于Borg/Omega系統(tǒng)的設計與經(jīng)驗范嘱。
更重要的是送膳,這些特性在開源社區(qū)落地的過程中,又在整個社區(qū)的合力之下得到了極大的改進丑蛤,修復了很多當年遺留在Borg體系中的缺陷和問題肠缨。
盡管在發(fā)布之初被批“曲高和寡”,但在逐漸覺察到Docker技術棧的“稚嫩”和Mesos社區(qū)的“老邁”盏阶,社區(qū)很快就明白了:Kubernetes項目在Borg體系的指導下晒奕,體現(xiàn)出了一種獨有的先進與完備性,這些才是一個基礎設施領域開源項目的核心價值名斟。
從Kubernetes的頂層設計說起脑慧。
Kubernetes要解決什么?
編排砰盐?調(diào)度闷袒?容器云?還是集群管理岩梳?
至今其實都沒有標準答案囊骤。在不同的發(fā)展階段,Kubernetes需要著力的問題是不同的冀值。
但對于大多數(shù)用戶也物,他們希望Kubernetes項目帶來的體驗是確定的:
現(xiàn)在我有應用的容器鏡像,請幫我在一個給定的集群上把應用運行起來
更進一步說列疗,還希望Kubernetes能給我提供路由網(wǎng)關滑蚯、水平擴展、監(jiān)控、備份告材、災難恢復等一系列運維能力坤次。
這不就是經(jīng)典PaaS(eg. Cloud Foundry)項目的能力嗎!
而且,有了Docker后斥赋,根本不需要什么Kubernetes缰猴、PaaS,只要使用Docker公司的Compose+Swarm項目疤剑,就完全可以很方便DIY出這些功能滑绒!
所以說,如果Kubernetes項目只是停留在拉取用戶鏡像骚露、運行容器蹬挤,以及提供常見的運維功能的話缚窿,那別說跟嫡系的Swarm競爭棘幸,哪怕跟經(jīng)典的PaaS項目相比也難有優(yōu)勢
而實際上,在定義核心功能過程中倦零,Kubernetes項目正是依托著Borg項目的理論優(yōu)勢
才在短短幾個月內(nèi)迅速站穩(wěn)了腳跟
- 進而確定了一個如下所示的全局架構
可以看到误续,Kubernetes項目的架構,跟它的原型項目Borg類似扫茅,都由Master和Node兩種節(jié)點組成蹋嵌,分別對應著控制節(jié)點和計算節(jié)點。
其中葫隙,控制節(jié)點 --- 即Master節(jié)點栽烂,由三個緊密協(xié)作的獨立組件組合而成,它們分別是
- 負責API服務的kube-apiserver
- 負責調(diào)度的kube-scheduler
- 負責容器編排的kube-controller-manager
整個集群的持久化數(shù)據(jù)恋脚,由kube-apiserver處理后保存在Ectd
而計算節(jié)點上最核心的是
kubelet組件
在Kubernetes中腺办,kubelet主要負責同容器運行時(比如Docker項目)打交道。
而這個交互所依賴的糟描,是一個稱作CRI(Container Runtime Interface) 的遠程調(diào)用接口怀喉,這個接口定義了容器運行時的各項核心操作,比如:啟動一個容器需要的所有參數(shù)船响。
這也是為何躬拢,Kubernetes項目并不關心你部署的是什么容器運行時、使用的什么技術實現(xiàn)见间,只要你的這個容器運行時能夠運行標準的容器鏡像聊闯,它就可以通過實現(xiàn)CRI接入到Kubernetes項目當中。
而具體的容器運行時米诉,比如Docker項目馅袁,則一般通過OCI這個容器運行時規(guī)范同底層的Linux操作系統(tǒng)進行交互,即:把CRI請求翻譯成對Linux操作系統(tǒng)的調(diào)用(操作Linux Namespace和Cgroups等)荒辕。
此外汗销,kubelet還通過gRPC協(xié)議同一個叫作Device Plugin的插件進行交互犹褒。
這個插件,是Kubernetes項目用來管理GPU等宿主機物理設備的主要組件弛针,也是基于Kubernetes項目進行機器學習訓練叠骑、高性能作業(yè)支持等工作必須關注的功能。
而kubelet的另一個重要功能削茁,則是調(diào)用網(wǎng)絡插件和存儲插件為容器配置網(wǎng)絡和持久化存儲宙枷。
這兩個插件與kubelet進行交互的接口,分別是
- CNI(Container Networking Interface)
- CSI(Container Storage Interface)茧跋。
實際上慰丛,kubelet這個奇怪的名字,來自于Borg項目里的同源組件Borglet瘾杭。
不過诅病,如果你瀏覽過Borg論文的話,就會發(fā)現(xiàn)粥烁,這個命名方式可能是kubelet組件與Borglet組件的唯一相似之處贤笆。因為Borg項目,并不支持我們這里所講的容器技術讨阻,而只是簡單地使用了Linux Cgroups對進程進行限制芥永。
這就意味著,像Docker這樣的“容器鏡像”在Borg中是不存在的钝吮,Borglet組件也自然不需要像kubelet這樣考慮如何同Docker進行交互埋涧、如何對容器鏡像進行管理的問題,也不需要支持CRI奇瘦、CNI棘催、CSI等諸多容器技術接口。
kubelet完全就是為了實現(xiàn)Kubernetes項目對容器的管理能力而重新實現(xiàn)的一個組件链患,與Borg之間并沒有直接的傳承關系巧鸭。
雖然不使用Docker,但Google內(nèi)部確實在使用一個包管理工具麻捻,名叫Midas Package Manager (MPM)纲仍,其實它可以部分取代Docker鏡像的角色。
Borg對于Kubernetes項目的指導作用又體現(xiàn)在哪里呢贸毕?
Master節(jié)點!
雖然在Master節(jié)點的實現(xiàn)細節(jié)上Borg與Kubernetes不盡相同郑叠,但出發(fā)點高度一致
即:如何編排、管理明棍、調(diào)度用戶提交的作業(yè)
所以乡革,Borg項目完全可以把Docker鏡像看做是一種新的應用打包方式。
這樣,Borg團隊過去在大規(guī)模作業(yè)管理與編排上的經(jīng)驗就可以直接“套”在Kubernetes項目沸版。
這些經(jīng)驗最主要的表現(xiàn)就是嘁傀,從一開始,Kubernetes就沒有像同期的各種“容器云”項目视粮,把Docker作為整個架構的核心细办,而是另辟蹊徑, 僅僅把它作為最底層的一個容器運行時實現(xiàn)
而Kubernetes著重解決的問題,則來自于Borg的研究人員在論文中提到的一個非常重要的觀點:
運行在大規(guī)模集群中的各種任務之間蕾殴,實際上存在著各種各樣的關系笑撞。這些關系的處理,才是作業(yè)編排和管理系統(tǒng)最困難的地方钓觉。
這種任務 <=>任務之間的關系隨處可見
比如
- 一個Web應用與數(shù)據(jù)庫之間的訪問關系
- 一個負載均衡器和它的后端服務之間的代理關系
- 一個門戶應用與授權組件之間的調(diào)用關系
而且同屬于一個服務單位的不同功能之間茴肥,也完全可能存在這樣的關系
比如
- 一個Web應用與日志搜集組件之間的文件交換關系。
而在容器技術普及之前荡灾,傳統(tǒng)虛擬機對這種關系的處理方法都比較“粗粒度”
- 很多功能并不相關應用被一鍋部署在同臺虛擬機瓤狐,只是因為偶爾會互相發(fā)幾個HTTP請求!
- 更常見的情況則是,一個應用被部署在虛擬機里之后卧晓,你還得手動維護很多跟它協(xié)作的守護進程(Daemon)芬首,用來處理它的日志搜集赴捞、災難恢復逼裆、數(shù)據(jù)備份等輔助工作。
在“功能單位”劃分上赦政,容器卻有著獨到的“細粒度”優(yōu)勢:
畢竟容器的本質(zhì)胜宇,只是一個進程而已
就是說,只要你愿意恢着,那些原擠在同一VM里的各個應用桐愉、組件、守護進程掰派,都可被分別做成鏡像!
然后運行在一個一個專屬的容器中从诲。
它們之間互不干涉,擁有各自的資源配額靡羡,可以被調(diào)度在整個集群里的任何一臺機器上系洛。
而這,正是一個PaaS系統(tǒng)最理想的工作狀態(tài)略步,也是所謂微服務思想得以落地的先決條件
描扯。
如果只做到 封裝微服務、調(diào)度單容器 這層次趟薄,Docker Swarm 就已經(jīng)綽綽有余了绽诚。
如果再加上Compose項目,甚至還具備了處理一些簡單依賴關系的能力
比如
- 一個“Web容器”和它要訪問的數(shù)據(jù)庫“DB容器”
在Compose項目中,你可以為這樣的兩個容器定義一個“l(fā)ink”恩够,而Docker項目則會負責維護這個“l(fā)ink”關系
其具體做法是:Docker會在Web容器中卒落,將DB容器的IP地址、端口等信息以環(huán)境變量的方式注入進去蜂桶,供應用進程使用导绷,比如:
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容器發(fā)生變化時(比如鏡像更新,被遷移至其他宿主機)屎飘,這些環(huán)境變量的值會由Docker項目自動更新
可是妥曲,如果需求是,要求這個項目能夠處理前面提到的所有類型的關系钦购,甚至能
支持未來可能的更多關系
這時檐盟,“l(fā)ink”這針對一種案例設計的解決方案就太過簡單了
一旦要追求項目的普適性,那就一定要從頂層開始做好設計
Kubernetes最主要的設計思想是押桃,從更宏觀的角度葵萎,以統(tǒng)一的方式來定義任務之間的各種關系,并且為將來支持更多種類的關系留有余地唱凯。
比如羡忘,Kubernetes對容器間的“訪問”進行了分類,首先總結出了一類常見的“緊密交互”的關系磕昼,即:
- 應用之間需要非常頻繁的交互和訪問
- 會直接通過本地文件進行信息交換
常規(guī)環(huán)境下的應用往往會被直接部在同一臺機器卷雕,通過Localhost通信,通過本地磁盤目錄交換文件
而在Kubernetes票从,這些容器會被劃分為一個“Pod”
Pod里的容器共享同一個Network Namespace漫雕、同一組數(shù)據(jù)卷,從而達到高效率交換信息的目的峰鄙。
Pod是Kubernetes中最基礎的一個對象浸间,源自于Google Borg論文中一個名叫Alloc的設計
Borg分配(分配的縮寫)是一臺機器上可以運行一個或多個任務的資源的保留集。無論是否使用資源吟榴,資源都會保持分配狀態(tài)魁蒜。 Alloc可用于為將來的任務留出資源,在停止任務和重新啟動任務之間保留資源吩翻,以及將來自不同作業(yè)的任務收集到同一臺計算機上–例如兜看,一個Web服務器實例和一個關聯(lián)的logaver任務,用于復制服務器的URL日志從本地磁盤記錄到分布式文件系統(tǒng)仿野。分配資源與機器資源的處理方式相似铣减。在一個內(nèi)部運行的多個任務共享其資源。如果必須將分配重定位到另一臺計算機脚作,則其任務將隨之重新安排葫哗。
分配集就像一項工作:它是一組在多臺機器上保留資源的分配缔刹。創(chuàng)建分配集后,可以提交一個或多個作業(yè)以在其中運行劣针。為簡便起見校镐,我們通常使用“任務”來指代分配或頂級任務(在分配外的一個),使用“作業(yè)”來指代作業(yè)或分配集捺典。
而對于另外一種更為常見的需求鸟廓,比如
Web應用與數(shù)據(jù)庫之間的訪問關系
Kubernetes則提供了一種叫作“Service”的服務。像這樣的兩個應用襟己,往往故意不部署在同一機器引谜,即使Web應用所在的機器宕機了,數(shù)據(jù)庫也不受影響擎浴。
可對于一個容器來說员咽,它的IP地址等信息是不固定的,Web應用又怎么找到數(shù)據(jù)庫容器的Pod呢贮预?
所以贝室,Kubernetes的做法是給Pod綁定一個Service服務
Service服務聲明的IP地址等信息是“終生不變”的。Service主要就是作為Pod的代理入口(Portal)仿吞,從而代替Pod對外暴露一個固定的網(wǎng)絡地址滑频。
這樣,對于Web應用的Pod來說唤冈,它需要關心的就是數(shù)據(jù)庫Pod的Service信息峡迷。不難想象,Service后端真正代理的Pod的IP地址务傲、端口等信息的自動更新凉当、維護枣申,則是Kubernetes的職責售葡。
圍繞著容器和Pod不斷向真實的技術場景擴展,我們就能夠摸索出一幅如下所示
-
Kubernetes核心功能“全景圖”
從容器這個最基礎的概念出發(fā)忠藤,首先遇到了容器間“緊密協(xié)作”關系的難題挟伙,于是就擴展到了Pod
有了Pod之后,我們希望能一次啟動多個應用的實例模孩,這樣就需要Deployment這個Pod的多實例管理器
而有了這樣一組相同的Pod后尖阔,我們又需要通過一個固定的IP地址和端口以負載均衡的方式訪問它,于是就有了Service
如果現(xiàn)在
兩個不同Pod之間不僅有“訪問關系”榨咐,還要求在發(fā)起時加上授權信息
最典型的例子就是Web應用對數(shù)據(jù)庫訪問時需要Credential(數(shù)據(jù)庫的用戶名和密碼)信息介却。
那么,在Kubernetes中這樣的關系又如何處理呢块茁?
Kubernetes項目提供了一種叫作Secret的對象齿坷,是一個保存在Etcd里的鍵值對桂肌。
這樣,你把Credential信息以Secret的方式存在Etcd里永淌,Kubernetes就會在你指定的Pod(比如崎场,Web應用Pod)啟動時,自動把Secret里的數(shù)據(jù)以Volume的方式掛載到容器里遂蛀。這樣谭跨,這個Web應用就可以訪問數(shù)據(jù)庫了。
除了應用與應用之間的關系外李滴,應用運行的形態(tài)是影響“如何容器化這個應用”的第二個重要因素螃宙。
為此,Kubernetes定義了基于Pod改進后的對象所坯。比如
- Job
描述一次性運行的Pod(比如污呼,大數(shù)據(jù)任務) - DaemonSet
描述每個宿主機上必須且只能運行一個副本的守護進程服務 - CronJob
描述定時任務
如此種種,正是Kubernetes定義容器間關系和形態(tài)的主要方法包竹。
Kubernetes并沒有像其他項目那樣燕酷,為每一個管理功能創(chuàng)建一個指令,然后在項目中實現(xiàn)其中的邏輯
這種做法周瞎,的確可以解決當前的問題苗缩,但是在更多的問題來臨之后,往往會力不從心
相比之下声诸,在Kubernetes中酱讶,我們推崇
- 首先,通過一個“編排對象”彼乌,比如Pod/Job/CronJob等描述你試圖管理的應用
- 然后泻肯,再為它定義一些“服務對象”,比如Service/Secret/Horizontal Pod Autoscaler(自動水平擴展器)等慰照。這些對象灶挟,會負責具體的平臺級功能。
這種使用方法毒租,就是所謂的“聲明式API”稚铣。這種API對應的“編排對象”和“服務對象”,都是Kubernetes中的API對象(API Object)墅垮。
這就是Kubernetes最核心的設計理念惕医,也是接下來我會重點剖析的關鍵技術點。
Kubernetes如何啟動一個容器化任務
現(xiàn)在已經(jīng)制作好了一個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的副本數(shù)是2(replicas=2)毛俏。
然后執(zhí)行:
$ kubectl create -f nginx-deployment.yaml
這樣炭庙,兩個完全相同的Nginx容器副本就被啟動了。
不過煌寇,這么看來焕蹄,做同樣一件事情,Kubernetes用戶要做的工作也不少啊
別急阀溶,在后續(xù)會陸續(xù)介紹Kubernetes這種“聲明式API”的好處腻脏,以及基于它實現(xiàn)的強大的編排能力。
總結
首先银锻,一起回顧了容器的核心知識永品,說明了容器其實可以分為兩個部分
- 容器運行時
- 容器鏡像
然后,重點介紹了Kubernetes的架構击纬,詳細講解了它如何使用“聲明式API”來描述容器化業(yè)務和容器間關系的設計思想鼎姐。
調(diào)度
過去很多的集群管理項目(比如Yarn、Mesos更振,以及Swarm)所擅長的炕桨,都是把一個容器,按照某種規(guī)則肯腕,放置在某個最佳節(jié)點上運行起來献宫。這種功能,稱為“調(diào)度”乎芳。
編排
而Kubernetes所擅長的遵蚜,是按照用戶的意愿和整個系統(tǒng)的規(guī)則,完全自動化地處理好容器之間的各種關系奈惑。
這種功能,就是我們經(jīng)常聽到的一個概念:編排睡汹。
所以說肴甸,Kubernetes的本質(zhì),是為用戶提供一個具有普遍意義的容器編排工具囚巴。
Kubernetes為用戶提供的不僅限于一個工具原在。它真正的價值友扰,還是在于提供了一套基于容器構建分布式系統(tǒng)的基礎依賴
參考
- 深入剖析Kubernetes
- Large-scale cluster management at Google with Borg