1. 程式人生 > >K8S的網絡接口CNI及靈雀雲的實踐

K8S的網絡接口CNI及靈雀雲的實踐

docker k8s 靈雀雲 kubernetes

K8S的網絡接口CNI及靈雀雲的實踐

K8S的網絡模型


我們從底層網絡來看,分為三個層面。首先是Pod之間的多個容器的網絡互通。我們知道,K8S的Pod可以由多個容器組成,這個層面網絡互通是比較簡單的,因為所有的容器都是共享一個網卡,可以直接通信。


第二個,一臺虛擬機上多個容器之間的網絡是如何通信的。這塊兒其實也比較好解決,例如Docker會搭一個網橋,讓上面所有的東西、網卡接到網橋上,他們之間的網絡就可以互通。Docker默認服務會創建一個Docker0的網橋,其它主流的像Calico、Flannel的模式也是類似的。這種方式的實現也是現在的主流。


第三種是比較難的,Docker一開始就沒有做好,是跨主機的Pod之間的網絡通信。對於K8S來說,這個網卡如何分配其實K8S是沒有定義的。不同Pod跨主機網絡之間如何通信、如何打通網絡,K8S不管,是交給第三方實現的。在這塊,是有很多工作可以做的。

技術分享圖片

容器網絡設計給運維人員帶來的困惑


現在我們再想一些問題,容器網絡設計會給傳統運維工作帶來怎樣的困惑?


傳統運維工作強調對IP要有很強的管控。容器時代,Pod需不需要有固定的IP?Pod重啟之後,IP是否不變?其實K8S沒有規定,而且從大部分主流的實現來看,容器IP是可以變的。既然容器的IP是會變的,就會帶來一個很直接的問題,我想訪問這個服務怎麽訪問?之前兩位同事講過很多,我不做很細的介紹。


這個大致的原理是,盡管底層Pod IP不斷變化,但是我會給你提供固定的域名或者DNS的方式,或者固定的Cluster IP的方式等等。不管Pod方式如何變,總會給你固定的方式,通過固定的方式可以訪問到一直變化的IP,這些方式都是通過Kube-proxy、iptables、Kube-dns實現。

技術分享圖片

我們實際測的時候發現,K8S的服務發現功能比較少,隨著訪問量越來越大,會發現它的性能有很多問題,還有穩定性也有很嚴重的問題,而且它的穩定性缺陷是它從設計當初就很難避免。我們正在改進這些設計缺陷問題。


為此很多人就會問,說你的容器IP是一直變的,怎麽管?但容器就是這樣的,容器生命周期很短,不斷創建、不斷消失,IP肯定不斷變。容器的好處是,你可以在這臺機器上掛,在另外一臺機器也可以。但運維人員首先覺得IP這樣飄不好。對於運維來說,網絡方面是很重要的資源,要對IP進行強管控,服務來回飄,會讓他的安全感下降很多。


運維服務有很多基於IP的東西,有流量和突發的監控,如果你服務的IP一直變化,通過這個IP它很難用到這個服務,相當於IP的監控就沒有意義,因為根本不知道IP流量上去了是哪個服務的,很難對應到這個事。

還有是對於IP安全策略沒有辦法做。如果以前IP是固定的,iptables或者網絡底層防火墻,這樣的東西都是很好做的,可以很好的進行服務之間的安全訪問限制。如果IP都是變的,這個事情就變得沒有辦法做了。


第四是定位分析和數據處理問題。我們見到,有的客戶把網絡正常在跑的所有流量鏡像一份,每天對復制的流量進行分析,看你有沒有違規操作或者有什麽惡意攻擊,或者哪些行為不正常,這樣他們通過IP再定位服務。如果IP是變的,相當於把他們很多能做的事情去掉了。


還有一些特殊的軟件,他們的license是根據網卡來的,就是說他的license是根據網卡的IP加上Mac計算出來的。這種情況下相當於IP變了,license失效,軟件跑不起來。


還有你的IP不斷變化,實際中也有問題,有很多服務和軟件就是基於IP部署的,基於IP相互發現的,最典型的是Etcd這樣的。如果你是用K8S或者其他的容器,IP是變的,這個東西怎麽填都是很難的事情。


K8S有它的解決方案,Headless Server和 Stateful Set可以做這個事情。但是這個事情在我看來有兩個問題,它引入兩個不好理解的概念,如果大家對K8S比較關心,第一次看,我覺得你看好半天才能明白它幹什麽,它為什麽這麽設計。它的核心理想是你的Pod IP是變的,然後給Pod一個順序,給每個Pod一個標誌符,etcd1、2、3,給他們三個人每個人用k8s Service+域名,你在裏面不用填IP,填對應的三個域名就可以。但域名有cash緩存的問題,底層Pod IP失效,域名的緩存沒有失效就會造成研判時間的不一致,我覺得靠Service做這件事情也是有問題的,並不能完美的解決這個問題。


