Kubernetes探索實踐之網絡通信

前言

計算機間的信息和數據在網絡中必須按照數據傳輸的順序、數據的格式內容等方面的約定或規則進行傳輸,這種約定或規則稱做協議。各種網絡協議分佈於不同的網絡分層中,網絡分層分爲OSI七層模型和TCP/IP五層模型兩種。TCP/IP五層模型分別是應用層、傳輸層、網絡層、鏈路層和物理層,與OSI七層模型的區別是在TCP/IP五層模型中,OSI七層模型中的會話層、表示層、應用層統一被稱爲應用層。計算機網絡數據是按照協議規範採用分層的結構由發送端自上而下流動到物理層,再從物理層在網絡分層中自下而上的流動到接收端的應用層,完成數據通信。網絡分層中,高層級的應用模塊僅利用低層級應用模塊提供的接口和功能,低層級應用模塊也僅是使用高層級應用模塊傳來的參數響應相關的操作,層次間每個應用模塊都是可被能提供相同功能的應用模塊替代。

Kubernetes的網絡通信也遵守TCP/IP五層模型的定義,通過不同的資源對象在相應的層級提供相應的模塊功能。Kubernetes資源對象在相應的網絡層級與傳統網絡設備模塊的對照表如表12-2所示。

表 12-2 設備模塊對照表

網絡分層 設備模塊 Kubernetes資源對象
應用層 F5、Haproxy、Nginx Ingress
傳輸層 四層交換、路由 Service
網絡層 路由器、三層交換機 flannel、calico、Pod(容器間通信)
鏈路層 網橋、二層交換機、網卡 vnet、bridge
物理層 中繼器、集線器、網線

原生Docker的網絡通信

Kubernetes是基於容器的管理系統,其使用Docker容器版本的Pod是由多個Docker容器組成的,爲方便理解Pod的網絡通信方式,先了解下Docker自有的網絡模式,Docker容器有如下4種常見的網絡模式。

  • 主機模式(host),容器與宿主機共享網絡命名空間(netns,network namespace),在容器中可以查看到宿主機的所有網卡,可以通過訪問宿主機IP,訪問到容器中運行應用的所有網絡端口。主機模式下網絡傳輸效率最高,但宿主機上已經存在的網絡端口將無法被該模式下的容器使用。

  • 無網卡模式(none),容器中只有環回(lo,Lookback)接口,運行在容器內的應用僅能使用環回接口實現網絡層的數據傳輸。

  • 橋接模式(bridge), 容器內會被創建veth(Virtual ETHernet)設備並接入宿主機的橋接網絡,通過宿主機的橋接網絡,可與宿主機及宿主機中接入同一橋設備的其它容器進行通信。

  • Macvlan網絡模式(macvlan),當宿主機的網絡存在多個不同的VLAN時,可以通過該模式爲容器配置VLAN ID, 使該容器與宿主機網絡中同一VLAN ID的設備實現網絡通信。

Docker容器間可以通過IP網絡、容器名解析、joined容器三種方式實現通信。IP網絡是在網絡聯通的基礎上通過IP地址實現互訪通信。容器名解析是在網絡聯通的基礎上,由Docker內嵌的DNS進行容器名解析實現的互訪通信方式,同一主機橋接模式的容器間需要啓動時使用“--link”參數啓用這一功能。joined 容器方式可以使多個容器共享一個網絡命名空間,多個容器間通過環回接口直接通信,這種方式容器間傳輸效率最高。

Kubernetes的網絡通信

Kubernetes的Pod網絡通信包括如下幾種方式:

Pod內容器間的數據通信

Pod是由多個Docker容器以joined容器方式構成的,多個容器共享由名爲pause的容器創建的網絡命名空間(netns,network namespace),容器內的進程彼此間通過環回(lo,Lookback)接口實現數據通信。環回接口不依賴鏈路層和物理層協議,一旦傳輸層檢測到目的端地址是環回接口地址時,數據報文離開網絡層時會把它返回給自己。這種模式傳輸效率較高,非常適用於容器間進程的頻繁通信。

