容器和Kubernetes的應(yīng)用與開發(fā)
容器就是新的進(jìn)程
讓我們從計(jì)算機(jī)開聊。 當(dāng)計(jì)算機(jī)啟動(dòng)時(shí),它會(huì)運(yùn)行一個(gè)叫init的程序,然后init會(huì)啟動(dòng)其他所需的程序:服務(wù)器、終端、窗口管理器等。 Init能做幾件有趣的事情, 例如讓一個(gè)程序開機(jī)啟動(dòng), 隔一段時(shí)間運(yùn)行一個(gè)程序, 還有確保一個(gè)程序沒有失敗或者crash,如果有就重啟它。 正在運(yùn)行的程序可以看到這臺(tái)機(jī)器上的所有東西: 其它在運(yùn)行的程序,所有的文件,以及網(wǎng)絡(luò)。

多個(gè)進(jìn)程同時(shí)跑在一臺(tái)計(jì)算機(jī)上。所有的進(jìn)程可以自由的互相之間交互,或者與常規(guī)的資源交互。
通過將進(jìn)程進(jìn)行劃分, 程序員可以有一個(gè)更加簡(jiǎn)單的模型來方便理解, 所以創(chuàng)建命名空間(namespace)的工具也被開發(fā)出來了。 程序或者進(jìn)程只能看到運(yùn)行在同一個(gè)命名空間下的其他進(jìn)程。 如果它們尋找文件,那么只能看見硬盤上分配到這個(gè)命名空間的那一部分。 從安全的角度而言,一個(gè)命名空間里面的某個(gè)進(jìn)程被黑掉了影響的僅僅也只是這個(gè)命名空間而已。
類似于Docker和Rkt這樣的工具被開發(fā)出來以后使得我們能系統(tǒng)化地使用這些特性。 這些工具提供了打包的功能,將一個(gè)命名空間打包成一個(gè)容器,使得我們可以很方便的將它搬到另一臺(tái)機(jī)器上運(yùn)行,不出意外的它會(huì)跟之前完全一致的方式繼續(xù)運(yùn)行,因?yàn)樗旧淼母綦x特性。 事實(shí)上,通??梢院苋菀椎膶⑷萜飨胂鬄榭梢酝耆?dú)立的運(yùn)行的小計(jì)算機(jī). 因?yàn)檫@些新的工具非常易用,它們漸漸成為一種流行的構(gòu)建軟件方式。
容器就是新的進(jìn)程。

容器中的進(jìn)程。 在這里,一個(gè)進(jìn)程僅僅能夠與所在同一個(gè)容器里面的其他進(jìn)程和資源交互。
擴(kuò)展: 一個(gè)好“難題”
一臺(tái)計(jì)算機(jī)的資源是有限的,而且同時(shí)僅能處理有限的數(shù)據(jù)和運(yùn)行有限的進(jìn)程。 當(dāng)面臨增長(zhǎng)的負(fù)載時(shí)(比如更多用戶,更大的數(shù)據(jù)集)一個(gè)簡(jiǎn)單的應(yīng)對(duì)方式是垂直擴(kuò)展,也即是增加更多的處理能力和內(nèi)存給到這臺(tái)計(jì)算機(jī),但是很快這個(gè)代價(jià)就會(huì)非常昂貴,而且本身擴(kuò)展的空間也相當(dāng)有限。 另一種方式就是通過增加更多的計(jì)算機(jī)來水平擴(kuò)展。 這些計(jì)算機(jī)一起就組成了集群。
為了能跑在集群上,應(yīng)用也需要以不同的方式架構(gòu)。 例如,如果我們確認(rèn)同一個(gè)程序的兩份拷貝可以不需要訪問對(duì)方的數(shù)據(jù)就能運(yùn)行,那么我們就能放心的將它的多份拷貝放到不同的計(jì)算機(jī)上運(yùn)行。

水平擴(kuò)展:在這里集群里,三臺(tái)計(jì)算機(jī)每臺(tái)運(yùn)行兩個(gè)容器。 一共有兩個(gè)app server的實(shí)例來處理大的負(fù)載。
雖然容器本身并沒有給我們?nèi)魏纹渌墓ぞ邅順?gòu)建分布式應(yīng)用,但是考慮一下這個(gè)級(jí)別上的抽象能讓構(gòu)建集群的應(yīng)用方便一些。容器模型所鼓勵(lì)的假設(shè)情形是:
- 可以有多份拷貝同時(shí)運(yùn)行(架構(gòu)要考慮并發(fā)性)。
- 容器可以在集群中的任意一臺(tái)機(jī)器上動(dòng)態(tài)啟動(dòng)和停止(***是無狀態(tài)或者臨時(shí)的)。
- 計(jì)算機(jī)或者進(jìn)程可能會(huì)在任意的時(shí)間點(diǎn)失敗或者不可用但是整個(gè)系統(tǒng)仍然保持工作(架構(gòu)要考慮失敗和恢復(fù))。
由于在集群里面有這么多的計(jì)算機(jī)要管理,我們面臨一些額外挑戰(zhàn):
- 首先,我們需要管理計(jì)算機(jī)上的資源,比如處理能力和存儲(chǔ)。這意味著我們不得不有效地分發(fā)和調(diào)度進(jìn)程到不同的計(jì)算機(jī)上去執(zhí)行。
- 我們也需要“親和性”和方法將相關(guān)的進(jìn)程放在一起跑,以便高效利用共享存儲(chǔ);而同時(shí)“反親和性”的要求又需要保證對(duì)同一個(gè)資源有競(jìng)爭(zhēng)性的進(jìn)程不能運(yùn)行在同一臺(tái)機(jī)器上。例如,如果我們想要將應(yīng)用服務(wù)器的進(jìn)程跑兩份來服務(wù)兩倍的請(qǐng)求,我們可能希望他們跑在集群里兩臺(tái)不同的服務(wù)器上。
- 當(dāng)許多的進(jìn)程跑在不同的地方時(shí),我們需要一種方式讓他們互相發(fā)現(xiàn)和溝通。我們只需要某個(gè)進(jìn)程運(yùn)行所在的機(jī)器ip就可以與這個(gè)進(jìn)程通信。
在只有一臺(tái)計(jì)算機(jī)的時(shí)候,只有一個(gè)ip地址就可以了。 在有多個(gè)計(jì)算機(jī)之后,我們需要維護(hù)一個(gè)進(jìn)程到ip的映射,例如像etcd這樣的分布式數(shù)據(jù)庫。 當(dāng)一個(gè)進(jìn)程在一臺(tái)機(jī)器上啟動(dòng)時(shí),這個(gè)信息就被加入到數(shù)據(jù)庫中。 如果進(jìn)程掛掉或者機(jī)器宕機(jī),也需要將這個(gè)條目從數(shù)據(jù)庫中刪除。
程序員對(duì)于開發(fā)跑在一臺(tái)計(jì)算機(jī)上的應(yīng)用很得心應(yīng)手了。 理想狀態(tài)下,我們想要的是有一個(gè)工具能將集群里面所有的計(jì)算機(jī)管理起來,而展現(xiàn)給程序員的就像一臺(tái)“巨型”的計(jì)算機(jī)。
這個(gè)方向上的一個(gè)進(jìn)展是CoreOS的Fleet項(xiàng)目,它的基本思想就是像一臺(tái)計(jì)算機(jī)上的init進(jìn)程那樣延伸做整個(gè)集群的init。
Google 貢獻(xiàn)的Kubernetes項(xiàng)目則讓我們更加接近我們想要一臺(tái)”巨型”計(jì)算機(jī)的模型。
Kubernetes:pod就是新的計(jì)算機(jī)
Kubernetes做的***件事情就是拿走你的所有計(jì)算機(jī),然后還回給你一個(gè)”巨型”計(jì)算機(jī)--一個(gè)Kubernetes的集群。
一個(gè)Kubernetes的pod指定一組需要運(yùn)行Docker或者rkt容器。
之前我們描述的是一個(gè)集群里面不同計(jì)算機(jī)上跑著不同進(jìn)程,現(xiàn)在我們看到的是Kubernetes集群里面的不同pod里跑著不同進(jìn)程。

