Kubernetes入門(三)
我並沒有按原文一字一句的翻譯,對於理解這些知識點沒有幫助的語句我一般都忽略了。
我的測試環境和筆者不同,我使用的是單臺物理機,用Vagrant模擬多臺VM Host,VM Host使用和物理機相同網段的public網路,作業系統是CentOS 7.0,Docker版本1.6.2。Kubernetes網路的要求是不同主機上的容器間要能夠互通。
本文使用了MLS來打通不同機器Docker私網的通訊。我嘗試用家用路由器的靜態路由完成,但是因為Vagrant網路設定原因,一直沒有成功。我還在分析,叢當前情況看是Vagrant每個Vagrant虛擬機器有兩個網路介面,其中一個管理介面每臺機器的IP是一樣的,我估計這個產生了干擾,因為我抓的不同機器的IP包都轉換成為了物理機器的IP地址而不是各自虛擬機器的IP地址。所以在我的實驗中,我使用了Flannel來打通不同主機間容器網路的連線問題。這個對很多場景是更好的選擇,很多情況下你很難在為新增一個Kubernetes的主機就在交換機上去增加一條路由。而且牽涉到虛擬機器和虛擬網路時,難定位的問題會更多。
我對Docker和其相關技術如此感興趣的一個原因是因為隨之而來的新的網路模式。Kubernetes有一個獨特的(並且相當棒)方式來處理這些網路挑戰,但是他第一眼看上去有些難以理解。我這個帖子目的是帶你通過部署幾個Kubernetes的construct來分析Kubernetes在網路層做了什麼來達成這點。不過讓我們從部署一個Pod的基礎開始。我們將使用第一個帖子(Kubernetes入門(一) - 構建)構建的實驗環境和在第二個帖子(Kubernetes入門(二) - Constructs)建立的一些配置檔案例子。
注意:我必須在這裡指出這個實驗環境是基於裸機硬體的。在這個型別的實驗環境的網路模型和你見到的雲提供商提供的相比有些許不同。儘管如此,Kubernetes在網路層面所做的事的背後的機制是一樣的。
我們的實驗環境是這樣的:
之前我們提到了Pod IP地址的話題,讓我們提供一些背景資訊,對情況有一些基本的瞭解。Kubernetes的網路模型決定了Kubernetes的每個節點可以被路由。回憶一下docker網路模型,他提供了一個IP地址段為172.17.0.0/16的docker0網橋。每個容器可以從這個子網獲得IP地址並用docker0網橋的IP(172.17.42.1)做為其預設閘道器。值得注意的是網路不需要知道172.17.0.0/16如何到達,因為docker主機為從容器發出的流量在真實的NIC的IP地址背後做了一個IP masquerade(或者hide NAT)。這就是說網路將任何從容器來的流量看做是從主機物理IP地址發出的。
注意:我們在這個帖子裡面使用網路總是指連線主機的物理網路。
Docker的網路模型對使用方便是有意義的,但並不理想。這個模型需要對各種埠進行對映並且一般會限制Docker主機的能力。在Kubernetes模型中,每個Docker主機的docker0網橋都是可以路由的。 那就是說,當一個Pod部署後,叢集其他主機能夠不在物理主機上做埠對映就可以直接訪問Pod。有了這種說法,從網路觀點來看,你可以將Kubernetes節點當做路由器。我們將我們的實驗環境圖變為一個網路圖,他看起來就像這樣。
多層交換機(MLS)有兩個3層分段。一個支援10.20.30.0/24網路,另一個支援192.168.10.0/24網路。此外上面還有路由告訴如何到達每個掛在路由器(Kubernetes節點)上的子網。這意味著任何節點上產生的容器會使用節點(docker0網橋IP)做為他們的預設閘道器,接著節點使用MLS做為他的預設閘道器。我有點和這個概念糾纏,不過他非常重要。網路管理員喜歡邊緣三層網路。
現在讓我們轉到一些例子來看在不同情況下Kubernetes在網路側是如何做的...
部署一個Pod
譯者注: 帖子是筆者2015年初寫的,所以這個construct的語法已經過時了,在我測試使用的比較新的v0.19.0版本上已經不能執行,我重新用最新的construct語法重寫了一下:
讓我們假設我們在一個空白的master上進行工作。我清理了上個帖子用到的所有Replication controller、Pods和Service...
讓我們找一個節點檢查一下這個點的執行情況,現在讓我們選擇kubminion1。
現在沒有任何容器在執行,我只想指出網路配置符合預期。我們有一個docker0網橋介面和minions本地IP介面。讓我們回到master,部署我們上面配置的pod,看看會發生什麼...
一些有趣的事情發生了,Kubernetes指派10.20.30.62(kubminion1)這臺主機執行Pod。注意Pod也分配了一個IP地址,這個地址正位於kubmini1的docker0網橋。讓我們拎出kubminion1來看看發生了什麼。
kuminion1現在運行了3個容器。我們的Pod規格里只定義了2個,那麼第三個從哪裡來的?第3個執行的容器映象叫"kubernetes/pause:go"。注意到這個容器有所有埠對映。為什麼會這樣?我們深入觀察一下容器看看為什麼會這樣。我們使用Docker的"inspect"命令看看每個容器的一些資訊。也就是說看每個部署的容器的網路模式。
譯者注:現在這個映象預設是放在GCE的映象倉庫裡了,gcr.io/google_containers/puase:0.8.0,不幸的是Google的伺服器都被牆了,要麼翻牆,要麼將這個映象重新指向到Docker公共倉庫。這個映象在公共倉庫裡就是kubernetes/puase:0.8.0。在kubelet的啟動引數裡面指定:
有趣,如果我們檢查每個容器的網路模式,我們可以看到一個非常有趣的配置。第一個執行"kubernetes/pause:go"的容器使用的是預設的網路模式。我們觀察的第二和第三個容器是在我們pod中定義的執行"web_container_80"和"web_container_8080"映象的容器。注意每個pod容器都有一個非預設網路配置。特別是每個pod容器使用mapped container mode並指定執行"Kubernetes/pause:go"映象的容器為目標容器。
譯者注:mapped container mode參見Docker網路入門(三) - 映象容器
讓我們想一想,為什麼他們這樣做?首先,所有在一個pod內的容器需要共享相同的IP地址。這使得映象容器模式必不可少。既然如此,為什麼他們不啟動第一個pod容器,並將第二個pod容器連線到第一個?我想這個問題的答案有兩個方面。第一,連線一個有兩個以上容器的pod很痛苦(譯者注:這句話有些不太清楚,我覺得作者可能的意思是一個pod有多個容器,你不知道哪個是第一個容器,有一個pause容器就確定了)。其次,你對被連線的第一個容器產生了依賴。如果第二個容器連線到第一個容器並且第一個容器掛掉,第二個容器的網路棧也掛了。執行一個基礎容器,將其他的pod容器連線到這個容器更加容易(也更加聰明)。這也簡化了埠對映,我們只需要將埠對映規則應用到pause容器。
於是我們的pod網路圖看上去就像這樣...
所以真實網路對於pod IP流量的目的地就是這個pause容器。上面這個圖有一些欺騙性因為他將pause容器顯示為將80和8080埠轉發到相關容器。pause容器並沒有真的做這個轉發,這是邏輯上像這樣做了因為兩個容器直接監聽了兩個埠並與pause容器共享了相同的網路棧。這就是為什麼所以對真實容器的埠對映都顯示為在pause容器上的對映。我們通過
docker
port
命令檢查。所以pause容器真的只是持有pod的網路端點(Endpoint) ,沒有做任何其他事情。那麼主機做了什麼?他做了什麼事情將流量匯入到pause容器嗎?讓我們看一下iptables規則:
這裡有一些規則,但是沒有任何規則是作用於我們剛才定義的pod的。就像我在上一篇帖子提到的,在每一個Kubernetes節點上Kubernetes提供了一些預設服務。那就是我們在上面輸出中看到的。關鍵點是我們沒有看見任何masquerade規則或者任何為pod10.10.10.2做的入站埠對映。
部署一個服務
我們已經看到Kubernetes如何處理連線到他的最基本的構件,讓我討論一下他如何處理服務。就像我們在上個帖子討論的那樣,服務允許你抽象在pod裡託管的服務。此外服務允許你通過提供跨託管服務的多個Pod的負載均衡機制來提供服務的水平擴充套件能力。讓我再次將我們剛才建立的pod刪除重置實驗環境以確保從空白開始現在讓我們嘗試我們在上個帖子中定義的服務並再次對其審視。 這裡是我們稱為"myfirstservice“的服務配置檔案
為了讓事情解釋得更清晰,我們將服務做一些輕微的改變。
相同的配置,我們只是將容器埠變為了8080。讓我們在Kubernetes叢集定義這個服務。
譯者注: 帖子是筆者2015年初寫的,所以這個construct的語法已經過時了,在我測試使用的比較新的v0.19.0版本上已經不能執行,我重新用最新的construct語法重寫了一下:
注意:我不記得我以前是否提到過,但是服務必需在符合服務的selector的pod之前構建。這個能保證服務相關的變數能存在於容器中。
譯者注:Kubernetes容器訪問服務有兩種方式:
1.一種是通過環境變數訪問,服務的IP和埠在容器啟動時做為環境變數傳遞進入容器,容器的應用可以根據這些環境變數訪問服務,例如訪問redis或者mysql。這樣就要求服務先啟動,否則服務的相關資訊就沒法傳入Pod(因為此時服務還沒有建立,這些資訊都還沒有)。這是一種比較糟糕的方法,對順序有依賴且要求應用要適配環境變數。其中環境變數的規則是"<服務名稱(大寫)>PORT<服務埠>TCP_ADDR"這個環境變數的值是服務的IP,"<服務名稱(大寫)>_PORT<服務埠>_TCP_PORT"這個環境變數是服務埠。例如訪問上面的服務就用WEBFRONTEND_PORT_8080_TCP_ADDR和WEBFRONTEND_PORT_8080_TCP_ADDR兩個環境變數
2.另一種是通過dns訪問,這顯然是比較好的辦法,在後面的帖子中會提到如何在Kubernetes叢集中部署用於服務訪問的本地DNS。這樣服務直接就可以通過服務名訪問,由DNS將服務名轉變為服務的IP,不再需要依賴環境變數,只要依賴服務名稱即可。如: curl -L http://webfrontend
服務的建立符合預期。我們檢查可用的服務時會發現叢集給服務指定了一個IP地址。這個IP地址被分配到了Kubernetes所說的"Portal Netwokr”範圍內。如果你還記得,當我們在kubmasta上構建API服務時,我們定義的一個標識就是"PortalNet"
他可以是任意子網,只要不和物理主機或者docker0的子網重合即可。他可以是任意子網的原因是他永遠不會路由到網路上。Portal net只對本地節點有意義,他只是一個將流量從容器流出並導向預設閘道器(docker0網橋)的方法。在下一步動作之前,讓我們檢查一下kubminion1看看我們定義了服務定義後發生了什麼改變。我們從檢查netfilter規則開始:
這些規則做了什麼?第一行告訴主機匹配10.100.87.105:80的TCP流。如果他看到一個流匹配到這個規範,他會將流量重定向到本地的39770埠。第二個行告訴節點以不同的方式做同樣的事情,因為覆蓋的是從主機而不是容器產生的流量。這兩條規則不相同的原因是REDIRECT只對通過主機的流量生效(產生於容器,通過主機)。從主機產生的流量需要用DNAT規則進行處理。長話短說,他們用不同的方式完成同樣的事情,於是所有流出節點指向10.100.87.105:80的流量會被重定向到主機的39770埠
於是我們知道所有去向服務IP和埠的流量會重定向到本機的39770埠。但那又什麼效果呢?這是kubernetes-proxy服務開始起作用的地方。proxy服務為每個新建立的服務隨機分配了一個埠並建立了一個服務內建立一個負載均衡物件並監聽指定的埠。在這個場景裡,埠敲好是39770。如果當我們建立服務時我們查看了kubminion1上kubernetes-service的日誌(譯者注:感覺是kubernetes-proxy service),我們會看到像這樣的條目:
現在指向服務的流量被重定向到了proxy。為了完成負載均衡,我們啟動我們上個帖子的一個replication controller以便於我們檢視其執行情況。我將為我的replication controller用這個配置:
譯者注
新版的construct應該這樣寫:
讓我們將其載入到叢集並讓所有pod啟動
看上去不錯。現在我們讓所有的pod執行起來了,服務會選擇匹配標籤"web8080"的pod進行負載均衡。因為replication controller selector通過標籤"web8080"匹配所有的pod,我們將有4個pod用於負載均衡。在這一點上,我認為我們的實驗環境看起來像這樣:
Kubernetes proxy被描述為一種墊片,他只是執行在節點上的另外一個服務。我們上面看到的規則讓Kubernetes proxy成為了流量指向服務IP地址的墊片。
看看他的執行情況,我們使用tcpdump進行一系列包捕捉。 為了做這件事,我們需要在kubminion1上安裝tcpdump,我們使用這個命令進行安裝:
完成安裝後,我們開啟三個kubminion1的SSH會話。第一個視窗我們執行如下tcpdump指令
注意:在這個例子裡我們會捕捉物理介面的包。在我的例子裡,他叫"ens18" (譯者注:譯者使用的環境是通過flannel來打通跨主機容器網路的連線,所以是對flannel0網橋進行抓包)
在第二個視窗我們執行另外一個tcpdump,但是我們需要首先獲得一些資訊。換句話說我們需要獲得容器連線到docker0網橋的虛擬介面的名稱。假設你在主機上運行了一個webpod容器,你可以完成簡單的"ifconfig",你將只有一個"Veth"介面。
拷貝介面的名字,在你的第二個視窗將其拷貝到tcpdump命令。
同時執行兩個命令,將視窗疊在一起,這樣你可以同時檢視。
你同時運行了兩個抓包後,讓我們將注意力轉移到第三個視窗。讓我們使用"docker exec"命令附著到“web_container_8080”容器(首先使用“docker ps”命令獲取容器的名稱)
在進入執行容器後我們嘗試通過curl命令訪問服務
當我們第一次訪問服務,我們在窗口裡會看到:
這說明了什麼?讓我們在我們的網路圖上畫出來,上面視窗的抓包(通過物理NIC的流量)用紅色顯示,下面視窗抓包(通過docker0網橋)用藍色顯示:
注意:我在畫kubminio3上的線的時候繞過了Kubernetes proxy。 我這樣做是因為在kubminion3上的Kubernetes proxy並沒有對這個流起作用。換句話說,proxy service攔截了服務請求直接傳送到他負載均衡的pod。
讓我們先看下方的視窗,我們看到的是從容器角度出發的流量。容器企圖建立一個10.100.87.105:80的TCP連線。我們看到叢服務的IP地址10.100.87.105返回的流量。叢容器的角度來看,整個通訊是與服務進行的。如果我們看第二個視窗(上面那個)我們能看到實際發生了什麼。我們看到叢節點的實體地址(10.20.30.62)發出的到託管在kubminion3(10.10.30.4)上的pod的TCP會話。總結一下,Kubernetes proxy服務做為一個全代理維護了兩個不同的連線。第一個是叢container到proxy的,第二個是叢proxy到負載均衡目的地的。
如果我們清理我們的抓包資訊並再次執行curl,我們會看到流量負載均衡到另一個節點。
在這個例子裡,Kubernetes proxy決定將流量流到執行在kubminion2的pod。我們的網路流圖看起來像這樣。
我想你不需要我再展示其他兩種負載均衡我們測試服務的可能的結果了。關於理解服務很重要的一點是服務能讓我們方便的擴充套件服務的pod。 我們可以看到結合使用replication controller一起,這會是一個強大的特性。
但是服務處理了Kubernetes叢集一個重要方面的同時,他們卻只對pod訪問服務有意義。回憶一下,portal IP的空間不能叢外部網路訪問,他只對於本地主機有意義。那麼叢集之外如何訪問部署在叢集中的應用呢?我們在下一個帖子中說明。
相關推薦
Kubernetes入門(三)
這是我在國外的部落格上看到的一系列關於Docker和Kubernetes網路分析的文章,感覺描述得比較清晰,便於剛接觸Docker和Kubernetes的朋友瞭解相關的知識。在看的同時順便就翻譯了,方便和網友一起交流探討。但我不僅僅是翻譯,還按照文章中描述的進行了實際測試,對於測試中出現的問題和由於環境不同
Storm入門(三)HelloWorld示例
right 出現 9.png context color tro order tput 執行 一、關聯代碼 使用maven,代碼如下。 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:x
java加密算法入門(三)-非對稱加密詳解
共享數據 net clas 實例 查看 安全性 自己的 generator mir 1、簡單介紹 這幾天一直在看非對稱的加密,相比之前的兩篇內容,這次看了兩倍多的時間還雲裏霧裏的,所以這篇文章相對之前的兩篇,概念性的東西多了些,另外是代碼的每一步我都做了介紹,方便自己以後
Linux入門(三)
ls cat hwclock cd date echo Linux常用命令: Linux文件系統: 1.文件名名稱嚴格區分大小寫 2.文件可以使用除/意外的任意字符;不建議使用特殊字符 3.文件名長度不能超過255個字符 4. 以. 開頭的文件為隱藏文件 工作目錄:workin
Bootstrap入門(三):Less
樣式 ttr edi local 編輯 修改文件 方便 code b2c 很多時候我們需要定制Bootstrap的樣式,然後根制入門初步中,每次都定制網頁(http://v3.bootcss.com/customize/)生成我們需要的Css是一件很麻煩又不方便的事件。幸好
leaflet入門(三)使用GeoJSON創建矢量圖形
onf ack type play coo bus blog content roc 點對象: function g(feature, layer) { // does this feature have a property named popu
Spring入門(三)之IoC
使用 bsp martin 需要 容器 nbsp 依賴註入 tin 這就是 一、IoC定義 IoC,即控制反轉。開發者在使用類的實例之前,需要先創建對象的實例。但是IoC將創建實例的任務交給IoC容器,這樣開發應用代碼時只需要直接使用類的實例,這就是IoC。在討論控制反
吉他入門(三)
入門 strong 一個 nbsp 吉他 音符 str bsp ron 節奏1: | 0 0 0 0| 每個0代表四分音符,以四分音符為一拍每小節一拍 節奏2 | _0_ _0_ _0_ _0_ _0_. _
HTML入門(三)後臺系統顯示頁面_框架標簽
row http head span 技術分享 target html top logs <!DOCTYPE html> <html> <head> <meta charset="UTF-8">
kubernetes入門(06)kubernetes的helloworld(Kubernetes 201-2)
作者 聯邦 cnblogs ges -1 logs .cn 參考 ber 前言 本文是讀書筆記,具體可參考 倪朋飛 先生的原文《kubernetes指南》,多謝原作者,致敬! kubernetes 集群 集群聯邦 創建kubernetes集群 kubernete
react 入門(三)
復選框 sta 執行 ava ... 初始 targe 對象 mount <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <
ZooKeeper入門(三) ZooKeeper數據模型
每次 con ges 可用 同文件 2.3 per 而是 創建時間 1 簡述 ZooKeeper可以看成一種高可用性的文件系統,但是,它沒有文件和目錄,而是使用節點,稱為znode。 znode可以作為保存數據的容器(如同文件),也可以作為保存其他節點的容器(如同目錄)。
03-Linux基礎入門(三)-系統的基礎優化[對於使用虛擬機學習的學習者]
技術分享 ifcfg-eth 特殊 ... selinux 排查 運行 版本 get 1、關閉SELinux功能①SELinux配置文件路徑 /etc/selinux/config ②查看SELinux狀態 getenforce ③關閉SELinux 方法一:常規方法→修
Spring入門(三)— AOP註解、jdbc模板、事務
list() 規範 行數 get attribute 樂觀鎖 過濾 callback 賬號 一、AOP註解開發 導入jar包 aop聯盟包、 aspectJ實現包 、 spring-aop-xxx.jar 、 spring-aspect-xxx.jar 導入約束 a
WPF自學入門(三)WPF路由事件之內置路由事件
順序 初學者 rgs 理念 技術 設計 行處理 再處理 之前 有沒有想過在.NET中已經有了事件機制,為什麽在WPF中不直接使用.NET事件要加入路由事件來取代事件呢?最直觀的原因就是典型的WPF應用程序使用很多元素關聯和組合起來,是否還記得在WPF自學入門(
PHP基礎入門(三)
pos public enc 靜態變量 類型 對象共享 cname 創建 不用 PHP的面向對象:聲明類: 訪問權限關鍵字 class 類名{成員屬性: 訪問權限關鍵字 $屬性名;成員方法: 訪問權限關鍵字 function 方法名(){}構造方法:function __c
python3入門(三)字典的使用
python3 python3字典 python的常見字典用{}花括號來表示dict1 = {key1 : value1, key2 : value2 }字典的每個鍵值key:value對應冒號分割,每個鍵值用逗號分割字典的定義方法,比如a={name:huang}print aname就是key,h
python入門(三)判斷語句
邏輯判斷 if lse while python中的常用判斷語句if....elif....else,whileifif的用法: if + 條件判斷: 邏輯操作..... 例子:比如讓你輸入一個數字,來判斷這個數字的大小 #如果這個數字大於80 if 90>80:
性能測試入門(三):性能測試工具
需求 pre tor 不足 廠商 ecdh 腳本編寫 ssi 3.1 這篇文章介紹下性能測試工具: 簡單模擬工具 有很多場景下,我們只是想做一個簡單的壓測,對於監測結果要求並不高,壓測的場景也比較簡單,不想安裝復雜的工具,這種情況下,推薦使用簡單的模擬工具進行就可以了。 a
rabbitmq+java入門(三)exchange的使用
int none throw receive spa 必須 消息 tostring message 參考:http://www.rabbitmq.com/tutorials/tutorial-three-java.html 先決條件 本教程假定RabbitMQ 在標準端口