天天看點

了解OpenShift(3):網絡之 SDN

了解OpenShift(1):網絡之 Router 和 Route

了解OpenShift(2):網絡之 DNS(域名服務)

了解OpenShift(3):網絡之 SDN

了解OpenShift(4):使用者及權限管理

了解OpenShift(5):從 Docker Volume 到 OpenShift Persistent Volume

** 本文基于 OpenShift 3.11,Kubernetes 1.11 進行測試 ***

1. 概況

為了OpenShift 叢集中 pod 之間的網絡通信,OpenShift 以插件形式提供了三種符合Kubernetes CNI 要求的 SDN實作:

  • ovs-subnet:ovs-subnet 實作的是一種扁平網絡,未實作租戶之間的網絡隔離,這意味着所有租戶之間的pod 都可以互訪,這使得該實作無法用于絕大多數的生産環境。
  • ovs-multitenant:基于 OVS 和 VxLAN 等技術實作了項目(project)之間的網絡隔離。
  • ovs-networkpolicy:介于ovs-subnet 和 ovs-multitenant 之間的一種實作。考慮到 ovs-multitenant  隻是實作了項目級别的網絡隔離,這種隔離粒度在一些場景中有些過大,使用者沒法做更精細的控制,這種需求導緻了ovs-networkpolicy的出現。預設地,它和ovs-subnet 一樣,所有租戶之間都沒有網絡隔離。但是,管理者可以通過定義 NetworkPolicy 對象來精細地進行網絡控制。可以粗略地将它類比為OpenStack neutron 中的neutron 網絡防火牆和Nova安全組。具體請查閱有關文檔。

當使用 ansible 部署 OpenShift 時,預設會啟用ovs-subnet,但是可以在部署完成後修改為其它兩種實作。本文中的說明都是針對 ovs-multitenant。

1.1 OpenShift 叢集的網絡設計

要部署一個OpenShift 生産環境,主要的網絡規劃和設計如下圖所示:

了解OpenShift(3):網絡之 SDN

節點角色類型:

  • Master 節點:隻承擔 Master 角色,可不也可以承擔Node 角色。主要運作 API 服務、controller manager 服務、etcd 服務、web console 服務等。
  • Infra 節點:作為 Node 角色,通過設定并應用節點标簽,隻用于部署系統基礎服務,包括Registry、Router、Prometheus 以及 EFK 等。
  • Node 節點:作為 Node 角色,用于運作使用者業務系統的Pod。

網絡類型:

  • 外部網絡:這是一個外部網絡,用于從外部通路叢集。和該網絡連接配接的伺服器或元件需要被配置設定公網IP位址才能被從外部通路。從内部通路外網中的服務時,比如DNS或者鏡像倉庫,可以通過NAT實作,而無需公網IP位址。
  • 管理網絡:這是一個内部網絡,用于叢集内部 API 通路。
  • IPMI網絡:這是一個内部網絡,用于管理實體伺服器。
  • SDN網絡:這是一個内部網絡,用于叢集内部Pod 之間的通信,承載 VxLAN Overlay 流量。
  • 存儲網絡:這是一個内部網絡,用于各節點通路基于網絡的存儲。

在PoC 或開發測試環境中,管理/SDN/存儲網絡可以合并為一個網絡。

1.2 Node節點中的網絡

了解OpenShift(3):網絡之 SDN

節點上的主要網絡裝置:

  • br0:OpenShift 建立和管理的 Open vSwitch 網橋, 它會使用 OpenFlow 規則來實作網絡隔離和轉發。
  • vethXXXXX:veth 對,它負責将 pod 的網絡命名空間連接配接到 br0 網橋。
  • tun0 :一OVS 内部端口,它會被配置設定本機的 pod 子網的網關IP 位址,用于OpenShift pod 以及Docker 容器與叢集外部的通信。iptables 的 NAT 規則會作用于tun0。
  • docker0:Docker 管理和使用的 linux bridge 網橋,通過 veth 對将不受 OpenShift 管理的Docker 容器的網絡位址空間連接配接到 docker0 上。
  • vovsbr/vlinuxbr:将 docker0 和 br0 連接配接起來的 veth 對,使得Docker 容器能和 OpenShift pod 通信,以及通過 tun0 通路外部網絡
  • vxlan0:一OVS VXLAN 隧道端點,用于叢集内部 pod 之間的網絡通信。

