Docker容器實戰(八) - 漫談 Kubernetes 的本質

在前面以Docker項目爲例,一步步剖析了Linux容器的具體實現方式。
經過這些應該明白:一個「容器」,其實是一個由Linux Namespace、Linux Cgroups和rootfs三種技術構建出來的進程的隔離環境。java

一個正在運行的Linux容器,其實能夠被看作nginx

  • 一組聯合掛載在 /var/lib/docker/aufs/mnt 上的rootfs,這部分稱爲「容器鏡像」(Container Image),是容器的靜態視圖
  • 一個由Namespace+Cgroups構成的隔離環境,這部分稱爲「容器運行時」(Container Runtime),是容器的動態視圖。

做爲一名開發者,我並不關心容器運行時的差別。
由於,在整個「開發-測試-發佈」的流程中,真正承載着容器信息進行傳遞的,是容器鏡像,而不是容器運行時。web

這個重要假設,正是容器技術圈在Docker項目成功後不久,就迅速走向了「容器編排」這個「上層建築」的主要緣由:
做爲雲基礎設施提供商,只要可以將用戶提交的Docker鏡像以容器的方式運行,就能成爲容器生態圖上的一個承載點,從而將整個容器技術棧上的價值,沉澱在這個節點上。docker

更重要的是,只要從我這個承載點向Docker鏡像製做者和使用者方向回溯,整條路徑上的各個服務節點
好比CI/CD、監控、安全、網絡、存儲等,都有我能夠發揮和盈利的餘地。
這個邏輯,正是全部雲計算提供商如此熱衷於容器技術的重要緣由:經過容器鏡像,它們能夠和開發者關聯起來。數據庫

從一個開發者和單一的容器鏡像,到無數開發者和龐大容器集羣,容器技術實現了從「容器」到「容器雲」的飛躍,標誌着它真正獲得了市場和生態的承認。
**`容器從一個開發者手裏的小工具,一躍成爲了雲計算領域的絕對主角
而可以定義容器組織和管理規範的「容器編排」技術,坐上了容器技術領域的「頭把交椅」`**segmentfault

最具表明性的容器編排工具,當屬後端

  • Docker公司的Compose+Swarm組合
  • Google與RedHat公司共同主導的Kubernetes項目

在前面介紹容器技術發展歷史中,已經對這兩個開源項目作了詳細地剖析和評述。因此,在今天專一於主角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項目帶來的體驗是肯定的:
如今我有應用的容器鏡像,請幫我在一個給定的集羣上把應用運行起來
更進一步說,還但願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核心功能「全景圖」

  • 從容器這個最基礎的概念出發,首先遇到了容器間「緊密協做」關係的難題,因而就擴展到了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爲用戶提供的不只限於一個工具。它真正的價值,仍是在於提供了一套基於容器構建分佈式系統的基礎依賴

參考

本文由博客一文多發平臺 OpenWrite 發佈!
相關文章
相關標籤/搜索