Kubernetes的本質

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

從這個結構中咱們不難看出,一個正在運行的 Linux 容器,其實能夠被「一分爲二」地看待:web

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

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

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

更重要的是,只要從我這個承載點向 Docker 鏡像製做者和使用者方向回溯,整條路徑上的各個服 務節點,好比 CI/CD、監控、安全、網絡、存儲等等,都有我能夠發揮和盈利的餘地。這個邏輯, 正是全部雲計算提供商如此熱衷於容器技術的重要緣由:經過容器鏡像,它們能夠和潛在用戶 (即,開發者)直接關聯起來。 從一個開發者和單一的容器鏡像,到無數開發者和龐大的容器集羣,容器技術實現了從「容 器」到「容器雲」的飛躍,標誌着它真正獲得了市場和生態的承認。後端

這樣,容器就從一個開發者手裏的小工具,一躍成爲了雲計算領域的絕對主角;而可以定義容器組 織和管理規範的「容器編排」技術,則當仁不讓地坐上了容器技術領域的「頭把交椅」。 這其中,最具表明性的容器編排工具,當屬 Docker 公司的 Compose+Swarm 組合,以及 Google 與 RedHat 公司共同主導的 Kubernetes 項目。api

我在前面介紹容器技術發展歷史的四篇預習文章中,已經對這兩個開源項目作了詳細地剖析和評 述。因此,在今天的此次分享中,我會專一於本專欄的主角 Kubernetes 項目,談一談它的設計與 架構。安全

跟不少基礎設施領域先有工程實踐、後有方法論的發展路線不一樣,Kubernetes 項目的理論基礎則要 比工程實踐走得靠前得多,這固然要歸功於 Google 公司在 2015 年 4 月發佈的 Borg 論文了。 Borg 系統,一直以來都被譽爲 Google 公司內部最強大的「祕密武器」。雖然略顯誇張,但這個說 法倒不算是吹牛。由於,相比於 Spanner、BigTable 等相對上層的項目,Borg 要承擔的責任,是承載 Google 公司 整個基礎設施的核心依賴。網絡

在 Google 公司已經公開發表的基礎設施體系論文中,Borg 項目當仁不 讓地位居整個基礎設施技術棧的最底層。架構

上面這幅圖,來自於 Google Omega 論文的第一做者的博士畢業論文。它描繪了當時 Google 已 經公開發表的整個基礎設施棧。在這個圖裏,你既能夠找到 MapReduce、BigTable 等知名項目, 也能看到 Borg 和它的繼任者 Omega 位於整個技術棧的最底層。 正是因爲這樣的定位,Borg 能夠說是 Google 最不可能開源的一個項目。而幸運地是,得益於 Docker 項目和容器技術的風靡,它卻終於得以以另外一種方式與開源社區見面,這個方式就是 Kubernetes 項目。app

因此,相比於「小打小鬧」的 Docker 公司、「舊瓶裝新酒」的 Mesos 社區,Kubernetes 項目從 一開始就比較幸運地站上了一個他人難以企及的高度:在它的成長階段,這個項目每個核心特性 的提出,幾乎都脫胎於 Borg/Omega 系統的設計與經驗。更重要的是,這些特性在開源社區落地的 過程當中,又在整個社區的協力之下獲得了極大的改進,修復了不少當年遺留在 Borg 體系中的缺陷和 問題。

因此,儘管在發佈之初被批評是「曲高和寡」,可是在逐漸覺察到 Docker 技術棧的「稚嫩」和 Mesos 社區的「老邁」以後,這個社區很快就明白了:Kubernetes 項目在 Borg 體系的指導下, 體現出了一種獨有的「先進性」與「完備性」,而這些特質纔是一個基礎設施領域開源項目賴以生 存的核心價值。 爲了更好地理解這兩種特質,咱們不妨從 Kubernetes 的頂層設計提及。

首先,Kubernetes 項目要解決的問題是什麼? 編排?調度?容器雲?仍是集羣管理? 實際上,這個問題到目前爲止都沒有固定的答案。由於在不一樣的發展階段,Kubernetes 須要着重解 決的問題是不一樣的。 可是,對於大多數用戶來講,他們但願 Kubernetes 項目帶來的體驗是肯定的:如今我有了應用的 容器鏡像,請幫我在一個給定的集羣上把這個應用運行起來。 更進一步地說,我還但願 Kubernetes 能給我提供路由網關、水平擴展、監控、備份、災難恢復等 一系列運維能力。

