Kubernetes 網路入門
Service 是 k8s 網路部分的核心概念,在 k8s 中,Service 主要擔任了四層負載均衡的職責。本文從負載均衡、外網訪問、DNS 服務的搭建及 Ingress 七層路由機制等方面,講解 k8s 的網路相關原理。
Service 詳解
Service 是主要用來實現應用程式對外提供服務的機制。
如上圖所示,Service 是對 Pod 的一層抽象,主要通過 TCP/IP 機制及監聽 IP 和埠號來對外提供服務。與 Pod 不同的是,Service 一旦建立,系統會為其分發一個 ClusterIP (也可以自己指定),且在其生命週期內不會發生變化。
Service 的建立
命令列快速建立
在建立好 RC 後,可以通過命令列 kubectl expose
來快速建立一個對應的 Service 。比如現已有一個名為 hdls 的 rc:
這種方式創建出來的 Service,其 ClusterIP 是系統自動為其分配的,而 Service 的埠號是從 Pod 中的 containerPort 複製而來。
通過 YAML 建立
定義好 YAML 檔案後,通過命令 kubectl create-f<service.yml>
即可建立。Service 的定義需要指定以下幾個關鍵欄位:
-
ports
-
port: Service 的虛擬埠
-
targetPort: 後端 Pod 的埠號,若不填則預設與 Service 的埠一致
-
selector: Label 選擇 器,指定後端 Pod 所擁有的 Label
負載分發策略
k8s 提供了兩種負載分發策略:
-
RoundRobin:輪詢方式。即輪詢將請求轉發到後端的各個 Pod 上。
-
SessionAffinity:基於客戶端 IP 地址進行會話保持模式。即相同 IP 的客戶端發起的請求被轉發到相同的 Pod 上。
在預設情況下,k8s 採用輪詢模式進行路由選擇,但我們也可以通過將 service.spec.SessionAffinity 設定為 “ClusterIP” 來啟用 SessionAffinity 模式。
一些特殊情況
開發人員需要自己控制負載均衡策略的情況
在這種情況下,k8s 通過 Headless Service 的概念來實現,即不給 Service 設定 ClusterIP (無入口 IP),僅通過 Label Selector 將後端的 Pod 列表返回給呼叫的客戶端。
該 Service 沒有虛擬的 ClusterIP ,對其訪問可以獲得所有具有 app=hdls
的 Pod 列表,客戶端需要實現自己的負責均衡策略,再確定具體訪問哪一個 Pod。
需要將某些服務作為後端服務
一般來說,應用系統需要將外部資料庫作為後端服務進行連線,或另一個叢集或 namespace 中的服務作為後端服務。這些情況,可以通過建立一個無 Label Selector 的 Service 來實現:
該 Service 沒有標籤選擇器,即無法選擇後端 Pod。這時系統不會自動建立 Endpoint,需要手動建立一個與該 Service 同名的 Endpoint,用於指向實際的後端訪問地址。
此時,如上面的 YAML 創建出來 Endpoint,訪問無 Label Selector 的 Service ,即可將請求路由到使用者指定的 Endpoint 上。
多埠的 Service
在 service.spec.ports 中定義多個 port 即可,包括指定 port 的名字和協議。
外網訪問
Pod 和 Service 都是 k8s 叢集內部的虛擬概念,所以叢集外的客戶無法訪問。但在某些特殊條件下,我們需要外網可以訪問 Pod 或 Service,這時我們需要將 Pod 或 Service 的埠號對映到宿主機,這樣客戶就可以通過物理機訪問容器應用。
外網訪問 Pod
將容器應用的埠號對映到物理機上。有兩種方式,如下。
設定容器級別的 hostPort
這種是將容器應用的埠號對映到物理機。設定如下:
hostNetwork
這種是將該 Pod 中所有容器埠號都直接對映到物理機上。此時需要注意的是,在容器的 ports 定義部分,若不指定 hostPort,預設 hostPort=containerPort,若設定了 hostPort,則 hostPort 必須等於 containerPort。設定如下:
外網訪問
對於外網訪問 Service 也有兩種方式。
設定 nodePort 對映到物理機
首先需要設定 nodePort 對映到物理機,同時需要設定 Service 的型別為 NodePort:
設定 LoadBalancer 對映到雲服務商提供的 LoadBalancer 地址
這種用法僅用於在公有云服務提供商的雲平臺上設定 Service 的場景。需要將 service.status.loadBalancer.ingress.ip 設定為雲服務商提供的負載均衡器的 IP。則對該 Service 的訪問請求將會通過 LoadBalancer 轉發到後端 Pod,且負載均衡的實現方式依賴於雲服務商提供的 LoadBalancer 的實現機制。
DNS 搭建
為了能夠實現通過服務名在叢集內部進行服務的相互訪問,需要建立一個虛擬的 DNS 服務來完成服務名到 ClusterIP 的解析。
k8s 提供的 DNS
k8s 提供的 DNS 服務名為 skydns,由下面四個元件組成:
-
etcd: DNS 儲存;
-
kube2sky: 將 k8s Master 中的 Service 註冊到 etcd ;
-
skyDNS: DNS 域名解析服務;
-
healthz: 對 skyDNS 的健康檢查。
skyDNS 服務由一個 RC 和一個 Service 組成。在 RC 的配置檔案中,需要定義 etcd / kube2sky / skydns / healthz 四個容器,以保證 DNS 服務正常工作。需要注意的是:
-
kube2sky 容器需要訪問 k8s Master,所以需要在配置檔案中為其配置 Master 所在物理主機的 IP 地址和埠;
-
需要將 kube2sky 和 skydns 容器的啟動引數
--domain
設定為 k8s 叢集中 Service 所屬域名。容器啟動後 kube2sky 會通過 API Server 監控叢集中所有 service 的定義,生成相應的記錄並儲存到 etcd ; -
skydns 的啟動引數
-addr=<IP:Port>
表示本機 TCP 和 UDP 的 Port 埠提供服務。
在 DNS Service 的配置檔案中,skyDNS 的 ClusterIP 需要我們指定,每個 Node 的 kubelet 都會使用這個 IP 地址,不會通過系統自動分配;另外,這個 IP 需要在 kube-apiserver 啟動引數 --service-cluster-ip-range
內。
在 skydns 容器建立之前,需要先修改每個 Node 上 kubelet 的啟動引數:
-
--cluster dns=
,dns cluster_ip 為 DNS 服務的 ClusterIP ; -
--cluster domain=
, dns domain 為 DNS 服務中設定的域名。
DNS 工作原理
-
首先 kube2sky 容器應用通過呼叫 k8s Master 的 API 獲得叢集中所有 Service 資訊,並持續監控新 Service 的生成,寫入 etcd;
-
根據 kubelet 的啟動引數的設定,kubelet 會在每個新建立的 Pod 中設定 DNS 域名解析配置檔案
/etc/resolv.conf
中增加一條 nameserver 配置和 search 配置,通過 nameserver 訪問的實際上就是 skydns 在對應埠上提供的 DNS 解析服務; -
最後,應用程式就可以像訪問網站域名一樣,僅通過服務的名字就能訪問服務了。
Ingress
Service 工作在 TCP/IP 層,而 Ingress 將不同的 URL 訪問請求轉發到後端不同的 Service ,實現 HTTP 層的業務路由機制。而在 k8s 中,需要結合 Ingress 和 Ingress Controller ,才能形成完整的 HTTP 負載均衡。
Ingress Controller
Ingress Controller 用來實現為所有後端 Service 提供一個統一的入口,需要實現基於不同 HTTP URL 向後轉發的負載分發規則。Ingress Controller 以 Pod 的形式執行,需要實現的邏輯:
-
監聽 APIServer,獲取所有 Ingress 定義;
-
基於 Ingress 的定義,生成 Nginx 所需的配置檔案
/etc/nginx/nginx.conf
; -
執行
nginx-s reload
,重新載入 nginx.conf 配置檔案的內容。
定義 Ingress
k8s 中有一種單獨的名為 Ingress 的資源,在其配置檔案中可以設定到後端 Service 的轉發規則。比如,為 hdls.me 定義一個 ingress.yml:
最後採用 kubectl create-f ingress.yml
建立 Ingress。可以登入 nginx-ingress Pod 檢視其自動生成的 nginx.conf 配置檔案內容。
對 Docker 和 K8S 感興趣?不如加入我們!