一文講明白K8S各核心架構組件

一、寫在前面

K8S的文章很多人都寫過,若要想好好研讀,系統的學習,真推薦去看官方文檔。但是若是當上下班路上的爽文,可以看下我的筆記,我也會盡力多寫點自己的理解進來。

推薦手機閱讀原文,有動態表情圖,閱讀體驗感更佳:https://mp.weixin.qq.com/s/bL-85BhOj8H5Dis_94lmrQ
推薦手機閱讀原文,有動態表情圖,閱讀體驗感更佳:https://mp.weixin.qq.com/s/bL-85BhOj8H5Dis_94lmrQ
推薦手機閱讀原文,有動態表情圖,閱讀體驗感更佳:https://mp.weixin.qq.com/s/bL-85BhOj8H5Dis_94lmrQ

二、K8S爲我們提供了怎樣的能力

大家都知道Docker,我們可以將自己的應用打包製作成Image,然後通過docker run命令將Image啓動成Container對外提供服務。

點擊查看白日們的Docker網絡筆記

點擊查看白日夢的視頻教程-二十分鐘徹底搞懂Docker網絡!

基於此,K8S不僅能將用戶提供的單個容器運行起來,將其對外暴露出去提供服務。還提供了:路由網關、集羣監控、災難恢復,以及應用的水平擴展等能力。

大家常聽過一個詞:微服務、雲原生應用,如何理解這個詞自然也是見仁見智。

如下圖是SpringCloud的架構圖:

在SpringCloud中有不同的組件,諸如提供服務發現能力的:Eureka、提供負載均衡機制的Ribbon、以及微服務的統一入口Zuul,基於這套框架做過開發的同學都知道,無論是Eureka還是Zuul,無論開發量大小,都需要程序員開發相應的代碼,即使這些代碼和業務本身並沒有什麼關係。

而在K8S中,像Eureka的服務發現能力,Zuul的網關能力、以及Ribbon的負載均衡能力,K8S都是原生支持的,開發人員只需要寫好自己的業務代碼,提供一個可執行的jar包,或者二進制文件即可部署進K8S中就行

當然不僅於此,K8S的服務網格組件如:Istio還提供了流量治理能力,比如按不同的請求頭做不同比例的流量分發調度、亦或者是金絲雀發佈。


說起容器編排,像Docker的Compose或者是Docker-Swarm都提供了簡單的容器編排的能力。

點擊查看白日夢的-玩轉Docker容器編排-DockerCompose、Docker-Swarm

像Docker-Compose或者Docker-Swarm的通病就是過於以Docker核心,提供的能力也過於簡單比如定義誰先啓動誰後啓動。無法滿足比較複雜的場景

而K8S的容器編排設計是站在更高的維度,Docker對於K8S而言只是運行它編排產出的介質,K8S針對不同的編排場景提供了不同的編排資源對象,如提供Deployment編排無狀態應用,提供了Cronjob編排定時任務,提供了StatefulSet編排ES、Redis集羣這種有狀態應用等等,這都是前者所不能及的....

三、架構

K8S架構簡圖如上,分爲MasterNode、WorkNode兩大部分和五大組件,一開始接觸這些概念難免會有些陌生,但是本質上這些組件都是K8S的開發者對各種能力的抽象和封裝,下文會展開介紹

tip:上圖中很多xxx.pem文件是K8S的https安全認證依賴的證書,想進一步瞭解原理可以閱讀這篇筆記:十二張圖,從零禮哦啊姐對稱加密/非對稱加密/CA認證/以及K8S各組件證書頒發原由

3.1、MasterNode

和Redis或者Nginx這種由二進制文件啓動後得到一個對外提供服務的守護進程不同,K8S中的MasterNode其實並不是一個二進制文件啓動後得到的對外提供服務的守護進程,它本質上是一個抽象的概念。