同節點的Pod間數據通信

每個Pod擁有唯一的IP和彼此隔離的網絡命名空間,在Linux系統中,Pod間跨網絡命名空間的數據通信是通過Veth(Virtual ETHernet)設備實現的。Veth設備工作在鏈路層,總是成對出現也被稱爲Veth-pair設備。在網絡插件是Flannel的虛擬網絡結構中,Flannel被Kubernetes觸發並收到相關Pod參數時,會爲Pod創建Veth設備並分配IP,Veth設備一端是Pod的eth0接口,一端是Node節點中網絡空間名爲default的Veth虛擬接口。Flannel在初始安裝時,創建了網橋設備cni0,網絡空間default中被創建的Veth虛擬接口都被加入到網橋設備cni0中,相當於所有的Pod都被接入到這個虛擬交換機中,在同一虛擬交換機中的Pod實現了鏈路層的互聯並進行網絡通信。工作原理如圖12-5所示。

Kubernetes探索實踐之網絡通信

圖 12-5 同節點的Pod間數據通信

可用如下命令查看當前節點服務器的網絡命名空間和網橋信息。

# 查看系統中的網絡命名空間
ls /var/run/docker/netns

# 查看每個命名空間的網絡接口信息
nsenter --net=/var/run/docker/netns/default ifconfig -a

# 查看網橋信息
brctl show

跨主機的Pod間數據通信

Flannel是由CoreOS使用Go語言開發的,Flannel實現了一種基於Vxlan(Virtual eXtensible Local Area Network)封裝的覆蓋網絡(overlay network),其將TCP數據封裝在另一種網絡包中進行路由轉發和通信。

Vxlan協議是一種隧道協議,基於UDP協議傳輸數據。Flannel的Vxlan虛擬網絡是比較簡單的,在每個Kubernetes節點上只有1個Vxlan網絡和1個Vxlan接口(默認爲flannel.1)。Vxlan接口叫做VTEP(Vxlan tunnel endpoint)設備,VTEP設備的MAC地址由Flannel被Kubernetes觸發並同步緩存在本地的fdb(forwarding database)中。Kubernetes集羣中整個flannel網絡默認配置網段爲10.244.0.0/16,每個節點都分配了唯一的24位的子網,Flannel在Kubernetes集羣中類似一個傳統網絡中的三層交換設備,每個Node節點的橋設備通過VTEP設備接口互聯,使運行在不同Node節點中不同子網IP的容器實現跨主機互通。

可用如下命令查看當前節點服務器的arp信息。

# 本地橋 arp表
bridge fdb

bridge fdb show dev flannel.1

Pod應用在Kubernetes集羣內發佈服務

Kubernetes 通過副本集控制器能夠動態地創建和銷燬 Pod,每個Pod可被動態的創建或銷燬於集羣中的任意一個節點,因此Pod IP也將隨之變化。Flannel構建的虛擬網絡使得集羣中的每個Pod在網絡上已經實現互聯互通,由於Pod IP變化的不確定性,使得運行在Pod中的應用服務無法被其他應用固定訪問。爲使動態變化IP的Pod應用可以被其他應用訪問,Kubernetes通過標籤篩選的形式對具有相同指定標籤的一組Pod定義爲Service,每個Service的Pod成員信息通過端點控制器在etcd中保存及更新。Service爲應用Pod提供了固定的虛擬IP和端口用以提供固定的訪問,讓集羣內其他Pod應用可以訪問這個服務。

Service 是 “4層”(TCP/UDP over IP)概念,其構建了一個有固定ClusterIP(集羣虛擬IP,Virtual IP) 和 Port 的虛擬集羣,每個節點上運行的kube-proxy 進程通過主節點的接口服務監控資源對象Service和 Endpoints內Pod列表的變化,kube-proxy 默認使用iptables 代理模式,其通過對每個Service配置對應的iptables 規則捕獲到達該 Service 的 ClusterIP和 Port 的請求,當捕獲到請求時,會將訪問請求按比例隨機分配給Service 中的一個Pod,如果被選擇的Pod沒有響應(依賴“readiness probes”的配置),則自動重試另一個Pod。Service訪問邏輯如圖12-6 所示。