等一下,這些功能聽起來好像有些耳熟?這不就是經典 PaaS(好比,Cloud Foundry)項目的能力 嗎? 並且,有了 Docker 以後,我根本不須要什麼 Kubernetes、PaaS,只要使用 Docker 公司的 Compose+Swarm 項目,就徹底能夠很方便地 DIY 出這些功能了! 因此說,若是 Kubernetes 項目只是停留在拉取用戶鏡像、運行容器,以及提供常見的運維功能的 話,那麼別說跟「原生」的 Docker Swarm 項目競爭了,哪怕跟經典的 PaaS 項目相比也難有什麼 優點可言。

 

 而實際上,在定義核心功能的過程當中,Kubernetes 項目正是依託着 Borg 項目的理論優點,纔在短 短几個月內迅速站穩了腳跟,進而肯定了一個以下圖所示的全局架構:

 

 

咱們能夠看到,Kubernetes 項目的架構,跟它的原型項目 Borg 很是相似,都由 Master 和 Node 兩種節點組成,而這兩種角色分別對應着控制節點和計算節點。

其中,控制節點,即 Master 節點,由三個緊密協做的獨立組件組合而成,它們分別是負責 API 服 務的 kube-apiserver、負責調度的 kube-scheduler,以及負責容器編排的 kube-controllermanager。整個集羣的持久化數據,則由 kube-apiserver 處理後保存在 Etcd 中。 而計算節點上最核心的部分,則是一個叫做 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 鏡像的角色。

 答案是,Master 節點。 雖然在 Master 節點的實現細節上 Borg 項目與 Kubernetes 項目不盡相同,但它們的出發點卻高度 一致,即:如何編排、管理、調度用戶提交的做業? 因此,Borg 項目徹底能夠把 Docker 鏡像看作是一種新的應用打包方式。這樣,Borg 團隊過去在 大規模做業管理與編排上的經驗就能夠直接「套」在 Kubernetes 項目上了。

這些經驗最主要的表現就是,從一開始,Kubernetes 項目就沒有像同時期的各類「容器雲」項目 那樣,把 Docker 做爲整個架構的核心,而僅僅把它做爲最底層的一個容器運行時實現。 而 Kubernetes 項目要着重解決的問題,則來自於 Borg 的研究人員在論文中提到的一個很是重要 的觀點: 事實也正是如此。 其實,這種任務與任務之間的關係,在咱們日常的各類技術場景中隨處可見。

好比,一個 Web 應用 與數據庫之間的訪問關係,一個負載均衡器和它的後端服務之間的代理關係,一個門戶應用與受權 組件之間的調用關係。 更進一步地說,同屬於一個服務單位的不一樣功能之間,也徹底可能存在這樣的關係。好比,一個 Web 應用與日誌蒐集組件之間的文件交換關係。 而在容器技術普及以前,傳統虛擬機環境對這種關係的處理方法都是比較「粗粒度」的。你會常常 發現不少功能並不相關的應用被一古腦兒地部署在同一臺虛擬機中,只是由於它們之間偶爾會互相 發起幾個 HTTP 請求。 更常見的狀況則是,一個應用被部署在虛擬機裏以後,你還得手動維護不少跟它協做的守護進程 (Daemon),用來處理它的日誌蒐集、災難恢復、數據備份等輔助工做。

但容器技術出現之後,你就不難發現,在「功能單位」的劃分上,容器有着獨一無二的「細粒 度」優點:畢竟容器的本質,只是一個進程而已。 也就是說,只要你願意,那些原先擁擠在同一個虛擬機裏的各個應用、組件、守護進程,均可以被 分別作成鏡像,而後運行在一個個專屬的容器中。它們之間互不干涉,擁有各自的資源配額,能夠 被調度在整個集羣裏的任何一臺機器上。

而這,正是一個 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 的設 計。

在後續的章節中,咱們會對 Pod 作更進一步地闡述。 而對於另一種更爲常見的需求,好比 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 項目中,咱們所推崇的使用方法是: 這種使用方法,就是所謂的「聲明式 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 項目爲用戶提供的不只限於一個工具。它真正的價值,乃在於提供 了一套基於容器構建分佈式系統的基礎依賴。關於這一點,相信你會在從此的學習中,體會的越來 越深。  

相關文章
相關標籤/搜索