標籤:names coreos 中斷連線 交換 刪除 抽象 位元 eth 啟用
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 a1: 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 show1: 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:002: 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:ff3: [email protected]: <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:ff4: [email protected]: <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 show1: 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:002: 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:ff4: [email protected]: <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 a1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:003: [email protected]: <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 veth4: [email protected]: <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 veth3: [email protected]: <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.2PING 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 ms64 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: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000# 本地的veth0 編號為4# ip link |grep veth4: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000#在對端驗證# ip netns exec test ethtool -S veth1NIC 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 a1: 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 forever2: 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 forever3: 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 forever7: [email protected]: <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 forever19: [email protected]: <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 showbridge name bridge id STP enabled interfacesdocker0 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 list506a694d09fb (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 forever6: [email protected]: <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 forever18: [email protected]: <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的名稱([email protected])也可以發現所指的介面資訊。
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的無縫整合。主要有以下功能:
etcd
Calico使用etcd提供組件之間的通訊,並作為一致的資料存放區,確保Calico始終可以構建準確的網路。
根據orchestrator外掛程式,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 網路原理