咱們常說的CNI,能夠包括IPAM(IP地址管理),也能夠不包括IPAM。不過,經過狀況下,CNI插件的實現和 IPAM插件的實現是分開爲不一樣的可執⾏⽂件的。可是若是你寫到⼀起,這樣的CNI也能夠⽤。按照K8S本⾝規 範,咱們在使⽤CNI的時候,是要區分CNI和IPAM的。node
K8S 啓動 POD ⽹絡的配置流程是,kubelet->docker(pause)->cni->ipam。所以,K8S⽹絡的中⼼,即是 CNI 插 件和 IPAM 插件。由於 CNI 和 IPAM 插件不少,因此不打算都講⼀講。總體來講,CNI + IPAM 的實現能夠分爲2 類:linux
①:第⼀類:與宿主機平⾏⽹絡(2層⽹絡或3層⽹絡) ②:第⼆類:利⽤ SDN 技術的虛擬⽹絡git
⽽第⼀類的 CNI ⽹絡插件,主要是:bridge、macvlan、ipvlan、calico bgp、flannel host-gw 等。第⼆類的⽹絡 插件主要有:flannel vxlan、calico ipip、weave 等。github
⼤部分使⽤K8S的公司,都是使⽤的K8S經典⽹絡模型,⽐如:K8S+Flannel(VXLAN)、K8S+Calico(IPIP) 等。這種模式下的⽹絡有⼀個共同特色,就是容器⽹絡在K8S集羣內是隔離的,集羣外的宿主機是⽆法直接訪問容 器IP的。之因此會這樣,是由於容器⽹絡是虛擬的,它這個K8S的虛擬⽹絡,⼤部分上都是藉助 路由表 + iptables + 隧道 模式來作的。docker
這種經典的⽹絡模式,有優點,也有劣勢。bash
優點1:⽹絡隔離。網絡
這種模式下,K8S集羣內的節點的容器,是集羣外宿主機節點⽆法訪問的,因此,它能很好的起到隔離性的做⽤。 在有些公司這種模式⽐較好,⽐如:爲多客戶服務的公有云⼚商。另外,不一樣K8S的集羣⽹絡隔離,各個集羣徹底 可使⽤同⼀個虛擬IP段,沒有太多IP地址分配和管理的痛苦。架構
優點2:部署⽅便。運維
雖然這種⽅式不少公司會使⽤flannel vxlan模式的⽹絡插件,或者calico ipip模式的⽹絡插件,這2個插件的部署存 在⼀定複雜度,可是這種經典⽹絡模式,有⾮常多的⼀鍵部署⼯具。⽐如:github.com/kubernetes-…微服務
劣勢1:⽹絡隔離。
⽹絡隔離,既是優點,也是劣勢。關鍵仍是看公司的使⽤場景是什麼。若是是公有云,就有⼀定優點,但若是是私 有云,⽐如⼀個公司作整個容器平臺。後期遇到的痛點之⼀,就是⾮K8S集羣的機器,如何訪問K8S集羣內容器的 IP地址。
這⼀點可能不少⼈說不要直接訪問容器IP就⾏了,但問題在於,業務的發展每每超過預期,搞K8S的部分,每每都 是基礎架構部,或者系統部,但問題在於,這種部分,是⽀持部門,其職責之⼀,就是更好的⽀撐業務發展的多變 性。舉個例⼦:如何讓 Java 的服務,既能夠部署到K8S集羣內,還可使⽤ Spring Cloud 或者 Dubbo+Zookeepr 這種服務發現架構?你可能會說,Java 的微服務要麼⽤它們⾃⼰的體系,要麼⽤ K8S 體系就⾏了,這就是⽀持部 分,⽀持的程度不夠了。
換句話說,⽹絡隔離,對公司多技術語⾔和其語⾔⽣態的的發展,存在⼀定阻礙。
劣勢2:流量穿透的狀況。
K8S 內雖然是⽹絡隔離的。但其隔離的並不完全。它的隔離性,原來的是 kube-proxy 組件⽣成的⼤量 iptables 規 則和路由表來實現的。這種模式下,iptables 的動態變化,其實依賴 kube-proxy 這個組件,⽽這個組件,其實監 聽的 K8S 集羣的 service + endpoint 資源。咱們部署在集羣內的服務,訪問某個內部服務,⽤的是基於 DNS 的服 務發現機制。這就帶來了下⾯⼀個問題:
若是服務A訪問服務B,必先通過 DNS 解析拿到 service ip(⽐如 172.18.42.56) 這個虛擬 IP 地址。然⽽,若是 A 是拿着這個IP做爲長鏈接服務,頻繁對B發包。這個時候,B服務下掉了。下掉後,kube-proxy 組件,將從 iptables 中刪除 B 服務的iptables 規則,也就是說,172.18.42.56 這個虛擬 IP 從 iptables 中刪除了。此時,A 若是 還拿着這個 IP 發包,那麼由於 集羣內已經缺失了這個虛擬 IP 的記錄,必然致使這部分流量,將穿越 iptables 規 則,發到上層的⽹關。若是這個流量⾮常⼤的話,將對⽹關路由器形成⾮常⼤的衝擊。
這個是切實存在的⼀個問題,也是咱們以前遇到的⼀個問題。這個問題,你只能去⼿動修改iptables規則來處理。 由於 kube-proxy 也在修改 iptables 規則,因此,這個操做存在⼀定風險性,須要謹慎操做。
劣勢3:最重要的,⽹絡的性能損耗嚴重。
這個問題,要從2個⽅⾯看:
①:基本上作 K8S 和 Docker 的同窗都知道。flannel vxlan 和 calico ipip 模式 這種虛擬⽹絡,在容器跨宿主機通 信時,存在包的封包和解包操做,性能損耗⼤。 ②:K8S 內 有⼤量服務和容器的時候,iptables 會⾮常多,這對⽹絡的性能損耗也是很⼤的。
基於上⾯的內容,不少⼈開始嘗試將容器⽹絡和公司全部物理機的⽹絡打通,也就是造成⼀個平⾏⽹絡體系。這個 體系,去除了封包和解包操做,也再也不使⽤ kube-proxy 組件⽣成iptables規則。⽹絡的性能⾮常⾼。
打通平⾏⽹絡的⽅案又不少,⽐如 Calico BGP、Linux Bridge、Linux Macvlan、Linux IPVlan 等等。
這⼏個⽅案的特色以下:
⽅案/特色 | 複雜度 | 機房改形成本 | 運維成本 |
---|---|---|---|
calico bgp | 很⾼ | 很⾼ | ⾼ |
linux Bridge | 低 | 無 | 低 |
linux ipvlan | 很低 | 無 | 很低 |
linux macvlan | 很低 | 無 | 很低 |
整體來講:
calico bgp 這種⽅案:實現成本⽐較⾼,須要整個公司的基礎⽹絡架構的物理設備(交換機或路由器)⽀持 BGP 協議才⾏。⽽且,⼀旦出現問題,排除的複雜度也⾼,須要公司的⼈很懂才⾏。我我的以爲,若是公司的研發和網絡的掌控力沒有達到必定程度的話,不建議採用這種方式。
linux bridge:這種⽅案的成本較低,不過須要將物理⽹關掛到⽹橋下,若是是單⽹卡的物理機,這個操做會斷 ⽹,建議多⽹卡環境進⾏操做。
linux macvlan:這種⽅案成本很低,須要⽹卡⽀持混雜模式(⼤部分⽹卡都是⽀持的)。混雜模式須要⼿動開啓。
linux ipvlan:這種⽅案成本很低,和 Macvlan 很像。區別是不一樣 IP 公⽤ mac 地址,性能可能相對 macvlan 好⼀ 些,具體其優點不作細談。
打通容器和宿主機平⾏⽹絡,其實也會存在⼀些問題,須要提早規劃好。⽐如,是直接2層⽹絡打通,仍是3層⽹ 絡打通?2層⽹絡打通,可能既須要⼀個強⼒的交換機設備,也須要防⽌⼴播風暴產⽣。若是是3層⽹絡打通,意 味着多⽹段,⽽多⽹段的狀況,每新增⼀個IP段,宿主機都要進⾏⼀個路由表的處理操做,這倒也不是⼀個⿇煩的 問題。
另外,macvlan、ipvlan 對操做系統的內核版本都有⼀定的要求。內核版本⽀持很差的狀況下,會遇到各類各樣的 異常問題,很差排查。⽐如 macvlan 在 Linux 4.20.0 這個內核版本上,就有不少問題,⽐如:
kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1
複製代碼
我⾸先考慮的⽅案,就是實施成本要低、維護簡單,⽬的不變,也就是投⼊產出⽐要⾼。因此,macvlan 和 ipvlan 就是⾸選。
可是,macvlan 和 ipvlan 都有⼀個共同的特色,就是虛擬 ip 所在的⽹絡,⽆法直接和宿主機⽹絡互通。簡單來 ⾸,利⽤這2個模型,將虛擬 ip 加⼊到容器⾥後,容器⽆法直接 ping 通宿主機。這個不是bug,⽽是這2個⽹卡虛 擬化技術,在設計之初,就是這樣的。
可是,實際狀況下,咱們不太可能不讓容器的⽹絡訪問宿主機的⽹絡。因此,這個問題,必需要解決。
要解決這個問題,使⽤ macvlan 的話,有⼀個⽐較好的自然解決⽅案。就是使⽤ macvlan 也在宿主機上⽣成⼀個 虛擬⽹卡。這樣⼀來,容器⽹絡就能夠和宿主機互訪,操做上就是⼀⾏命令的事⼉。可是!!咱們本機利⽤蘋果本 虛擬機調試是沒有問題的,⽤ KVM 調試,但 Pod 變化觸發容器⽹絡銷燬和新建這種變化時,宿主機就⽆法訪問容 器⽹絡了,⽽且,在同⼀個宿主機上,宿主機對其內的有些容器能夠訪問,有些容器不能訪問,⾮常奇怪,這個問 題,可能在物理機上不⼀定存在,可是在調研階段,咱們必需要考慮其在線上物理機環境也⽆法實施或者後續遇到 相似問題的狀況。因此,咱們嘗試更換了多個操做系統的Linux內核版本,問題依然,只能另尋他法。
解決互訪問題,前⾯提到的⽅案是,若是使⽤macvlan能夠⽤其在宿主機虛擬⼀個⽹卡出來配置⼀個和宿主機的平 ⾏⽹絡IP。這個弊端前⾯提到了很重要的⼀部分,另外還有⼀部分沒有提到,就是IP資源浪費的狀況。至關於每一個 主機都須要多⽤⼀個IP地址。
須要⼀個⽅案,能夠通⽤的解決 macvlan 和 ipvlan 這種容器和宿主機⽹絡⽆法互訪的問題。
解決這個問題,咱們能夠藉助 veth pair 來完成。其原理是,建立⼀對 veth pair,⼀端掛⼊容器內,⼀端掛⼊宿主 機內。如圖:
如上:
①:藉助 ipvlan 或 macvlan 在 em1 上建立2個虛擬⽹卡,而後分別加⼊到 container-1 和 container-2 中,容器內 ⽹卡命名爲 eth0,且 IP地址 和宿主機平⾏。 ②:上⾯的操做完成後,容器之間能夠互訪,可是,容器和宿主機⽆法互訪,這是 ipvlan/macvlan 的制約。 ③:建立 2 個 veth-pair 對,⼀對加⼊宿主機和容器 container-1 中(容器端叫 veth0,宿主機端爲 veth-0x1),另 外⼀對加⼊宿主機和容器 container-2 中(容器端叫 veth0,宿主機端爲 veth-0x2),而後在容器內建立指向到宿 主機⽹卡的路由表規則,於此同時,在宿主機上,也要建⽴指向到容器⽹絡的路由表規則。
綜上,能夠完成容器和宿主機⽹絡互訪。
使⽤這種⽅案有⼏個有點:
①:CNI 插件使⽤ macvlan 或 ipvlan 都沒有關係。 ②:不須要藉助 macvlan 在宿主機上虛擬⽹卡出來,由於這種⽅案,不具有通⽤性,⽽且,並不穩定(在某些vm 主機的場景下)。 其實,上述⽅案的實現,即是 K8S CNI 插件 ptp 的原理。
上⾯的操做完成後,解決了 CNI 的問題。剩下的,就是 IPAM 的問題。在整個流程中,CNI 插件主要負責⽹絡的 定製,⽽ IP 如何獲取,並⾮ CNI 的⼯做內容,它能夠經過調⽤ IPAM 插件來完成IP的管理。
IPAM 插件有不少,最簡單的,是 K8S CNI 官⽅提供的 host-local IPAM 插件:github.com/containerne… 。CNI 的插件,K8S 官⽅也有提 供:github.com/containerne… 。
咱們在實際測試過程當中發現,K8S 的 CNI 插件,在 kubelet 1.13.4 版本上存在問題,報找不到 cni 名稱的問題。所 以,我將 lyft 的⼀個開源項⽬ cni-ipvlan-vpc-k8s 改造了⼀下(主要是解決 ptp 插件設置默認路由規則失敗致使 CNI總體調⽤失敗的問題,此問題不⼀定在全部場景中存在,須要使⽤者結合⾃⼰業務來處理)。
下⾯是⼀個 CNI 配置⽂件的樣例:
//cat /etc/cni/net.d/10-maclannet.conflist
複製代碼
{
"name": "cni0",
"cniVersion": "0.3.1",
"plugins": [
{
"nodename": "k8s-node-2",
"name": "myipvlan",
"type": "ipvlan",
"debug": true,
"master": "eth0",
"mode": "l2",
"ipam": {
"type": "host-local",
"subnet": "172.18.12.0/24",
"rangeStart": "172.18.12.211",
"rangeEnd": "172.18.12.230",
"gateway": "172.18.12.1",
"routes": [
{
"dst": "0.0.0.0/0"
}
]
}
},
{
"name": "ptp",
"type": "unnumbered-ptp",
"hostInterface": "eth0",
"containerInterface": "veth0",
"ipMasq": true
}
]
}
複製代碼
配置⽂件說明:插件是2個,⼀個是名字爲 myipvlan 的插件,類型爲 ipvlan(也就意味着 kubelet 會去 CNI 插件 ⽬錄找名爲 ipvlan 的可執⾏⽂件),⼀個名爲 unnumbered-ptp。
其中,ipvlan 插件要配置的 IP,會從 CNI 插件⽬錄,尋找名爲 host-local 的可執⾏⽂件來獲取 IP 地址。
再次說明,ipvlan 和 unnumbered-ptp 是基於 lyft 的⼀個開源項⽬ cni-ipvlan-vpc-k8s 改造過的。
歡迎關注「海角之南」公衆號獲取更新動態