Kubernetes 網絡原理

Docker網絡基礎

因爲Kubernetes是基於Docker容器做爲應用發佈的載體,而Docker自己的網絡特性也決定了Kubernetes在構建一個容器互通網絡必需要解決Docker自身網絡的缺陷。node

網絡命名空間

爲了支持網絡協議棧的多個實例,Linux在網絡命名空間中引入了網絡命名空間(Network Namespace),這些網絡協議棧被隔離到不一樣的命名空間中。不一樣的命名空間中資源徹底隔離,彼此之間沒法徹底通訊。經過不一樣的網絡命名空間,就能夠在一臺宿主機上虛擬多個不一樣的網絡環境。Docker正是利用了網絡命令空間的特性實現了不一樣容器之間的網絡隔離。docker

在Linux的網絡命名空間中能夠配置本身獨立的iptables規則來設置包轉發,NAT和包過濾等。數據庫

因爲網絡命名空間彼此隔離,沒法直接通訊,若是要打通兩個隔離的網絡命名空間,實現數據互通,就須要用到Veth設備對。Veth設備對的一個主要做用就是打通不一樣的網絡協議棧,它就像是一個網線,兩端分別鏈接不一樣的網絡命名空間的協議棧。
若是想在兩個命名空間之間進行通訊,就必須有一個Veth設備對。編程

網絡命名空間的操做

一、建立一個名爲test的網絡命名空間:安全

# ip netns add test

二、在此命名空間中執行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設備對,因爲一個設備只能屬於一個網絡命名空間,因此當設備被轉移後,在當前的命名空間中就沒法查看到此設備了。ssh

Veth設備對

Kubernetes 網絡原理

因爲Veth須要鏈接兩個不一樣的網絡命名空間,因此Veth設備通常是成對出現的,稱其中一端爲另外一端的peer。分佈式

一、建立Veth設備對ide

# ip link add veth0 type veth peer name veth1

建立一個veth設備對,本端爲veth0, 對端爲veth1

二、查看設備對:

# 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

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

# ip link set veth1 netns test

四、查看當前設備對狀況:

# 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

五、查看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

六、因爲兩端的設備對尚未地址,因此沒法通訊,如今分別分配地址:

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

七、能夠查看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

八、啓動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

九、查看對端設備
當設備對比較多的狀況下,沒法確認對端的設備是屬於哪一個設備對的,可使用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。

一、若是要配置橋接網卡,須要安裝bridge-utils工具:

# yum install bridge-utils -y

二、添加一個網橋設備br0

# brctl addbr br0

三、將eth0添加到br0上(此步執行後,eth0上的IP會失效,雖然IP還在eth0上,可是沒法接收數據,若是使用ssh將會斷開鏈接):

# brctl addif br0 eth0

四、 刪除eth0上的ip:

ip addr del dev eth0 10.0.0.1/24

五、給br0添加此IP

ifconfig br0  10.0.0.1/24 up

六、給br0添加默認路由:

route add default gw 10.0.0.254

七、咱們能夠經過以下命令查卡當前的路由信息:

ip route list
 netstat -rn
 route -n

Docker 網絡實現

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

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

因爲Kubernetes中只使用bridge模式,因此這裏只討論bridge模式。

Docker 網絡模型

網絡示例圖:

Kubernetes 網絡原理

經過上圖,能夠清楚的表示容器的網絡結構,其中容器中的網卡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中的容器以下圖:

Kubernetes 網絡原理

Pod與Pod之間的通訊

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

Kubernetes 網絡原理

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

另外一種狀況是在不一樣宿主機上的不一樣Pod之間通訊,其原理圖以下:

Kubernetes 網絡原理

網絡插件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原理圖:

Kubernetes 網絡原理

咱們之因此要單獨使用第三方的網絡插件來擴展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網絡數據包結構對比(簡圖):

Kubernetes 網絡原理

特性:

  • 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架構圖:

Kubernetes 網絡原理

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網絡中的其餘節點。
相關文章
相關標籤/搜索