k8s 中的 service 如何找到綁定的 Pod 以及如何實現 Pod 負載均衡( 二 )


同理對于外部訪問 service 的請求,不論是 Cluster IP+TargetPort 的方式;還是用 Node 節點 IP+NodePort 的方式,都被 Node 節點的 Iptables 規則重定向到 Kube-proxy 監聽 Service 服務代理端口 。kube-proxy 接收到 Service 的訪問請求后,根據負載策略,轉發到后端的 Pod 。
kube-proxy 的路由轉發規則是通過其后端的代理模塊實現的,其中 kube-proxy 的代理模塊目前有四種實現方案,userspace、iptables、ipvs、kernelspace。
userspace 模式userspace 模式在 k8s v1.2 后就已經被淘汰了,userspace 的作用就是在 proxy 的用戶空間監聽一個端口,所有的 svc 都轉到這個端口,然后 proxy 內部應用層對其進行轉發 。proxy 會為每一個 svc 隨機監聽一個端口,并增加一個 iptables 規則 。
從客戶端到 ClusterIP:Port 的報文都會通過 iptables 規則被重定向到 Proxy Port,Kube-Proxy 收到報文后,然后分發給對應的 Pod 。

k8s 中的 service 如何找到綁定的 Pod 以及如何實現 Pod 負載均衡

文章插圖
userspace 模式下,流量的轉發主要是在用戶空間下完成的,上面提到了客戶端的請求需要借助于 iptables 規則找到對應的 Proxy Port,因為 iptables 是在內核空間,這里就會請求就會有一次從用戶態到內核態再返回到用戶態的傳遞過程, 一定程度降低了服務性能 。所以就會認為這種方式會有一定的性能損耗 。
默認情況下,用戶空間模式下的 kube-proxy 通過輪轉算法選擇后端 。
iptables首先來簡單了解下 iptables:
iptables 是 Linux 中最常用的一種防火墻工具 , 除了防火墻它還可以用作 IP 轉發和簡單的負載均衡功能 ?;?Linux 中的 netfilter 內核模塊實現 。Netfilter 在協議中添加了一些鉤子,它允許內核模塊通過這些鉤子注冊回調函數,這樣經過鉤子的所有數據都會被注冊在響應鉤子上的函數處理 , 包括修改數據包內容、給數據包打標記或者丟掉數據包等 。iptables 是運行在用戶態的一個程序 , 通過 netlink 和內核的 netfilter 框架打交道,具有足夠的靈活性來處理各種常見的數據包操作和過濾需求 。它允許將靈活的規則序列附加到內核的數據包處理管道中的各種鉤子上 。
Netfilter 是 Linux 2.4.x 引入的一個子系統,它作為一個通用的、抽象的框架,提供一整套的 hook 函數的管理機制 , 使得諸如數據包過濾、網絡地址轉換(NAT)和基于協議類型的連接跟蹤成為了可能 。
在 kubernetes v1.2 之后 iptables 成為默認代理模式,這種模式下,kube-proxy 會監視 Kubernetes master 對 Service 對象和 Endpoints 對象的添加和移除 。對每個 Service,它會安裝 iptables 規則,從而捕獲到達該 Service 的 clusterIP(虛擬 IP)和端口的請求,進而將請求重定向到 Service 的一組 backend 中的某個上面 。因為流量轉發都是在內核進行的,所以性能更高更加可靠 。
k8s 中的 service 如何找到綁定的 Pod 以及如何實現 Pod 負載均衡

文章插圖
可以看到該模式下 iptables 來做用戶態的入口 , kube-proxy 只是持續監聽 Service 以及 Endpoints 對象的變化,iptables 通過設置的轉發策略 , 直接將對 VIP 的請求轉發給后端 Pod,iptables 使用 DNAT 來完成轉發,其采用了隨機數實現負載均衡 。
如果 kube-proxy 在 iptables 模式下運行,并且所選的第一個 Pod 沒有響應 , 則連接失敗 。這與用戶空間模式不同:在這種情況下 , kube-proxy 將檢測到與第一個 Pod 的連接已失敗,并會自動使用其他后端 Pod 重試 。
該模式相比 userspace 模式,克服了請求在用戶態-內核態反復傳遞的問題 , 性能上有所提升 , 但使用 iptables NAT 來完成轉發,存在不可忽視的性能損耗,iptables 模式最主要的問題是在 service 數量大的時候會產生太多的 iptables 規則,使用非增量式更新會引入一定的時延,大規模情況下有明顯的性能問題 。
ipvs當集群的規模比較大時,iptables 規則刷新就會很慢,難以支撐大規模的集群 。因為 iptables 的底層實現是鏈表,對路由規則的增刪查改都需要遍歷一次鏈表 。
在 kubernetes v1.2 之后 ipvs 成為kube-proxy的默認代理模式 。ipvs 正是解決這一問題的 , ipvs 是 LVS 的負載均衡模塊,與 iptables 比較像的是,ipvs 的實現雖然也基于 netfilter 的鉤子函數 , 但是它卻使用哈希表作為底層的數據結構并且工作在內核態,也就是說 ipvs 在重定向流量和同步代理規則有著更好的性能,幾乎允許無限的規模擴張 。
k8s 中的 service 如何找到綁定的 Pod 以及如何實現 Pod 負載均衡

推薦閱讀