2. 實作

2.1 pod 網絡總體設定流程

Pod 網絡總體設定流程如下(來源:OpenShift源碼簡析之pod網絡配置(上)):

了解OpenShift(3):網絡之 SDN

簡單說明:

  • OpenShift 使用運作在每個節點上的 kubelet 來負責pod 的建立和管理,其中就包括網絡配置部分。
  • 當 kubelet 接受到 pod 建立請求時,會首先調用docker client 來建立容器,然後再調用 docker api接口啟動上一步中建立成功的容器。kubelet 在建立 pod 時是先建立一個 infra 容器,配置好該容器的網絡,然後建立真正用于業務的應用容器,最後再把業務容器的網絡加到infra容器的網絡命名空間中,相當于業務容器共享infra容器的網絡命名空間。業務應用容器和infra容器共同組成一個pod。
  • kubelet 使用 CNI 來建立和管理Pod網絡(openshift在啟動kubelet時傳遞的參數是--netowrk-plugin=cni)。OpenShift 實作了 CNI 插件(由 /etc/cni/net.d/80-openshift-network.conf 檔案指定),其二進制檔案是 /opt/cni/bin/openshift-sdn 。是以,kubelet 通過 CNI 接口來調用 openshift sdn 插件,然後具體做兩部分事情:一是通過 IPAM 擷取 IP 位址,二是設定 OVS(其中,一是通過調用 ovs-vsctl 将 infra 容器的主機端虛拟網卡加入 br0,二是調用 ovs-ofctl 指令來設定規則)。

2.2 OVS 網橋 br0 中的規則

本部分内容主要引用自 OVS 在雲項目中的使用:

流量規則表:

  • table 0: 根據輸入端口(in_port)做入口分流,來自VXLAN隧道的流量轉到表10并将其VXLAN VNI 儲存到 OVS ***後續使用,從tun0過阿裡的(來自本節點或進本節點來做轉發的)流量分流到表30,将剩下的即本節點的容器(來自veth***)發出的流量轉到表20;
  • table 10: 做入口合法性檢查,如果隧道的遠端IP(tun_src)是某叢集節點的IP,就認為是合法,繼續轉到table 30去處理;
  • table 20: 做入口合法性檢查,如果資料包的源IP(nw_src)與來源端口(in_port)相符,就認為是合法的,設定源項目标記,繼續轉到table 30去處理;如果不一緻,即可能存在ARP/IP欺詐,則認為這樣的的資料包是非法的;
  • table 30: 資料包的目的(目的IP或ARP請求的IP)做轉發分流,分别轉到table 40~70 去處理;
  • table 40: 本地ARP的轉發處理,根據ARP請求的IP位址,從對應的端口(veth)發出;
  • table 50: 遠端ARP的轉發處理,根據ARP請求的IP位址,設定VXLAN隧道遠端IP,并從隧道發出;
  • table 60: Service的轉發處理,根據目标Service,設定目标項目标記和轉發出口标記,轉發到table 80去處理;
  • table 70: 對通路本地容器的包,做本地IP的轉發處理,根據目标IP,設定目标項目标記和轉發出口标記,轉發到table 80去處理;
  • table 80: 做本地的IP包轉出合法性檢查,檢查源項目标記和目标項目标記是否比對,或者目标項目是否是公開的,如果滿足則轉發;(這裡實作了 OpenShift 網絡層面的多租戶隔離機制,實際上是根據項目/project 進行隔離,因為每個項目都會被配置設定一個 VXLAN VNI,table 80 隻有在網絡包的VNI和端口的VNI tag 相同才會對網絡包進行轉發)
  • table 90: 對通路遠端容器的包,做遠端IP包轉發“尋址”,根據目标IP,設定VXLAN隧道遠端IP,并從隧道發出;
  • table 100: 做出外網的轉出處理,将資料包從tun0發出。
