首頁>技術>

概念

Kubernetes Service是一種資源物件,我們可以利用它對一組提供相同服務的pod建立一個統一的、固定的訪問入口。每個service在存續期間都有一個不會改變的IP地址和埠。客戶端可以與這個IP和埠建立連線,然後這些連線會被路由到這個service後面的其中一個pod上。透過這種方式,service的客戶端就不需要知道提供該服務的每個pod的地址,這樣這些pod在叢集中就可以隨時被建立或者移除。

假定我們現在有一個前端的web伺服器和一個後端的資料庫伺服器。可以有多個pod充當前端伺服器,但是隻能有一個pod充當後端資料庫。要想這樣的一個系統正常運轉,我們需要解決如下兩個問題:

外部客戶端需要連線到前端web伺服器的pod,不需要關心web伺服器的數量前端web伺服器pod需要連線到後端資料庫。由於資料庫執行在一個pod中,隨著時間的推移,它可能在叢集中不斷地變換所在的節點,導致IP地址不斷變化。我們肯定不希望資料庫伺服器被移動後就重新配置前端pod。

透過為前端pod建立一個service以及將其配置成從叢集外部可訪問,我們可以對外暴露一個單一的、不變的IP地址,外部客戶端可以透過這個IP地址連線到pod。同樣地,透過為後端pod建立一個service,我們可以為後端pod建立一個固定的地址。即使pod的IP地址變了,service的地址也不會變化。除此之外,透過建立service,前端pod能夠輕易地透過環境變數或者DNS根據名稱找到後端服務。如下圖顯示了兩個service、支援這些service的兩組pod以及它們之間的相互依賴關係:

上圖中,內部和外部客戶端透過service連線到pod。

建立service

服務的後端可以不止一個pod。連線到該服務的請求可以透過負載均衡的方式分發到服務後面的pod上。但是要如何準確地定義哪些pod是服務的一部分哪些不是呢?

在之前的章節中我們學習過標籤選擇器,知道怎樣在ReplicationController以及其他pod控制器中指定哪些pod屬於相同的集合。service也使用與此相同的機制,如下圖:

標籤選擇器用於決定哪些pod屬於相同的服務。

在之前的章節中,我們建立了一個ReplicationController,它會執行包含Node.js應用的三個pod例項。本節中我們再次建立這個ReplicationController,然後驗證是否有三個pod例項啟動並執行。在此之後,我們再為這三個pod建立一個service。

我們透過YAML檔案來手動建立一個service。

vim test-svc.yaml
apiVersion: v1kind: Servicemetadata: name: test-svcspec: ports: - port: 80   targetPort: 8080 selector:   app: test1

上面的檔案定義了一個名為test-svc的service,它會在埠80上接收連線,並將每個連線路由到匹配標籤選擇器app=test1的所有pod中的某個pod的8080埠。

然後執行:

kubectl create -f test-svc.yaml

從上圖可以看到分配給test-svc服務的IP地址是10.106.143.212。由於這是一個叢集IP,所以只能在叢集內部訪問。服務的主要目的是將一組pod暴露給叢集中的其他pod,但是我們通常也希望將服務暴露給外部,這點在後面會講到。

從叢集內部訪問服務

現在我們先從叢集內部來使用剛剛建立的服務並瞭解服務的功能。

可以透過如下幾種方式從叢集內部向服務傳送請求:

建立一個pod,它將請求傳送到服務的叢集IP並記錄響應。然後可以透過pod的日誌來檢視服務的具體響應資訊。可以透過ssh遠端登入到其中一個Kubernetes節點上,然後使用curl命令。可以在某個已經存在的pod中透過kubectl exce命令來執行curl命令。

我們採用第三種方式並演示如何在已有的pod中執行命令。

kubectl exec命令使我們能夠在已有pod的容器中遠端執行任意命令。這非常有助於我們檢視容器的內容、狀態以及環境。

從現有的pod中選取某一個pod:

kubectl exec test-rc-8n2b8 -- curl -s http://10.106.143.212

上面命令中的(--)代表kubectl命令選項的結束。雙破折號之後的所有命令都應該在pod中執行。如果命令中沒有使用單破折號(-)指定引數,那麼就可以不用指定雙破折號,但是在上面的例子中,如果我們不使用折號,-s 選項就會被解釋為kubectl exec命令的選項:

kubectl exec test-rc-8n2b8 curl -s http://10.106.143.212

kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

在上面的例子中,我們以獨立程序的方式執行curl命令,但是是在pod的主容器中。這與容器中真正的主程序和服務通訊並沒有什麼區別。

配置會話繫結(Session Affinity)

vim test-svc2.yaml
apiVersion: v1kind: Servicemetadata: name: test-svc1spec: sessionAffinity: ClientIP ports: - port: 80   targetPort: 8080 selector:   app: test1
kubectl create -f test-svc2.yaml

基於該YAML檔案建立的服務代理會將來自於同一個客戶端IP的所有請求重定向到同一個pod。

kubectl exec test-rc-8n2b8 -- curl -s http://10.107.168.13

透過上圖可以看到,反覆呼叫5次,每次請求都指向同一個pod。

Kubernetes僅支援兩種型別的服務會話繫結:None和ClientIP。可能你會驚訝它居然沒有基於cookie的會話繫結選項,不過我們需要知道Kubernetes服務並不是在HTTP層面上工作。服務處理TCP和UDP包,並不關心它的載荷內容。由於cookie是HTTP協議中的一種構造,服務並不知道它們,這就解釋了為什麼不能基於cookie進行會話繫結。

一個服務暴露多個埠

Kubernetes中service支援多個埠。例如,如果pod監聽兩個埠:比如HTTP監聽8080埠,HTTPS監聽8443埠,我們可以使用一個服務將80和443埠分別轉發到pod的8080和8443埠,完全沒必要建立兩個不同的服務。透過一個叢集IP,使用一個多埠的服務就能將服務的所有埠全部暴露出來。

需要注意的是,當建立多埠服務時,必須為每個埠指定一個名字。

多埠服務的YAML定義如下:

apiVersion: v1kind: Servicemetadata: name: test-svcspec: ports: - name: http   port: 80   targetPort: 8080 - name: https   port: 443   targetPort: 8443 selector:   app: test1

使用命名埠

在之前的例子中我們都是透過埠號來引用埠本身的,還可以為每個pod的埠取一個名字並在service的spec區段中透過名稱來引用埠。這對於不常用的埠號來說就比較容易區分。

例如,假設我們為pod的每個埠定義了一個名稱,如下所示:

apiVersion: v1kind: Podspec: containers: - name: test1   ports:   - name: http     containersPort: 8080   - name: https     containersPort: 8443

然後在服務的定義檔案中就可以透過名稱引用埠:

apiVersion: v1kind: Servicespec:ports:- name: http  port: 80  targetPort: http- name: https  port: 443  targetPort: https

使用命名埠最大的好處就是即使更改了pod中的埠號也不用修改服務的spec。如果pod當前將8080用於http,但是如果後面決定將埠號換成80呢?

9
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • C語言 | 輸出4*5的矩陣