Kubernetes-網絡

前言

本篇是Kubernetes第十一篇,大家一定要把環境搭建起來,看是解決不了問題的,必須實戰,此篇文章概念比較多,後續我會繼續出一些網絡相關實戰以及原理探索篇。

Kubernetes系列文章:
  1. Kubernetes介紹
  2. Kubernetes環境搭建
  3. Kubernetes-kubectl介紹
  4. Kubernetes-Pod介紹(-)
  5. Kubernetes-Pod介紹(二)-生命周期
  6. Kubernetes-Pod介紹(三)-Pod調度
  7. Kubernetes-Pod介紹(四)-Deployment
  8. Kubernetes-Service介紹(一)-基本概念
  9. Kubernetes-Service介紹(二)-服務發現
  10. Kubernetes-Service介紹(三)-Ingress(含最新版安裝踩坑實踐)

Kubernetes網絡設計

在實際生產中,我們面臨多種多樣的業務場景,導致業務之間的調用關係更加複雜化,從而對網絡提出更高的要求,對於Kubernetes來說主要解決以下四個問題:

  1. Pod中容器與容器之間的通信問題;

  2. Pod與Pod之間的網絡通信問題;

  3. Pod與Service之間的通信問題;

  4. 集群內部與外部的通信問題;

第三和第四點我們在前面的章節已經講過,大家可以在Service章節具體回憶下,重點介紹下第一、二點;

容器與容器之間的通信問題

介紹Pod時候我們說過Pod內部的容器共享Pod的命名空間的,因此在同一個Pod內的容器之間對於網絡的各類操作,就和它們在同一台機器上一樣,可以用localhost地址訪問彼此的端口。

img
img

Pod與Pod之間的網絡通信問題

每個Pod都有自己單獨的IP,兩個Pod既有可能運行在同一個Node上,也有可能運行在不同的Node上,因此對於Pod之間的通信可以分為兩類:

同一個Node內的Pod之間的通信

對於在同一台機器上的兩個Pod來說,都是連接到同一個docker0網橋上,它們的IP地址IP1、IP2都是從docker0網段上動態獲取的,它們和網橋本身的IP3是同一個網段的,Pod1和Pod2處於同一局域網內,它們之間可以通過docker0作為路由進行通信。

img
img
不同Node上的Pod之間的通信

在Kubernetes的網絡世界中,Pod之間假設是通過訪問對方的Pod IP進行通信的,而不同Node之間的通信只能通過Node的物理網卡進行,Pod的IP地址是由各Node上的docker0網橋動態分配的。我們想要實現跨Node的Pod之間的通信,至少需要滿足下面兩個條件:

  1. Kubernetes集群中對Pod的IP分配不能有衝突;
  2. Pod IP 和Node IP之間的映射關係,通過Node IP轉發到Pod IP;

根據條件1的要求,Kubernetes會再部署的時候對docker0的IP地址進行規劃,保證每個Node上的docker0地址都沒有衝突,此外Kubernetes會記錄所有正在運行的Pod的IP分配信息,並將這些信息保存到etcd中,這樣我們就可以知道Pod IP和Node IP之間的映射關係。

根據條件2要求,Pod中的數據在發送出去的時候,需要一個機制能夠知道對方Pod的IP地址掛載到那個具體的Node上,這樣就可以將數據發送到對應的宿主機上,宿主機將響應的數據轉發到具體的docker0上,當數據到達docker0以後,就會將對應的數據轉發到對應的容器上了。

img
img

CNI

Kubernetes本身並不負責網絡通信,Kubernetes提供了容器網絡接口CNI,具體的網絡通信交給CNI插件來負責,開源的CNI插件非常多,像Flannel、Calico等,各大雲廠商也都自己定製的CNI。

img
img

為什麼會有CNI

CNI是Container Network Interface的縮寫,簡答說就是容器解決網絡問題,提供的一個標準的,通用的接口。現在有各種各樣的容器平台:docker,kubernetes,mesos,我們也有各種各樣的容器網絡解決方案:flannel,calico,weave,並且還有各種新的解決方案在不斷湧現。如果每出現一個新的解決方案,我們都需要對兩者進行適配,那麼由此帶來的工作量必然是巨大的,而且也是重複和不必要的。事實上,我們只要提供一個標準的接口,更準確的說是一種協議,就能完美地解決上述問題。一旦有新的網絡方案出現,只要它能滿足這個標準的協議,那麼它就能為同樣滿足該協議的所有容器平台提供網絡功能,而CNI正是這個標準接口協議。

什麼是CNI

CNI是一個接口協議,用於連接容器管理系統和網絡插件。前者提供一個容器所在的network namespace(從網絡的角度來看,network namespace和容器是完全等價的),後者負責將network interface插入該network namespace中(比如veth的一端),並且在宿主機做一些必要的配置(例如將veth的另一端加入bridge中),最後對namespace中的interface進行IP和路由的配置。那麼CNI的工作其實主要是從容器管理系統處獲取運行時信息,包括network namespace的路徑,容器ID以及network interface name,再從容器網絡的配置文件中加載網絡配置信息,再將這些信息傳遞給對應的插件,由插件進行具體的網絡配置工作,並將配置的結果再返回到容器管理系統中。

