1.Kubernetes通信問題
1.容器間通信:即同一個Pod內多個容器間通信,通常使用loopback來實現。
2.Pod間通信:K8s要求,Pod和Pod之間通信必須使用Pod-IP 直接訪問另一個Pod-IP
3.Pod與Service通信:即PodIP去訪問ClusterIP,當然,clusterIP實際上是IPVS或iptables規則的虛擬IP,是沒有TCP/IP協議棧支持的。但不影響Pod訪問它.
4.Service與集群外部Client的通信,即K8s中Pod提供的服務必須能被互聯網上的用戶所訪問到。
需要注意的是,k8s集群初始化時的service網段,pod網段,網絡插件的網段,以及真實服務器的網段,都不能相同,如果相同就會出各種各樣奇怪的問題,而且這些問題在集群做好之后是不方便改的,改會導致更多的問題,所以,就在搭建前將其規划好。
CNI(容器網絡接口):
這是K8s中提供的一種通用網絡標准規范,因為k8s本身不提供網絡解決方案。
目前比較知名的網絡解決方案有:
-
- Flannel
- calico
- canel
- kube-router
- .......
等等,目前比較常用的時flannel和calico,flannel的功能比較簡單,不具備復雜網絡的配置能力,calico是比較出色的網絡管理插件,單具備復雜網絡配置能力的同時,往往意味着本身的配置比較復雜,所以相對而言,比較小而簡單的集群使用flannel,考慮到日后擴容,未來網絡可能需要加入更多設備,配置更多策略,則使用calico更好
所有的網絡解決方案,它們的共通性:
1.虛擬網橋
2.多路復用:MacVLAN
3.硬件交換:SR-IOV(單根-I/O虛擬網絡):它是一種物理網卡的硬件虛擬化技術,它通過輸出VF(虛擬功能)來將網卡虛擬為多個虛擬子接口,每個VF綁定給一個VM后,該VM就可以直接操縱該物理網卡。
kubelet來調CNI插件時,會到 /etc/cni/net.d/目錄下去找插件的配置文件,並讀取它,來加載該插件,並讓該網絡插件來為Pod提供網絡服務。
2.Calico
2.1 Calico簡介
Calico是一個純三層的網絡插件,calico的bgp模式類似於flannel的host-gw。
Calico方便集成OpenStack這種 IaaS雲架構,為openstack虛擬機、容器、裸機提供多主機間通信。
2.2 calico架構

calico包括如下重要組件:Felix,etcd,BGP Client,BGP Route Reflector。下面分別說明一下這些組件:
-
- Felix:主要負責路由配置以及ACLS規則的配置以及下發,它存在在每個node節點上。
- etcd:分布式鍵值存儲,主要負責網絡元數據一致性,確保Calico網絡狀態的准確性,可以與kubernetes共用;
- BGPClient(BIRD):主要負責把 Felix寫入 kernel的路由信息分發到當前 Calico網絡,確保 workload間的通信的有效性;
- BGPRoute Reflector(BIRD):大規模部署時使用,摒棄所有節點互聯的mesh模式,通過一個或者多個 BGPRoute Reflector 來完成集中式的路由分發;

