1. 程式人生 > >Kubernetes 網絡原理

Kubernetes 網絡原理

做出 內存 沒有 實時 ping bgp協議 橋接 dde 包過濾

Docker網絡基礎

由於Kubernetes是基於Docker容器作為應用發布的載體,而Docker本身的網絡特性也決定了Kubernetes在構建一個容器互通網絡必須要解決Docker自身網絡的缺陷。

網絡命名空間

為了支持網絡協議棧的多個實例,Linux在網絡命名空間中引入了網絡命名空間(Network Namespace),這些網絡協議棧被隔離到不同的命名空間中。不同的命名空間中資源完全隔離,彼此之間無法完全通信。通過不同的網絡命名空間,就可以在一臺宿主機上虛擬多個不同的網絡環境。Docker正是利用了網絡命令空間的特性實現了不同容器之間的網絡隔離。

在Linux的網絡命名空間中可以配置自己獨立的iptables規則來設置包轉發,NAT和包過濾等。

由於網絡命名空間彼此隔離,無法直接通信,如果要打通兩個隔離的網絡命名空間,實現數據互通,就需要用到Veth設備對。Veth設備對的一個主要作用就是打通不同的網絡協議棧,它就像是一個網線,兩端分別連接不同的網絡命名空間的協議棧。
如果想在兩個命名空間之間進行通信,就必須有一個Veth設備對。

網絡命名空間的操作

1、創建一個名為test的網絡命名空間:

# ip netns add test

2、在此命名空間中執行ip a命令

# ip netns exec test ip a

1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

如果想執行多個命令,可以直接進入此網絡命名空間中執行:

# ip netns exec test sh

退出執行
# exit

我們可以在不同的網絡命名空間中轉義設備,如上面提到的Veth設備對,由於一個設備只能屬於一個網絡命名空間,所以當設備被轉移後,在當前的命名空間中就無法查看到此設備了。

Veth設備對

技術分享圖片

由於Veth需要連接兩個不同的網絡命名空間,所以Veth設備一般是成對出現的,稱其中一端為另一端的peer。

1、創建Veth設備對

# ip link add veth0 type veth peer name veth1

創建一個veth設備對,本端為veth0, 對端為veth1

2、查看設備對:

# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
    link/ether 52:54:00:7f:52:5a brd ff:ff:ff:ff:ff:ff
3: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether 26:3f:dd:c0:70:cb brd ff:ff:ff:ff:ff:ff
4: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether a2:91:f4:9c:5b:6b brd ff:ff:ff:ff:ff:ff

3、將veth1 分配到test網絡命名空間中:

# ip link set veth1 netns test

4、查看當前設備對情況:

# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
    link/ether 52:54:00:7f:52:5a brd ff:ff:ff:ff:ff:ff
4: veth0@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether a2:91:f4:9c:5b:6b brd ff:ff:ff:ff:ff:ff link-netnsid 0

5、查看test網絡命名空間的情況,發現此設備對已經分配進來:

# ip netns exec test ip a

1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 26:3f:dd:c0:70:cb brd ff:ff:ff:ff:ff:ff link-netnsid 0

6、由於兩端的設備對還沒有地址,所以無法通信,現在分別分配地址:

ip addr add 172.16.0.1/24 dev veth0  # 給本端的veth0分配ip地址
ip netns exec test ip addr add 172.16.0.2/24 dev veth1  # 為對端的veth1 配置IP

7、可以查看veth的狀態,默認情況下都為DOWN:

# ip a|grep veth
4: veth0@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast state DOWN qlen 1000
    inet 172.16.0.1/24 scope global veth0

# ip netns exec test ip a|grep veth
3: veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast state DOWN qlen 1000
    inet 172.16.0.2/24 scope global veth1

8、啟動veth設備對,查看網絡是否打通:

# ip link set dev veth0 up
# ip netns exec test ip link set dev veth1 up