Kubernetes探索實踐之網絡通信

圖 12-6 Service訪問邏輯

  • kube-proxy 根據集羣中Service和Endpoint資源對象的狀態初始化所在節點的iptables規則。
  • kube-proxy 通過接口服務監聽集羣中Service和Endpoint資源對象的變化並更新本地的iptables規則。
  • iptables規則監聽所有請求,將對應ClusterIP和 Port 的請求使用隨機負載均衡算法負載到後端Pod。

kube-proxy 在集羣中的每個節點都會配置集羣中所有Service 的iptables規則,iptables規則設置如下。

  • kube-proxy 首先是建立filter表的INPUT規則鏈和nat表的PREROUTING規則鏈,將訪問節點的流量全部跳轉到KUBE-SERVICES規則鏈進行處理。
  • kube-proxy 遍歷集羣中的Service資源實例,爲每個Service資源實例創建兩條 KUBE-SERVICES 規則。
  • KUBE-SERVICES 中一條規則是對訪問Service的非集羣Pod IP交由KUBE-MARK-MASQ規則標記爲0x4000/0x4000,在執行到POSTROUTING規則鏈時由KUBE-POSTROUTING規則鏈對數據流量實現SNAT。
  • KUBE-SERVICES 中另一條規則將訪問目標是Service的請求跳轉到對應的KUBE-SVC規則鏈。
  • KUBE-SVC 規則鏈是由組成目標Service端點列表中每個Pod的處理規則組成,這些規則包括隨機負載均衡策略及會話保持(sessionAffinity)的實現。
  • KUBE-SVC 每條規則鏈命名是將“服務名+協議名”按照SHA256 算法生成哈希值後通過base32對該哈希值再編碼,取編碼的前16位與KUBE-SVC爲前綴組成的字符串。
  • KUBE-SEP 每個Pod有兩條KUBE-SEP規則,一條是將請求數據DNAT到Pod IP,另一條用來將Pod返回數據交由KUBE-POSTROUTING規則鏈實現SNAT。
  • KUBE-SEP 每條規則鏈命名是將“服務名+協議名+端口”按照SHA256 算法生成哈希值後通過base32對該哈希值再編碼,取編碼的前16位與KUBE-SEP爲前綴組成的字符串。

Service的負載均衡是由iptables的statistic模塊實現的,statistic模塊的random模式可以將被設定目標的請求數在參數probability設定的概率範圍內分配,參數設定的值在0.0至1.0之間,當參數設定值爲0.5時,表示該目標有50%概率分配到請求。kube-proxy 遍歷Service中的Pod列表時,按照公式1.0/float64(n-i)爲每個Pod計算概率值,n是Pod的總數量,i是當前計數。當有3個Pod時,計算值分別爲33%、50%、100%,3個Pod的總流量負載分配分別爲33%,35%,32%。

Service也支持會話保持功能,是應用iptables的recent模塊實現的。recent允許動態的創建源地址列表,並對源地址列表中匹配的來源IP執行相應的Iptables動作。recent模塊參數如表12-3所示。

表 12-3 模塊參數

參數 參數說明
set 把匹配動作的源IP添加到地址列表
name 源地址列表名稱
mask 源地址列表中IP的掩碼
rsource 設置源地址列中保存數據包的源IP地址
rcheck 檢查當前數據包源IP是否在源地址列表中
seconds 與rcheck配合使用,設置對指定時間內更新的IP地址與數據包源IP進行匹配檢查,單位爲秒
reap 與seconds配合使用,將清除源地址列表中指定時間內沒被更新的IP地址

配置Service 會話保持,只需在Service 中做如下配置即可。

spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800

kube-proxy 實現Service的方法有四種,分別是userspace、iptables、ipvs和winuserspace,iptables只是默認配置,因kube-proxy的其他將在其他章節進行深入探討。

Pod應用在Kubernetes集羣外發布服務