MasterNode包含3個程序,分別是:

  • ApiServer
    • 提供HTTP Rest接口,是集羣中各種核心資源的CRUD的統一入口,是集羣中各個組件交互的核心樞紐
    • 集羣資源配額的統一入口
    • 提供了完備的集羣安全機制
  • ControllerManager
    • 實時監控集羣中如Service等各種資源的狀態變化,不斷嘗試將它們的副本數量維持在一個期望的狀態。
  • Scheduler:
    • 負責調度功能,如:爲Pod找到一個合適的宿主機器

3.2、WorkerNode

和MasterNode類似,WorkerNode本質也並不是一個獨立的應用程序,它包含兩個組件,如下

  • kubelet
    • Node節點管理
    • Pod管理,同容器運行時交互下發容器的創建/關閉命令
    • 容器健康狀態檢查
  • kube-proxy
    • 通過爲Service資源的ClusterIP生成iptable或ipvs規則,實現將K8S內部的服務暴露到集羣外面去

既然WorkNode也是抽象的概念,那麼若在MasterNode啓動kube-proxy和kubelet進程,那麼MasterNode也會擁有WorkNode的能力,雙重角色,但生產環境不推薦這樣搞。

四、核心組件

4.1、ApiServer

4.1.1、概述

APIServer有完備的集羣安全驗證機制,提供了對K8S中如Pod、Service等資源CRUD等HttpRest接口,是集羣中各個組件之間數據交互的核心樞紐。

4.1.2、是集羣管理API的統一入口

爲了更好的理解這個概念可以看如下圖

圖片摘自Kubernetes權威指南

通過kubectl命令執行創建kubectl apply -f rs.yaml創建pod時,經歷的流程如上圖,大概流程爲

  1. apiserver接收kubectl的創建資源的請求
  2. apiserver將創建請求寫入ECTD
  3. apiserver接收到etcd的回調事件
  4. apiserver將回調事件發送給ControllerManager
  5. controllerManager中的ReplicationController處理本次請求,創建RS,然後它會調控RS中的Pod的副本數量處於期望值,比期望值小就新創建Pod,於是它告訴ApiServer要創建Pod
  6. apiserver將創建pod的請求寫入etcd集羣
  7. apiserver接收etcd的創建pod的回調事件
  8. apiserver將創建pod的回調事件發送給scheduler,由它爲pod挑選一個合適的宿主node
  9. scheduler告訴apiserver,這個pod可以調度到哪個node上
  10. apiserver將scheduler告訴他的事件寫入etcd
  11. apiserver接收到etcd的回調,將更新pod的事件發送給對應node上的kubelet進程
  12. kubelet通過CRI接口同容器運行時(Docker)交互,維護更新對應的容器。
4.1.3、提供了完備的安全認證機制

ApiServer採用https+ca簽名證書強制雙向認證,也就說是想順利訪問通ApiServer的接口需要持有對應的證書

進一步瞭解Https/CA原理可以閱讀這篇筆記:十二張圖,從零理解對稱加密/非對稱加密/CA認證/以及K8S各組件證書頒發原由

4.1.4、典型使用場景

比如在K8S內部搭建一個Elasticsearch的集羣,每個pod中運行一個Pod,想搭集羣的前提是得先知道有哪些運行着ES的Pod的ip地址。獲取的途徑只有一個:問ApiServer要。

那應用程序怎麼知道ApiServer的地址呢?如下:

K8S中的Service佔用單獨的網段(啓用ipvs的前提下,我的service網段爲:192.168.0.0)

當我們安裝完K8S集羣后,在default命名空間中會有個默認的叫kubernetes的Service,這個service使用的service網段的第一個地址,而且這個service是ApiServer的service,換句話說,通過這個service可以訪問到apiserver

可以通過如下命令訪問到本地的apiserver

curl --cert /etc/kubernetes/pki/admin.pem \
		 --key /etc/kubernetes/pki/admin-key.pem \
		 --cacert /etc/kubernetes/pki/ca.pem https://127..0.1:6443/api/v1/

將回環地址換成名爲kubernetes的service的cluster-ip也能訪問到api-server