# ping 172.16.0.2
PING 172.16.0.2 (172.16.0.2) 56(84) bytes of data.
64 bytes from 172.16.0.2: icmp_seq=1 ttl=64 time=0.150 ms
64 bytes from 172.16.0.2: icmp_seq=2 ttl=64 time=0.028 ms

9、查看對端設備
當設備對比較多的情況下,無法確認對端的設備是屬於哪個設備對的,可以使用ethtool命令來查看對端的設備編號:

# ethtool -S veth0       # 查看veth0的對端設備編號
NIC statistics:
     peer_ifindex: 3     # 這裏顯示的對端的設備編號為3

# ip netns exec test ip link |grep 3:  # 對端設備編號為3的設備信息
3: veth1@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000

# 本地的veth0 編號為4
# ip link |grep veth
4: veth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000

#在對端驗證
# ip netns exec test ethtool -S veth1
NIC statistics:
     peer_ifindex: 4

網橋

Linux中的網橋和現實中的交換機類似,是一個虛擬的二層設備。網橋上可以attach若幹個網絡接口設備,如eth0,eth1等,當有數據到達網橋時,網橋會根據報文中MAC地址信息進行轉發或丟棄處理。網橋會自動學習內部的MAC端口映射,並會周期性的更新。

網橋和現實中的設備有一個區別,那就是從網絡接口過來的數據會直接發送到網橋上,而不是從特定的端口接收。

網橋可以設置IP地址,當一個設備如eth0添加到網橋上之後,綁定在設備上的IP就無效了,如果要實現通信,需要給網橋配置一個IP。

1、如果要配置橋接網卡,需要安裝bridge-utils工具:

 # yum install bridge-utils -y

2、添加一個網橋設備br0

 # brctl addbr br0

3、將eth0添加到br0上(此步執行後,eth0上的IP會失效,雖然IP還在eth0上,但是無法接收數據,如果使用ssh將會斷開連接):

 # brctl addif br0 eth0 

4、 刪除eth0上的ip:

 ip addr del dev eth0 10.0.0.1/24

5、給br0添加此IP

 ifconfig br0  10.0.0.1/24 up

6、給br0添加默認路由:

 route add default gw 10.0.0.254

7、我們可以通過如下命令查卡當前的路由信息:

 ip route list
 netstat -rn
 route -n

Docker 網絡實現

在純Docker的環境,Docker支持4類網絡模式:

  • host模式:使用宿主機的IP和端口
  • container模式:和已存在的容器共享網絡
  • none模式: 不進行網絡配置
  • bridge模式: 默認模式,使用橋接網絡,Kubernetes使用此模式。

由於Kubernetes中只使用bridge模式,所以這裏只討論bridge模式。

Docker 網絡模型

網絡示例圖:

技術分享圖片

通過上圖,可以清楚的表示容器的網絡結構,其中容器中的網卡eth0和綁定在Docker0網橋上的vethxxx設備是一對veth設備對。其中vethxxx由於綁定到docker0網橋,所以沒有IP地址,容器中的eth0分配了和docker0同一網段的地址,這樣就實現了容器的互聯。

通過查看運行兩個容器的宿主:

# ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:15:c2:12 brd ff:ff:ff:ff:ff:ff
    inet 192.168.20.17/24 brd 192.168.20.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe15:c212/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:fa:6f:13:18 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:faff:fe6f:1318/64 scope link 
       valid_lft forever preferred_lft forever
7: veth37e9040@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP 
    link/ether f2:4e:50:a5:fb:b8 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::f04e:50ff:fea5:fbb8/64 scope link 
       valid_lft forever preferred_lft forever
19: veth36fb1f6@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP 
    link/ether 7a:96:bc:c7:03:d8 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::7896:bcff:fec7:3d8/64 scope link 
       valid_lft forever preferred_lft forever

通過查看橋接網卡信息,可以驗證這兩個veth綁定在docker0上:

# brctl  show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242fa6f1318   no      veth36fb1f6
                                        veth37e9040

