Kubernetes的網(wǎng)絡實現(xiàn)
在實際的業(yè)務場景中缺狠,業(yè)務組件之間的關系十分復雜,特別是隨著微服務理念逐步深入人心,應用部署的粒度更加細小和靈活颈将。為了支持業(yè)務應用組件的通信,Kubernetes網(wǎng)絡的設計主要致力于解決以下問題言疗。
(1)容器到容器之間的直接通信晴圾。
(2)抽象的Pod到Pod之間的通信。
(3)Pod到Service之間的通信洲守。
(4)集群外部與內部組件之間的通信疑务。
其中第3條、第4條在之前的章節(jié)里都有所講解梗醇,本節(jié)對更為基礎的第1條與第2條進行深入分析和講解知允。
1.容器到容器的通信
? 同一個Pod內的容器(Pod內的容器是不會跨宿主機的)共享同一個網(wǎng)絡命名空間,共享同一個Linux協(xié)議棧叙谨。所以對于網(wǎng)絡的各類操作温鸽,就和它們在同一臺機器上一樣,它們甚至可以用localhost地址訪問彼此的端口。
? 這么做的結果是簡單涤垫、安全和高效姑尺,也能減小將已經(jīng)存在的程序從物理機或者虛擬機移植到容器下運行的難度。其實蝠猬,在容器技術出來之前切蟋,大家早就積累了如何在一臺機器上運行一組應用程序的經(jīng)驗,例如榆芦,如何讓端口不沖突柄粹,以及如何讓客戶端發(fā)現(xiàn)它們等。
我們來看一下Kubernetes是如何利用Docker的網(wǎng)絡模型的匆绣。
如上圖中的陰影部分所示驻右,在Node上運行著一個Pod實例。在我們的例子中崎淳,容器就是圖中的容器1和容器2堪夭。容器1和容器2共享一個網(wǎng)絡的命名空間,共享一個命名空間的結果就是它們好像在一臺機器上運行拣凹,它們打開的端口不會有沖突森爽,可以直接使用Linux的本地IPC進行通信(例如消息隊列或者管道)。其實嚣镜,這和傳統(tǒng)的一組普通程序運行的環(huán)境是完全一樣的拗秘,傳統(tǒng)程序不需要針對網(wǎng)絡做特別的修改就可以移植了,它們之間的互相訪問只需要使用localhost就可以祈惶。例如雕旨,如果容器2運行的是MySQL,那么容器1使用localhost:3306就能直接訪問這個運行在容器2上的MySQL了捧请。
2.Pod之間的通信
? 我們看了同一個Pod內的容器之間的通信情況凡涩,再看看Pod之間的通信情況。
? 每一個Pod都有一個真實的全局IP地址疹蛉,同一個Node內的不同Pod之間可以直接采用對方Pod的IP地址通信活箕,而且不需要采用其他發(fā)現(xiàn)機制,例如DNS可款、Consul或者etcd育韩。
? Pod容器既有可能在同一個Node上運行,也有可能在不同的Node上運行闺鲸,所以通信也分為兩類:同一個Node內Pod之間的通信和不同Node上的Pod之間的通信筋讨。
同一個Node內Pod之間的通信
我們看一下同一個Node內兩個Pod之間的關系,如下圖所示:
可以看出摸恍,Pod1和Pod2都是通過Veth連接到同一個docker0網(wǎng)橋上的悉罕,它們的IP地址IP1赤屋、IP2都是從docker0的網(wǎng)段上動態(tài)獲取的,它們和網(wǎng)橋本身的IP3是同一個網(wǎng)段的壁袄。
另外类早,在Pod1、Pod2的Linux協(xié)議棧上嗜逻,默認路由都是docker0的地址涩僻,也就是說所有非本地地址的網(wǎng)絡數(shù)據(jù),都會被默認發(fā)送到docker0網(wǎng)橋上栈顷,由docker0網(wǎng)橋直接中轉令哟。
綜上所述,由于它們都關聯(lián)在同一個docker0網(wǎng)橋上妨蛹,地址段相同,所以它們之間是能直接通信的晴竞。
不同Node上Pod之間的通信
Pod的地址是與docker0在同一個網(wǎng)段的蛙卤,我們知道docker0網(wǎng)段與宿主機網(wǎng)卡是兩個完全不同的IP網(wǎng)段,并且不同Node之間的通信只能通過宿主機的物理網(wǎng)卡進行噩死,因此要想實現(xiàn)不同Node上Pod容器之間的通信颤难,就必須想辦法通過主機的這個IP地址進行尋址和通信。
另一方面已维,這些動態(tài)分配且藏在docker0之后的所謂“私有”IP地址也是可以找到的行嗤。Kubernetes會記錄所有正在運行的Pod的IP分配信息,并將這些信息保存在etcd中(作為Service的Endpoint)垛耳。這些私有IP信息對于Pod到Pod的通信也是十分重要的栅屏,因為我們的網(wǎng)絡模型要求Pod到Pod使用私有IP進行通信。所以首先要知道這些IP是什么堂鲜。
之前提到栈雳,Kubernetes的網(wǎng)絡對Pod的地址是平面的和直達的,所以這些Pod的IP規(guī)劃也很重要缔莲,不能有沖突哥纫。只要沒有沖突,我們就可以想辦法在整個Kubernetes的集群中找到它痴奏。
綜上所述蛀骇,要想支持不同Node上Pod之間的通信,就要滿足兩個條件:
(1)在整個Kubernetes集群中對Pod的IP分配進行規(guī)劃读拆,不能有沖突擅憔;
(2)找到一種辦法,將Pod的IP和所在Node的IP關聯(lián)起來檐晕,通過這個關聯(lián)讓Pod可以互相訪問雕欺。
根據(jù)條件1的要求,我們需要在部署Kubernetes時對docker0的IP地址進行規(guī)劃,保證每個Node上的docker0地址都沒有沖突屠列。我們可以在規(guī)劃后手工配置到每個Node上啦逆,或者做一個分配規(guī)則,由安裝的程序自己去分配占用笛洛。例如夏志,Kubernetes的網(wǎng)絡增強開源軟件Flannel就能夠管理資源池的分配。
根據(jù)條件2的要求苛让,Pod中的數(shù)據(jù)在發(fā)出時沟蔑,需要有一個機制能夠知道對方Pod的IP地址掛在哪個具體的Node上。也就是說先要找到Node對應宿主機的IP地址狱杰,將數(shù)據(jù)發(fā)送到這個宿主機的網(wǎng)卡瘦材,然后在宿主機上將相應的數(shù)據(jù)轉發(fā)到具體的docker0上。一旦數(shù)據(jù)到達宿主機Node仿畸,則那個Node內部的docker0便知道如何將數(shù)據(jù)發(fā)送到Pod食棕。如下圖所示:
? 在上圖中:IP1對應的是Pod1,IP2對應的是Pod2错沽。Pod1在訪問Pod2時簿晓,首先要將數(shù)據(jù)從源Node的eth0發(fā)送出去,找到并到達Node2的eth0千埃。即先是從IP3到IP4的遞送憔儿,之后才是從IP4到IP2的遞送。
在谷歌的GCE環(huán)境中放可,Pod的IP管理(類似docker0)谒臼、分配及它們之間的路由打通都是由GCE完成的。Kubernetes作為主要在GCE上面運行的框架耀里,它的設計是假設底層已經(jīng)具備這些條件屋休,所以它分配完地址并將地址記錄下來就完成了它的工作。在實際的GCE環(huán)境中备韧,GCE的網(wǎng)絡組件會讀取這些信息劫樟,實現(xiàn)具體的網(wǎng)絡打通。
而在實際生產(chǎn)環(huán)境中织堂,因為安全叠艳、費用、合規(guī)等種種原因易阳,Kubernetes的客戶不可能全部使用谷歌的GCE環(huán)境附较,所以在實際的私有云環(huán)境中,除了需要部署Kubernetes和Docker潦俺,還需要額外的網(wǎng)絡配置拒课,甚至通過一些軟件來實現(xiàn)Kubernetes對網(wǎng)絡的要求徐勃。做到這些后,Pod和Pod之間才能無差別地進行透明通信早像。
為了達到這個目的僻肖,開源界有不少應用增強了Kubernetes、Docker的網(wǎng)絡卢鹦,在后面的章節(jié)中會介紹幾個常用的組件及其組網(wǎng)原理臀脏。