因爲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須要鏈接兩個不一樣的網絡命名空間,因此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支持4類網絡模式:
因爲Kubernetes中只使用bridge模式,因此這裏只討論bridge模式。
網絡示例圖:
經過上圖,能夠清楚的表示容器的網絡結構,其中容器中的網卡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主要解決如下幾個問題:
同一個Pod中的容器屬於同一個網絡命名空間,共享同一個Linux網絡協議棧,經過本地的localhost網絡來與Pod內的其餘容器通訊,pod中的容器以下圖:
同宿主機上的通訊示意圖:
在宿主機內部經過docker0的橋接網卡,能夠實現Pod之間的直接通訊,這裏和純docker環境下的多個容器互通原理類似。
另外一種狀況是在不一樣宿主機上的不一樣Pod之間通訊,其原理圖以下:
CNI是由CoreOS公司提出的一種容器網絡規範,定義容器運行環境與網絡插件之間的簡單接口規範。
CNI模型涉及兩個概念:
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原理圖:
咱們之因此要單獨使用第三方的網絡插件來擴展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做爲底層傳輸協議。
Kubernetes中若是要實現Network Policy,僅僅使用Flannel網絡是沒法實現的,其自己只是解決了Pod互聯的問題,若是須要Network Policy功能,須要使用如Calico、Romana、Weave Net和trireme等支持Network Policy的網絡插件。這裏將介紹經常使用的Calico的原理。
Calico是一個基於BGP的純三層的網絡解決方案。Calico在每一個節點利用Linux Kernel實現了一個高效的vRouter來負責數據的轉發。 每一個vRouter經過BGP1協議把在本節點上運行的容器的路由信息向整個Calico網絡廣播,並自動設置到其它節點的路由轉發規則。Calico保證全部全部容器之間的數據流量都是經過IP路由的方式完成互聯。Calico節點組網能夠直接利用數據中心的網絡結構(L2和L3),不須要額外的NAT、隧道或者Overlay Network,因此就不會有額外的封包和解包過程,可以節省CPU的運算,提高網絡效率,相比而言Calico網絡比Flannel性能更高。
Overlay網絡和Calico網絡數據包結構對比(簡圖):
特性:
使用Calico的簡單策略語言,您能夠實現對容器,虛擬機工做負載和裸機主機端點之間通訊的細粒度控制。
Calico v3.0與Kubernetes和OpenShif集成的環境已通過大規模的生產驗證。
Calico架構圖:
Calico組件:
Felix是一個守護進程,它在每一個提供endpoint的計算機上運行:在大多數狀況下,這意味着在託管容器或VM的宿主節點上運行。 它負責設置路由和ACL以及主機上所需的任何其餘任務,以便爲該主機上的端點提供所需的鏈接。
Felix通常負責如下任務:
==接口管理==:
Felix將有關接口的一些信息編程到內核中,以使內核可以正確處理該端點發出的流量。 特別是,它將確保主機使用主機的MAC響應來自每一個工做負載的ARP請求,並將爲其管理的接口啓用IP轉發。它還監視出現和消失的接口,以便確保在適當的時間應用這些接口的編程。
==路由規劃==:
Felix負責將到其主機端點的路由編程到Linux內核FIB(轉發信息庫)中。 這確保了發往那些到達主機的端點的數據包被相應地轉發。
==ACL規劃==:
Felix還負責將ACL編程到Linux內核中。 這些ACL用於確保只能在端點之間發送有效流量,並確保端點沒法繞過Calico的安全措施。
與Felix 沒有單獨的Orchestrator插件相反,每一個主要的雲編排平臺(如Kubernetes)都有單獨的插件。這些插件時將Calico更緊密的綁定到協調器中,容許用戶管理Calico網絡,就像他們管理協調器中的網絡工具同樣。Kubernetes中能夠直接使用CNI插件來代替此功能。
一個好的Orchestrator插件示例是Calico Neutron ML2機制驅動程序。 該組件與Neutron的ML2插件集成,容許用戶經過Neutron API調用來配置Calico網絡。 這提供了與Neutron的無縫集成。主要有如下功能:
==API轉換==:
協調器將不可避免地擁有本身的一套用於管理網絡的API。 Orchestrator插件的主要工做是將這些API轉換爲Calico的數據模型,而後將其存儲在Calico的數據存儲區中。
這種轉換中的一些將很是簡單,其餘比特可能更復雜,以便將單個複雜操做(例如,實時遷移)呈現爲Calico網絡的其他部分指望的一系列更簡單的操做。
Calico使用etcd提供組件之間的通訊,並做爲一致的數據存儲,確保Calico始終能夠構建準確的網絡。
根據orchestrator插件,etcd能夠是主數據存儲,也能夠是單獨數據存儲的輕量級副本鏡像.主要功能:
==數據存儲==:
etcd以分佈式,容錯的方式存儲Calico網絡的數據(這裏指使用至少三個etcd節點的etcd集羣)。 這確保Calico網絡始終處於已知良好狀態。
Calico數據的這種分佈式存儲還提升了Calico組件從數據庫讀取的能力,使它們能夠在集羣周圍分發讀取。
Calico在每一個也承載Felix的節點上部署BGP客戶端。 BGP客戶端的做用是讀取Felix程序進入內核並將其分佈在數據中心周圍的路由狀態。
在Calico中,這個BGP組件最多見的是BIRD,可是任何BGP客戶端(例如能夠從內核中提取路由並分發它們的GoBGP)都適用於此角色。
對於大型集羣的部署,簡單的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客戶端。