curl --cert /etc/kubernetes/pki/admin.pem  --key /etc/kubernetes/pki/admin-key.pem --cacert /etc/kubernetes/pki/ca.pem https://192.168.0.1:6443/api/v1/

4.1.5、Api Proxy接口

ApiServer提供了一種特殊的Restful接口,它本身不處理這些請求而將請求轉發給對應的Kubelet處理

如:關於Node相關的接口

# 查詢指定節點上所有的pod信息
/api/v1/nodes/{nodename}/proxy/pods
# 查詢指定節點上物理資源的統計信息
/api/v1/nodes/{nodename}/proxy/stats
# 查詢指定節點上的摘要信息
/api/v1/nodes/{nodename}/proxy/spec

更多接口查詢官網~

關於Pod的接口

# 訪問pod
/api/v1/namespace/{namespace-name}/pods/{name}/proxy
# 訪問pod指定路徑
/api/v1/namespace/{namespace-name}/pods/{name}/{path:*}

4.2、ControllerManager

圖片摘自Kubernetes權威指南

藉助上圖理解Controller的作用,它是K8S自動化管理各類資源的核心組件,所謂的自動化管理,其實就是實時監控集羣中各類資源的狀態的變化,不斷的嘗試將各類資源的副本數調整爲期望值~

ControllerManager細分爲8種Controller,如下

4.2.1、Replication Controller
  1. 確保任何時候ReplicaSet管理的Pod副本數量都爲期望值
  2. 實際值高於期望值告訴apisever應該關閉多餘的pod,反之亦然
  3. 只有當Pod的重啓策略爲Always,Replication Controller纔會管理
  4. Replication Controller通過標籤管理Pod,若pod標籤被改,pod將脫離管控
  5. 提供重新調度、彈性擴/縮容、滾動更新能力
4.2.2、Node Controller

我們通過kubectl工具請求K8S的ApiServer獲取集羣中的Node信息,如下

可以看到集羣中所有的節點和它的狀態都是Ready

apiserver是如何知道集羣中有哪些節點的呢?答案是:kubelet上報給apiserver,再經由watch機制轉發Node Controller,由Node Controller統一實現對集羣中Node的管理。若節點長時間異常,NodeControler會刪除該節點,節點上的資源重新調度到正常的節點中~

4.2.3、ResourceQuota Controller

ResourceQuota Controller的功能是確保:指定資源對象數量在任何時候都不要超過指定的最大數量,進而保證由於業務設計的缺陷導致整個系統的紊亂甚至宕機。

使用示例:將ResourceQuota創建到哪個namespace中,便對哪個namespace生效

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
  	# ==對象數量配額==
    # 最多創建的cm數
    configmaps: "10"
    # 最多創建的pvc數
    persistentvolumeclaims: "4"
    # 最多啓動的pod數
    pods: "4"
    # 在該命名空間中允許存在的 ReplicationController 總數上限。
    replicationcontrollers: "20"
    # 最多創建的secret數
    secrets: "10"
    # 最多創建的service數
    services: "10"
    # 在該命名空間中允許存在的 LoadBalancer 類型的 Service 總數上限。
    services.loadbalancers: "2"
    # 在該命名空間中允許存在的 NodePort 類型的 Service 總數上限。
    services.nodeports: 2
    
    
    # ==存儲型==
    # 所有 PVC,存儲資源的需求總量不能超過該值。
    requests.storage: 40Gi
    # 在命名空間的所有 Pod 中,本地臨時存儲請求的總和不能超過此值。
    requests.ephemeral-storage: 512Mi
    # 在命名空間的所有 Pod 中,本地臨時存儲限制值的總和不能超過此值。
    limits.ephemeral-storage: 40Gi
    # ephemeral-storage 	與 requests.ephemeral-storage 相同。
    
    
    # ==cpu、memory==
    # 所有非終止狀態的 Pod,其 CPU 限額總量不能超過該值。
    limits.cpu: 1
    # 所有非終止狀態的 Pod,其內存限額總量不能超過該值。
    limits.memory: 16Gi
    # 所有非終止狀態的 Pod,其 CPU 需求總量(pod中所有容器的request cpu)不能超過該值。
    requests.cpu: 0.5
    # 所有非終止狀態的 Pod,其內存需求總量(pod中所有容器的request cpu)不能超過該值。
    requests.memory: 512Mi
    # cpu: 0.5       和requests.cpu相同
    # memory: 512Mi  和 requests.memory相同