img
img

CNI僅關注在創建容器時分配網絡資源,和在銷毀容器時刪除網絡資源,這使得CNI規範非常輕巧、易於實現,得到了廣泛的支持。在CNI模型中只涉及兩個概念:容器和網絡。

容器(Container):是擁有獨立Linux網絡命名空間的環境,例如使用Docker或rkt創建的容器。容器需要擁有自己的Linux網絡命名空間,這是加入網絡的必要條件。

網絡(Network):表示可以互連的一組實體,這些實體擁有各自獨立、唯一的IP地址,可以是容器、物理機或者其他網絡設備(比如路由器)等。

對容器網絡的設置和操作都通過插件(Plugin)進行具體實現,CNI插件包括兩種類型:CNI Plugin和IPAM(IP Address Management)Plugin。CNI Plugin負責為容器配置網絡資源,IPAM Plugin負責對容器的IP地址進行分配和管理。IPAM Plugin作為CNI Plugin的一部分,與CNIPlugin協同工作。

插件介紹

CNI Plugin插件

CNI Plugin包括3個基本接口的定義:添加(ADD)、刪除(DELETE)、檢查(CHECK)和版本查詢(VERSION)。這些接口的具體實現要求插件提供一個可執行的程序,在容器網絡添加或刪除時進行調用,以完成具體的操作。

添加:將容器添加到某個網絡。主要過程為在Container Runtime創建容器時,先創建好容器內的網絡命名空間(Network Namespace),然後調用CNI插件為該netns進行網絡配置,最後啟動容器內的進程。添加接口的參數如下:

  • Version:CNI版本號;

  • ContainerID:容器ID;

  • Network Namespace path:容器的網絡命名空間路徑,例如/proc/[pid]/ns/net;

  • Network configuration:網絡配置JSON文檔,用於描述容器待加入的網絡;

  • Extra arguments:其他參數,提供基於容器的CNI插件簡單配置機制;

  • Name of the interface inside the container:容器內的網卡名,返回的信息如下:

  • Interfaces list:網卡列表,根據Plugin的實現,可能包括Sandbox Interface名稱、主機Interface名稱、每個Interface的地址等信息;

  • IPs assigned to the interface:IPv4或者IPv6地址、網關地址、路由信息等;

  • DNS information:DNS相關的信息;

刪除:容器銷毀時將容器從某個網絡中刪除。刪除接口的參數如下:

  • Version:CNI版本號;

  • ContainerID:容器ID;

  • Network Namespace path:容器的網絡命名空間路徑,例如/proc/[pid]/ns/net;

  • Network configuration:網絡配置JSON文檔,用於描述容器待加入的網絡;

  • Extra arguments:其他參數,提供基於容器的CNI插件簡單配置機制;

  • Name of the interface inside the container:容器內的網卡名;

檢查:檢查容器網絡是否正確設置。檢查接口的參數如下:

  • Version:CNI版本號;

  • ContainerID:容器ID;

  • Network Namespace path:容器的網絡命名空間路徑,例如/proc/[pid]/ns/net;

  • Network configuration:網絡配置JSON文檔,用於描述容器待加入的網絡;

  • Extra arguments:其他參數,提供基於容器的CNI插件簡單配置機制;

  • Name of the interface inside the container:容器內的網卡名;

版本查詢:查詢網絡插件支持的CNI規範版本號。無參數,返回值為網絡插件支持的CNI規範版本號。

IPAM Plugin插件

為了減輕CNI Plugin對IP地址管理的負擔,在CNI規範中設置了一個新的插件專門用於管理容器的IP地址(還包括網關、路由等信息),被稱為IPAM Plugin。通常由CNI Plugin在運行時自動調用IPAM Plugin完成容器IP地址的分配。

IPAM Plugin負責為容器分配IP地址、網關、路由和DNS,典型的實現包括host-local和dhcp。與CNI Plugin類似,IPAM插件也通過可執行程序完成IP地址分配的具體操作。IPAM可執行程序也處理傳遞給CNI插件的環境變量和通過標準輸入(stdin)傳入的網絡配置參數。

如果成功完成了容器IP地址的分配,則IPAM插件應該通過標準輸出(stdout)返回JSON報文。包括ips、routes和dns三段內容:

  • ips段:分配給容器的IP地址(也可能包括網關);

  • routes段:路由規則記錄;

  • dns段:DNS相關的信息;

Kubernetes網絡插件

Kubernetes管理的是集群,Kubernetes中的網絡要解決的核心問題就是每台主機的IP地址網段劃分,以及單個容器的IP地址分配。需要做到以下幾點:

  1. 保證每個Pod擁有一個集群內唯一的IP地址;

  2. 保證不同節點的IP地址劃分不會重複;

  3. 保證跨節點的Pod可以互相通信;

  4. 保證不同節點的Pod可以與跨節點的主機互相通信;

Kubernetes目前支持兩種網絡插件的實現。

CNI插件:根據CNI規範實現其接口,以與插件提供者進行對接。

kubenet插件:使用bridge和host-local CNI插件實現一個基本的cbr0。