默認情況下,docker隱藏了網絡命名空間的配置,如果要通過ip netns list命令查看信息,需要進行如下操作:

# docker inspect 506a694d09fb|grep Pid
            "Pid": 2737,
            "PidMode": "",
            "PidsLimit": 0,

# mkdir /var/run/netns

# ln -s /proc/2737/ns/net /var/run/netns/506a694d09fb

# ip netns list
506a694d09fb (id: 0)
6d9742fb3c2d (id: 1)

分別查看兩個容器的IP:

# ip netns exec 506a694d09fb ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft foreve

# ip netns exec 6d9742fb3c2d ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 scope global eth0
       valid_lft forever preferred_lft forever

可以發現這兩個容器屬於不同網絡命名空間,但是在同一網段,通過veth設備對,綁定docker0互聯。
通過ethtool -S veth-name 可以查看到對應的peer端,這裏就不再演示,其實通過veth的名稱(vethxxx@ifNO)也可以發現所指的接口信息。

Kubernetes的網絡實現

Kubernetes主要解決以下幾個問題:

  • 容器到容器之間的通信
  • 抽象Pod到Pod之間的通信
  • Pod到Service之間的通信
  • 集群外部和集群內部之間的通信

容器與容器之間的通信

同一個Pod中的容器屬於同一個網絡命名空間,共享同一個Linux網絡協議棧,通過本地的localhost網絡來與Pod內的其他容器通信,pod中的容器如下圖:

技術分享圖片

Pod與Pod之間的通信

同宿主機上的通信示意圖:

技術分享圖片

在宿主機內部通過docker0的橋接網卡,可以實現Pod之間的直接通信,這裏和純docker環境下的多個容器互通原理相似。

另一種情況是在不同宿主機上的不同Pod之間通信,其原理圖如下:

技術分享圖片

網絡插件CNI

CNI是由CoreOS公司提出的一種容器網絡規範,定義容器運行環境與網絡插件之間的簡單接口規範。
CNI模型涉及兩個概念:

  • 容器:擁有獨立Linux網絡命名空間的環境,如Docker和rkt。
  • 網絡:網絡表示可以互聯的一組實體,這些實體擁有獨立,唯一的IP地址。

Kubernetes 中使用的網絡插件

Kubernetes目前支持多種網絡插件,可以使用CNI插件規範實現的接口,與插件提供者進行對接。當在Kubernetes中指定插件時,需要在kubelet服務啟動參數中指定插件參數:

...
 --network-plugin=cni   --cni-conf-dir=/etc/cni/net.d \  # 此目錄下的配置文件要符合CNI規範。
  --cni-bin-dir=/opt/kubernetes/bin/cni ...

目前有多個開源的項目支持以CNI網絡插件的形式部署到Kubernetes,包括 Calico、Canal、Cilium、Contiv、Fannel、Romana、Weave等。

Flannel網絡實現原理

Flannel原理圖:

技術分享圖片

我們之所以要單獨使用第三方的網絡插件來擴展k8s,主要原因是在使用docker的環境中,在每個node節點的docker0默認的網段都是172.17.0.0/16的網絡。如果要實現不同宿主node上pod(這裏也可以理解為容器)互相通信,就不能使用默認的docker0提供的網段,我們需要部署一個Fannel的覆蓋網絡,讓每個node節點的docker0網絡都處於不同的網段,這樣,通過添加一些路由轉發策略,就能讓集群中各個pod在同一個虛擬的網絡中實現通信。

Fannel首先連上etcd,利用etcd來管理可分配的IP地址段資源,同時監控etcd中每個Pod的實際地址,並在內存中建立一個Pod節點路由表,將docker0發給它的數據包封裝,利用物理網絡的連接將數據投遞到目標flannel上,從而完成Pod到Pod之間的通信。
Fannel為了不和其他節點上的Pod IP產生沖突,每次都會在etcd中獲取IP,Flannel默認使用UDP作為底層傳輸協議。