目前支持的資源配額如下

  • 容器級別
    • 限制容器的CPU、內存
  • Pod級別
    • 對Pod中所有的容器的可用之和資源統一限制
  • Namespace級別
    • Pod數量
    • RC的數量
    • Service數量
    • ResourceQuota的數量
    • Secret數量
    • 可持有的PV的數量等

感興趣自己去了解LimitRange資源對象,LimitRange的配額自動爲Pod添加Cpu和Memory的配置,這裏不再展開

4.2.4、Namespace Controller

用戶通過apiserver創建namespace,apiserver會將namespace的信息存儲入etcd中。

namespaceController通過apiserver讀取維護這些namespace的信息,若ns被標記爲刪除狀態,namespaceController會將其狀態改爲Terminating

然後會刪除它裏面的ServiceAccount、RC、Pod等一切資源對象。

4.2.5、Service Controller - Endpoint Controller

Service Controller 會監聽維護Service的狀態、變化

Service、Pod、Endpoint之間的關係如下圖:Service通過標籤選擇器找到並代理對應的pod,pod的ip維護和Service同名的Endpoint中~

Endpoint Controller的作用是:監聽Service和他對應的pod的變化

  • 當Service被刪除時,同步刪除和Service同名的Endpoint對象
  • 新建/更新Service時,同步更新對應Endpoint的ip:port列表信息

endpoint對象由每個node上的kube-proxy的進程使用,下文會說

4.3、Scheduler

圖片摘自Kubernetes權威指南

負責接收ApiServer創建Pod的請求,爲Pod選擇一個合適的Node,由該Node上的kubelet啓動Pod中的相應容器

調度流程分兩大步

  • 預選策略
    • 初步篩選出符合條件的Node
  • 優選策略
    • 在第一步的基礎上選擇更合適的Node

4.4、Kubelet

4.4.1、Node管理

通過設置啓動參數register-node參數開啓kubelet向apiserver註冊自己的信息,apiserver會將數據

註冊之後通過kubectl如下命令可用查看到集羣中的所有node 的信息

若Node有問題,可以看到Status被標記爲NotReady

4.4.2、Pod管理

圖片摘自Kubernetes權威指南

最終管理員下發的創建Pod的請求由Kubelet轉發給它所在節點的容器運行時(Docker)處理

4.4.3、容器健康檢查

容器的livenessProbe探針(httpGet、tcpSocket、ExecAction)用於判斷容器的健康狀態。

kubelet會定期調用這些探針來判斷容器是否存活~

httpGet

向容器發送Http Get請求,調用成功(通過Http狀態碼判斷)則確定Pod就緒;

使用方式:

livenessProbe:
  httpGet:
    path: /app/healthz
    port: 80

exec

在容器內執行某命令,命令執行成功(通過命令退出狀態碼爲0判斷)則確定Pod就緒;

使用方式:

livenessProbe:
  exec:
    command:
    - cat 
    - /app/healthz

tcpSocket

打開一個TCP連接到容器的指定端口,連接成功建立則確定Pod就緒。

使用方式:

livenessProbe:
  tcpSocket:
    port: 80