了解OpenShift(3):網絡之 SDN

備注一些常用的操作指令:

  • 查詢OVS 流表: ovs-ofctl -O OpenFlow13 dump-flows br0
  • 查詢OVS裝置: ovs-vsctl show
  • 檢視OVS網橋: ovs-ofctl -O OpenFlow13 show br0
  • 檢視路由表:route -n
  • 在容器中運作指令:nsenter -t <容器的PiD> -n ip a
  • 查詢 iptables NAT 表:iptables -t nat -S

3. 流程

3.1 同一個節點上的兩個pod 之間的互訪

了解OpenShift(3):網絡之 SDN

通路:pod 1 (ip:10.131.1.150)通路 pod2(10.131.1.152)

網絡路徑::pod1的eth0 → veth12 → br0 → veth34 → pod2的eth0。 

OVS 流表:

table=0, n_packets=14631632, n_bytes=1604917617, priority=100,ip actions=goto_table:20
table=20, n_packets=166585, n_bytes=12366463, priority=100,ip,in_port=96,nw_src=10.131.1.152 actions=load:0xbe3127->NXM_NX_REG0[],goto_table:21
table=21, n_packets=14671413, n_bytes=1606835395, priority=0 actions=goto_table:30
table=30, n_packets=8585493, n_bytes=898571869, priority=200,ip,nw_dst=10.131.0.0/23 actions=goto_table:70
table=70, n_packets=249967, n_bytes=16177300, priority=100,ip,nw_dst=10.131.1.152 actions=load:0xbe3127->NXM_NX_REG1[],load:0x60->NXM_NX_REG2[],goto_table:80
table=80, n_packets=0, n_bytes=0, priority=100,reg0=0xbe3127,reg1=0xbe3127 actions=output:NXM_NX_REG2[]
table=80, n_packets=0, n_bytes=0, priority=0 actions=drop #不合法的包會被丢棄
      

表 20 會判斷包類型(IP)、源位址(nw_src)、進來端口的ID(96),将其對應的 VNI ID(這裡是 0xbe3127,十進制是12464423)儲存在 REG0 中。這意味着所有通過OVS 端口進入OVS br0 網橋的來自pod 的網絡包都會被打上對口對應的VNID 标簽。叢集中所有項目對應的 VNID 可以使用 oc get netnamespaces 指令查到:

[root@master1 cloud-user]# oc get netnamespaces
NAME                                NETID      EGRESS IPS
cicd                                16604171   []
default                             0          []
demoproject2                        16577323   []
demoprojectone                      1839630    []
dev                                 12464423   []      

表 70 會根據目的位址,也就是目的 pod 的位址,将網絡包的目的出口标記(這裡為 0x60,十進制為96)儲存到REG2,同時設定其項目的 VNI ID 到 REG1(這裡是0xbe3127).

根據端口的ID 96 找到veth網絡裝置:

96(veth0612e07f): addr:66:d0:c3:e3:be:cf
     config:     0
     state:      0
     current:    10GB-FD COPPER
     speed: 10000 Mbps now, 0 Mbps max      

查找其對應的容器中的網卡。

[root@node1 cloud-user]# ip link  | grep veth0612e07f
443: veth0612e07f@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue master ovs-system state UP mode DEFAULT       

這與pod2容器中的 eth0 正好吻合:

3: eth0@if443: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP 
    link/ether 0a:58:0a:83:01:98 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.131.1.152/23 brd 10.131.1.255 scope global eth0
       valid_lft forever preferred_lft forever
      

表80 會檢查報的來源 VNI ID (REG0)和目的端口的 VNI ID (REG1),将相符的合法的包轉發到表70 設定的出口,以完成轉發。

3.2 不同節點上的同一個網絡的兩個pod 之間的互訪

了解OpenShift(3):網絡之 SDN

網絡路徑:節點1上的Pod1的eth0→veth1→br0→vxlan0→ 節點1的eth0網卡→ 節點2的eth0網卡→vxlan0→br0→veth1→ Pod3的eth0流表:

發送端(node1)的OVS 流表:

table=0, n_packets=14703186, n_bytes=1612904326, priority=100,ip actions=goto_table:20
table=20, n_packets=167428, n_bytes=12428845, priority=100,ip,in_port=96,nw_src=10.131.1.152 actions=load:0xbe3127->NXM_NX_REG0[],goto_table:21
table=21, n_packets=14736461, n_bytes=1613954556, priority=0 actions=goto_table:30
table=30, n_packets=1143761, n_bytes=1424533777, priority=100,ip,nw_dst=10.128.0.0/14 actions=goto_table:90
table=90, n_packets=0, n_bytes=0, priority=100,ip,nw_dst=10.128.2.0/23 actions=move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],set_field:172.22.122.9->tun_dst,output:1
      
  • 表21 同樣是将源pod 的 VNI ID 儲存在 REG0 中。
  • 表30 會判斷目的位址是不是叢集的大的 pod 的 IP CIDR。
  • 表90 會設定 VNI ID 為之前儲存在 REG0 中的值,然後根據目的位址的網段(這裡是 10.128.2.0/23),計算出其所在的節點的IP 位址(這裡是 172.22.122.9)并設定為tun_dst,然後發到 vxlan0,它會負責根據提供的資訊來做VXLAN UDP 包封裝。

接收端(node2)的OVS 流表: 

table=0, n_packets=1980863, n_bytes=1369174876, priority=200,ip,in_port=1,nw_src=10.128.0.0/14 actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[],goto_table:10
table=10, n_packets=0, n_bytes=0, priority=100,tun_src=172.22.122.8 actions=goto_table:30
table=30, n_packets=16055284, n_bytes=1616511267, priority=200,ip,nw_dst=10.128.2.0/23 actions=goto_table:70
table=70, n_packets=248860, n_bytes=16158751, priority=100,ip,nw_dst=10.128.2.128 actions=load:0xbe3127->NXM_NX_REG1[],load:0x32->NXM_NX_REG2[],goto_table:80
table=80, n_packets=0, n_bytes=0, priority=100,reg0=0xbe3127,reg1=0xbe3127 actions=output:NXM_NX_REG2[]
      
  • 表0 會将發送到儲存在 NXM_NX_TUN_ID[0..31] 中的源 VNI ID 取出來儲存到 REG0.
  • 表10 會檢查包的來源節點的位址。
  • 表30 會檢查包的目的位址是不是本機上 pod 的網段。
  • 表70 會根據目的位址,将目的 VNI ID 儲存到 REG1,将目的端口 ID 儲存到 REG2
  • 表80 會檢查目的 VNI ID 和源 VNI ID,如果相符的話,則将包轉發到儲存在 REG2 中的目的端口ID 指定的端口。然後包就會通過 veth 管道進入目的 pod。

3.3 pod 内通路外網

了解OpenShift(3):網絡之 SDN

網絡路徑:PodA的eth0 → vethA → br0 → tun0 → 通過iptables實作SNAT → 實體節點的 eth0  → 網際網路

NAT:将容器發出的IP包的源IP位址修改為主控端的 eth0 網卡的IP 位址。

OVS 流表:

table=0, n_packets=14618128, n_bytes=1603472372, priority=100,ip actions=goto_table:20
table=20, n_packets=0, n_bytes=0, priority=100,ip,in_port=17,nw_src=10.131.1.73 actions=load:0xfa9a3->NXM_NX_REG0[],goto_table:21
table=21, n_packets=14656675, n_bytes=1605262241, priority=0 actions=goto_table:30
table=30, n_packets=73508, n_bytes=6820206, priority=0,ip actions=goto_table:100
table=100, n_packets=44056, n_bytes=3938540, priority=0 actions=goto_table:101
table=101, n_packets=44056, n_bytes=3938540, priority=0 actions=output:2      

表20 會檢查 IP 包的來源端口和IP 位址,并将源項目的 VNI ID 儲存到 REG0.

表101 會将包發送到端口2 即 tun0. 然後被 iptables 做 NAT 然後發送到 eth0.

3.4 外網通路 pod

了解OpenShift(3):網絡之 SDN

因為 Infra 節點上的 HAproxy 容器采用了 host-network 模式,是以它是直接使用主控端的 eth0 網卡的。

下面是主控端的路由表:

[root@infra-node1 /]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.22.122.1    0.0.0.0         UG    100    0        0 eth0
10.128.0.0      0.0.0.0         255.252.0.0     U     0      0        0 tun0
169.254.169.254 172.22.122.1    255.255.255.255 UGH   100    0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.22.122.0    0.0.0.0         255.255.255.0   U     100    0        0 eth0
172.30.0.0      0.0.0.0         255.255.0.0     U     0      0        0 tun0      

從 HAProxy 容器内出來目的位址為業務pod(ip:10.128.2.128)的網絡包,根據上面的路由表,其下一跳是 tun0,也就是說它又進入了 OVS 網橋 br0. 對應的 OVS 流表規則為:

ip,in_port=2 actions=goto_table:30
ip,nw_dst=10.128.0.0/14 actions=goto_table:90
ip,nw_dst=10.128.2.0/23 actions=move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],set_field:172.22.122.9->tun_dst,output:1      

可見它最終又被發到了端口1 即 vxlan0,它會負責做 vxlan 封包,并通過 eth0 網卡發出去。 

3.5 彙總

總體來說,OVS 中的OpenFlow流表根據網絡包的目的位址将其分為四類來處理:

  • 到本地pod的,直接在 br0 中轉發。
  • 到本叢集pod 的,經過 br0 後發到 vxlan0,封裝為 vxlan udp 包經實體網卡發到對方節點。
  • 到本地不受OpenShift SDN管理的docker容器的,還未具體研究。
  • 到叢集外的,經過 br0 後發到 tun0,經過 iptables 做SNAT,然後經實體網卡發出。
了解OpenShift(3):網絡之 SDN

3.6. 項目(project)級别的網絡隔離

3.6.1 原理

OpenShift 中的網絡隔離是在項目(project)級别實作的。OpenShfit 預設的項目 『default』的 VNID (Virtual Network ID)為0,表明它是一個特權項目,因為它可以發網絡包到其它所有項目,也能接受其它所有項目的pod發來的網絡包。這從 table 80 的規則上可以看出來,如果來源項目的 VNID (reg0)或目标項目的 VNID(reg1)為0,都會允許包轉發到pod 的端口:

table=80, n_packets=8244506, n_bytes=870316191, priority=200,reg0=0 actions=output:NXM_NX_REG2[]
table=80, n_packets=13576848, n_bytes=1164951315, priority=200,reg1=0 actions=output:NXM_NX_REG2[]      

其它所有項目都會有一個非0的 VNID。在 OpenShift ovs-multitenant 實作中,非0 VNID 的項目之間的網絡是不通的。

從一個本地 pod 發出的所有網絡流量,在它進入 OVS 網橋時,都會被打上它所通過的 OVS 端口ID相對應的 VNID。port:VNID 映射會在pod 建立時通過查詢master 上的 etcd 來确定。從其它節點通過 VXLAN發過來的網絡包都會帶有發出它的pod 所在項目的 VNID。

根據上面的分析,OVS 網橋中的 OpenFlow 規則會阻止帶有與目标端口上的 VNID 不同的網絡包的投遞(VNID 0 除外)。這就保證了項目之間的網絡流量是互相隔離的。

可以使用下面的指令檢視namespace 的 NETID 也就是 VNID:

了解OpenShift(3):網絡之 SDN

在我的環境裡面,default 項目預設就是 global的,我還把 cicd 項目設定為 gloabl 的了,因為它也需要通路其它項目。

3.6.2 實驗