為了在Kubernetes集群中使用網絡插件,需要在kubelet服務的啟動參數上設置下面兩個參數:

  • –network-plugin-dir:kubelet啟動時掃描網絡插件的目錄。
  • –network-plugin:網絡插件名稱,對於CNI插件,設置為cni即可,無須關注–network-plugin-dir的路徑。對於kubenet插件,設置為kubenet,目前僅實現了一個簡單的cbr0 Linux網橋。

在設置–network-plugin=”cni”時,kubelet還需設置下面兩個參數。

  • –cni-conf-dir:CNI插件的配置文件目錄,默認為/etc/cni/net.d。該目錄下配置文件的內容需要符合CNI規範。
  • –cni-bin-dir:CNI插件的可執行文件目錄,默認為/opt/cni/bin。

開源容器網絡方案

Kubernetes的網絡模型假定了所有Pod都在一個可以直接連通的扁平網絡空間中。若需要實現這個網絡假設,需要實現不同節點上的Docker容器之間的互相訪問,然後運行Kubernetes。目前已經有多個開源組件支持容器網絡模型。如Flannel、Open vSwitch、直接路由和Calico。

Flannel

Flannel是一種基於overlay網絡的跨主機容器網絡解決方案,即將TCP數據包封裝在另一種網絡包裏面進行路由轉發和通信,Flannel是CoreOS開發,Flannel之所以可以搭建Kubernetes依賴的底層網絡,是因為它能實現以下兩點:

  1. 它能協助Kubernetes,給每一個Node上的Docker容器都分配互相不衝突的IP地址;
  2. 它能在這些IP地址之間建立一個Overlay Network,通過這個網絡將數據包原封不動地傳遞到目標容器內
image.png
image.png

Flannel首先創建了一個名為flannel0的網橋,而且這個網橋的一端連接docker0網橋,另一端連接一個叫作flanneld的服務進程。flanneld進程上連etcd,利用etcd來管理可分配的IP地址段資源,同時監控etcd中每個Pod的實際地址,並在內存中建立了一個Pod節點路由表;flanneld進程下連docker0和物理網絡,使用內存中的Pod節點路由表,將docker0發給它的數據包包裝起來,利用物理網絡的連接將數據包投遞到目標flanneld上,從而完成Pod到Pod之間的直接地址通信。

Flannel之間的底層通信協議的可選技術包括UDP、VxLan、AWS VPC等多種方式。通過源flanneld封包、目標flanneld解包,最終docker0收到的就是原始的數據,對容器應用來說是透明的,感覺不到中間Flannel的存在。

Flannel每次分配的地址段都在同一個公共區域獲取,存儲在集中的etcd上,從而實現不同Node上的Pod分配的IP不產生衝突。Flannel通過修改Docker的啟動參數將分配給它的地址段傳遞進去。

通過如上方式,Flannel就控制了每個Node上的docker0地址段的地址,從而保障了所有Pod的IP地址在同一個水平網絡中且不產生衝突。Flannel完美地實現了對Kubernetes網絡的支持,但是它引入了多個網絡組件,在網絡通信時需要轉到flannel0網絡接口,再轉到用戶態的flanneld程序,到對端後還需要走這個過程的反過程,所以也會引入一些網絡的時延損耗。另外,Flannel模型默認採用了UDP作為底層傳輸協議,UDP本身是非可靠協議,雖然兩端的TCP實現了可靠傳輸,但在大流量、高並發的應用場景下還建議多次測試。

Calico

Calco組件簡介

Calico是一個基於BGP的純三層的網絡方案,與OpenStack、Kubernetes、AWS、GCE等雲平台都能夠良好地集成。Calico在每個計算節點都利用Linux Kernel實現了一個高效的vRouter來負責數據轉發。每個vRouter都通過BGP1協議把在本節點上運行的容器的路由信息向整個Calico網絡廣播,並自動設置到達其他節點的路由轉發規則。

Calico保證所有容器之間的數據流量都是通過IP路由的方式完成互聯互通的。Calico節點組網時可以直接利用數據中心的網絡結構(L2或者L3),不需要額外的NAT、隧道或者Overlay Network,沒有額外的封包解包,能夠節約CPU運算,提高網絡效率。

img
img

Calico在小規模集群中可以直接互聯,在大規模集群中可以通過額外的BGP route reflector來完成。Calico基於iptables還提供了豐富的網絡策略,實現了Kubernetes的Network Policy策略,提供容器間網絡可達性限制的功能。

Calico架構
image.png
image.png

Calico的主要組件:

Felix:Calico Agent,運行在每個Node上,負責為容器設置網絡資源(IP地址、路由規則、iptables規則等),保證跨主機容器網絡互通;

etcd:Calico使用的後端存儲;

BGP Client:負責把Felix在各Node上設置的路由信息通過BGP協議廣播到Calico網絡;

Route Reflector:通過一個或者多個BGP Route Reflector來完成大規模集群的分級路由分發;

CalicoCtl:Calico命令行管理工具;

本文主要參考《Kubernetes權威指南實戰》

結束

歡迎大家點點關注,點點贊!

Tags: