天天看點

Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考

Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考

在前面以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項目當仁不讓地位居整個基礎設施技術棧的最底層。

Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考
這幅圖,來自于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能給我提供路由網關、水準擴充、監控、備份、災難恢複等一系列運維能力。

Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考

這不就是經典PaaS(eg. Cloud Foundry)項目的能力嗎!

而且,有了Docker後,根本不需要什麼Kubernetes、PaaS,隻要使用Docker公司的Compose+Swarm項目,就完全可以很友善DIY出這些功能!

是以說,如果Kubernetes項目隻是停留在拉取使用者鏡像、運作容器,以及提供常見的運維功能的話,那别說跟嫡系的Swarm競争,哪怕跟經典的PaaS項目相比也難有優勢

而實際上,在定義核心功能過程中,Kubernetes項目正是依托着Borg項目的理論優勢

才在短短幾個月内迅速站穩了腳跟

  • 進而确定了一個如下所示的全局架構
Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考

可以看到,Kubernetes項目的架構,跟它的原型項目Borg類似,都由Master和Node兩種節點組成,分别對應着控制節點和計算節點。

其中,控制節點 --- 即Master節點,由三個緊密協作的獨立元件組合而成,它們分别是

  • 負責API服務的kube-apiserver
  • 負責排程的kube-scheduler
  • 負責容器編排的kube-controller-manager

整個叢集的持久化資料,由kube-apiserver處理後儲存在Ectd

而計算節點上最核心的是

kubelet元件

Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考

在Kubernetes中,kubelet主要負責同容器運作時(比如Docker項目)打交道。

而這個互動所依賴的,是一個稱作CRI(Container Runtime Interface) 的遠端調用接口,這個接口定義了容器運作時的各項核心操作,比如:啟動一個容器需要的所有參數。

這也是為何,Kubernetes項目并不關心你部署的是什麼容器運作時、使用的什麼技術實作,隻要你的這個容器運作時能夠運作标準的容器鏡像,它就可以通過實作CRI接入到Kubernetes項目當中。

而具體的容器運作時,比如Docker項目,則一般通過OCI這個容器運作時規範同底層的Linux作業系統進行互動,即:把CRI請求翻譯成對Linux作業系統的調用(操作Linux Namespace和Cgroups等)。

此外,kubelet還通過gRPC協定同一個叫作Device Plugin的插件進行互動。

Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考

這個插件,是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的設計

Docker容器實戰(八) - 漫談 Kubernetes 的本質Kubernetes要解決什麼?kubelet元件Borg對于Kubernetes項目的指導作用又展現在哪裡呢?支援未來可能的更多關系Web應用與資料庫之間的通路關系兩個不同Pod之間不僅有“通路關系”,還要求在發起時加上授權資訊Kubernetes如何啟動一個容器化任務總結參考
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為使用者提供的不僅限于一個工具。它真正的價值,還是在于提供了一套基于容器建構分布式系統的基礎依賴

參考