十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
計算、存儲和網(wǎng)絡(luò)是云時代的三大基礎(chǔ)服務(wù),作為新一代基礎(chǔ)架構(gòu)的 Kubernetes 也不例外。而這三者之中,網(wǎng)絡(luò)又是一個最難掌握和最容易出問題的服務(wù);本文通過對Kubernetes網(wǎng)絡(luò)流量模型進行簡單梳理,希望對初學(xué)者能夠提供一定思路。先看一下kubernetes 總體模型:

成都創(chuàng)新互聯(lián)公司服務(wù)項目包括臨澤網(wǎng)站建設(shè)、臨澤網(wǎng)站制作、臨澤網(wǎng)頁制作以及臨澤網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,臨澤網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到臨澤省份的部分城市,未來相信會繼續(xù)擴大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
容器網(wǎng)絡(luò)中涉及的幾個地址:
Node Ip:物理機地址。
POD Ip:Kubernetes的最小部署單元是Pod,一個pod 可能包含一個或多個容器,簡單來講容器沒有自己單獨的地址,他們共享POD 的地址和端口區(qū)間。
ClusterIp:Service的Ip地址,外部網(wǎng)絡(luò)無法ping通改地址,因為它是虛擬IP地址,沒有網(wǎng)絡(luò)設(shè)備為這個地址負責(zé),內(nèi)部實現(xiàn)是使用Iptables規(guī)則重新定向到其本地端口,再均衡到后端Pod;只有Kubernetes集群內(nèi)部訪問使用。
Public Ip :Service對象在Cluster IP range池中分配到的IP只能在內(nèi)部訪問,適合作為一個應(yīng)用程序內(nèi)部的層次。如果這個Service作為前端服務(wù),準(zhǔn)備為集群外的客戶提供業(yè)務(wù),我們就需要給這個服務(wù)提供公共IP。
容器網(wǎng)絡(luò)至少需要解決如下幾種場景的通信:①POD內(nèi)容器間通信
②同主機POD間 通信
③跨主機POD間 通信
④集群內(nèi)Service Cluster Ip和外部訪問下面具體介紹實現(xiàn)方式
Pod中的容器可以通過“l(fā)ocalhost”來互相通信,他們使用同一個網(wǎng)絡(luò)命名空間,對容器來說,hostname就是Pod的名稱。Pod中的所有容器共享同一個IP地址和端口空間,你需要為每個需要接收連接的容器分配不同的端口。也就是說,Pod中的應(yīng)用需要自己協(xié)調(diào)端口的使用。實驗如下:首先我們創(chuàng)建一個Pod ,包含兩個容器,容器參數(shù)如下:
查看:
可以看到容器共享Pod 的地址,那么他們是否使用同一端口資源呢,我們可以簡單實驗一下:首先在容器1監(jiān)聽一個端口:
然后在容器2查看該端口是否被占用:
可見端口也是共享的;所以簡單理解,可以把Pod看做一個小系統(tǒng),容器當(dāng)做系統(tǒng)中的不同進程;內(nèi)部實現(xiàn):同POD 內(nèi)的容器實際共享同一個Namespace,因此使用相同的Ip和Port空間,該Namespace 是由一個叫Pause的小容器來實現(xiàn),每當(dāng)一個Pod被創(chuàng)建,那么首先創(chuàng)建一個pause容器, 之后這個pod里面的其他容器通過共享這個pause容器的網(wǎng)絡(luò)棧,實現(xiàn)外部pod進行通信,因此對于同Pod里面的所有容器來說,他們看到的網(wǎng)絡(luò)視圖是一樣的,我們在容器中看的地址,也就是Pod地址實際是Pause容器的IP地址??傮w模型如下:
我們在node 節(jié)點查看之前創(chuàng)建的POD,可以看到該pause容器 :
這種新創(chuàng)建的容器和已經(jīng)存在的一個容器(pause)共享一個 Network Namespace(而不是和宿主機共享) 就是我們常說的container 模式。
每個節(jié)點上的每個Pod都有自己的namespace,同主機上的POD之間怎么通信呢?我們可以在兩個POD之間建立Vet Pair進行通信,但如果有多個容器,兩兩建立Veth 就會非常麻煩,假如有N 個POD ,那么我們需要創(chuàng)建n(n-1)/2個Veth Pair,擴展性非常差,如果我們可以將這些Veth Pair 連接到一個集中的轉(zhuǎn)發(fā)點,由它來統(tǒng)一轉(zhuǎn)發(fā)就就會非常便捷,這個集中轉(zhuǎn)發(fā)點就是我們常說的bridge;如下所示(簡單起見,這里把pause忽略):
仍然以我們的測試環(huán)境為例,創(chuàng)建pod1 和pod2地址分別為:10.244.1.16、10.244.1.18,位于node1 節(jié)點
查看節(jié)點下的namespace:
這兩個NS就是上述兩個POD 對應(yīng)的namespace,查詢對應(yīng)namespace 下的接口:
可以看到標(biāo)紅處的地址,實際就是POD 的ip地址;NS 和對應(yīng)的POD 地址都找到了,那么如何確認這兩個ns 下的虛接口的另一端呢? 比較直觀的確認方式為:上述接口如 3: eth0@if7,表示本端接口id 為3 ,對端接口id是7,我們看下default namespace(我們平時看的默認都在default下) 的veth口:
7: veth3b416eb5@if3 ,該接口的id 正是我們要找的id 為7的接口 ,是veth pair的另一端;
簡單來看,對于網(wǎng)絡(luò)上兩個端點之間的互通無非兩種方案,一種是underlay 直接互通,那么就需要雙方有彼此的路由信息并且該路由信息在underlay的路徑上存在,一種是overlay 方案,通過隧道實現(xiàn)互通,underlay 層面保證主機可達即可,前者代表方案有 Calico(direct模式)和Macvlan,后者有Overlay,OVS,F(xiàn)lannel和Weave。我們?nèi)〈硇缘腇lannel 和calico 插件進行介紹;
2.3.1 Flannel
總體通信流程如下:
通信過程
2.3.1.1地址分配
flanneld第一次啟動時,從 etcd 獲取配置的 Pod 網(wǎng)段信息,為本節(jié)點分配一個未使用的地址段,然后創(chuàng)建 flannedl.1 網(wǎng)絡(luò)接口(也可能是其它名稱,如 flannel1 等),flannel 將分配給自己的 Pod 網(wǎng)段信息寫入 /run/flannel/docker 文件(不同k8s版本文件名存在差異),docker 后續(xù)使用這個文件中的環(huán)境變量設(shè)置 docker0 網(wǎng)橋,從而使這個地址段為本節(jié)點的所有;
查看flannel 為docker 分配的地址段:
表示該節(jié)點創(chuàng)建的POD 地址都從10.244.1.1/24中分配,比如node1 節(jié)點的如下2個pod。
2.3.1.2路由下發(fā)
每臺主機上,flannel 運行一個daemon 進程叫flanneld,它可以在內(nèi)核中創(chuàng)建路由表,查看node1節(jié)點的路由表如下:
可以看到node2 節(jié)點的路由match 10.244.2.0 一行規(guī)則,出接口為flannel.1 口(接口名flannel后數(shù)字可能不一樣) flannel.1 是flanneld程序創(chuàng)建的一個隧道口;這里有一個問題,就是如何判斷隧道打到那里呢,很顯然,flannld存儲了類似容器-物理節(jié)點之間的映射關(guān)系,這種信息存放在etcd里面,flannld進程通過讀取etcd中的映射關(guān)系信息,決定隧道外層封裝。
2.3.1.3數(shù)據(jù)面封裝
Flannel 知道外層封裝地址后,對報文進行封裝,源采用自己的物理ip 地址,目的采用對端的,vxlan 外層的udp port 8472(如果是UDP封裝使用8285作為默認目的端口,下文會提到),對端只需監(jiān)控port 即可,當(dāng)改端口收到報文后將報文送到flannedld 進程,進程將報文送到flanned 接口接封裝,然后查詢本地路由表:
可以看到目的地址為cni0 ;Flannel功能內(nèi)部支持三種不同后端實現(xiàn),分別是:
Host-gw:需要兩臺host 在同一網(wǎng)段,不支持跨網(wǎng),因此不適合大規(guī)模部署
UDP:不建議使用,除非內(nèi)核不支持vxlan 或者debugg時候使用,當(dāng)前也已經(jīng)廢棄;
Vxlan : vxlan 封裝,flannel 使用 vxlan 技術(shù)為各節(jié)點創(chuàng)建一個可以互通的 Pod 網(wǎng)絡(luò),使用的端口為 UDP 8472(需要開放該端口,如公有云 AWS 等)。
我們在node 節(jié)點進行抓包驗證一下:
(注:因為在linux 環(huán)境中,F(xiàn)lannel的vxlan 封裝中UDP 目的port 是 8472 ,標(biāo)準(zhǔn)Vxlan 報文的識別依據(jù)是目的端口4789,因此需要手動指定按照vxlan 來解析,否則無法識別內(nèi)層信息)
2.3.2 CalicoCalico支持3種路由模式:
Direct: 路由轉(zhuǎn)發(fā),報文不做封裝;
Ip-In-Ip:Calico 默認的路由模式,數(shù)據(jù)面采用ipip封裝;
Vxlan:vxlan 封裝;
這里主要介紹Direct模式,采用軟路由建立BGP 宣告容器網(wǎng)段,使得全網(wǎng)所有的Node和網(wǎng)絡(luò)設(shè)備都有到彼此的路由的信息,然后直接通過underlay 轉(zhuǎn)發(fā)。Calico實現(xiàn)的總體結(jié)構(gòu)如下:
組件包含:
Felix:Calico agent:運行在每臺node上,為容器設(shè)置網(wǎng)絡(luò)信息:IP,路由規(guī)則,iptable規(guī)則等BIRD:
BGP Client:監(jiān)聽 Host上由 Felix 注入的路由信息,然后通過 BGP 協(xié)議廣播告訴其他Host節(jié)點,從而實現(xiàn)網(wǎng)絡(luò)互通
BGP Route Reflector: BGP peer建立方式多樣,可以在node 之間兩兩建立bgp peer(默認模式),和傳統(tǒng)ibgp peer問題類似,這會帶來n*(n-1)/2 的鄰居量,因此也可以自建RR 反射器(上圖中結(jié)構(gòu)),node 節(jié)點和RR 建立peer,當(dāng)然node也可以和Tor 建peer,詳細的組網(wǎng)討論可以參考官網(wǎng):
https://docs.projectcalico.org/reference/architecture/design/l3-interconnect-fabric
Calicoctl: calico命令行管理工具。
具體選擇哪種peer方式?jīng)]有固定標(biāo)準(zhǔn),要適配總體網(wǎng)絡(luò)規(guī)劃,只要最終保證容器網(wǎng)絡(luò)可正確發(fā)布到物理網(wǎng)絡(luò)即可;
數(shù)據(jù)通信的流程為:數(shù)據(jù)包先從veth設(shè)備對另一口發(fā)出,到達宿主機上的Cali開頭的虛擬網(wǎng)卡上,到達這一頭也就到達了宿主機上的網(wǎng)絡(luò)協(xié)議棧,然后查詢路由表轉(zhuǎn)發(fā);因為本機通過bird 和RR 建立bgp 鄰居關(guān)系,會將本地的容器地址發(fā)送到RR 從而反射到網(wǎng)絡(luò)其它節(jié)點,同樣,其它節(jié)點的網(wǎng)絡(luò)地址也會傳送到本地,然后由Felix 進程進行管理并下發(fā)到路由表中,報文匹配路由規(guī)則后正常進行轉(zhuǎn)發(fā)即可(實際還有復(fù)雜的iptables 規(guī)則,這里不做展開)
下面通過簡單實驗學(xué)習(xí)下:
具體安裝過程不再討論,可參考官網(wǎng):https://www.projectcalico.org/進行安裝部署;
Node節(jié)點bgp配置如下:
為了簡化實驗,我們再啟用一臺機器運行FRR 來充當(dāng)RR(關(guān)于Frr參考官網(wǎng)https://frrouting.org/) ,RR配置如下:
這樣所有節(jié)點都和RR 建立了bgp 鄰居,通過如下方式檢查鄰居狀態(tài):
我們新建兩個pod ,分別位于兩個node節(jié)點:
默認情況下,當(dāng)網(wǎng)絡(luò)中出現(xiàn)第一個容器,calico會為容器分配一段子網(wǎng)(子網(wǎng)掩碼/26),后續(xù)出現(xiàn)該節(jié)點上的pod都從這個子網(wǎng)中分配ip地址,這樣做的好處是能夠縮減節(jié)點上的路由表的規(guī)模.進入容器查看路由我們發(fā)現(xiàn)網(wǎng)關(guān)地址為169.254.1.1
實際上在calico 網(wǎng)絡(luò)中,容器網(wǎng)關(guān)始終是169.254.1.1,該地址在實際網(wǎng)絡(luò)中不存在的,是直接進行的ARP 代理(ee:ee:ee:ee:ee:ee),我們在創(chuàng)建Pod的時候系統(tǒng)會在對應(yīng)的node 上新增一個cali開頭的虛擬網(wǎng)卡,它就是veth Pair的另一端(本端是容器本地eth0口),它的mac 就是上面的169.254.1.1 對應(yīng)的mac地址
此時的報文已經(jīng)進入default namespace ,這里開始查看路由表:
其中192.168.23.128/26 是node2上的地址空間,該路由由node2 節(jié)點bird發(fā)送到RR,RR 反射到node1節(jié)點的bird ,然后由felix來進行管理和下發(fā)到路由表中,我們可以在node1節(jié)點抓包進一步確認:
同時因為calico 的代理方式,使得同node的不同POD通信也比較特殊,它也是通過三層轉(zhuǎn)發(fā)來實現(xiàn),比如node2 節(jié)點的2個地址,在路由表中都是/32位存在,下一跳接口為veth-pair的一端,另一端就是對應(yīng)的pod內(nèi)接口;
這和flannel 經(jīng)過bridge 方式實現(xiàn)是不一樣的;
2.3.3 總結(jié)
這里我們從網(wǎng)絡(luò)角度對flannel 和calico 進行簡單對比:
總體來看,對性能敏感、策略需求較高時偏向于Calio方案,否則的話,采用Flannel會是更好的選擇;
Serice 和外部通信場景實現(xiàn)涉及較多iptables 轉(zhuǎn)發(fā)原理,限于篇幅這里不再展開,簡單介紹如下:
Pod與service通信: Pod間可以直接通過IP地址通信,但前提是Pod知道對方的IP。在 Kubernetes集群中,Pod可能會頻繁地銷毀和創(chuàng)建,也就是說Pod的IP 不是固定的。為了解決這個問題,Service提供了訪問Pod的抽象層。 無論后端的Pod如何變化,Service都作為穩(wěn)定的前端對外提供服務(wù)。 同時,Service還提供了高可用和負載均衡功能,Service負責(zé)將請求轉(zhuǎn) 給正確的Pod;
外部通信:無論是Pod的IP還是Service的Cluster IP,它們只能在Kubernetes集群中可見,對集群之外的世界,這些IP都是私有的Kubernetes提供了兩種方式讓外界能夠與Pod通信:
NodePort:Service通過Cluster節(jié)點的靜態(tài)端口對外提供服務(wù), 外部可以通過:訪問Service。
LoadBalancer:Service利用cloud provider提供的load balancer對外提供服務(wù),cloud provider負責(zé)將load balancer 的流量導(dǎo)向Service。目前支持的cloud provider有GCP、AWS、 Azur等。
容器網(wǎng)絡(luò)場景復(fù)雜,涉及面廣,希望一些心得體會可以給大家?guī)韰⒖迹e誤之處還望指正。