在前面以Docker項目爲例,一步步剖析了Linux容器的具體實現方式。
經過這些應該明白:一個「容器」,其實是一個由Linux Namespace、Linux Cgroups和rootfs三種技術構建出來的進程的隔離環境。java
一個正在運行的Linux容器,其實能夠被看作nginx
做爲一名開發者,我並不關心容器運行時的差別。
由於,在整個「開發-測試-發佈」的流程中,真正承載着容器信息進行傳遞的,是容器鏡像,而不是容器運行時。web
這個重要假設,正是容器技術圈在Docker項目成功後不久,就迅速走向了「容器編排」這個「上層建築」的主要緣由:
做爲雲基礎設施提供商,只要可以將用戶提交的Docker鏡像以容器的方式運行,就能成爲容器生態圖上的一個承載點,從而將整個容器技術棧上的價值,沉澱在這個節點上。docker
更重要的是,只要從我這個承載點向Docker鏡像製做者和使用者方向回溯,整條路徑上的各個服務節點
好比CI/CD、監控、安全、網絡、存儲等,都有我能夠發揮和盈利的餘地。
這個邏輯,正是全部雲計算提供商如此熱衷於容器技術的重要緣由:經過容器鏡像,它們能夠和開發者關聯起來。數據庫
從一個開發者和單一的容器鏡像,到無數開發者和龐大容器集羣,容器技術實現了從「容器」到「容器雲」的飛躍,標誌着它真正獲得了市場和生態的承認。
**`容器從一個開發者手裏的小工具,一躍成爲了雲計算領域的絕對主角
而可以定義容器組織和管理規範的「容器編排」技術,坐上了容器技術領域的「頭把交椅」`**segmentfault
最具表明性的容器編排工具,當屬後端
在前面介紹容器技術發展歷史中,已經對這兩個開源項目作了詳細地剖析和評述。因此,在今天專一於主角Kubernetes項目,談一談它的設計與架構。api
跟不少基礎設施領域先有工程實踐、後有方法論的發展路線不一樣,Kubernetes項目的理論基礎則要比工程實踐走得靠前得多,這固然要歸功於Google公司在2015年4月發佈的Borg論文了。安全
Borg系統,一直以來都被譽爲Google公司內部最強大的「祕密武器」。
相比於Spanner、BigTable等相對上層的項目,Borg要承擔的責任,是承載Google整個基礎設施的核心依賴。
在Google已經公開發表的基礎設施體系論文中,Borg項目當仁不讓地位居整個基礎設施技術棧的最底層。
bash
這幅圖,來自於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能給我提供路由網關、水平擴展、監控、備份、災難恢復等一系列運維能力。
這不就是經典PaaS(eg. Cloud Foundry)項目的能力嗎!
並且,有了Docker後,根本不須要什麼Kubernetes、PaaS,只要使用Docker公司的Compose+Swarm項目,就徹底能夠很方便DIY出這些功能!
因此說,若是Kubernetes項目只是停留在拉取用戶鏡像、運行容器,以及提供常見的運維功能的話,那別說跟嫡系的Swarm競爭,哪怕跟經典的PaaS項目相比也難有優點
而實際上,在定義核心功能過程當中,Kubernetes項目正是依託着Borg項目的理論優點
纔在短短几個月內迅速站穩了腳跟
能夠看到,Kubernetes項目的架構,跟它的原型項目Borg相似,都由Master和Node兩種節點組成,分別對應着控制節點和計算節點。
其中,控制節點 --- 即Master節點,由三個緊密協做的獨立組件組合而成,它們分別是
整個集羣的持久化數據,由kube-apiserver處理後保存在Ectd
而計算節點上最核心的是
在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進行交互的接口,分別是
實際上,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鏡像的角色。
Master節點!
雖然在Master節點的實現細節上Borg與Kubernetes不盡相同,但出發點高度一致
即:如何編排、管理、調度用戶提交的做業
因此,Borg項目徹底能夠把Docker鏡像看作是一種新的應用打包方式。
這樣,Borg團隊過去在大規模做業管理與編排上的經驗就能夠直接「套」在Kubernetes項目。
這些經驗最主要的表現就是,從一開始,Kubernetes就沒有像同期的各類「容器雲」項目,把Docker做爲整個架構的核心,而是另闢蹊徑, 僅僅把它做爲最底層的一個容器運行時實現
而Kubernetes着重解決的問題,則來自於Borg的研究人員在論文中提到的一個很是重要的觀點:
運行在大規模集羣中的各類任務之間,實際上存在着各類各樣的關係。這些關係的處理,纔是做業編排和管理系統最困難的地方。
這種任務 <=>任務之間的關係隨處可見
好比
並且同屬於一個服務單位的不一樣功能之間,也徹底可能存在這樣的關係
好比
而在容器技術普及以前,傳統虛擬機對這種關係的處理方法都比較「粗粒度」
在「功能單位」劃分上,容器卻有着獨到的「細粒度」優點:畢竟容器的本質,只是一個進程而已
就是說,只要你願意,那些原擠在同一VM裏的各個應用、組件、守護進程,均可被分別作成鏡像!
而後運行在一個一個專屬的容器中。
它們之間互不干涉,擁有各自的資源配額,能夠被調度在整個集羣裏的任何一臺機器上。
而這,正是一個PaaS系統最理想的工做狀態,也是所謂微服務思想得以落地的先決條件
。
若是隻作到 封裝微服務、調度單容器 這層次,Docker Swarm 就已經綽綽有餘了。
若是再加上Compose項目,甚至還具有了處理一些簡單依賴關係的能力
好比
在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日誌從本地磁盤記錄到分佈式文件系統。分配資源與機器資源的處理方式類似。在一個內部運行的多個任務共享其資源。若是必須將分配重定位到另外一臺計算機,則其任務將隨之從新安排。
分配集就像一項工做:它是一組在多臺機器上保留資源的分配。建立分配集後,能夠提交一個或多個做業以在其中運行。爲簡便起見,咱們一般使用「任務」來指代分配或頂級任務(在分配外的一個),使用「做業」來指代做業或分配集。
而對於另一種更爲常見的需求,好比
Kubernetes則提供了一種叫做「Service」的服務。像這樣的兩個應用,每每故意不部署在同一機器,即便Web應用所在的機器宕機了,數據庫也不受影響。
可對於一個容器來講,它的IP地址等信息是不固定的,Web應用又怎麼找到數據庫容器的Pod呢?
因此,Kubernetes的作法是給Pod綁定一個Service服務
Service服務聲明的IP地址等信息是「終生不變」的。Service主要就是做爲Pod的代理入口(Portal),從而代替Pod對外暴露一個固定的網絡地址。
這樣,對於Web應用的Pod來講,它須要關心的就是數據庫Pod的Service信息。不難想象,Service後端真正代理的Pod的IP地址、端口等信息的自動更新、維護,則是Kubernetes的職責。
圍繞着容器和Pod不斷向真實的技術場景擴展,咱們就可以摸索出一幅以下所示
若是如今
最典型的例子就是Web應用對數據庫訪問時須要Credential(數據庫的用戶名和密碼)信息。
那麼,在Kubernetes中這樣的關係又如何處理呢?
Kubernetes項目提供了一種叫做Secret的對象,是一個保存在Etcd裏的鍵值對。
這樣,你把Credential信息以Secret的方式存在Etcd裏,Kubernetes就會在你指定的Pod(好比,Web應用Pod)啓動時,自動把Secret裏的數據以Volume的方式掛載到容器裏。這樣,這個Web應用就能夠訪問數據庫了。
除了應用與應用之間的關係外,應用運行的形態是影響「如何容器化這個應用」的第二個重要因素。
爲此,Kubernetes定義了基於Pod改進後的對象。好比
描述一次性運行的Pod(好比,大數據任務)
描述每一個宿主機上必須且只能運行一個副本的守護進程服務
描述定時任務
如此種種,正是Kubernetes定義容器間關係和形態的主要方法。
Kubernetes並無像其餘項目那樣,爲每個管理功能建立一個指令,而後在項目中實現其中的邏輯這種作法,的確能夠解決當前的問題,可是在更多的問題來臨以後,每每會力不從心
相比之下,在Kubernetes中,咱們推崇
這種使用方法,就是所謂的「聲明式API」。這種API對應的「編排對象」和「服務對象」,都是Kubernetes中的API對象(API Object)。
這就是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爲用戶提供的不只限於一個工具。它真正的價值,仍是在於提供了一套基於容器構建分佈式系統的基礎依賴
本文由博客一文多發平臺 OpenWrite 發佈!