下圖顯示了兩個項目之間的三種網絡狀态:

  • 左圖顯示的是預設狀态:SIT 項目和 Dev 項目之間的 pod 無法通路。根據前面對 OVS 流表的分析,表80 會檢查IP 包的來源Pod的項目 VNI ID 和目标Pod的項目 VNI ID。如果兩者不符合,這些IP網絡包就會被丢棄。
  • 中間圖顯示的是打通這兩個項目的網絡:通過運作 oc adm pod-network join-projects 指令,将兩個項目連接配接在一起,結果就是 DEV 項目的 VNI ID 變成了 SIT 項目的 VNI ID。這時候兩個項目中的 pod 網絡就通了。
  • 右圖顯示的是分離這兩個項目的網絡:通過運作 oc adm pod-network isolate-projects 指令,将兩個項目分離,其結果是 DEV 項目被配置設定了新的 VNI ID。此時兩個項目中的pod 又不能互通了。
了解OpenShift(3):網絡之 SDN

3.7 CluserIP 類型的 Service

OpenShift Serivce 有多種類型,預設的和最常用的是 ClusterIP 類型。每個這種類型的Service,建立時都會被從一個子網中配置設定一個IP位址,在叢集内部可以使用該IP位址來通路該服務,進而通路到它後端的pod。是以,Service 實際上是用于OpenShift 叢集内部的四層負載均衡器,它是基于 iptables 實作的。

接下來我以 mybank 服務為例進行說明,它的  ClusterIP 是  172.30.162.172,服務端口是8080;它有3個後端 10.128.2.128:8080,10.131.1.159:8080,10.131.1.160:8080。

主控端上的路由表:

[root@node1 cloud-user]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.22.122.1    0.0.0.0         UG    100    0        0 eth0
10.128.0.0      0.0.0.0         255.252.0.0     U     0      0        0 tun0   #3.7.1 中會用到
169.254.169.254 172.22.122.1    255.255.255.255 UGH   100    0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.22.122.0    0.0.0.0         255.255.255.0   U     100    0        0 eth0   #3.7.1 中會用到
172.30.0.0      0.0.0.0         255.255.0.0     U     0      0        0 tun0   #3.7.2 中會用到      

3.7.1 從主控端***問服務

每當建立一個 service 後,OpenShift 會在叢集的每個節點上的 iptables 中添加以下記錄:

-A KUBE-SERVICES -d 172.30.162.172/32 -p tcp -m comment --comment "dev/mybank:8080-tcp cluster IP" -m tcp --dport 8080 -j KUBE-SVC-3QLA52JX7QFEEEC5      

-A KUBE-SVC-3QLA52JX7QFEEEC5 -m comment --comment "dev/mybank:8080-tcp" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-AWPSVWBUXH7A2CLB

-A KUBE-SVC-3QLA52JX7QFEEEC5 -m comment --comment "dev/mybank:8080-tcp" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-ESYZLBFGDE6MOHX2

-A KUBE-SVC-3QLA52JX7QFEEEC5 -m comment --comment "dev/mybank:8080-tcp" -j KUBE-SEP-ENPHHSSNP6FR7JJI

-A KUBE-SEP-AWPSVWBUXH7A2CLB -p tcp -m comment --comment "dev/mybank:8080-tcp" -m tcp -j DNAT --to-destination 10.128.2.128:8080

-A KUBE-SVC-3QLA52JX7QFEEEC5 -m comment --comment "dev/mybank:8080-tcp" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-ESYZLBFGDE6MOHX2

-A KUBE-SEP-ENPHHSSNP6FR7JJI -p tcp -m comment --comment "dev/mybank:8080-tcp" -m tcp -j DNAT --to-destination 10.131.1.160:8080

  • 第1條:檢查目的IP位址以及端口,添加comment
  • 第2到5條:以随機配置設定(random)方式将流量平均地轉發到三條規則上
  • 第6條:第一條轉發規則通過DNAT 将目的IP位址和端口修改為第一個endpoint 的IP 和位址,第7和8條相同

DNAT 後,根據路由表,下一跳将是 tun0,也就是說它會進入 OVS 網橋 br0。在進入網橋之前,如果是從pod 中發出的網絡包,還會進行SNAT,将其源IP位址修改為 tun0 的IP 位址。其目的是使得傳回包能回到tun0,然後能通過反SNAT 操作,将目的IP位址由 tun0 的IP 修改為原來的源IP。具體見下文的分析。

