最近一直在設計借貸寶的底層k8s架構,大體方案已經明確,資源層Pod網路採用macvlan+hostnetwork的方式,服務層採用nginx(後期切入到ingress-nginx-controller)+dubbo(還有一部分基於zk的自研註冊中心)的服務發現機制,域名發現採用外部DNS。從這個架構就可以看出來,我們不打算用k8s的clusterip機制,但是,自己的應用Pod不用,k8s的自身管理Pod是會用到clusterip的,例如coredns、dashboard、prometheus等內部管理Pod都會通過kubernetes service的clusterip來訪問apiserver,還有prometheus內部之間,也會通過service clusterip來互訪。這就帶來了一個問題,如果這些Pod都是使用macvlan網路的話,他們是無法訪問clusterip的,因為macvlan不是一個cluster network plugins(具體原因見後文),所以這怎麼辦?帶來了三個問題需要解決:

1、clusterip的實現機制是什麼,是否可以改造?

2、不能改造的話,如何能夠沿用macvlan網路插件,但是讓這些管理Pod可以正常運行?

3、如果管理Pod確實不能使用macvlan網路插件的話,如何讓應用Pod繼續採用macvlan,而單獨讓管理Pod使用其他網路插件?

本文就是對這三個問題的調研分析思考過程的一個整理和總結。

問題1:clusterip的實現機制是什麼,是否可以改造?

clusterip是k8s service用來對內部網路Pod暴露服務的一種方式,可以認為就是service的內部負載均衡ip,一個vip,這個vip分布在每一個node節點上,可以理解成是一個分散式的內部負載均衡。它的配置使用,如何內部聯動,也即管理平面的東西,我這裡就不說了,可以參考官方文檔:kubernetes.io/docs/conc,裡面幾個圖都不錯,可以好好看下。

這裡重點講clusterip的數據平面的實現(基於iptables/ipvs),也就是網路到底是怎麼實現負載分攤,路由轉發的,關於這塊內容,本文重點參考《Kubernetes Service詳解(概念、原理、流量分析、代碼)》這個文章,這個文章對於實現機制的說明和驗證,已經寫的很好,尤其是那個表格,很不錯,但是少了一種情況,所以這裡記錄補充下。

對於clusterip的訪問情況,可以分為以下四種情況,但是不管哪種情況,其實都是通過DNAT+SNAT(可選)的方式來實現的,DNAT用來實現負載均衡,不管是用iptables還是ipvs,都是將目標地址clusterip:port轉換成後端service的具體某個pod的ip:port。而SNAT是為了讓內部Pod可以出訪到外部網路,所以只有Pod訪問內部網路Pod的service clusterip的時候,才不需要做SNAT,因為網路流量全部在內部,除此之外,都需要做SNAT,即使是Pod訪問apiserver的clusterip也需要SNAT,因為apiserver使用hostnetwork網路,不屬於內部網路,這一情況,上面那個文章里沒有列出來,也沒有說明SNAT的本質原因,所以本文補充一下。下面列出具體情況,以及示例圖。

情況一:其他Pod(或node節點上的其他獨立ip,例如非k8s納管的docker容器)訪問內部網路Pod的 service clusterip。只有這種情況不需要SNAT。

情況二:自身Pod訪問內部網路Pod的service clusterip,且負載均衡到了自己。

情況三:其他Pod(或node節點上的其他獨立ip)訪問hostnetwork網路Pod的service clusterip。

情況四:自身Pod訪問hostnetwork網路Pod的service clusterip,且負載均衡到了自己。

具體實現這個能力的iptables/ipvs規則,這裡就不寫了,可以參考上面提到那個文章。

了解了clusterip的實現機制,那macvlan能用嗎?很遺憾,答案是不能。

原因是,macvlan網路屬於外部網路,並且擁有獨立的網路空間namespace,所以並不會經過node的網路空間的內核協議棧,進而造成並不會經過iptables/ipvs的配置,所以,不會被DNAT和SNAT,現象就是,macvlan的pod可以ping通clusterip,但並無法telnet埠,更無法使用後端的負載均衡。具體原因可以看下面這個圖(來源《圖解幾個與Linux網路虛擬化相關的虛擬網卡-VETH/MACVLAN/MACVTAP/IPVLAN》,這個文章不錯,可以細看下)。

可以看到,處於獨立namespace的macvlan,是不會走node的協議棧的。那問題來了,對於k8s來講,除了hostnetwork網路的Pod,所有基於cluster network網路插件的Pod都是獨立的網路namespace,為什麼它們就能夠走node的協議棧呢?答案在下圖,也出自上面這個文章。

因為對於cluster network來講,它們都屬於cluster的內部網路,在做三層路由的時候,是會通過類似圖中Peer的方式,進入node的網路空間,從而被iptables/ipvs轉發的。

可能你現在有點蒙,但其實整理一下你就理解了。像macvlan這樣的網路,你可以認為它是一個與node的hostnetwork網路平齊的網路,它們都是需要外部進行三層路由轉發的,這個時候,它們的結構大概是這樣,如下圖。

