一文看懂Flannel-UDP在kubernetes中如何工做

本文介紹了flannel網絡在Kubernetes中的工做方式web

Kubernetes是用於大規模管理容器化應用程序出色的編排工具。可是,您可能知道,使用kubernetes並不是易事,尤爲是後端網絡實現。我在網絡中遇到了許多問題,花了我不少時間弄清楚它是如何工做的。docker

在本文中,我想以最簡單的實現爲例,來解釋kubernetes的網絡工做。但願本文能夠爲像我這樣正在研究kubernetes的人們提供幫助。後端

Kubernetes網絡模型

下圖顯示了kubernetes集羣的簡單圖像:服務器

kubernetes中的pod

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節點。網絡以下所示:

flannel network

此羣集中有三個網絡:

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

flannel0TUN是由咱們的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封裝用於生產,它表示僅應將其用於調試和測試目的。緣由之一是性能。

儘管flannel0TUN設備提供了一種經過內核獲取和發送數據包的簡單方法,但它會下降性能:必須將數據包從用戶空間來回複製到內核空間:

封包複製

如上所述,必須從原始容器進程發送數據包,而後在用戶空間和內核空間之間複製3次,這將顯着增長網絡開銷,所以,若是能夠的話,應避免在生產中使用UDP。

結論

Flannel是kubernetes網絡模型的最簡單實現之一。它使用現有的Docker網絡和帶有守護進程的額外Tun設備進行UDP封裝。我解釋了核心部分的詳細信息:跨主機容器通訊,並簡要提到了性能損失。我但願本文能幫助人們理解kuberentes網絡的基礎知識,並以此爲基礎,您能夠開始探索更有趣的kubernetes網絡世界,並弄清楚?Calico,?WeaveNet,?Romana等許多高級實現。

https://blog.laputa.io/kubernetes-flannel-networking-6a1cb1f8ec7c

參考資料

[1]

kubernetes networking: https://kubernetes.io/docs/concepts/cluster-administration/networking/


本文分享自微信公衆號 - 雲原生生態圈(CloudNativeEcoSystem)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索