Kubernetes:服務與負載均衡
- 2022 年 3 月 16 日
- 筆記
- cloudnative, Kubernetes, service, 服務, 負載均衡
Blog:部落格園 個人
參考:Service | Kubernetes、《Kubernetes進階實戰》
有了 Workload,我們可以方便地管理多實例的應用,但是要想能夠方便地訪問應用,我們還需要一個類似於 負載均衡 的資源來分發請求,在 kubernetes 中,有兩個資源負責這個功能,分別是 Service 以及 Ingress。其中 Service 主要負責集群內部的訪問,而 Ingress 主要負責來自集群外部的訪問。
Kubernetes Service從邏輯上代表了一組Pod(通常稱為微服務),具體是哪些Pod則是由label來挑選的(selector)。Service有自己的IP,而且這個IP是不變的。客戶端只需要訪問Service的IP,Kubernetes則負責建立和維護Service與Pod的映射關係。無論後端Pod如何變化,對客戶端不會有任何影響,因為Service沒有變。
舉個例子,考慮一個圖片處理後端,它運行了 3 個副本。這些副本是可互換的 —— 前端不需要關心它們調用了哪個後端副本。 然而組成這一組後端程式的 Pod 實際上可能會發生變化, 前端客戶端不應該也沒必要知道,而且也不需要跟蹤這一組後端的狀態。
Service 定義的抽象能夠解耦這種關聯。
Service類型
Service有4種類型:
- ClusterIP:通過集群的內部 IP 暴露服務,選擇該值時服務只能夠在集群內部訪問。 這也是默認的
ServiceType。 - NodePort:通過每個節點上的 IP 和靜態埠(
NodePort)暴露服務。NodePort服務會路由到自動創建的ClusterIP服務。 通過請求<節點 IP>:<節點埠>,你可以從集群的外部訪問一個NodePort服務。 - LoadBalancer:使用雲提供商的負載均衡器向外部暴露服務。 外部負載均衡器可以將流量路由到自動創建的
NodePort服務和ClusterIP服務上。 - ExternalName:通過返回
CNAME和對應值,可以將服務映射到externalName欄位的內容(例如,foo.bar.example.com)。 無需創建任何類型代理。
總體來說,若需要將Service資源發布至集群外部,應該將其配置為NodePort或Load-Balancer類型,而若要把外部的服務發佈於集群內部供Pod對象使用,則需要定義一個ExternalName類型的Service資源,只是這種類型的實現要依賴於v1.7及更高版本的Kubernetes。
💡Tips:Service的默認協議是 TCP。
代理模式(proxy mode)
代理模式分為3種:userspace、iptables和ipvs。
userspace代理模式
此處的userspace是指Linux作業系統的用戶空間。在這種模型中,kube-proxy負責跟蹤API Server上Service和Endpoints對象的變動(創建或移除),並據此調整Service資源的定義。
對於每個Service對象,它會隨機打開一個本地埠(運行於用戶空間的kube-proxy進程負責監聽),任何到達此代理埠的連接請求都將被代理至當前Service資源後端的各Pod對象,至於哪個Pod對象會被選中則取決於當前Service資源的調度方式,默認調度演算法是輪詢(round-robin)。
另外,此類Service對象還會創建iptables規則以捕獲任何到達ClusterIP和埠的流量。在Kubernetes 1.1版本之前,userspace是默認的代理模型。

iptables代理模式
創建Service對象的操作會觸發集群中的每個kube-proxy並將其轉換為定義在所屬節點上的iptables規則,用於轉發工作介面接收到的、與此Service資源ClusterIP和埠相關的流量。客戶端發來請求將直接由相關的iptables規則進行目標地址轉換(DNAT)後根據演算法調度並轉發至集群內的Pod對象之上,而無須再經由kube-proxy進程進行處理,因而稱為iptables代理模式。
使用 iptables 處理流量具有較低的系統開銷,因為流量由 Linux netfilter 處理, 而無需在用戶空間和內核空間之間切換。 這種方法也可能更可靠。但是性能一般,而且受規模影響較大,僅適用於少量Service規模的集群。
對於每個Endpoints對象,Service資源會為其創建iptables規則並指向其iptables地址和埠,而流量轉發到多個Endpoint對象之上的默認調度機制是隨機演算法。iptables代理模型由Kubernetes v1.1版本引入,並於v1.2版本成為默認的類型。

ipvs代理模式
Kubernetes自v1.9版本起引入ipvs代理模式,且自v1.11版本起成為默認設置。在此種模型中,kube-proxy跟蹤API Server上Service和Endpoints對象的變動,並據此來調用netlink介面創建或變更ipvs(NAT)規則。
它與iptables規則的不同之處僅在於客戶端請求流量的調度功能由ipvs實現,餘下的其他功能仍由iptables完成。

ipvs代理模型中Service的服務發現和負載均衡功能均基於內核中的ipvs規則實現。類似於iptables,ipvs也構建於內核中的netfilter之上,但它使用hash表作為底層數據結構且工作於內核空間,因此具有流量轉發速度快、規則同步性能好的特性,適用於存在大量Service資源且對性能要求較高的場景。
支援的調度演算法:
rr:輪替(Round-Robin)lc:最少鏈接(Least Connection),即打開鏈接數量最少者優先dh:目標地址哈希(Destination Hashing)sh:源地址哈希(Source Hashing)sed:最短預期延遲(Shortest Expected Delay)nq:從不排隊(Never Queue)
示例
創建一個 Nginx Pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
然後執行:
kubectl apply -f ./run-nginx.yaml
查看運行:
[root@master test]# kubectl get pods -l run=my-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-5b56ccd65f-rnv9b 1/1 Running 0 34s 10.233.112.27 node-1 <none> <none>
my-nginx-5b56ccd65f-rx2mq 1/1 Running 0 34s 10.233.112.26 node-1 <none> <none>
檢查 Pod 的 IP 地址:
[root@master test]# kubectl get pods -l run=my-nginx -o yaml | grep ' podIP:'
podIP: 10.233.112.27
podIP: 10.233.112.26
創建service:
[root@master test]# kubectl expose deployment/my-nginx
service/my-nginx exposed
這等價於使用 kubectl create -f 命令創建,對應如下的 yaml 文件:
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
查看 Service 資源:
[root@master test]# kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.233.22.145 <none> 80/TCP 79s
一個 Service 由一組 backend Pod 組成。這些 Pod 通過 endpoints 暴露出來。 Service Selector 將持續評估,結果被 POST 到一個名稱為 my-nginx 的 Endpoint 對象上。 當 Pod 終止後,它會自動從 Endpoint 中移除,新的能夠匹配上 Service Selector 的 Pod 將自動地被添加到 Endpoint 中。 檢查該 Endpoint:
[root@master test]# kubectl describe svc my-nginx
Name: my-nginx
Namespace: default
Labels: <none>
Annotations: <none>
Selector: run=my-nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.233.22.145
IPs: 10.233.22.145
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.233.112.26:80,10.233.112.27:80
Session Affinity: None
Events: <none>
查看endporints:
[root@master test]# kubectl get ep my-nginx
NAME ENDPOINTS AGE
my-nginx 10.233.112.26:80,10.233.112.27:80 3m22s
任意節點測試:
# master節點
[root@master test]# curl 10.233.22.145
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="//nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="//nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# worker節點
[root@node-2 ~]# curl 10.233.22.145
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="//nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="//nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

