從「控制器」模型,談容器編排

若是你對容器稍有涉獵,應該知道:Pod這個看似複雜的API對象,實際上就是對容器的進一步抽象和封裝而已。node

說得更形象些,「容器」鏡像雖然好用,可是容器這樣一個「沙盒」的概念,對於描述應用來講,仍是太過簡單了。這就比如,集裝箱當然好用,可是若是它四面都光禿禿的,吊車還怎麼把這個集裝箱吊起來並擺放好呢?nginx

因此,Pod對象,其實就是容器的升級版。它對容器進行了組合,添加了更多的屬性和字段。這就比如給集裝箱四面安裝了吊環,使得Kubernetes這架「吊車」,能夠更輕鬆地操做它。編程

而Kubernetes操做這些「集裝箱」的邏輯,都由控制器(Controller)完成。在專欄的第12篇文章《牛刀小試:個人第一個容器化應用》中,咱們曾經使用過Deployment這個最基本的控制器對象。api

如今,咱們一塊兒來回顧一下這個名叫nginx-deployment的例子:bash

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80複製代碼

這個Deployment定義的編排動做很是簡單,即:請確保攜帶了app=nginx標籤的Pod的個數,永遠等於spec.replicas指定的個數,即2個。網絡

這就意味着,若是在這個集羣中,攜帶app=nginx標籤的Pod的個數大於2的時候,就會有舊的Pod被刪除;反之,就會有新的Pod被建立。架構

這時,你也許就會好奇:到底是Kubernetes項目中的哪一個組件,在執行這些操做呢?app

在Kubernetes架構中,有一個叫做kube-controller-manager的組件。oop

實際上,這個組件,就是一系列控制器的集合。咱們能夠查看一下Kubernetes項目的pkg/controller目錄:ui

$ cd kubernetes/pkg/controller/
$ ls -d */              
deployment/             job/                    podautoscaler/          
cloud/                  disruption/             namespace/              
replicaset/             serviceaccount/         volume/
cronjob/                garbagecollector/       nodelifecycle/          replication/            statefulset/            daemon/
...複製代碼

這個目錄下面的每個控制器,都以獨有的方式負責某種編排功能。而咱們的Deployment,正是這些控制器中的一種。

實際上,這些控制器之因此被統一放在pkg/controller目錄下,就是由於它們都遵循Kubernetes項目中的一個通用編排模式,即:控制循環(control loop)。

好比,如今有一種待編排的對象X,它有一個對應的控制器。那麼,我就能夠用一段Go語言風格的僞代碼,爲你描述這個控制循環:

for {
  實際狀態 := 獲取集羣中對象X的實際狀態(Actual State)
  指望狀態 := 獲取集羣中對象X的指望狀態(Desired State)
  if 實際狀態 == 指望狀態{
    什麼都不作
  } else {
    執行編排動做,將實際狀態調整爲指望狀態
  }
}複製代碼

在具體實現中,實際狀態每每來自於Kubernetes集羣自己

好比,kubelet經過心跳彙報的容器狀態和節點狀態,或者監控系統中保存的應用監控數據,或者控制器主動收集的它本身感興趣的信息,這些都是常見的實際狀態的來源。

而指望狀態,通常來自於用戶提交的YAML文件

好比,Deployment對象中Replicas字段的值。很明顯,這些信息每每都保存在Etcd中。

接下來,以Deployment爲例,我和你簡單描述一下它對控制器模型的實現:

  1. Deployment控制器從Etcd中獲取到全部攜帶了「app: nginx」標籤的Pod,而後統計它們的數量,這就是實際狀態;
  2. Deployment對象的Replicas字段的值就是指望狀態;
  3. Deployment控制器將兩個狀態作比較,而後根據比較結果,肯定是建立Pod,仍是刪除已有的Pod(具體如何操做Pod對象,我會在下一篇文章詳細介紹)。

能夠看到,一個Kubernetes對象的主要編排邏輯,其實是在第三步的「對比」階段完成的。

這個操做,一般被叫做調諧(Reconcile)。這個調諧的過程,則被稱做「Reconcile Loop」(調諧循環)或者「Sync Loop」(同步循環)。

因此,若是你之後在文檔或者社區中碰到這些詞,都不要擔憂,它們其實指的都是同一個東西:控制循環。

而調諧的最終結果,每每都是對被控制對象的某種寫操做。

好比,增長Pod,刪除已有的Pod,或者更新Pod的某個字段。這也是Kubernetes項目「面向API對象編程」的一個直觀體現。

其實,像Deployment這種控制器的設計原理,就是咱們前面提到過的,「用一種對象管理另外一種對象」的「藝術」。

其中,這個控制器對象自己,負責定義被管理對象的指望狀態。好比,Deployment裏的replicas=2這個字段。

而被控制對象的定義,則來自於一個「模板」。好比,Deployment裏的template字段。

能夠看到,Deployment這個template字段裏的內容,跟一個標準的Pod對象的API定義,絲絕不差。而全部被這個Deployment管理的Pod實例,其實都是根據這個template字段的內容建立出來的。

像Deployment定義的template字段,在Kubernetes項目中有一個專有的名字,叫做PodTemplate(Pod模板)。

這個概念很是重要,由於後面我要講解到的大多數控制器,都會使用PodTemplate來統必定義它所要管理的Pod。更有意思的是,咱們還會看到其餘類型的對象模板,好比Volume的模板。

至此,咱們就能夠對Deployment以及其餘相似的控制器,作一個簡單總結了:

如上圖所示,相似Deployment這樣的一個控制器,實際上都是由上半部分的控制器定義(包括指望狀態),加上下半部分的被控制對象的模板組成的。

這就是爲何,在全部API對象的Metadata裏,都有一個字段叫做ownerReference,用於保存當前這個API對象的擁有者(Owner)的信息。

那麼,對於咱們這個nginx-deployment來講,它建立出來的Pod的ownerReference就是nginx-deployment嗎?或者說,nginx-deployment所直接控制的,就是Pod對象麼?

這個問題的答案,我會在「深刻剖析Kubernetes」專欄第33講深刻解析容器跨主機網絡中進行詳細解釋。


文章相關:

完整文章: 極客時間「深刻剖析Kubernetes」第32講 | 淺談容器網絡
  
拓展閱讀:

深刻解析容器跨主機網絡

解讀Kubernetes三層網絡方案

相關文章
相關標籤/搜索