Calico 網絡實現原理

Kubernetes中如果要實現Network Policy,僅僅使用Flannel網絡是無法實現的,其本身只是解決了Pod互聯的問題,如果需要Network Policy功能,需要使用如Calico、Romana、Weave Net和trireme等支持Network Policy的網絡插件。這裏將介紹常用的Calico的原理。

Calico介紹

Calico是一個基於BGP的純三層的網絡解決方案。Calico在每個節點利用Linux Kernel實現了一個高效的vRouter來負責數據的轉發。 每個vRouter通過BGP1協議把在本節點上運行的容器的路由信息向整個Calico網絡廣播,並自動設置到其它節點的路由轉發規則。Calico保證所有所有容器之間的數據流量都是通過IP路由的方式完成互聯。Calico節點組網可以直接利用數據中心的網絡結構(L2和L3),不需要額外的NAT、隧道或者Overlay Network,所以就不會有額外的封包和解包過程,能夠節省CPU的運算,提升網絡效率,相比而言Calico網絡比Flannel性能更高。

Overlay網絡和Calico網絡數據包結構對比(簡圖):

技術分享圖片

特性:

  • Calico在小規模集群中可以直接互聯,在大規模集群中可以通過額外的BGP route reflector來完成。
  • Calico基於iptables還提供了豐富的網絡策略。實現了Kubernetes的Network Policy策略,用於提供容器間網絡可達性限制的功能。
  • 在需要使用Overlay網絡的環境,Calico使用 IP-in-IP隧道的方式,也可以與其它的overlay網絡兼容,如flannel。
  • Calico還提供網絡安全規則的動態實施。
  • Calico比較適合部署在物理機和私有雲環境中的大型Kubernetes集群,相比於覆蓋網絡(如flannel)性能更高,更加簡單,易於部署和維護。

使用Calico的簡單策略語言,您可以實現對容器,虛擬機工作負載和裸機主機端點之間通信的細粒度控制。

Calico v3.0與Kubernetes和OpenShif集成的環境已經過大規模的生產驗證。

Calico架構及組件

Calico架構圖:

技術分享圖片

Calico組件:

  • Felix: Calico的agent,需要運行在每一臺主機上,主要為容器或虛擬機設置網絡資源(IP地址,路由規則,Iptables規則等),保證跨主機容器網絡互通。
  • etcd: 用於Calico的數據存儲。
  • Orchestrator Plugin : 特定於orchestrator的代碼,將Calico緊密集成到該orchestrator中,主要提供與集成平臺的API轉換和反饋Felix agent的狀態。
  • BIRD: BGP的客戶端組件,負責分發各個節點的路由信息到Calico網絡中。(使用BGP協議)
  • BGP Route Reflector(BIRD): 可以通過單獨一個和多個BGP Router Reflector 來完成大規模集群的分級路由分發。(可選組件)
  • Calicoctl: Calico的命令行工具。

各組件的功能和實現

Felix

Felix是一個守護進程,它在每個提供endpoint的計算機上運行:在大多數情況下,這意味著在托管容器或VM的宿主節點上運行。 它負責設置路由和ACL以及主機上所需的任何其他任務,以便為該主機上的端點提供所需的連接。
Felix一般負責以下任務:

  • ==接口管理==:
    Felix將有關接口的一些信息編程到內核中,以使內核能夠正確處理該端點發出的流量。 特別是,它將確保主機使用主機的MAC響應來自每個工作負載的ARP請求,並將為其管理的接口啟用IP轉發。它還監視出現和消失的接口,以便確保在適當的時間應用這些接口的編程。

  • ==路由規劃==:
    Felix負責將到其主機端點的路由編程到Linux內核FIB(轉發信息庫)中。 這確保了發往那些到達主機的端點的數據包被相應地轉發。

  • ==ACL規劃==:
    Felix還負責將ACL編程到Linux內核中。 這些ACL用於確保只能在端點之間發送有效流量,並確保端點無法繞過Calico的安全措施。

  • ==狀態報告==:
    Felix負責提供有關網絡健康狀況的數據。 特別是,它報告配置其主機時的錯誤和問題。 該數據被寫入etcd,以使其對網絡的其他組件和操作人員可見。
