超全面的 Kubernetes 容器網絡技能,運維看后都說好
-
pod 無論運行在任何節點都可以互相直接通信,而不需要借助 NAT 地址轉換實現。 -
node 與 pod 可以互相通信,在不限制的前提下,pod 可以訪問任意網絡。 -
pod 擁有獨立的網絡棧,pod 看到自己的地址和外部看見的地址應該是一樣的,并且同個 pod 內所有的容器共享同個網絡棧。
容器網絡基礎
一個 Linux 容器的網絡棧是被隔離在它自己的 Network Namespace中,Network Namespace 包括了:網卡(Network Interface),回環設備(Lookback Device),路由表(Routing Table)和 iptables 規則,對于服務進程來講這些就構建了它發起請求和相應的基本環境。
而要實現一個容器網絡,離不開以下 Linux 網絡功能:
-
網絡命名空間:將獨立的網絡協議棧隔離到不同的命令空間中,彼此間無法通信。 -
Veth Pair:Veth設備對的引入是為了實現在不同網絡命名空間的通信,總是以兩張虛擬網卡(veth peer)的形式成對出現的。并且,從其中一端發出的數據,總是能在另外一端收到。 -
Iptables/Netfilter:Netfilter 負責在內核中執行各種掛接的規則(過濾、修改、丟棄等),運行在內核中;Iptables 模式是在用戶模式下運行的進程,負責協助維護內核中 Netfilter 的各種規則表;通過二者的配合來實現整個 Linux 網絡協議棧中靈活的數據包處理機制 -
網橋:網橋是一個二層網絡虛擬設備,類似交換機,主要功能是通過學習而來的Mac地址將數據幀轉發到網橋的不同端口上。 -
路由:Linux系統包含一個完整的路由功能,當IP層在處理數據發送或轉發的時候,會使用路由表來決定發往哪里
在容器中,以上的實現是通過 docker0 網橋,凡是連接到 docker0 的容器,就可以通過它來進行通信。要想容器能夠連接到 docker0 網橋,我們也需要類似網線的虛擬設備Veth Pair 來把容器連接到網橋上。
veth20b3dac
,并且可以通過?brctl
?查看網橋信息看到這張網卡是在 docker0 上。172.17.0.3
時,會匹配到我們的路由表第二條規則,網關為0.0.0.0
,這就意味著是一條直連路由,通過二層轉發到目的地。
要通過二層網絡到達172.17.0.3
,我們需要知道它的 Mac 地址,此時就需要第一個容器發送一個ARP廣播,來通過IP地址查找Mac。
此時 Veth peer 另外一段是?docker0
?網橋,它會廣播到所有連接它的?veth peer
?虛擬網卡去,然后正確的虛擬網卡收到后會響應這個ARP報文,然后網橋再回給第一個容器。
跨主機網絡通信
它是 K8s 中標準的一個調用網絡實現的接口,kubelet通過這個API來調用不同的網絡插件以實現不同的網絡配置,實現了這個接口的就是CNI插件,它實現了一系列的CNI API接口。目前已經有的包括flannel、calico、weave、contiv等等。
實際上 CNI 的容器網絡通信流程跟前面的基礎網絡一樣,只是CNI維護了一個單獨的網橋來代替 docker0。這個網橋的名字就叫作:CNI 網橋,它在宿主機上的設備名稱默認是:cni0。
cni的設計思想,就是:Kubernetes 在啟動 Infra 容器之后,就可以直接調用 CNI 網絡插件,為這個 Infra 容器的 Network Namespace,配置符合預期的網絡棧。
CNI 插件三種網絡實現模式:
-
overlay 模式是基于隧道技術實現的,整個容器網絡和主機網絡獨立,容器之間跨主機通信時將整個容器網絡封裝到底層網絡中,然后到達目標機器后再解封裝傳遞到目標容器。不依賴與底層網絡的實現。實現的插件有flannel(UDP、vxlan)、calico(IPIP)等等 -
三層路由模式中容器和主機也屬于不通的網段,他們容器互通主要是基于路由表打通,無需在主機之間建立隧道封包。但是限制條件必須依賴大二層同個局域網內。實現的插件有flannel(host-gw)、calico(BGP)等等 -
underlay網絡是底層網絡,負責互聯互通。容器網絡和主機網絡依然分屬不同的網段,但是彼此處于同一層網絡,處于相同的地位。整個網絡三層互通,沒有大二層的限制,但是需要強依賴底層網絡的實現支持.實現的插件有calico(BGP)等等
我們看下路由模式的一種實現 flannel Host-gw:
如圖可以看到當 node1上container-1 要發數據給 node2 上的 container2 時,會匹配到如下的路由表規則:
表示前往目標網段 10.244.1.0/24 的 IP 包,需要經過本機 eth0 出去發往的下一跳ip地址為10.168.0.3(node2)。然后到達 10.168.0.3 以后再通過路由表轉發 cni 網橋,進而進入到 container2。
這種網絡模式最大的好處就是避免了額外的封包和解包帶來的網絡性能損耗。缺點我們也能看見主要就是容器ip包通過下一跳出去時,必須要二層通信封裝成數據幀發送到下一跳。如果不在同個二層局域網,那么就要交給三層網關,而此時網關是不知道目標容器網絡的(也可以靜態在每個網關配置pod網段路由)。所以 flannel host-gw 必須要求集群宿主機是二層互通的。
而為了解決二層互通的限制性,calico提供的網絡方案就可以更好的實現,calico 大三層網絡模式與flannel 提供的類似,也會在每臺宿主機添加如下格式的路由規則:
BGP 全稱是 Border Gateway Protocol邊界網關協議,linxu原生支持的、專門用于在大規模數據中心為不同的自治系統之間傳遞路由信息。只要記住BGP簡單理解其實就是實現大規模網絡中節點路由信息同步共享的一種協議。而BGP這種協議就能代替flannel 維護主機路由表功能。
-
calico cni插件: 主要負責與kubernetes對接,供kubelet調用使用。 -
felix: 負責維護宿主機上的路由規則、FIB轉發信息庫等。 -
BIRD: 負責分發路由規則,類似路由器。 -
confd: 配置管理組件。
有了這樣的veth pair設備以后,容器發出的IP包就會通過veth pair設備到達宿主機,然后宿主機根據路有規則的下一條地址,發送給正確的網關(10.100.1.3),然后到達目標宿主機,在到達目標容器。
這些路由規則都是felix維護配置的,而路由信息則是calico bird組件基于BGP分發而來。calico實際上是將集群里所有的節點都當做邊界路由器來處理,他們一起組成了一個全互聯的網絡,彼此之間通過BGP交換路由,這些節點我們叫做BGP Peer。
需要注意的是calico 維護網絡的默認模式是 node-to-node mesh ,這種模式下,每臺宿主機的BGP client都會跟集群所有的節點BGP client進行通信交換路由。這樣一來,隨著節點規模數量N的增加,連接會以N的2次方增長,會集群網絡本身帶來巨大壓力。
所以一般這種模式推薦的集群規模在50節點左右,超過50節點推薦使用另外一種RR(Router Reflector)模式,這種模式下,calico 可以指定幾個節點作為RR,他們負責跟所有節點 BGP client 建立通信來學習集群所有的路由,其他節點只需要跟RR節點交換路由即可。這樣大大降低了連接數量,同時為了集群網絡穩定性,建議RR>=2.
以上的工作原理依然是在二層通信,當我們有兩臺宿主機,一臺是10.100.0.2/24,節點上容器網絡是10.92.204.0/24;另外一臺是10.100.1.2/24,節點上容器網絡是10.92.203.0/24,此時兩臺機器因為不在同個二層所以需要三層路由通信,這時calico就會在節點上生成如下路由表:
這時候問題就來了,因為10.100.1.2跟我們10.100.0.2不在同個子網,是不能二層通信的。這之后就需要使用Calico IPIP模式,當宿主機不在同個二層網絡時就是用overlay網絡封裝以后再發出去。如下圖所示:
IPIP模式下在非二層通信時,calico 會在node節點添加如下路由規則:
而node1添加如下的路由表: