覆蓋網絡(overlay network)是將TCP數據包裝在另外一種網絡包裏面進行路由轉發和通訊的技術。Overlay網絡不是默認必須的,可是它們在特定場景下很是有用。好比當咱們沒有足夠的IP空間,或者網絡沒法處理額外路由,抑或當咱們須要Overlay提供的某些額外管理特性。一個常見的場景是當雲提供商的路由表能處理的路由數是有限制時,例如AWS路由表最多支持50條路由纔不至於影響網絡性能。所以若是咱們有超過50個Kubernetes節點,AWS路由表將不夠。這種狀況下,使用Overlay網絡將幫到咱們。node
本質上來講,Overlay就是在跨節點的本地網絡上的包中再封裝一層包。你可能不想使用Overlay網絡,由於它會帶來由封裝和解封全部報文引發的時延和複雜度開銷。一般這是沒必要要的,所以咱們應當在知道爲何咱們須要它時才使用它。web
爲了理解Overlay網絡中流量的流向,咱們拿Flannel作例子,它是CoreOS 的一個開源項目。Flannel經過給每臺宿主機分配一個子網的方式爲容器提供虛擬網絡,它基於Linux TUN/TAP,使用UDP封裝IP包來建立overlay網絡,並藉助etcd維護網絡的分配狀況。後端
這裏咱們注意到它和以前咱們看到的設施是同樣的,只是在root netns中新增了一個虛擬的以太網設備,稱爲flannel0。它是虛擬擴展網絡Virtual Extensible LAN(VXLAN)的一種實現,可是在Linux上,它只是另外一個網絡接口。api
從pod1到pod4(在不一樣節點)的數據包的流向相似以下:安全
1.它由pod1中netns的eth0網口離開,經過vethxxx進入root netns。網絡
2.而後被傳到cbr0,cbr0經過發送ARP請求來找到目標地址。負載均衡
3.數據封裝分佈式
*a. 因爲本節點上沒有Pod擁有pod4的IP地址,所以網橋把數據包發送給了flannel0,由於節點的路由表上flannel0被配成了Pod網段的目標地址。
*b. flanneld daemon和Kubernetes apiserver或者底層的etcd通訊,它知道全部的Pod IP,而且知道它們在哪一個節點上。所以Flannel建立了Pod IP和Node IP之間的映射(在用戶空間)。flannel0取到這個包,並在其上再用一個UDP包封裝起來,該UDP包頭部的源和目的IP分別被改爲了對應節點的IP,而後發送這個新包到特定的VXLAN端口(一般是8472)。ide
儘管這個映射發生在用戶空間,真實的封裝以及數據的流動發生在內核空間,所以仍然是很快的工具
*c. 封裝後的包經過eth0發送出去,由於它涉及了節點間的路由流量。
4.包帶着節點IP信息做爲源和目的地址離開本節點。
5.雲提供商的路由表已經知道了如何在節點間發送報文,所以該報文被髮送到目標地址node2。
6.數據解包
*a. 包到達node2的eth0網卡,因爲目標端口是特定的VXLAN端口,內核將報文發送給了 flannel0。
*b. flannel0解封報文,並將其發送到 root 命名空間下。從這裏開始,報文的路徑和咱們以前在Part 1 中看到的非Overlay網絡就是一致的了。
*c. 因爲IP forwarding開啓着,內核按照路由表將報文轉發給了cbr0。
7.網橋獲取到了包,發送ARP請求,發現目標IP屬於vethyyy。
8.包跨過管道對到達pod4
這就是Kubernetes中Overlay網絡的工做方式,雖然不一樣的實現仍是會有細微的差異。有個常見的誤區是,當咱們使用Kubernetes,咱們就不得不使用Overlay網絡。事實是,這徹底依賴於特定場景。所以請確保在確實須要的場景下才使用。
因爲Kubernetes(更通用的說法是分佈式系統)天生具備不斷變化的特性,所以它的Pod(以及Pod的IP)老是在改變。變化的緣由能夠是針對不可預測的Pod或節點崩潰而進行的滾動升級和擴展。這使得Pod IP不能直接用於通訊。
咱們看一下Kubernetes Service,它是一個虛擬IP,並伴隨着一組Pod IP做爲Endpoint(經過標籤選擇器識別)。它們充當虛擬負載均衡器,其IP保持不變,然後端Pod IP可能會不斷變化。
整個虛擬IP的實現其實是一組iptables(最新版本能夠選擇使用IPVS,但這是另外一個討論)規則,由Kubernetes組件kube-proxy管理。 這個名字如今其實是誤導性的。 它在v 1.0以前確實是一個代理,而且因爲其實現是內核空間和用戶空間之間的不斷複製,它變得很是耗費資源而且速度較慢。 如今,它只是一個控制器,就像Kubernetes中的許多其它控制器同樣,它watch api server的endpoint的更改並相應地更新iptables規則。
有了這些iptables規則,每當數據包發往Service IP時,它就進行DNAT(DNAT=目標網絡地址轉換)操做,這意味着目標IP從Service IP更改成其中一個Endpoint - Pod IP - 由iptables隨機選擇。這可確保負載均勻分佈在後端Pod中。
當這個DNAT發生時,這個信息存儲在conntrack中——Linux鏈接跟蹤表(iptables規則5元組轉譯並完成存儲:protocol,srcIP,srcPort,dstIP,dstPort)。 這樣當請求回來時,它能夠un-DNAT,這意味着將源IP從Pod IP更改成Service IP。 這樣,客戶端就不用關心後臺如何處理數據包流。
所以經過使用Kubernetes Service,咱們可使用相同的端口而不會發生任何衝突(由於咱們能夠將端口從新映射到Endpoint)。 這使服務發現變得很是容易。 咱們可使用內部DNS並對服務主機名進行硬編碼。 咱們甚至可使用Kubernetes提供的service主機和端口的環境變量來完成服務發現。
專家建議: 採起第二種方法,你可節省沒必要要的DNS調用,可是因爲環境變量存在建立順序的侷限性(環境變量中不包含後來建立的服務),推薦使用DNS來進行服務名解析。
到目前爲止咱們討論的Kubernetes Service是在一個集羣內工做。可是,在大多數實際狀況中,應用程序須要訪問一些外部api/website。
一般,節點能夠同時具備私有IP和公共IP。對於互聯網訪問,這些公共和私有IP存在某種1:1的NAT,特別是在雲環境中。
對於從節點到某些外部IP的普統統信,源IP從節點的專用IP更改成其出站數據包的公共IP,入站的響應數據包則恰好相反。可是,當Pod發出與外部IP的鏈接時,源IP是Pod IP,雲提供商的NAT機制不知道該IP。所以它將丟棄具備除節點IP以外的源IP的數據包。
所以你可能也猜對了,咱們將使用更多的iptables!這些規則也由kube-proxy添加,執行SNAT(源網絡地址轉換),即IP MASQUERADE(IP假裝)。它告訴內核使用此數據包發出的網絡接口的IP,代替源Pod IP同時保留conntrack條目以進行反SNAT操做。
到目前爲止一切都很好。Pod能夠互相交談,也能夠訪問互聯網。但咱們仍然缺乏關鍵部分 - 爲用戶請求流量提供服務。截至目前,有兩種主要方法能夠作到這一點:
* NodePort /雲負載均衡器(L4 - IP和端口)
將服務類型設置爲NodePort默認會爲服務分配範圍爲30000-33000的nodePort。即便在特定節點上沒有運行Pod,此nodePort也會在每一個節點上打開。此NodePort上的入站流量將再次使用iptables發送到其中一個Pod(該Pod甚至可能在其它節點上!)。
雲環境中的LoadBalancer服務類型將在全部節點以前建立雲負載均衡器(例如ELB),命中相同的nodePort。
* Ingress(L7 - HTTP / TCP)
許多不一樣的工具,如Nginx,Traefik,HAProxy等,保留了http主機名/路徑和各自後端的映射。一般這是基於負載均衡器和nodePort的流量入口點,其優勢是咱們能夠有一個入口處理全部服務的入站流量,而不須要多個nodePort和負載均衡器。
能夠把它想象爲Pod的安全組/ ACL。 NetworkPolicy規則容許/拒絕跨Pod的流量。確切的實現取決於網絡層/CNI,但大多數只使用iptables。
目前爲止就這樣了。 在前面的部分中,咱們研究了Kubernetes網絡的基礎以及overlay網絡的工做原理。 如今咱們知道Service抽象是如何在一個動態集羣內起做用並使服務發現變得很是容易。咱們還介紹了出站和入站流量的工做原理以及網絡策略如何對集羣內的安全性起做用。
An illustrated guide to Kubernetes Networking - part1
An illustrated guide to Kubernetes Networking - part2
An illustrated guide to Kubernetes Networking - part3