Service 實現了Pod訪問的固定IP和端口,但ClusterIP並不是綁定在網絡設備上的,其只是kube-proxy 進程設定的iptables本地監聽轉發規則,只能在K8集羣內的節點上進行訪問。Kubernetes系統同默認提供兩種方式實現Pod應用在集羣外發布服務,一種是基於資源對象Pod的hostPort和hostNetwork方式,另一種是基於資源對象Service的NodePort、LoadBalancer和externalIPs方式。

  • hostPort方式

hostPort方式相當於創建Docker容器時使用"-p"參數提供容器的端口映射,只能通過運行容器的節點IP進行訪問,其屬於資源對象Pod的運行方式,不支持多個Pod的Service負載均衡等功能。資源配置如下所示。

apiVersion: v1
kind: Pod
metadata:
  name: apps
  labels:
    app: web
spec:
  containers:
  - name: apps
    image: apache
    ports:
    - containerPort: 80
      hostPort: 8080
  • hostNetwork方式

hostNetwork方式相當於創建Docker容器時以主機模式爲網絡模式的Pod運行方式,該方式運行的容器與所在節點共享網絡命名空間,屬於資源對象Pod的運行方式,不支持多個Pod的Service負載均衡等功能。資源配置如下所示。

apiVersion: v1
kind: Pod
metadata:
  name: nginx-web
  namespace: default
  labels:
    run: nginx-web
spec:
  hostNetwork: true
  containers:
  - name: nginx-web
    image: nginx
    ports:
    - containerPort: 80
  • NodePort方式

NodePort方式是在集羣中每個節點監聽固定端口(nodePort)的訪問,外部用戶對任意節點IP和nodePort的訪問,都會被Service負載到後端的Pod,全局nodePort的默認可用範圍爲30000-32767。NodePort方式訪問邏輯如圖12-7所示。

Kubernetes探索實踐之網絡通信

圖 12-7 NodePort方式訪問邏輯

kube-proxy 初始化時,會對NodePort方式的Service 在Iptables nat表中創建規則鏈 KUBE-NODEPORTS,用於監聽本機nodePort的請求。

外部請求訪問節點IP和端口(nodePort)後,被Iptables 規則KUBE-NODEPORTS 匹配後跳轉給對應的KUBE-SVC規則鏈執行負載均衡等操作。

選定Pod後,請求被轉發到選定的Pod IP 和目標端口(targetPod)。

NodePort方式資源配置如下所示。

apiVersion: v1
kind: Service
metadata:
  name: nginx-web
  namespace: default
  labels:
    run: nginx-web
spec:
  type: NodePort
  ports:
  - nodePort: 31804
    port: 8080           
    protocol: TCP
    targetPort: 8080
  • LoadBalancer 方式

LoadBalancer 方式是一種通過Kubernetes自動對外發布的解決方案,該方案是將外部負載均衡器作爲上層負載,在創建Service時自動與外部負載均衡器互動,完成對Kubernetes Service負載均衡創建的操作,將Service按照外部負載均衡器的負載策略對外提供服務。該方案依賴外部負載均衡器的支持,通常如阿里雲、騰訊雲的容器雲都提供了這個方案的支持。資源配置如下所示。

apiVersion: v1
kind: Service
metadata:
  name: nginx-web
  namespace: default
  labels:
    run: nginx-web
spec:
  type: LoadBalancer
  ports:
  - port: 8080           
    protocol: TCP
    targetPort: 8080

不同的外部負載均衡器需要有對應的負載均衡控制器(Loadbalancer Controller)。

負載均衡控制器實時通過接口服務監聽資源對象Service的變化。

LoadBalancer類型的Service被創建時,Kubernetes會爲該Service自動分配nodePort。

當監聽到LoadBalancer類型的Service 創建時,負載均衡控制器將觸發外部負載均衡器(LoadBalancer)創建外部VIP、分配外部IP或將現有節點IP綁定nodePort端口添加到外部負載均衡器的負載均衡池,完成負載均衡的配置。

當外部用戶訪問負載均衡器的外部VIP時,外部負載均衡器會將流量負載到Kubernetes節點或Kubernetes集羣中的Pod(視外部負載均衡器的功能而定)。

不能與NodePort方式同時使用。

  • External IPs方式

External IPs方式提供了一種指定外部IP綁定Service端口的方法,該方法可以指定節點內某幾個節點IP地址或綁定外部路由到節點網絡的非節點IP對外提供訪問。Kubernetes通過externalIPs參數將被指定的IP與Service 端口通過Iptables監聽,其使用與Service 一致的端口,相對NodePort方式配置更加簡單靈活。由於是直接將Service端口綁定被路由的IP對外暴露服務,使用者需要對整個集羣對外服務的端口做好相應的規劃,避免端口衝突。資源配置如下所示。

spec:
  externalIPs:
  - 192.168.1.101
  - 192.168.1.102
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
  - name: https
    port: 443
    targetPort: 443
    protocol: TCP

externalIPs設置的IP可以爲集羣中現有的節點IP,也可以是上層網絡設備路由過來的IP。kube-proxy 初始化時,會對externalIPs方式的Service 在Iptables nat表中創建規則鏈 KUBE-SERVICES,用於訪問到externalIPs列表中IP及Service port請求的監聽。

外部或本地訪問externalIPs列表中IP及port的請求被匹配後,跳轉給對應的KUBE-SVC規則鏈執行負載均衡等操作。

Service中Pod的調度策略

Kubernetes系統中,Pod的部署是隨機的,使用者也可通過不同的調度策略進行調整,Pod的調度策略同樣對Pod通信存在一定的影響,相關的調度策略有如下兩種。

  • 部署調度策略(Affinity)

Kubernetes集羣中的Pod 是被隨機調度,並創建在集羣中的節點上的,在實際使用中,有時需要考慮節點資源的有效利用及不同應用間的訪問效率等因素,也需要對這種調度設置相關期望的策略。主要體現在節點與Pod間的關係、同Service下的Pod間關係、不同Service的Pod間關係這3個方面。節點與Pod間關係可以使用“nodeAffinity” 在資源配置文件中設置,其可以在設置Pod資源對象時,將Pod部署到具有指定標籤的集羣節點上。Pod間關係可通過“podAntiAffinity”的配置儘量把同一Service下的Pod分配到不同的節點,提高自身的高可用性,也可以把互相影響的不同Service的Pod分散到不同的集羣節點上。對於Pod間訪問比較頻繁的應用可以使用“podAffinity”配置,儘量把被配置的Pod部署到同一節點服務器上。

  • 流量調度策略(externalTrafficPolicy)

Service的流量調度策略(externalTrafficPolicy)有兩種Pod調度策略,分別是Cluster和Local。Cluster是默認調度策略,其會依據iptables的隨機負載算法,將用戶請求負載均衡分配給Pod,但該方式會隱藏客戶端的源IP。Local策略則會將請求只分配給請求節點IP的Pod,而不會轉發給其他節點的Pod,這樣就保留了最初的源 IP 地址。但該方式不會對Service的Pod進行負載均衡,同時被訪問IP的節點上如果沒有該應用的Pod,則會報錯。Local策略僅適用於NodePort和LoadBalancer類型的Service。

Kubernetes中Service的Pod是通過Service實現Pod應用訪問的,在流量調度策略的Cluster調度策略下,對一個Service的訪問請求會被隨機分配到Service中的任意Pod,即便該Service與發出請求的Pod在同一節點有可提供服務的Pod,也不一定會被選中。在Kubernetes 計劃的1.16版本中增加了服務拓撲感知的流量管理功能,設計了新的Pod定位器(PodLocator)實現了服務的拓撲感知服務路由機制,使Pod總能優先使用“本地訪問”的策略找到最近的服務後端,這種拓撲感知服務使“本地訪問”具有更廣泛的意義,包括節點主機、機架、網絡、機房等,這樣可以有效的減少網絡延遲,提高訪問效率及安全性,更加節約成本。

結束語

Ingress 已經屬於七層範圍,其涵蓋的內容也較多,將會另起一章進行分析。

參考文檔

https://kubernetes.io/docs/home/

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