我花了 10 個小時,寫出了這篇 K8S 架構解析

互聯網技術飛速發展的今天,爲了承載請求的高併發和業務的多樣性,微服務的架構成了各個公司的標配。

圖片來自 Pexels

每個微服務通過 Docker 進行發佈,隨着業務的發展,系統中遍佈着各種各樣的容器。於是,容器的資源調度,部署運行,擴容縮容就是我們要面臨的問題。

基於 Kubernetes 作爲容器集羣的管理平臺被廣泛應用,今天我們一起來看看 Kubernetes 的架構中有那些常用的組件以及運行原理。

Kubernetes 架構概述

Kubernetes 是用來管理容器集羣的平臺。既然是管理集羣,那麼就存在被管理節點,針對每個 Kubernetes 集羣都由一個 Master 負責管理和控制集羣節點。

我們通過 Master 對每個節點 Node 發送命令。簡單來說,Master 就是管理者,Node 就是被管理者。

Node 可以是一臺機器或者一臺虛擬機。在 Node 上面可以運行多個 Pod,Pod 是 Kubernetes 管理的最小單位,同時每個 Pod 可以包含多個容器(Docker)。

通過下面的 Kubernetes 架構簡圖可以看到 Master 和 Node 之間的關係:

Kubernetes 架構簡圖

通常我們都是通過 kubectl 對 Kubernetes 下命令的,它通過 APIServer 去調用各個進程來完成對 Node 的部署和控制。

APIServer 的核心功能是對核心對象(例如:Pod,Service,RC)的增刪改查操作,同時也是集羣內模塊之間數據交換的樞紐。

它包括了常用的 API,訪問(權限)控制,註冊,信息存儲(etcd)等功能。在它的下面我們可以看到 Scheduler,它將待調度的 Pod 綁定到 Node 上,並將綁定信息寫入 etcd 中。

etcd 包含在 APIServer 中,用來存儲資源信息。接下來就是 Controller Manager 了,如果說 Kubernetes 是一個自動化運行的系統,那麼就需要有一套管理規則來控制這套系統。

Controller Manager 就是這個管理者,或者說是控制者。它包括 8 個 Controller,分別對應着副本,節點,資源,命名空間,服務等等。

緊接着,Scheduler 會把 Pod 調度到 Node 上,調度完以後就由 kubelet 來管理 Node 了。

kubelet 用於處理 Master 下發到 Node 的任務(即 Scheduler 的調度任務),同時管理 Pod 及 Pod 中的容器。

在完成資源調度以後,kubelet 進程也會在 APIServer 上註冊 Node 信息,定期向 Master 彙報 Node 信息,並通過 cAdvisor 監控容器和節點資源。

由於,微服務的部署都是分佈式的,所以對應的 Pod 以及容器的部署也是。爲了能夠方便地找到這些 Pod 或者容器,引入了 Service(kube-proxy)進程,它來負責反向代理和負載均衡的實施。

上面就是 Kubernetes 架構的簡易說明,涉及到了一些核心概念以及簡單的信息流動。

將一些功能收錄到了 APIServer 中,這個簡圖比官網的圖顯得簡單一些,主要是方便大家記憶。

後面我們會用一個簡單的例子,帶大家把 Kubernetes 的概念的由來做深入的瞭解。

從一個例子開始

假設使用 Kubernetes 部署 Tomcat 和 MySQL 服務到兩個 Node 上面。其中 Tomcat 服務生成兩個實例也就是生成兩個 Pod,用來對其做水平擴展。

MySQL 只部署一個實例,也就是一個 Pod。可以通過外網訪問 Tomcat,而 Tomcat 可以在內網訪問 MySQL。

例子示意圖

這裏我們假設 Kubernetes 和 Docker 的安裝都已經完成,並且鏡像文件都已經準備好了。重點看 Kubernetes 如何部署和管理容器。

kubectl 和 APIServer

既然我們要完成上面的例子,接下來就要部署兩個應用。

首先,根據要部署的應用建立 Replication Controller(RC)。RC 是用來聲明應用副本的個數,也就是 Pod 的個數。

按照上面的例子,Tomcat 的 RC 就是 2,MySQL 的 RC 就是 1。

由於 kubectl 作爲用戶接口向 Kubernetes 下發指令,那麼指令是通過 “.yaml” 的配置文件編寫的。

定義 mysql-rc.yaml 的配置文件來描述 MySQL 的 RC:

apiVersion: V1
kind: ReplicationController
metadata:
 name: mysql#RC的名稱,全局唯一
spec:
 replicas:1 #Pod 副本的期待數量
selector :
app: mysql
 template: #Pod模版,用這個模版來創建Pod
metadata:
 labels:
app:mysql#Pod副本的標籤
spec:
 containers:#容器定義部分
     -name:mysql
Image:mysql#容器對應的DockerImage
      Ports:
      -containerPort:3306#容器應用監聽的端口號
      Env:#注入容器的環境變量
      -name:MYSQL_ROOT_PASSWORD
      Value:”123456”

從上面的配置文件可以看出,需要對這個 RC 定義一個名字,以及期望的副本數,以及容器中的鏡像文件。然後通過 kubectl 作爲客戶端的 cli 工具,執行這個配置文件。

通過 kubectl 執行 RC 配置文件

執行了上面的命令以後,Kubernetes 會幫助我們部署副本 MySQL 的 Pod 到 Node。

此時先不着急看結果,回到最開始的架構圖,可以看到 kubectl 會向 Master 中的 APIServer 發起命令,看看 APIServer 的結構和信息的傳遞吧。

Kubernetes API Server 通過一個名爲 kube-apiserver 的進程提供服務,該進程運行在 Master 上。

可以通過 Master 的 8080 端口訪問 kube-apiserver 進程,它提供 REST 服務。

因此可以通過命令行工具 kubectl 來與 Kubernetes APIServer 交互,它們之間的接口是 RESTful API。

APIServer 的架構從上到下分爲四層:

APIServer 分層架構圖

當 kubectl 用 Create 命令建立 Pod 時,是通過 APIServer 中的 API 層調用對應的 RESTAPI 方法。

之後會進入權限控制層,通過 Authentication 獲取調用者身份,Authorization 獲取權限信息。

AdmissionControl 中可配置權限認證插件,通過插件來檢查請求約束。例如:啓動容器之前需要下載鏡像,或者檢查具備某命名空間的資源。

還記得 mysql-rc.yaml 中配置需要生成的 Pod 的個數爲 1。到了 Registry 層會從 CoreRegistry 資源中取出 1 個 Pod 作爲要創建的 Kubernetes 資源對象。

然後將 Node,Pod 和 Container 信息保存在 etcd 中去。這裏的 etcd 可以是一個集羣,由於裏面保存集羣中各個 Node/Pod/Container 的信息,所以必要時需要備份,或者保證其可靠性。

Controller Manager,Scheduler 和 kubelet

前面通過 kubectl 根據配置文件,向 APIServer 發送命令,在 Node 上面建立 Pod 和 Container。

在 APIServer,經過 API 調用,權限控制,調用資源和存儲資源的過程。實際上還沒有真正開始部署應用。

這裏需要 Controller Manager,Scheduler 和 kubelet 的協助才能完成整個部署過程。

在介紹他們協同工作之前,要介紹一下在 Kubernetes 中的監聽接口。從上面的操作知道,所有部署的信息都會寫到 etcd 中保存。

實際上 etcd 在存儲部署信息的時候,會發送 Create 事件給 APIServer,而 APIServer 會通過監聽(Watch)etcd 發過來的事件。其他組件也會監聽(Watch)APIServer 發出來的事件。

Kubernetes 就是用這種 List-Watch 的機制保持數據同步的,如上圖:

通過上面 List-Watch 的介紹大家發現了,除了之前引入的 kubectl 和 APIServer 以外又引入了 Controller Manager,Scheduler 和 kubelet。

這裏給大家介紹一下他們的作用和原理:

聚焦 Scheduler,Controller Manager,kubelet

Controller Manager

Kubernetes 需要管理集羣中的不同資源,所以針對不同的資源要建立不同的 Controller。

每個 Controller 通過監聽機制獲取 APIServer 中的事件(消息),它們通過 API Server 提供的(List-Watch)接口監控集羣中的資源,並且調整資源的狀態。

可以把它想象成一個盡職的管理者,隨時管理和調整資源。比如 MySQL 所在的 Node 意外宕機了,Controller Manager 中的 Node Controller 會及時發現故障,並執行修復流程。

在部署了成百上千微服務的系統中,這個功能極大地協助了運維人員。從此可以看出,Controller Manager 是 Kubernetes 資源的管理者,是運維自動化的核心。

它分爲 8 個 Controller,上面我們介紹了 Replication Controller,這裏我們把其他幾個都列出來,就不展開描述了。

Controller Manager 中不同的 Controller 負責對不同資源的監控和管理

Scheduler 與 kubelet

Scheduler 的作用是,將待調度的 Pod 按照算法和策略綁定到 Node 上,同時將信息保存在 etcd 中。

如果把 Scheduler 比作調度室,那麼這三件事就是它需要關注的,待調度的 Pod、可用的 Node,調度算法和策略。

簡單地說,就是通過調度算法 / 策略把 Pod 放到合適的 Node 中去。此時 Node 上的 kubelet 通過 APIServer 監聽到 Scheduler 產生的 Pod 綁定事件,然後通過 Pod 的描述裝載鏡像文件,並且啓動容器。

也就是說 Scheduler 負責思考,Pod 放在哪個 Node,然後將決策告訴 kubelet,kubelet 完成 Pod 在 Node 的加載工作。

說白了,Scheduler 是 boss,kubelet 是幹活的工人,他們都通過 APIServer 進行信息交換。

Scheduler 與 kubelet 協同工作圖

Service 和 kubelet

經歷上面一系列的過程,終於將 Pod 和容器部署到 Node 上了。

MySQL 部署成功

作爲部署在 Kubernetes 中,Pod 如何訪問其他的 Pod 呢?答案是通過 Kubernetes 的 Service 機制。

在 Kubernetes 中的 Service 定義了一個服務的訪問入口地址(IP+Port)。Pod 中的應用通過這個地址訪問一個或者一組 Pod 副本。

Service 與後端 Pod 副本集羣之間是通過 Label Selector 來實現連接的。Service 所訪問的這一組 Pod 都會有同樣的 Label,通過這樣的方法知道這些 Pod 屬於同一個組。

Pod 通過 Service 訪問其他 Pod

寫 MySQL 服務的配置文件(mysql-svc.yaml)如下:

apiVersion : v1
kind: Service #說明創建資源對象的類型是Service
metadata:
 name: mysql#Service全局唯一名稱
spec:
prots:
-port: 3306#Service的服務端口號
 selector:#Service對應的Pod標籤,用來給Pod分類
   app: mysql

按照慣例運行 kubectl,創建 Service:

再用 getsvc 命令檢查 Service 信息:

這裏的 Cluster-IP 169.169.253.143 是由 Kubernetes 自動分配的。當一個 Pod 需要訪問其他的 Pod 的時候就需要通過 Service 的 Cluster-IP 和 Port。

也就是說 Cluster-IP 和 Port 是 Kubernetes 集羣的內部地址,是提供給集羣內的 Pod 之間訪問使用的,外部系統是無法通過這個 Cluster-IP 來訪問 Kubernetes 中的應用的。

上面提到的 Service 只是一個概念,而真正將 Service 落實的是 kube-proxy。

只有理解了 kube-proxy 的原理和機制,我們才能真正理解 Service 背後的實現邏輯。

在 Kubernetes 集羣的每個 Node 上都會運行一個 kube-proxy 服務進程,我們可以把這個進程看作 Service 的負載均衡器,其核心功能是將到 Service 的請求轉發到後端的多個 Pod 上。

此外,Service 的 Cluster-IP 與 NodePort 是 kube-proxy 服務通過 iptables 的 NAT 轉換實現的。kube-proxy 在運行過程中動態創建與 Service 相關的 iptables 規則。

由於 iptables 機制針對的是本地的 kube-proxy 端口,所以在每個 Node 上都要運行 kube-proxy 組件。

因此在 Kubernetes 集羣內部,可以在任意 Node 上發起對 Service 的訪問請求。

集羣內部通過 kube-proxy(Service)訪問其他 Pod

正如 MySQL 服務,可以被 Kubernetes 內部的 Tomcat 調用,那麼 Tomcat 如何被 Kubernetes 外部調用?

先生成配置文件,myweb-rc.yaml 看看:

apiVersion: V1
kind: ReplicationController
metadata:
 name: myweb#RC的名稱,全局唯一
spec:
 replicas:2#Pod 副本的期待數量,這裏的數量是2,需要建立兩個Tomcat的副本
selector :
app: myweb
 template: #Pod模版,用這個模版來創建Pod
metadata:
 labels:
app:myweb#Pod副本的標籤
spec:
 containers: #容器定義部分
     -name:mysql
Image:kubeguide/tomcat-app:v1#容器對應的DockerImage
      Ports:
      -containerPort:8080#容器應用監聽的端口號