為什麽Docker、K8S都沒有做固定IP的事情?


剛才說了IP不固定的影響,做容器之前大家可能沒有想這個事,可能從一開始就認為IP是不固定的,我們想一下如果IP是固定的有什麽影響,其實好像還可以用,並且可能變得更簡單。比如Cluster IP或者DNS它們的映射由於IP是固定的可能變得更簡單了。如果容器的IP固定,運維傳統的基於IP的監控也可以做了,因為對他們來說沒有影響,和原來是一樣的,對他們的接受程度也會更高一點。


我們很多服務發現的方式,可能之前通過K8S的Sevrice來進行服務發現,既然IP固定,我們可以跳過,直接用IP服務發現就可以,沒有必要讓你的運維理解很復雜的概念。大家會問什麽是Cluster IP,什麽是Pod,這些還是有理解成本,如果提供原生的方式也是不錯的。


可能想到一個問題,既然固定IP還是有好處的,為什麽Docker還是K8S都沒有做這個事情呢?我覺得一開始Docker沒有想好這個事情怎麽做。最開始Docker就是一個單機的工具,沒有想做得很復雜,最早Docker的網絡和存儲都是很弱的,它根本沒有往這方面想。


還有一點,在我理解就是理念之爭。到底服務應該做成有狀態的還是無狀態的?有Docker之後大家認為容器應該是跑無狀態的東西,而不是有狀態的東西,但是網絡這塊是有狀態的。我們之前說有狀態想到的是存儲,認為存儲才是有狀態的服務,其實網絡也是狀態的一部分。大家可以想一下,如果Pod裏面的服務訪問外面,給外部留下的信息是某個IP訪問我,所以對外的狀態對你來說,IP其實也是你的一個狀態,所以說網絡其實也是狀態的一部分,如果要做有狀態這個事情就變得很難。


但是能不能做呢?可以做,我們做了一些嘗試,做的方式是我們自定義CNI插件。


如何自定義CNI插件?


CNI,這個概念大家平時聽的少一點。它的全稱是Container Network Interface,它註重給你的容器提供一套標準的網絡的接口。現在包括K8S等都是通過CNI這種方式實現的。CNI 是 CNCF 的一個項目,如果你對比其它項目,CNI應該是裏面最簡單的。如果你對這個項目有興趣,從這裏開始是比較好的,可能幾百行或者上千行的代碼就到頭了。一個是CNI的標準,包括CNI接口和庫,怎麽快速實現CNI,還有CNI提供的網絡插件。


介紹一下Kubernetes和CNI之間是如何交互的,給大家講完之後,大家可以比較容易上手,自己實現自己的網絡。

技術分享圖片

這是Kubernetes的基本參數,大家可以看到。紅色的標起來的是兩個選項。CNI在我看來是有點奇怪的實現。一般的時候一個程序和另一個程序交互,大概是HTTP REST或者是RPC或者TCP的通信,但是CNI和Kubernetes的交互方式是通過二進制文件的方式,它會調用二進制文件傳一個參數來做這樣的事情。第二張是CNI實現的組件,他們都是以二進制的形式放在目錄下面。左下角是CNI的配置,是最簡單的,參數很少,還有一些其他比較復雜的。其實比較重要的是type和IPAM。


大概講一下它交互的流程,是通過創建Pod和銷毀Pod兩個事件來觸發的。創建Pod的時候,它會去調CNI,它會根據這個文件會先調用macvlan,創建一個網卡,然後再分配IP,都是調裏面的DHCP和macvlan二進制文件。創建網卡,構建IP,把這個網卡放到Pod network namespace,相當於完成Pod網卡的創建。還有,Pod刪除的時候,可能調用二進制文件釋放掉,然後刪除網卡,完成網卡的銷毀。大概的交互比較簡單,就這兩個,實現的時候也是實現這兩個方法就可以。

技術分享圖片

最下面是main函數,其實有一個庫,可以方便實現,照著這個寫就可以,需要實現的是下面的函數就可以。函數其它的都不太用著重關註,都是做錯誤處理。最主要關註上面的,它是在NetNSpath裏面創建的一塊link,刪除的時候再把這塊網卡刪除,這是最簡單的設備創建。如果大家對網絡比較熟,或者對自己的網絡有特殊的要求或者定制化的需求,其實在裏面實現自己的邏輯就可以,整體來說它的邏輯還是比較簡單的。而且還有一個功能是多個命令是可以串聯的,比如我剛才舉的例子,macvlan和DHCP是串聯的,macvlan先創建網卡,然後再調DHCP,分配IP,他們之間有一個result的返回,通過result進行數據結構的傳輸。我們看一下result就可以知道網絡還可以做什麽別的事情。

技術分享圖片