-A OPENSHIFT-MASQUERADE -s 10.128.0.0/14 -m comment --comment "masquerade pod-to-service and pod-to-external traffic" -j MASQUERADE      

然後,進入網橋。在網橋中,會檢查目的位址。如果是本地 pod 網段内的,那麼将直接轉發給對應的pod;如果是遠端pod的,那麼轉發到 vxlan0 再通過 VXLAN 網絡發到對方節點。這過程跟上面說明的過程就差不多了,不再贅述。

3.7.2 從 pod 中通路 service

從某個 pod 中通路同一個 service。IP 包從 br0 的某個端口進入 OVS,然後執行以下流表規則:

table=30, n_packets=14212117, n_bytes=1219709382, priority=100,ip,nw_dst=172.30.0.0/16 actions=goto_table:60
table=60, n_packets=0, n_bytes=0, priority=100,ip,nw_dst=172.30.162.172,nw_frag=later actions=load:0xbe3127->NXM_NX_REG1[],load:0x2->NXM_NX_REG2[],goto_table:80
table=60, n_packets=0, n_bytes=0, priority=100,tcp,nw_dst=172.30.162.172,tp_dst=8080 actions=load:0xbe3127->NXM_NX_REG1[],load:0x2->NXM_NX_REG2[],goto_table:80
table=80, n_packets=0, n_bytes=0, priority=100,reg0=0xbe3127,reg1=0xbe3127 actions=output:NXM_NX_REG2[]      

從 table60 可以看出,OVS 流表給該網絡包設定的出口端口為2,即 tun0,因為要去做NAT。出去後,即開始 iptables NAT 過程,也就是 3.7.1 中的過程。最後還是要回到 OVS br0,再走到 vxlan0,通過 VXLAN 隧道發到目标pod 所在的主控端。該過程示意圖如下:

了解OpenShift(3):網絡之 SDN

對于傳回的網絡包,其目的位址是源pod 主控端上的 tun0,即左圖中的 10.131.0.1/23. 資料包到達左圖中的 br0 後,首先要出 tun0,因為要去做NAT:

table=30, n_packets=1214735, n_bytes=1135728626, priority=300,ip,nw_dst=10.131.0.1 actions=output:2      

根據這篇文章(https://superuser.com/questions/1269859/linux-netfilter-how-does-connection-tracking-track-connections-changed-by-nat),發送階段 iptables 在做 SNAT 時會利用 conntrack 記錄這次修改(在/proc/net/nf_conntrack 中);在現在回複包傳回的時候,會自動地做相反SNAT操作(類似DNAT),将包的目的IP位址(tun0的IP位址)修改為原來的源IP位址即源pod位址。

/proc/net/nf_conntrack 檔案的有關記錄:

ipv4     2 tcp      6 70 TIME_WAIT src=10.131.0.1 dst=10.131.1.72 sport=56862 dport=8080 src=10.131.1.72 dst=10.131.0.1 sport=8080 dport=56862 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2      

做完De-SNAT後,根據路由表,它又會回到 tun0, OVS 根據流表,會根據目的pod IP 位址對它進行轉發,使得它回到原來的出發pod。

參考文檔:

  • http://www.openvswitch.org/support/dist-docs-2.5/ovs-ofctl.8.html
  • OVS 在雲項目中的使用 (https://medoc.readthedocs.io/en/latest/docs/ovs/sharing/cloud_usage.html) 
  • 解析 | OpenShift源碼簡析之pod網絡配置
  • Troubeshooting OpenShift SDN (https://docs.openshift.com/container-platform/3.11/admin_guide/sdn_troubleshooting.html)
  • https://blog.openshift.com/openshift-container-platform-reference-architecture-implementation-guides/
  • https://github.com/openshift/openshift-sdn/blob/master/ISOLATION.md
  • https://blog.avinetworks.com/kubernetes-and-openshift-networking-primer

感謝您的閱讀,歡迎關注我的微信公衆号: