Service 是 k8s 網絡部分的核心概念,在 k8s 中,Service 主要擔任了四層負載均衡的職責。本文從負載均衡、外網通路、DNS 服務的搭建及 Ingress 七層路由機制等方面,講解 k8s 的網絡相關原理。
1.Service 詳解
Service 是主要用來實作應用程式對外提供 服務的機制。
如上圖所示,Service 是對 Pod 的一層抽象,主要通過 TCP/IP 機制及監聽 IP 和端口号來對外提供服務。與 Pod 不同的是,Service 一旦建立,系統會為其分發一個 ClusterIP (也可以自己指定),且在其生命周期内不會發生變化。
Service 的建立
指令行快速建立
在建立好 RC 後,可以通過指令行
kubectl expose
來快速建立一個對應的 Service 。比如現已有一個名為 hdls 的 rc:
kubectl expose rc hdls
這種方式建立出來的 Service,其 ClusterIP 是系統自動為其配置設定的,而 Service 的端口号是從 Pod 中的 containerPort 複制而來。
通過 YAML 建立
apiVersion: v1
kind: Service
metadata:
name: hdls
spec:
ports:
- port: 8080 # Service 的虛拟端口
targetPort: 8000 # 指定後端 Pod 的端口号
selector: # Label 選擇器
app: hdls
定義好 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 模式。
Notes:一些特殊情況
1.開發人員需要自己控制負載均衡政策的情況
在這種情況下,k8s 通過 Headless Service 的概念來實作,即不給 Service 設定 ClusterIP (無入口 IP),僅通過 Label Selector 将後端的 Pod 清單傳回給調用的用戶端。
apiVersion: v1
kind: Service
metadata:
name: hdls
spec:
ports:
- port: 8080
targetPort: 8000
clusterIP: None
selector:
app: hdls
該 Service 沒有虛拟的 ClusterIP ,對其通路可以獲得所有具有
app=hdls
的 Pod 清單,用戶端需要實作自己的負責均衡政策,再确定具體通路哪一個 Pod。
2.需要将某些服務作為後端服務
一般來說,應用系統需要将外部資料庫作為後端服務進行連接配接,或另一個叢集或 namespace 中的服務作為後端服務。這些情況,可以通過建立一個無 Label Selector 的 Service 來實作:
apiVersion: v1
kind: Service
metadata:
name: hdls
spec:
ports:
- port: 8080
targetPort: 8000
該 Service 沒有标簽選擇器,即無法選擇後端 Pod。這時系統不會自動建立 Endpoint,需要手動建立一個與該 Service 同名的 Endpoint,用于指向實際的後端通路位址。
apiVersion: v1
kind: Endpoints
metadata:
name: hdls # 與 Service 同名
subsets:
- addresss:
- IP: 1.2.3.4 # 使用者指定的 IP
ports:
- port: 8000
此時,如上面的 YAML 建立出來 Endpoint,通路無 Label Selector 的 Service ,即可将請求路由到使用者指定的 Endpoint 上。
3.多端口的 Service
在 service.spec.ports 中定義多個 port 即可,包括指定 port 的名字和協定。
apiVersion: v1
kind: Service
metadata:
name: hdls
spec:
ports:
- name: dns
port: 8080
protocol: TCP
- name: dns-tcp
port: 8080
protocol: UDP
selector:
app: hdls
2.外網通路
Pod 和 Service 都是 k8s 叢集内部的虛拟概念,是以叢集外的客戶無法通路。但在某些特殊條件下,我們需要外網可以通路 Pod 或 Service,這時我們需要将 Pod 或 Service 的端口号映射到主控端,這樣客戶就可以通過實體機通路容器應用。
外網通路 Pod
将容器應用的端口号映射到實體機上。有兩種方式,如下。
設定容器級别的 hostPort
這種是将容器應用的端口号映射到實體機。設定如下:
apiVersion: v1
kind: Pod
metadata:
name: hdls-pod
spec:
containers:
- name: hdls-container
image: ***
ports:
- containerPort: 8000
hostPort: 8000
設定 Pod 級别的 hostNetwork=true
這種是将該 Pod 中所有容器端口号都直接映射到實體機上。此時需要注意的是,在容器的 ports 定義部分,若不指定 hostPort,預設 hostPort=containerPort,若設定了 hostPort,則 hostPort 必須等于 containerPort。設定如下:
apiVersion: v1
kind: Pod
metadata:
name: hdls-pod
spec:
hostNetwork: true
containers:
- name: hdls-container
image: ***
ports:
- containerPort: 8000
外網通路 Service
對于外網通路 Service 也有兩種方式。
1.設定 nodePort 映射到實體機
首先需要設定 nodePort 映射到實體機,同時需要設定 Service 的類型為 NodePort:
apiVersion: v1
kind: Service
metadata:
name: hdls
spec:
type: NodePort # 指定類型為 NodePort
ports:
- port: 8080
targetPort: 8000
nodePort: 8000 # 指定 nodePort
selector:
app: hdls
2.設定 LoadBalancer 映射到雲服務商提供的 LoadBalancer 位址
這種用法僅用于在公有雲服務提供商的雲平台上設定 Service 的場景。需要将 service.status.loadBalancer.ingress.ip 設定為雲服務商提供的負載均衡器的 IP。則對該 Service 的通路請求将會通過 LoadBalancer 轉發到後端 Pod,且負載均衡的實作方式依賴于雲服務商提供的 LoadBalancer 的實作機制。
3.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 服務正常工作。需要注意的是:
1. kube2sky 容器需要通路 k8s Master,是以需要在配置檔案中為其配置 Master 所在實體主機的 IP 位址和端口;
2. 需要将 kube2sky 和 skydns 容器的啟動參數
--domain
設定為 k8s 叢集中 Service 所屬域名。容器啟動後 kube2sky 會通過 API Server 監控叢集中所有 service 的定義,生成相應的記錄并儲存到 etcd ;
3. skydns 的啟動參數
-addr=<IP:Port>
表示本機 TCP 和 UDP 的 Port 端口提供服務。
在 DNS Service 的配置檔案中,skyDNS 的 ClusterIP 需要我們指定,每個 Node 的 kubelet 都會使用這個 IP 位址,不會通過系統自動配置設定;另外,這個 IP 需要在 kube-apiserver 啟動參數
--service-cluster-ip-range
内。
在 skydns 容器建立之前,需要先修改每個 Node 上 kubelet 的啟動參數:
- --clusterdns= ,dnscluster_ip 為 DNS 服務的 ClusterIP ;
- --clusterdomain= , dnsdomain 為 DNS 服務中設定的域名。
DNS 工作原理
1. 首先 kube2sky 容器應用通過調用 k8s Master 的 API 獲得叢集中所有 Service 資訊,并持續監控新 Service 的生成,寫入 etcd;
2. 根據 kubelet 的啟動參數的設定,kubelet 會在每個新建立的 Pod 中設定 DNS 域名解析配置檔案
/etc/resolv.conf
中增加一條 nameserver 配置和 search 配置,通過 nameserver 通路的實際上就是 skydns 在對應端口上提供的 DNS 解析服務;
3. 最後,應用程式就可以像通路網站域名一樣,僅通過服務的名字就能通路服務了。
4.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.conf 配置檔案的内容。nginx-s reload
定義 Ingress
k8s 中有一種單獨的名為 Ingress 的資源,在其配置檔案中可以設定到後端 Service 的轉發規則。比如,為 hdls.me 定義一個 ingress.yml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hdls-ingress
spec:
rules:
- host: hdls.me
http:
paths:
- path: /web
backend:
serviceName: hdls
servicePort: 8000
最後采用
kubectl create-f ingress.yml
建立 Ingress。可以登入 nginx-ingress Pod 檢視其自動生成的 nginx.conf 配置檔案内容。
本文轉自掘金-
Kubernetes 入門之網絡詳解