這是官方定義的API標準,其實我們也是可以在裏面擴充的。上面是interfaces,Kubernetes這個網絡做得實在太簡單,包括容器中的很多東西都是太簡單了,可能一個pod裏面有一個網卡設一個IP就完了。其實一個pod裏面有多個網卡的需求,一個數據流,一個控制流,這種是可以做的。在它的interfaces裏面可以看到有內置的網卡和mac地址,因為我們碰到有些客戶,你的mac是可信域才可以做安全策略,這需要你要自定義的mac。再往下,網卡是不是有多個IP,而不是常用的網絡,一個容器的一個IP。再往下可以定義這個容器的路由表,包括路由的順序等等都是可以定義的。再往下是DNS server,容器裏面的DNS都可以自定義,容器通信網絡這塊有很多可以做的事情,如果大家想做這方面,我覺得機會還是很大的。現在的網絡說實在的,盡管實現很多,但是都還是蠻弱的。


容器網絡方面已經有一些第三方實現,比較知名的有flannel、calico、weave……。下面兩項其實是它官方已經實現的,看起來分兩大類:一是網卡生成的方式,比如bridge、ipvlan、macvlan、loopback、ptp、vlan。第二大塊是IPAM。我不知道大家是否理解IPAM,IPAM的意思是IP該怎麽分配。官方實現的有兩種,一種是DHCP,一種是host-local。


靈雀雲如何通過IPAM做到精確管控IP?


回到之前說的我們要做固定IP,大家可以很容易聯想IPAM這塊做一些東西,因為這個IPAM是針對IP的,如果要做的話,先看一下現有的兩種有沒有問題,或者現有的兩種怎麽實現。DHCP大家比較熟悉,就是自動分配IP,它的問題是沒有辦法精細管控IP。因為當需要通過主機發再發DHCP的廣播再獲得IP,相當於容器IP和宿主機的IP混在一起,很難精確分配到某個Pod使某個IP,因為你不知道這些信息。這在實際生產環境用得很少。因為IP管控要求很嚴格,不可能自己計算IP,我們家裏的wifi或者辦公才會用到,企業級業務碰到的機會都很少。

host-local,每臺機器上都有這個文件,這個文件上規定每臺機器能生成一些IP。可以看一下配置文件的格式。其實這兩個參數比較重要,一個是rangeStart和rangeEnd,就是每臺機器分配的範圍是多少。這種分配方式有一個很嚴重的問題, Calico 和 Flannel都有,就是每臺機器都是固定的,這給每臺機器劃分了一個網關,相當於你Pod是在機器上的,因為IP是很不靈活,很難實現IP的漂移。而且分配IP比較困難,新增機器要重新定義,這臺機器用多少IP,要考慮IP段的釋放,很不靈活,顯然沒有辦法實現固定IP的事情。


為什麽這個事情感覺這麽難?我覺得也是K8S設計理念的問題,我們可以看到K8S管了很多東西,管了Pod、configmap、Service等,你會發現它沒有管你的IP。理論上來說,IP對你的服務或者對你的集群來說是很重要的資源,但是K8S裏面IP不是它的一等應用,甚至你根本沒有一個跟容器相關的資源在開發裏面找到,所以使得你的IPAM變得很難。你要麽通過K8S完全無關的東西來實現,要麽通過鎖死的方式實現,這其實都是因為你沒有把IP資源看得特別重,你只是當做可以隨機分配的,所以它設置的理念就是你的IP我不關心,該是什麽就是什麽,所以導致這樣的結果。


最後講一下我們做的,我們把IP當做很重要的資源,進行單獨的管理。我們會專門實現IPAM的組件,實現網段的添加、刪除,可以在K8S管理網段,給某個業務或者某幾個用戶分配一個網關,然後對IP進行網關設置,路由設置,和DNS設置。有了網段之後,有IP的添加刪除,會有很重要的資源,該用哪些IP,哪些IP是可以用的,都是可以用管理員指定的,而不是像K8S隨機劃一個網段,還有權限、配額,之後就可以順利創建服務了。


技術分享圖片

現在創建K8S的Deploy的時候需要申明用哪幾個IP。如果IP信息傳輸到IPAM的組件,這個IPAM就是一個數據庫,這個數據庫就會顯示哪些IP可以使用,哪些應該被哪個服務使用,把這個記錄下來。在K8S啟動Pod,CNI裏面再實現一套邏輯,把IPAM的邏輯實現了,那套做的東西會進入IPAM的數據庫裏面找,如果用就占用這個,別人不能再用,就可以實現每個POD有自己的IP,這個IP是你之前想要的。如果在刪除的時候,CNI會把IP釋放掉,說明這個IP不會再被這個服務使用,別的服務可以使用這個IP。我們如果想創建etcd的服務,就不用再寫一個東西,你只要做你想要的IP就可以,很大的簡化你的工作量,而且實現我之前說的傳統的運維,IP的監控、管理的東西。

本文出自 “13481808” 博客,請務必保留此出處http://13491808.blog.51cto.com/13481808/1983816

K8S的網絡接口CNI及靈雀雲的實踐