K8S CNI之:利⽤ ipvlan + host-local 打通容器與宿主機的平⾏⽹絡

海角之南發表於2019-03-20

CNI 和 IPAM 概述

我們常說的CNI,可以包括IPAM(IP地址管理),也可以不包括IPAM。不過,通過情況下,CNI外掛的實現和 IPAM外掛的實現是分開為不同的可執⾏⽂件的。但是如果你寫到⼀起,這樣的CNI也可以⽤。按照K8S本⾝規 範,我們在使⽤CNI的時候,是要區分CNI和IPAM的。

K8S 啟動 POD ⽹絡的配置流程是,kubelet->docker(pause)->cni->ipam。因此,K8S⽹絡的中⼼,便是 CNI 插 件和 IPAM 外掛。因為 CNI 和 IPAM 外掛很多,所以不打算都講⼀講。整體來說,CNI + IPAM 的實現可以分為2 類:

①:第⼀類:與宿主機平⾏⽹絡(2層⽹絡或3層⽹絡) ②:第⼆類:利⽤ SDN 技術的虛擬⽹絡

⽽第⼀類的 CNI ⽹絡外掛,主要是:bridge、macvlan、ipvlan、calico bgp、flannel host-gw 等。第⼆類的⽹絡 外掛主要有:flannel vxlan、calico ipip、weave 等。

虛擬SDN網路的優勢和劣勢

⼤部分使⽤K8S的公司,都是使⽤的K8S經典⽹絡模型,⽐如:K8S+Flannel(VXLAN)、K8S+Calico(IPIP) 等。這種模式下的⽹絡有⼀個共同特點,就是容器⽹絡在K8S叢集內是隔離的,叢集外的宿主機是⽆法直接訪問容 器IP的。之所以會這樣,是因為容器⽹絡是虛擬的,它這個K8S的虛擬⽹絡,⼤部分上都是藉助 路由表 + iptables + 隧道 模式來做的。

這種經典的⽹絡模式,有優勢,也有劣勢。

優勢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
複製程式碼

ipvlan和macvlan⽅案實施遇到的容器與宿主機互訪問題

我⾸先考慮的⽅案,就是實施成本要低、維護簡單,⽬的不變,也就是投⼊產出⽐要⾼。所以,macvlan 和 ipvlan 就是⾸選。

但是,macvlan 和 ipvlan 都有⼀個共同的特點,就是虛擬 ip 所在的⽹絡,⽆法直接和宿主機⽹絡互通。簡單來 ⾸,利⽤這2個模型,將虛擬 ip 加⼊到容器⾥後,容器⽆法直接 ping 通宿主機。這個不是bug,⽽是這2個⽹卡虛 擬化技術,在設計之初,就是這樣的。

但是,實際情況下,我們不太可能不讓容器的⽹絡訪問宿主機的⽹絡。所以,這個問題,必須要解決。

要解決這個問題,使⽤ macvlan 的話,有⼀個⽐較好的天然解決⽅案。就是使⽤ macvlan 也在宿主機上⽣成⼀個 虛擬⽹卡。這樣⼀來,容器⽹絡就可以和宿主機互訪,操作上就是⼀⾏命令的事⼉。但是!!我們本機利⽤蘋果本 虛擬機器除錯是沒有問題的,⽤ KVM 除錯,但 Pod 變化觸發容器⽹絡銷燬和新建這種變化時,宿主機就⽆法訪問容 器⽹絡了,⽽且,在同⼀個宿主機上,宿主機對其內的有些容器可以訪問,有些容器不能訪問,⾮常奇怪,這個問 題,可能在物理機上不⼀定存在,但是在調研階段,我們必須要考慮其線上上物理機環境也⽆法實施或者後續遇到 類似問題的情況。所以,我們嘗試更換了多個作業系統的Linux核心版本,問題依然,只能另尋他法。

使⽤ ptp 優雅的解決使⽤macvlan(或ipvlan)時容器和宿主機互訪問題

解決互訪問題,前⾯提到的⽅案是,如果使⽤macvlan可以⽤其在宿主機虛擬⼀個⽹卡出來配置⼀個和宿主機的平 ⾏⽹絡IP。這個弊端前⾯提到了很重要的⼀部分,另外還有⼀部分沒有提到,就是IP資源浪費的情況。相當於每個 主機都需要多⽤⼀個IP地址。

需要⼀個⽅案,可以通⽤的解決 macvlan 和 ipvlan 這種容器和宿主機⽹絡⽆法互訪的問題。

解決這個問題,我們可以藉助 veth pair 來完成。其原理是,建立⼀對 veth pair,⼀端掛⼊容器內,⼀端掛⼊宿主 機內。如圖:

K8S CNI之:利⽤ ipvlan + host-local 打通容器與宿主機的平⾏⽹絡

如上:

①:藉助 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 的原理。

IPAM的使⽤

上⾯的操作完成後,解決了 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 改造過的。


歡迎關注“海角之南”公眾號獲取更新動態

K8S CNI之:利⽤ ipvlan + host-local 打通容器與宿主機的平⾏⽹絡

相關文章