而其他的cluster network,它們的網路結構是這樣的,如下圖:

因為cluster network是內部網路,在從Node節點出去的時候,必然要經過一次Node的網路namespace,所以iptables/ipvs對它們自然生效,我們可以認為,每台物理機上有一個分散式的路由和網關,在為這些內部網路服務。

所以,既然macvlan不能像cluster network一樣,使用clusterip,那我們可以改造一下嗎?答案是,可以改,但是太麻煩,不划算。其實從上面兩個圖就可以看出來了,本質上就是macvlan的命名空間里,沒有iptables/ipvs的規則而已,而這些規則,其實都是可以通過配置,放入到macvlan網路的pod所在的namespace的,一旦放入,從macvlan的Pod中訪問clusterip,就跟具有hostnetwork網路的Pod訪問clusterip的效果一致,上文提到了,macvlan網路的效果,其實跟hostnetwork的對外暴露效果等價。關於配置思路,網上有一個文章,可以看下《Run IPVS in a separate network namespace》,總之是可行的。如果這樣改造,意味著每個macvlan的Pod和Node節點本身,都在保存一份iptables/ipvs的規則,說實話,是有點麻煩,其實就是因為少了一層公共的內核協議棧(深入思考,這個應該給linux內核提個需求的,為什麼就沒有共享namespace,這麼一個東西呢?有的話,這個問題是不是就完美解決了),所以這個事情可以做,但是太麻煩了,想想,一旦規則變更,需要更新這麼多namespace的iptables/ipvs規則,也是夠心累的。所以這事,我相信k8s不會幹,我們也不打算自己干。其實共享namespace這件事,倒是可以看看,能否加入到linux里,等有時間再思考下可行性吧。

問題2:不能改造的話,如何能夠沿用macvlan網路插件,但是讓這些管理Pod可以正常運行?

既然不能改造,還是實際點,workaround一下吧,問題2的答案,是部分可以的。大部分的管理Pod都可以通過hostnetwork=true的方式運行,訪問clusterip就一點問題沒有了,原因可以見上文。那就剩下一部分管理Pod,就是沒辦法使用hostnetwork怎麼辦呢??帶入問題3。

問題3:如果管理Pod確實不能使用macvlan網路插件的話,如何讓應用Pod繼續採用macvlan,而單獨讓管理Pod使用其他網路插件?

可以有兩種方式:

第一種方式,部分Node標記master,採用cluster network,例如flannel/calico/weave,貌似最近weave比較火,可以藉機熟悉一下。然後部署管理Pod的時候,指定部署到master上去。

第二種方式,基於multus-cni插件做雙網卡,然後配置默認路由走macvlan的網卡,內部網路走cluster network那塊網卡。

兩種方式都可以,我個人傾向於第一種,因為首先是不想雙網卡管理,另外就是第二種方式的multus-cni插件,必須有一個網路是cluster network,這點就不符合我們的需求了,因為我們並不想把cluster網路作為每個Pod都有的網路,而恰恰相反,我們大部分的網路都是macvlan網路。話說,這個插件可以做的更自由一點,網路類型完全由用戶自己來選擇,只用macvlan也可以,只用cluster網路也可以,一起用也可以,這樣多好,個人認為,這才是k8s現在最欠缺的一個網路配置方式(正常來講,Pod是什麼網路,就應該由Pod來決定,但現在其實是Node來決定的,不覺得k8s這個設計有點怪怪的嗎?這點,其實就沒有docker做的合理了,docker是就是創建容器的時候,指定網路類型的。),本來這個multus-cni插件,可以在一定程度上,解決上面這個問題,但是並沒有做的很徹底,感覺有一些遺憾。

OK,最後總結一下,經過上面的分析,3個問題基本解決,完全基於macvlan的網路,對於基於Baremetal方式部署的k8s,完全是可行的。而且可以很好的實現資源層和服務層的管理,對於這個方案,我們借貸寶的雲團隊很有信心可以把它做成,預計未來一兩個月,會啟動遷移計劃,到時候,再把一些情況分享到這裡吧,也希望知乎的朋友們,能夠多提意見,共同進步。

參考:

IPVS-Based In-Cluster Load Balancing Deep Dive

DESIGN: Services v2 #1107

Kubernetes Service詳解(概念、原理、流量分析、代碼)

圖解幾個與Linux網路虛擬化相關的虛擬網卡-VETH/MACVLAN/MACVTAP/IPVLAN

Run IPVS in a separate network namespace

kubernetes系列之五:IPVS概覽

kubernetes的Kube-proxy的轉發規則分析

[轉]IPTABLES中SNAT和MASQUERADE的區別

iptables 命令介紹

Iptables規則執行順序詳解

超級詳細Tcpdump 的用法

Linux 虛擬網路設備 veth-pair 詳解,看這一篇就夠了

網卡也能虛擬化?網卡虛擬化技術 macvlan 詳解

Linux ip netns 命令

Get started with Macvlan network driver


推薦閱讀:
相关文章