Kubernetes 核心原理
本文主要介紹 k8s 的核心原理,包括淺析各個模塊的運行邏輯和 k8s 中的網絡通訊。
第一部分:模塊
核心架構
以上是在 k8s 中的各個模塊,下面就來詳細介紹一下各個模塊的作用和原理。
1、API Server
API Server 是集羣管理的 API 入口,控制資源配額,提供了完備的安全機制,包括增刪改查的 Rest API,也包括一些實時監聽的 Watch 接口。
以下是一個簡單的樣例,通過訪問 API 可以直接獲取信息:
[root@VM-0-11-centos ~]# curl -i 'http://localhost:8080/api'
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 24 Oct 2023 00:40:21 GMT
Content-Length: 183
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "172.27.0.11:6443"
}
]
}
通過 API Server 的接口可以操作 k8s 集羣或者查看集羣內的詳細信息,除了上面的接口還有如下接口可以嘗試:
curl localhost:8080/api/v1/pods # 返回集羣的pod列表
curl localhost:8080/api/v1/services # 返回集羣的service列表
curl localhost:8080/api/v1/replicationcontrollers # 返回集羣的rc列表
通過 API Server 我們不僅可以查詢 Master 上面的信息,還可以通過 API Server 將請求轉發到某個 Node 上,如:
curl -i 'http://127.0.0.1:8080/api/v1/proxy/nodes/127.0.0.1/pods/' # 指定節點127.0.0.1上的所有Pod信息
curl -i 'http://127.0.0.1:8080/api/v1/proxy/nodes/127.0.0.1/stats/' # 指定節點內物理資源的統計信息
curl -i 'http://127.0.0.1:8080/api/v1/proxy/nodes/127.0.0.1/spec/' # 指定節點的概要信息
curl -i 'http://127.0.0.1:8080/api/v1/proxy/nodes/127.0.0.1/metrics' # 節點上的metrics信息
curl -i 'http://127.0.0.1:8080/api/v1/proxy/nodes/127.0.0.1/logs/' # 節點上的日誌信息
... # 其他一些相關的API可以查看官方文檔
2、模塊之間通訊
從 k8s 的基礎架構圖中我們知道,k8s 包括:API Server,kubelet,etcd,controller-manager,scheduler 等,那他們之間是如何通訊的?
(1)etcd 是存儲各個模塊的狀態信息,操作數據等;
(2)每個 Node 節點上運行着 kubelet 進程,每隔一個時間週期就會調用一次 API Server 的接口,報告當前 Node 狀態,同時 API Server 收到這些信息以後,存入 etcd 中;
(3)kubelet 除了定時上報信息,還有監聽 API Server 的 Watch 接口,如果需要在本節點調度 Pod,則會執行創建或者銷燬等邏輯;
(4)controller-manager 也會與 API Server 交互,通過 API Server 的 Watch 接口,監聽 kubelet 上報過來的 Node 信息,如果 Node 掛了則負責重新調度,當然 controller-manager 還控制着其他各個 controller 模塊;
(5)scheduler 是通過 API Server 監聽 Pod 信息後,負責檢索所有符合該 Pod 條件的 Node 列表,執行 Pod 的調度,調度成功後將 Pod 綁定到目標節點上;
3、contoller-manager
contoller-manager 是集羣內的管理中心,負責集羣的 Node,Pod,Endpoint,Namespace,ServiceAccount,ResourceQuota 等的管理,當 Node 出現問題時,contoller-manager 負責及時發現故障,並執行自動修復流程,確保達到預期的工作狀態。
在 k8s 中有一些邏輯 contoller,分別管理集羣的每一個部分的控制權,下面是常用的介紹:
-
ReplicationController:核心作用是確保 - 個 RC 所關聯的 Pod 數量保持預設值,如果發現超過則銷燬一些,如果發現少了則增加一些,異常情況會銷燬重新調度到其他 Node 節點下,其中 RC 使用場景是:重新調度,彈性擴縮容和滾動更新;
-
NodeController:作用是通過 API Server 實時獲取 Node 信息,實現管理和監控集羣的 Node 節點。NodeController 會從 kubelet 上報的信息中判斷節點狀態信息,如果節點狀態變成非 "就緒",則將節點加入待刪除隊列,如果變成非就緒同時指定了 --cloud-provider 選項,則調用 cloudprovider 查看節點信息,如果有故障則刪除節點和節點上的相關的 Pod 等資源;
-
ResourceQuotaController:資源配額管理,作用是確保指定的資源對象在任何時候都不會超量佔用系統物理資源,防止雪崩。ResourceQuota 作用於 Namespace,限定一個 Namespace 裏的各類資源使用總額;
-
EndpointsController:負責監聽 Service 和對應 Pod 的副本變化,如果 Service 被刪除,則刪除關聯的 Endpoints 對象,如果被修改則更新 Pod 信息和 Endpoints;
4、scheduler
前面說到 controller 是負責創建 Pod,那麼如何調度到目標節點?scheduler 的作用就是將待調度的 Pod,按照特定的調度算法和調度策略調度到合適的 Node 節點上,並將信息保存到 etcd 中,scheduler 寫入信息後,節點上的 kubelet 通過監聽 API Server 獲取到對應需要調度的 Pod,執行鏡像下載,啓動容器等。
scheduler 架構圖
scheduler 調度簡單分爲兩步:
(1)預選調度過程,遍歷所有的目標 Node,篩選候選節點;
(2)確定最優節點,通過策略計算每個候選節點的分數,然後最高分數節點獲勝後確認調度節點;
其中 scheduler 調度流程可以通過 AlgorithmProvider 實現,其註冊函數如下:
func RegisterAlgorithmProvider(name string, predicateKeys, priorityKeys util.StringSet)
-
name:調度名稱
-
predicateKeys:算法預選策略集合,包括 NoDiskConflict,PodFitsResources,PodSelectorMatches,PodFitsHost 等,主要是通過加載多種策略來選擇 Node 節點
-
priorityKeys:算法優先策略集合,包括 LeastRequestedPriority,CalculateNodeLabelPriority,BalacedResourceAllocation 等,通過計算節點得分來決策 Node 節點
5、kubelet
在 k8s 集羣中,每個 Node 節點上都會啓動一個 kubelet 進程,從前面可以看出 kubelet 主要作用是符合幫助 Master 代理執行各種在 Node 上的命令,那具體會執行哪些呢?
(1)註冊
kubelet 啓動後,可以向 API Server 註冊,定時向 API Server 發送節點的新消息,然後 API Server 收到消息後都會寫入 etcd 中,有一些常用的啓動命令如下。
-
--api-servers:API Server 的 ip 和端口
-
--node-status-update-frequency:kubelet 上報 API Server 的時間週期,默認是 10s
(2)Pod 管理
kubelet 主要的作用就是監聽 Pod 任務,創建或者刪除,其步驟是:
-
從 API Server 讀取 Pod 任務清理
-
掛載外部捲到 Pod 中
-
下載 Pod 所需的安全文件 Secret
-
檢查是否已經有運行的 Pod,如果 Pod 沒有容器或者 pause 容器沒有啓動,則停止 Pod 的所有容器
-
爲每一個 Pod 啓動 pause 容器,通過 pause 容器共享當前 pod 的所有資源
-
啓動 pause 容器後,接着計算容器 hash,通過 hash 查找到容器,如果找到則不處理,沒有找到停止之前關聯的並停止容器
(3)監控
kubelet 在節點上要監聽 Pod 和 Node,這樣才能將信息上報給 API Server,以便 Master 節點做出決策。
-
kubelet 監控 Node 節點上的 Pod 是否正常,其中 Pod 提供了 LivenessProbe 和 ReadinessProbe 兩種探針檢測方式,具體的 yaml 配置可以看看《雲原生二十篇 | Kubernetes 基礎知識》,這裏 kubelet 底層是通過定時調用
ExecAction
,TCPSocketAction
和HTTPAction
實現 LivenessProbe 功能,從而判斷容器是否正常 -
kubelet 監控 Node 本身的信息和各個 Pod 的信息並上報,比如磁盤,CPU,IO 性能等,其原理是 kubelet 通過 cAdvisor 獲取對應的監控數據
6、kube-proxy
從前面的文章我們已經知道,可以通過 Service 提供對集羣外的服務,其背後的服務實際上是 Node 上的 kube-proxy 模塊,那 kube-proxy 具體做了哪些事情?
(1)每個 Node 上都會啓動 kube-proxy 進程,該進程相當於是一個透明代理,功能是將請求轉發到後端的 Pod 上;
(2)kube-proxy 運行過程中,動態創建與 Service 相關的 Iptables 規則,實現 Cluster IP+NodePort 的流量重定向,並將流量都轉發到 kube-proxy 對應服務的代理端口中;
(3)kube-proxy 查詢和監聽 API Server 中的 Service 和 Endpoints 的變化,創建服務代理對象,創建一個隨機端口,將 Pod 與 Node 本地端口映射,然後通過修改 Iptables 的規則,將請求轉發給映射端口,從而經過 DNAT 轉發到 Pod 內部;
不過值得注意的是現在 k8s 提供 IPVS 的模式,解決了 Iptables 配置規則太多導致的性能問題,具體網絡的一些詳細的內容放到網絡部分具體講解。
第二部分:網絡
k8s 實現網絡通訊主要包括兩個部分:集羣內和集羣外,其中集羣內通訊又包括 Pod 之間的通訊,容器之間的通訊,具體容器之間的通訊可以看《雲原生二十篇 | Docker 網絡篇》這篇文章,這裏已經詳細講述 Docker 的通訊原理,其他的部分接下來我們一起探索。
1、Pod 到 Pod 之間的網絡
Pod 與 Pod 的網絡通訊包括同一個 Node 下的 Pod 和不同 Node 下的 Pod:
(1)相同 Node 下的 Pod
bridge
同一個 Pod 下的通訊其實就是容器和容器之間通訊,其容器通訊如下:
-
通過
Network Namespace, bridge和veth pair
這三個虛擬設備實現一個簡單的二層網絡,不同的 namespace 實現了不同容器的網絡隔離讓他們分別有自己的 ip,通過veth pair
連接到docker0
網橋上實現了容器間和宿主機的互通; -
容器與外部或者主機通過端口映射通訊是藉助
iptables
,通過路由轉發到docker0
,容器通過查詢CAM
表,或者UDP
廣播獲得指定目標地址的 MAC 地址,最後將數據包通過指定目標地址的連接在docker0
上的veth pair
設備,發送到容器內部的eth0
網卡上;
(2)不同 Node 下的 Pod
-
不同的 Pod 在 k8s 中都會有獨立的虛擬 IP,當然這個 IP 只能在 k8s 當前集羣內通訊,不會衝突;
-
不同的 Pod 下的通訊看似容器通訊,其實通訊比 Docker 要簡單一些,不需要端口映射,因爲 PodIP 全局唯一了,所以只需要在每個 Node 節點上增加
ip route
,這樣每個 Node 節點就像路由器負責找到對應的 ip; -
k8s 的 etcd 中存儲了 Pod 的 IP 與 Node 映射關係,Pod 的 IP 在哪個 Node 下,Node 下有哪些 IP,這裏會以資源的形式統一管理;
-
從上圖看出 Pod 本身是多個容器組成,這裏的通訊方式根據安裝的網絡插件實現;
2、Service 到 Pod 之間網絡
官方架構圖
Service 是一組 pod 的服務抽象,相當於一組 pod 的 LB,負責將請求分發給對應的 pod,Service 會爲這個 LB 提供一個 IP,一般稱爲 cluster IP,下面我們來查看一下 Service,並查看路由轉發規則:
執行如下:
[root@VM-0-11-centos ~]# kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 14d
ngx-service 10.254.214.193 <nodes> 80:32500/TCP 5m
查看 iptable,執行 iptables-save:
...
[root@VM-0-11-centos ~]# iptables-save|grep 32500
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/ngx-service:" -m tcp --dport 32500 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/ngx-service:" -m tcp --dport 32500 -j KUBE-SVC-UY6H3HDLGIYXAG5H
[root@VM-0-11-centos ~]# iptables-save|grep KUBE-SVC-UY6H3HDLGIYXAG5H
:KUBE-SVC-UY6H3HDLGIYXAG5H - [0:0]
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/ngx-service:" -m tcp --dport 32500 -j KUBE-SVC-UY6H3HDLGIYXAG5H
-A KUBE-SERVICES -d 10.254.214.193/32 -p tcp -m comment --comment "default/ngx-service: cluster IP" -m tcp --dport 80 -j KUBE-SVC-UY6H3HDLGIYXAG5H
-A KUBE-SVC-UY6H3HDLGIYXAG5H -m comment --comment "default/ngx-service:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-NRDWGG4MSHANWBLB
-A KUBE-SVC-UY6H3HDLGIYXAG5H -m comment --comment "default/ngx-service:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-AV5WRKSNPI6I35GT
-A KUBE-SVC-UY6H3HDLGIYXAG5H -m comment --comment "default/ngx-service:" -j KUBE-SEP-T3MMHN7MMLAMVXYV
[root@VM-0-11-centos ~]# iptables-save|grep KUBE-SEP-AV5WRKSNPI6I35GT
:KUBE-SEP-AV5WRKSNPI6I35GT - [0:0]
-A KUBE-SEP-AV5WRKSNPI6I35GT -s 172.17.0.4/32 -m comment --comment "default/ngx-service:" -j KUBE-MARK-MASQ
-A KUBE-SEP-AV5WRKSNPI6I35GT -p tcp -m comment --comment "default/ngx-service:" -m tcp -j DNAT --to-destination 172.17.0.4:8081
-A KUBE-SVC-UY6H3HDLGIYXAG5H -m comment --comment "default/ngx-service:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-AV5WRKSNPI6I35GT
從上面信息可以看到 clusterIP 的 Port 和 NodePort 都會將 32500 和 80 轉發(執行iptables-save|grep KUBE-SVC-UY6H3HDLGIYXAG5H
的輸出)到KUBE-SVC-UY6H3HDLGIYXAG5H
,然後KUBE-SVC-UY6H3HDLGIYXAG5H
對應-j KUBE-SVC-UY6H3HDLGIYXAG5H
,最後KUBE-SEP-AV5WRKSNPI6I35GT
映射到-A KUBE-SEP-AV5WRKSNPI6I35GT -p tcp -m comment --comment "default/ngx-service:" -m tcp -j DNAT --to-destination 172.17.0.4:8081
,將流量進行導向到後端的 pod 上。
當有多個 Pod 服務,Service 如何實現負載均衡呢?可以看到上面的第二行命令iptables-save|grep KUBE-SVC-UY6H3HDLGIYXAG5H
輸出--mode random --probability
,這是利用了 iptables 的 --probability 的特性,一定概率進入某個 Pod 中。
3、開源的網絡組件
以下是一些支持 k8s 的開源網絡組件,這裏簡單介紹:
(1)Flannel
Flannel
主要功能:
-
爲每個 Node 上的 Pod 分配相互不衝突的 IP 地址
-
基於 k8s 集羣內建立一個 Overlay Network,通過 Overlay Network 可以將數據包原封不動的轉發給目標 Pod
(2)Calico
Calico
是一個純三層的虛擬網絡方案,Calico爲
每個容器分配一個 IP,每個 host 都是 router,把不同 host 的容器連接起來,與 VxLAN 不同的是,Calico
不對數據包做額外封裝,不需要 NAT 和端口映射,擴展性和性能都很好,Calico
依賴etcd
在不同主機間共享和交換信息,存儲Calico
網絡狀態。
(3)Weave
Weave
是 Weaveworks 開發的容器網絡解決方案,Weave
創建的虛擬網絡可以將部署在多個主機上的容器連接起來。
-
對容器來說,
Weave
就像一個巨大的以太網交換機,所有容器都被接入這個交換機,容器可以直接通信,無需 NAT 和端口映射 -
Weave
的DNS
模塊使容器可以通過hostname
訪問 -
Weave
不依賴分佈式數據庫(例如etcd
和consul
)交換網絡信息,每個主機上只需運行Weave
組件就能建立起跨主機容器網絡
參考
(1)https://zhuanlan.zhihu.com/p/476740348
(2)https://zhuanlan.zhihu.com/p/450263411?utm_id=0
(3)https://zhuanlan.zhihu.com/p/637672338
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/FiXMBmoC_NhYNbI2To7u0Q