一般就緒探針會在啓動容器一段時間後纔開始第一次的就緒探測,之後做週期性探測。所以在定義就緒指針時,會給以下幾個參數:

  • initialDelaySeconds:在初始化容器多少秒後開始第一次就緒探測;
  • timeoutSeconds:如果該次就緒探測超過多少秒後還未成功,判定爲超時,該次探測失敗,Pod不就緒。默認值1,最小值1;
  • periodSeconds:如果Pod未就緒,則每隔多少秒週期性的做就緒探測。默認值10,最小值1;
  • failureThreshold:如果容器之前探測成功,後續連續幾次探測失敗,則確定容器未就緒。默認值3,最小值1;
  • successThreshold:如果容器之前探測失敗,後續連續幾次探測成功,則確定容器就緒。默認值1,最小值1。

4.5、KubeProxy

K8S原生的支持通過Service的方式代理一組Pod暴露到集羣外部對外提供服務,創建Service時會這個Service創建一個Cluster-IP。而kubeproxy本質上就是這個Service的cluster-ip的負載均衡器。

在k8s的不同版本中,kubeproxy負載均衡Service的ClusterIp的方式不盡相同,主要如下

4.5.1、k8s1.2版本前

如上圖這個版本的KubeProxy的更像是一個TCP/UDP代理,最明顯的特徵是:外部用戶訪問cluster:port過來的流量真實的經過了KubeProxy的處理和轉發,即流量經過了userspace中。

4.5.2、k8s1.2~1.7版本

在這個版本KubeProxy端明顯特徵是:用戶的流量不再由kubeproxy負責轉發負載均衡。kubeproxy僅負責通過apiserver獲取service和它對應的endpoint,然後根據這些數據信息生成iptables的規則,用戶的流量通過iptables找到最終的pod。

4.5.3、k8s1.8版本及之後

和上一個版本的區別是支持了ipvs模塊。

兩者的定位不同:雖然iptables和ipvs都是基於netfilter實現的,但是iptables爲防火牆而設計,而ipvs的定位是高性能的負載均衡。

在數據結構層面實現不同:iptables是鏈式結構,流量流經iptables要過所謂的四表五鏈,當集羣中中的pod數量劇增,iptables會變得十分臃腫,效率下降。而ipvs是基於hash表實現的,理論上支持無限擴張,且尋址快。

ipvs也有不足,比如不支持包過濾、地址僞裝、SNAT,在使用Nodeport類型的Service時,依然需要和iptables搭配使用。

點擊查看白日夢的iptables筆記

視頻串講iptables和docker網絡

五、網絡組件

5.1、CoreDNS

CoreDNS是K8S的服務發現機制(有DNS能力),實現了通過解析service-name解析出cluster-ip的能力。而且service被重新創建時cluter-ip是可能發生變化的,但是server-name不會改變。

我們是以POD的形式將CoreDNS部署進K8S的,如下查看CoreDNS的service

這裏的kube-dns的ip地址通常可以在安裝coredns的配置文件中指定:coredns.yaml

驗證下kube-dns解析外網域名的DNS能力

[root@master01 ~]# yum -y install bind-utils
[root@master01 ~]# dig -t A www.baidu.com @192.168.0.10  +short
www.a.shifen.com.
39.156.66.18
39.156.66.14

驗證下kube-dns解析service名的DNS能力

 # ${serviceName}.${名稱空間}.svc.cluster.local.
[root@master01 ~]# dig -t A kubernetes.default.svc.cluster.local. @192.168.0.10 +short
192.168.0.1

5.2、CNI網絡插件

像Calico或者flanel等,都是符合CNI規範的網絡插件,作用如下:

  • 保證集羣中的pod生成集羣內全局唯一的IP地址。
  • 爲K8S集羣提供了一個扁平化的集羣網絡(service網絡)意思就是說,它實現了集羣內的Pod跨node也能直接互通。

注意點:flanel不支持NetworkPolicy資源對象定義的網絡策略,而Calico支持。

什麼是網絡策略?如下

  • 只有來自IngressController的流量才能訪問帶有role=frontend標籤的pod
  • 只有namespaceA的pod不能訪問其他namespace的pod
  • 只有源ip地址在某個網段的請求才能訪問Project-A
  • 從ProjectB出去的流量指定訪問指定網段的Pod

參考:kubernetes官網、《kubernetes權威指南》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章