2.3 calico 原理
calico是一個純三層的虛擬網絡,它沒有復用docker的docker0網橋,而是自己實現的,calico網絡不對數據包進行額外封裝,不需要NAT和端口映射,擴展性和性能都很好。Calico網絡提供了DockerDNS服務,容器之間可以通過hostname訪問,Calico在每一個計算節點利用Linux Kernel實現了一個高效的vRouter(虛擬路由)來負責數據轉發,它會為每個容器分配一個ip,每個節點都是路由,把不同host的容器連接起來,從而實現跨主機間容器通信。而每個vRouter通過BGP協議(邊界網關協議)負責把自己節點的路由信息向整個Calico網絡內傳播——小規模部署可以直接互聯,大規模下可通過指定的BGProute reflector來完成;Calico基於iptables還提供了豐富而靈活的網絡策略,保證通過各個節點上的ACLs來提供多租戶隔離、安全組以及其他可達性限制等功能。
Calico是一種非常復雜的網絡組件,它需要自己的etcd數據庫集群來存儲自己通過BGP協議獲取的路由等各種所需要持久保存的網絡數據信息,因此在部署Calico時,早期是需要單獨為Calico部署etcd集群的,因為在k8s中,訪問etcd集群只有APIServer可以對etcd進行讀寫,其它所有組件都必須通過APIServer作為入口,將請求發給APIServer,由APIServer來從etcd獲取必要信息來返回給請求者,但Caclico需要自己寫,因此就有兩種部署Calico網絡插件的方式,一種是部署兩套etcd,另一種就是Calico不直接寫,而是通過APIServer做為代理,來存儲自己需要存儲的數據。通常第二種使用的較多,這樣可降低系統復雜度。
當然由於Calico本身很復雜,但由於很多k8s系統可能存在的問題是,早期由於各種原因使用了flannel來作為網絡插件(flannel會講解原因)。
2.4 calico網絡模式
1.ipip模式
把一個IP數據包又套在一個IP包里,即把IP層封裝到IP層的一個tunnel,它的作用其實基本上就相當於一個基於IP層的網橋,一般來說,普通的網橋是基於mac層的,根本不需要IP,而這個ipip則是通過兩端的路由做一個tunnel,把兩個本來不通的網絡通過點對點連接起來;

calico以ipip模式部署完畢后,node上會有一個tunl0的網卡設備,這是ipip做隧道封裝用的,也是一種overlay模式的網絡。當我們把節點下線,calico容器都停止后,這個設備依然還在,執行 modprobe -r ipip 命令可以將它刪除。
9: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 inet 10.233.110.0/32 brd 10.233.110.0 scope global tunl0 valid_lft forever preferred_lft forever
官方提供的calico.yaml模板里,默認打開了ip-ip功能,該功能會在node上創建一個設備tunl0,容器的網絡數據會經過該設備被封裝一個ip頭再轉發。這里,calico.yaml中通過修改calico-node的環境變量:CALICO_IPV4POOL_IPIP來實現ipip功能的開關:默認是Always,表示開啟;Off表示關閉ipip。
# kubectl get daemonsets. calico-node -n kube-system -o yaml | grep -iA 1 ipip - name: CALICO_IPV4POOL_IPIP value: "Always"
當然Linux支持的五種ip隧道,可以通過ip tunnel help查看
[root@centos7 ~]# ip tunnel help Usage: ip tunnel { add | change | del | show | prl | 6rd } [ NAME ] [ mode { ipip | gre | sit | isatap | vti } ] [ remote ADDR ] [ local ADDR ]
-
- ipip:即IPv4 in IPv4,在IPv4報文的基礎上再封裝一個IPv4報文。
- gre:即通用路由封裝(Generic Routing Encapsulation),定義了在任意一種網絡層協議上封裝其他任意一種網絡層協議的機制,IPv4和IPv6都適用。
- sit:和ipip類似,不同的是sit是用IPv4報文封裝IPv6報文,即IPv6 over IPv4。
- isatap:即站內自動隧道尋址協議(Intra-Site Automatic Tunnel Addressing Protocol),和sit類似,也是用於IPv6的隧道封裝。
- vti:即虛擬隧道接口(Virtual Tunnel Interface),是cisco提出的一種IPsec隧道技術。
2.BGP模式
邊界網關協議(BorderGateway Protocol, BGP)是互聯網上一個核心的去中心化的自治路由協議。它通過維護IP路由表或‘前綴’表來實現自治系統(AS)之間的可達性,屬於矢量路由協議。BGP不使用傳統的內部網關協議(IGP)的指標,而是基於路徑、網絡策略或規則集來決定路由。因此,它更適合被稱為矢量性協議,而不是路由協議,通俗的說就是將接入到機房的多條線路(如電信、聯通、移動等)融合為一體,實現多線單IP;
BGP機房的優點:服務器只需要設置一個IP地址,最佳訪問路由是由網絡上的骨干路由器根據路由跳數與其它技術指標來確定的,不會占用服務器的任何系統。
3.Flannel
3.1 flannel架構

flannel的udp模式和vxlan模式都是屬於隧道方式,也就是在udp的基礎之上,構建虛擬網絡,然后通過一個封包解包的過程來實現數據的傳輸。
3.2 flannel的幾種模式
通過在每一個節點上啟動一個叫flannel的進程,負責為每一個節點上的子網划分,並將相關配置信息(如各節點的子網網段、外部IP等)保存到etcd中,而具體的網絡報文轉發交給backend實現。
flanneld可以在啟動時通過配置文件指定不同的backend進行網絡通信,目前比較成熟的backend有UDP、VXLAN和host-gateway三種。目前,VXLAN是官方比較推崇的一種backend實現方式。
UDP模式和VXLAN模式基於三層網絡層即可實現,而host-gateway模式就必須要求集群所有機器在同一個廣播域,也就是需要在二層網絡同一個交換機下才能實現。
host-gatewa一般用於對網絡性能要求比較高的場景,但需要基礎網絡架構的支持;UDP則用於測試及一般比較老的不支持VXLAN的Linux內核。
1.UDP模式
采用UDP模式時,需要在flanneld的配置文件中指定Backend.Type為UDP,可通過直接修改flanneld的ConfigMap的方式實現。
# kubectl get cm kube-flannel-cfg -n kube-system -o yaml kubectl net-conf.json: | { "Network": "10.233.64.0/18", "Backend": { "Type": "udp" } }
通過ip addr 命令可以發現節點上會多出一個flannel 0的網絡接口,在UDP模式中,flanneld的主要作用為:
(1)UDP包封包解包
(2)節點上路由表的動態更新
工作流程圖:

熟悉Linux的應該知道,Linux頻繁內核態-用戶態的切換,會造成頻繁的上下文切換,會引發性能問題,所以從上面可以看到container的數據包,從離開src container后,經過了多次內核態-用戶態的切換,並且,數據包是由用戶態的flannel進行進行封包/解包的,從而導致了比較大的性能損耗,這也是為什么生產基本不會用這個方案,因為性能實在非常差。
2.VXLAN模式
同樣需要在Backend.Type修改為VXLAN
net-conf.json: | { "Network": "10.233.64.0/18", "Backend": { "Type": "vxlan" } }
VXLAN模式下,會創建一個名為flannel 1的VTEP設備,數據的轉發由內核完成,並不是flanned,flanned僅動態設置ARP和FDB表項.
工作流程圖:

vxlan本身就是內核特性,使用vxlan會在服務器中創建一個vtep設備(flannel 1),設備的封包/解包操作都在該設備下操作,所以直接在內核態操作,不需要CPU上下文切換,且和UDP直接三層封包不一樣,vxlan是直接對二層數據幀進行封包。
3.host-gateway模式
同上需要在Backend.Type修改為host-gw。
由於host-gw是純路由模式,flannel需要通過etcd維護所有的靜態路由,核心是IP包在封裝成楨的時候,使用路由表的"下一跳"設置上的MAC地址,這樣可以經過二層網絡到達目的宿主機。這就要求所有的服務器在同一個二層網絡下,這就使host-gw模式無法適用於集群規模較大且需要對節點進行網段划分的場景。
host-gw另外一個限制則是隨着集群中節點規模的增大,flanneld維護主機上成千上萬條路由表的動態更新也是一個不小的壓力,因此在路由方式下,路由表規則的數量是限制網絡規模的一個重要因素。
工作流程圖:

在性能上,host-gw由於沒有封包/解包,故性能最好