在 kubectl 中使用 Create 建立 myweb 副本。

副本創建完畢以後,創建對應的服務配置文件 myweb-svc.yaml。

apiVersion : v1
kind: Service #說明創建資源對象的類型是Service
metadata:
 name: myweb#Service全局唯一名稱
spec:
prots:
-port: 8080#Service的服務端口號
nodePort: 30001#這個就是外網訪問Kubernetes內部應用的端口。
 selector: #Service對應的Pod標籤,用來給Pod分類
   app: myweb

同樣在 kubectl 中運行 Create 命令,建立 Service 資源。

從上面的配置文件可以看出,Tomcat 的 Service 中多了一個 nodePort 的配置,值爲 30001。

也就是說外網通過 30001 這個端口加上 NodeIP 就可以訪問 Tomcat 了。

運行命令之後,得到一個提示,大致意思是 “如果你要將服務暴露給外網使用,你需要設置防火牆規則讓 30001 端口能夠通行。”

由於 Cluster-IP 是一個虛擬的 IP,僅供 Kubernetes 內部的 Pod 之間的通信。

Node 作爲一個物理節點,因此需要使用 Node-IP 和 nodePort 的組合來從 Kubernetes 外面訪問內部的應用。

如果按照上面的配置,部署了兩個 Tomcat 應用,當外網訪問時選擇那個 Pod 呢?這裏需要通過 Kubernetes 之外的負載均衡器來實現的。

Kubernetes 之外的負載均衡器

可以通過 Kubernetes 的 LoadBlancerService 組件來協助實現。通過雲平臺申請創建負載均衡器,向外暴露服務。

目前 LoadBlancerService 組件支持的雲平臺比較完善,比如國外的 GCE、DigitalOcean,國內的阿里雲,私有云 OpenStack 等等。

從用法上只要把 Service 的 type=NodePort 改爲 type=LoadBalancer,Kubernetes 就會自動創建一個對應的 Load Balancer 實例並返回它的 IP 地址供外部客戶端使用。

至此,MySQL(RC 1)和 Tomcat(RC 2)已經在 Kubernetes 部署了。並在 Kubernetes 內部 Pod 之間是可以互相訪問的,在外網也可以訪問到 Kubernetes 內部的 Pod。

Pod 在 Kubernetes 內互相訪問,外網訪問 Pod

另外,作爲資源監控 Kubernetes 在每個 Node 和容器上都運行了 cAdvisor。它是用來分析資源使用率和性能的工具,支持 Docker 容器。

kubelet 通過 cAdvisor 獲取其所在 Node 及容器(Docker)的數據。cAdvisor 自動採集 CPU、內存、文件系統和網絡使用的統計信息。

kubelet 作爲 Node 的管理者,把 cAdvisor 採集上來的數據通過 RESTAPI 的形式暴露給 Kubernetes 的其他資源,讓他們知道 Node/Pod 中的資源使用情況。

總結

由於微服務的迅猛發展,Kubernetes 作爲微服務治理平臺被廣泛應用。由於其發展時間長,包含服務功能多我們無法一一列出。

因此,從一個簡單的創建應用副本的例子入手,介紹了各個重要組件的概念和基本原理。

Kubernetes 是用來管理容器集羣的,Master 作爲管理者,包括 APIServer,Scheduler,Controller Manager。

Node 作爲副本部署的載體,包含多個 Pod,每個 Pod 又包含多個容器(container)。用戶通過 kubectl 給 Master 中的 APIServer 下部署命令。

命令主體是以 “.yaml” 結尾的配置文件,包含副本的類型,副本個數,名稱,端口,模版等信息。

APIServer 接受到請求以後,會分別進行以下操作:權限驗證(包括特殊控制),取出需要創建的資源,保存副本信息到 etcd。

APIServer 和 Controller Manager,Scheduler 以及 kubelete 之間通過 List-Watch 方式通信(事件發送與監聽)。

Controller Manager 通過 etcd 獲取需要創建資源的副本數,交由 Scheduler 進行策略分析。

最後 kubelet 負責最終的 Pod 創建和容器加載。部署好容器以後,通過 Service 進行訪問,通過 cAdvisor 監控資源。

作者:崔皓

簡介:十六年開發和架構經驗,曾擔任過惠普武漢交付中心技術專家,需求分析師,項目經理,後在創業公司擔任技術 / 產品經理。善於學習,樂於分享。目前專注於技術架構與研發管理。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/KmD8QGjaq4nOG03TDc88dA