Orchestrator 插件(可選)

與Felix 沒有單獨的Orchestrator插件相反,每個主要的雲編排平臺(如Kubernetes)都有單獨的插件。這些插件時將Calico更緊密的綁定到協調器中,允許用戶管理Calico網絡,就像他們管理協調器中的網絡工具一樣。Kubernetes中可以直接使用CNI插件來代替此功能。
一個好的Orchestrator插件示例是Calico Neutron ML2機制驅動程序。 該組件與Neutron的ML2插件集成,允許用戶通過Neutron API調用來配置Calico網絡。 這提供了與Neutron的無縫集成。主要有以下功能:

  • ==API轉換==:
    協調器將不可避免地擁有自己的一套用於管理網絡的API。 Orchestrator插件的主要工作是將這些API轉換為Calico的數據模型,然後將其存儲在Calico的數據存儲區中。
    這種轉換中的一些將非常簡單,其他比特可能更復雜,以便將單個復雜操作(例如,實時遷移)呈現為Calico網絡的其余部分期望的一系列更簡單的操作。

  • ==反饋==:
    如有必要,orchestrator插件將從Calico網絡向協調器提供反饋。 例子包括:提供有關Felix活力的信息; 如果網絡設置失敗,則將某些端點標記為失敗。
etcd

Calico使用etcd提供組件之間的通信,並作為一致的數據存儲,確保Calico始終可以構建準確的網絡。
根據orchestrator插件,etcd可以是主數據存儲,也可以是單獨數據存儲的輕量級副本鏡像.主要功能:

  • ==數據存儲==:
    etcd以分布式,容錯的方式存儲Calico網絡的數據(這裏指使用至少三個etcd節點的etcd集群)。 這確保Calico網絡始終處於已知良好狀態。
    Calico數據的這種分布式存儲還提高了Calico組件從數據庫讀取的能力,使它們可以在集群周圍分發讀取。

  • ==通信樞紐==:
    etcd也用作組件之間的通信總線。通過查詢etcd中數據的變化來使得各個組件做出相應的操作。
BGP Client(BIRD)

Calico在每個也承載Felix的節點上部署BGP客戶端。 BGP客戶端的作用是讀取Felix程序進入內核並將其分布在數據中心周圍的路由狀態。
在Calico中,這個BGP組件最常見的是BIRD,但是任何BGP客戶端(例如可以從內核中提取路由並分發它們的GoBGP)都適用於此角色。

  • ==路由分發==:
    當Felix將路由插入Linux內核FIB時,BGP客戶端將接收它們並將它們分發到部署中的其他節點。 這可確保在部署周圍有效地路由流量。
BGP Route Reflector (BIRD)

對於大型集群的部署,簡單的BGP可能由於瓶頸而成為限制因素,因為它要求每個BGP客戶端連接到網狀拓撲中的每個其他BGP客戶端。 這使得客戶端的連接將以N ^ 2量級增長,當節點越來越多將變得難以維護。因此,在大型集群的部署中,Calico將部署BGP Route Reflector。 通常在Internet中使用的此組件充當BGP客戶端連接的中心點,從而防止它們需要與群集中的每個BGP客戶端進行通信。為了實現冗余,可以無縫部署多個BGP Route Reflector。
BGP Route Reflector純粹參與網絡控制:沒有端點數據通過它們。在Calico中,此BGP組件也是最常見的BIRD,配置為BGP Route Reflector而不是標準BGP客戶端。

  • ==大規模集群的路由分發==:
    當Calico BGP客戶端將路由從其FIB通告到BGP Route Reflector時,BGP Route Reflector會將這些路由通告給Calico網絡中的其他節點。

Kubernetes 網絡原理