本文介紹了flannel網絡在Kubernetes中的工做方式web
Kubernetes是用於大規模管理容器化應用程序出色的編排工具。可是,您可能知道,使用kubernetes並不是易事,尤爲是後端網絡實現。我在網絡中遇到了許多問題,花了我不少時間弄清楚它是如何工做的。docker
在本文中,我想以最簡單的實現爲例,來解釋kubernetes的網絡工做。但願本文能夠爲像我這樣正在研究kubernetes的人們提供幫助。後端
Kubernetes網絡模型
下圖顯示了kubernetes集羣的簡單圖像:服務器
Kubernetes管理Linux機器集羣(多是ECS之類的雲VM或物理服務器),在每臺主機上,kubernetes運行任意數量的Pod,在每一個Pod中能夠有任意數量的容器。用戶的應用程序正在這些容器之一中運行。微信
對於kubernetes,Pod是最小的管理單元,而且一個Pod中的全部容器共享相同的網絡名稱空間,這意味着它們具備相同的網絡接口而且可使用*localhost
*相互鏈接網絡
在官方文件[1]說kubernetes網絡模式要求:架構
-
全部容器無需NAT便可與全部其餘容器通訊 -
全部節點均可以與全部容器通訊(反之亦然),而無需NAT -
容器所看到的IP其餘人所看到的IP同樣
咱們能夠按照上述要求將全部容器替換爲Pod,由於容器與Pod網絡共享。運維
基本上,這意味着全部Pod都應該可以與羣集中的其餘Pod自由通訊,即便它們位於不一樣的主機中,而且它們也使用本身的IP地址相互識別,就像基礎主機不存在同樣。此外,主機也應該可以使用本身的IP地址與任何Pod通訊,而無需任何地址轉換。編輯器
Kubernetes不提供任何默認的網絡實現,而是僅定義模型,並由其餘工具來實現。現在有不少實現,Flannel是其中之一,也是最簡單的之一。在如下各節中,我將解釋Flannel的UDP模式實現。工具
The Overlay Network
Flannel是由CoreOS建立的,用於Kubernetes網絡,也能夠用做其餘目的的通用軟件定義網絡解決方案。
爲了知足kubernetes的網絡要求,flannel的想法很簡單:建立另外一個在主機網絡之上運行的扁平網絡,這就是所謂的覆蓋網絡overlay network。在此覆蓋網絡中,全部容器(Pod)將被分配一個IP地址,它們經過直接調用彼此的IP地址來相互通訊。
爲了幫助解釋,我在AWS上使用了一個小型的測試kubernetes集羣,該集羣中有3個Kubernetes節點。網絡以下所示:
此羣集中有三個網絡:
AWS VPC網絡:全部實例都在一個VPC子網中172.20.32.0/19
。它們已經在此範圍內分配了ip地址,全部主機均可以彼此鏈接,由於它們位於同一LAN中。
Flannel overlay network:flannel建立了另外一個網絡100.96.0.0/16
,它是一個更大的網絡,能夠容納65536個地址,而且遍佈全部kubernetes節點,將在此範圍內爲每一個Pod分配一個地址,稍後咱們將看到flannel如何實現此目的。
主機內docker網絡:在每一個主機內部,flannel爲該主機中的全部pod分配了一個網絡100.96.x.0/24
,它能夠容納256地址。docker橋接接口docker0
將使用此網絡建立新容器。
經過這種設計,每一個容器都有其本身的IP地址,都屬於覆蓋子網100.96.0.0/16
。同一主機內的容器能夠經過docker bridge網絡接口Docker0
相互通訊,這很簡單,所以在本文中我將跳過。爲了在主機上與覆蓋網絡中的其餘容器進行跨主機通訊,flannel使用內核路由表和UDP封裝來實現該功能,如下各節對此進行了說明。
跨主機容器通訊
假設具備IP地址的節點1中的容器(咱們將其稱爲容器1)100.96.1.2
要使用IP地址鏈接到節點2中的容器(咱們將其稱爲容器2)100.96.2.3
,讓咱們看看覆蓋網絡如何啓用數據包經過。
第一個container-1使用建立一個IP數據包src: 100.96.1.2 -> dst: 100.96.2.3
,該數據包將做爲容器的網關進入docker0網橋。
在每一個主機中,flannel運行一個名爲的守護進程flanneld
,它在內核的路由表中建立一些路由規則,這是節點1的路由表的樣子:
admin@ip-172-20-33-102:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.1.0
100.96.1.0/24 dev docker0 proto kernel scope link src 100.96.1.1
172.20.32.0/19 dev eth0 proto kernel scope link src 172.20.33.102
如咱們所見,數據包的目標地址100.96.2.3
位於更大的覆蓋網絡中100.96.0.0/16
,所以它與第二條規則匹配,如今內核知道應該將數據包發送到flannel0
。
flannel0
TUN是由咱們的flanneld
守護進程建立的TUN設備,TUN是在Linux內核中實現的軟件接口,它能夠在用戶程序和內核之間傳遞原始ip數據包。它在兩個方向上起做用:
-
將IP數據包寫入 flannel0
設備時,該數據包將直接發送到內核,內核將根據其路由表對數據包進行路由 -
當IP數據包到達內核,而且路由表說應該將其路由到 flannel0
設備時,內核會將數據包直接發送到建立該設備的flanneld
進程,該進程是守護進程。
當內核將數據包發送到TUN設備時,它將直接進入flanneld
進程,它看到目標地址爲100.96.2.3
,儘管從圖中能夠看出該地址屬於在Node 2上運行的容器,可是如何flanneld
知道呢?
Flannel碰巧將某些信息存儲在名爲etcd的鍵值存儲服務中,若是您知道kubernetes,則不該感到驚訝。在flannel,咱們能夠將其視爲常規鍵值存儲。
Flannel將子網映射信息存儲到etcd服務中,咱們可使用如下etcdctl
命令查看它:
admin@ip-172-20-33-102:~$ etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/100.96.1.0-24
/coreos.com/network/subnets/100.96.2.0-24
/coreos.com/network/subnets/100.96.3.0-24
admin@ip-172-20-33-102:~$ etcdctl get /coreos.com/network/subnets/100.96.2.0-24
{"PublicIP":"172.20.54.98"}
所以,每一個flanneld
進程查詢etcd都知道每一個子網屬於哪一個主機,並將目標ip地址與etcd中存儲的全部子網密鑰進行比較。在本例中,該地址100.96.2.3
將與子網匹配100.96.2.0-24
,而且如咱們所見,存儲在此鍵中的值表示Node ip爲172.20.54.98
。
如今flanneld
知道了目的地址,而後將原始IP數據包包裝到UDP數據包中,以其本身的主機ip做爲源地址,而目標主機的IP做爲目的地址。在每一個主機中,該flanneld
進程將偵聽默認的UDP端口:8285
。所以,只須要將UDP數據包的目標端口設置爲8285
,而後經過網絡發送它。
UDP數據包到達目標主機後,內核的IP堆棧會將數據包發送到flanneld
進程,由於那是用戶進程在UDP端口上偵聽:8285
。而後flanneld
將得到UDP數據包的有效負載,該數據包是由原始容器生成的原始IP數據包,只需將其寫入TUN設備flannel0
,而後該數據包將直接傳遞到內核,這就是TUN的工做方式。
與節點1相同,路由表將決定此數據包的去向,讓咱們看一下節點2的路由表:
admin@ip-172-20-54-98:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.2.0
100.96.2.0/24 dev docker0 proto kernel scope link src 100.96.2.1
172.20.32.0/19 dev eth0 proto kernel scope link src 172.20.54.98
IP數據包的目標地址是100.96.2.3
,內核將採用最精確的匹配,這是第三條規則。數據包將發送到docker0
設備。就像docker0
橋接設備同樣,此主機中的全部容器都鏈接到該橋接器,最終目的地容器2將看到並接收到該數據包。
最終,咱們的數據包完成了一種傳遞到目標的方式,當contianer-2將數據包發送回容器1時,反向路由將以徹底相同的方式工做。這就是跨主機容器通訊的工做方式。
使用Docker網絡進行配置
在以上解釋中,咱們遺漏了一點。這就是咱們如何配置docker使用較小的子網100.96.x.0/24
?
碰巧flanneld
會將其子網信息寫入主機中的文件中:
admin@ip-172-20-33-102:~$ cat /run/flannel/subnet.env
FLANNEL_NETWORK=100.96.0.0/16
FLANNEL_SUBNET=100.96.1.1/24
FLANNEL_MTU=8973
FLANNEL_IPMASQ=true
該信息將用於配置docker守護程序的選項,所以docker能夠將FLANNEL_SUBNET
用做其橋接網絡,而後主機容器網絡將起做用:
dockerd --bip = $ FLANNEL_SUBNET --mtu = $ FLANNEL_MTU
數據包複製和性能
較新版本的flannel不建議將UDP封裝用於生產,它表示僅應將其用於調試和測試目的。緣由之一是性能。
儘管flannel0
TUN設備提供了一種經過內核獲取和發送數據包的簡單方法,但它會下降性能:必須將數據包從用戶空間來回複製到內核空間:
如上所述,必須從原始容器進程發送數據包,而後在用戶空間和內核空間之間複製3次,這將顯着增長網絡開銷,所以,若是能夠的話,應避免在生產中使用UDP。
結論
Flannel是kubernetes網絡模型的最簡單實現之一。它使用現有的Docker網絡和帶有守護進程的額外Tun設備進行UDP封裝。我解釋了核心部分的詳細信息:跨主機容器通訊,並簡要提到了性能損失。我但願本文能幫助人們理解kuberentes網絡的基礎知識,並以此爲基礎,您能夠開始探索更有趣的kubernetes網絡世界,並弄清楚?Calico,?WeaveNet,?Romana等許多高級實現。
https://blog.laputa.io/kubernetes-flannel-networking-6a1cb1f8ec7c
參考資料
kubernetes networking: https://kubernetes.io/docs/concepts/cluster-administration/networking/
本文分享自微信公衆號 - 雲原生生態圈(CloudNativeEcoSystem)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。