一個(gè)Kubernetes集群圍繞著pod也就是容器組構(gòu)建了一個(gè)模型. 這些pod基于資源和”親和度”的約束被動(dòng)態(tài)分配到底層節(jié)點(diǎn)上。
之前,我們考慮的是什么進(jìn)程需要在一臺(tái)機(jī)器上一起運(yùn)行。 現(xiàn)在,我們考慮將哪些進(jìn)程組構(gòu)造成什么pod;pod已經(jīng)成為一種優(yōu)美的方式來對(duì)一個(gè)應(yīng)用的一個(gè)功能單元構(gòu)造模型。我們甚至可以直接使用社區(qū)構(gòu)造的pod,直接將他們跑起來,例如日志和監(jiān)控。
一個(gè)pod里面的所有進(jìn)程跑在同一臺(tái)機(jī)器上,這樣解決了類似掛載磁盤這樣的資源共享的問題。 背后是Kubernetes將pod分配到不同的計(jì)算節(jié)點(diǎn)也就是kubernetes node上,我們可以給pod或者node設(shè)置發(fā)生的條件例如資源約束、親和性等。
計(jì)算機(jī)就是資源的集合:計(jì)算能力、內(nèi)存、磁盤和網(wǎng)絡(luò)接口。與之類似,一個(gè)pod可以從底層的資源池中分配一定量的資源. 它也會(huì)有自己的網(wǎng)卡和pod所在的虛擬網(wǎng)絡(luò)的ip。所以,pod就是新的計(jì)算機(jī)。
如果我們需要某個(gè)特定功能進(jìn)行擴(kuò)展,我們只需要在集群中多跑幾個(gè)這個(gè)pod的拷貝。 當(dāng)硬件不足,我們就往集群里面增加更多的計(jì)算和存儲(chǔ)。 通過將資源與它所承載的功能解耦,調(diào)度器可以保證所有的可用資源會(huì)被盡可能高效利用。
Kubernetes復(fù)制控制器用來保證任意時(shí)間某個(gè)pod的一定數(shù)量的拷貝在運(yùn)行。 就像一個(gè)分布式的init,如果一個(gè)pod掛了: 起因可能是里面的一個(gè)進(jìn)程失敗了,或者pod 的依賴掛了,或者它所在的節(jié)點(diǎn)down了; kubernetes會(huì)探測(cè)到并在另一個(gè)可用的節(jié)點(diǎn)上啟動(dòng)一個(gè)新的拷貝。
一個(gè)Kubernetes的service會(huì)跟蹤集群里某種特定type的pod的所有實(shí)例。 例如,我們有一個(gè)ap server service,它會(huì)跟蹤cluster里面所有的app server的pod。service是一個(gè)非常簡(jiǎn)便的抽象;我們的應(yīng)用可以非??斓恼业侥撤N類型服務(wù)的所有功能單元然后將工作分發(fā)給他們。

一個(gè)完整的Kubernetes集群圖
Pod被動(dòng)態(tài)分配到節(jié)點(diǎn)上。 每一種pod對(duì)應(yīng)的服務(wù)都有服務(wù)發(fā)現(xiàn)和負(fù)載均衡,同時(shí)也描繪了pod和服務(wù)的虛擬網(wǎng)絡(luò)。
Kubernetes既是一個(gè)在集群里面管理和調(diào)度進(jìn)程的框架,也是一種構(gòu)建應(yīng)用的新的思維模型,基于的是pod里面的進(jìn)程分組和service所提供的服務(wù)發(fā)現(xiàn)。
整個(gè)生態(tài)以及未來發(fā)展
管理一臺(tái)計(jì)算機(jī)已經(jīng)是一個(gè)難題了。 管理一大群互相通訊的機(jī)器更是復(fù)雜得多. 感謝發(fā)明了像Docker、Kubernetes這樣非凡工具的好心人,我們現(xiàn)在有了容器這樣的簡(jiǎn)單模型,也有工具將集群管理起來就像一臺(tái)計(jì)算機(jī)。 構(gòu)建可擴(kuò)展的應(yīng)用也從沒像現(xiàn)在這樣如此簡(jiǎn)單。
容器和集群管理軟件業(yè)也影響了人們構(gòu)建應(yīng)用的方式。 他們創(chuàng)造了新的模式和抽象,很多的可能性仍在探索中, 例如, 使用容器來構(gòu)建可重用的應(yīng)用組件或者庫可能也會(huì)很有意思。 在Hasura,我們正為數(shù)據(jù)庫、搜索、用戶管理、文件管理等等創(chuàng)建組件,構(gòu)建應(yīng)用就只需將它們快速組裝起來。
總的來說,在追求創(chuàng)造更簡(jiǎn)模型的道路上我們已經(jīng)前進(jìn)了一大步。 當(dāng)今的所有軟件本質(zhì)就是運(yùn)行代碼,執(zhí)行功能。 從這個(gè)角度,我們做的所有的事情僅僅是管理這些功能:將它們分組,運(yùn)行它們的多份拷貝,找到并與它們交互,然后處理失敗的情況。 由此推出一個(gè)邏輯結(jié)論, 或許某一天我們會(huì)有這樣一個(gè)系統(tǒng),我們只需要描述我們需要的功能,余下的交給系統(tǒng)按照描述完成即可。 那確實(shí)是求之不得啊!