前言
Pod 已經成功運行起來了,但是有兩個問題,
一是這些 Pod 無法從集群外部直接訪問到,二是 Pod 出現故障自愈后,IP 會發生變化,
如何解決這兩個問題,這里有一個非常重要的概念:Service
更新歷史
- 20200625 - 初稿 - 左程立
- 原文地址 - https://blog.zuolinux.com/2020/06/25/about-service.html
Service 的意義和特點
- 對一組 Pod 提供負載均衡(作業在 TCP/UDP 4 層)
- 防止 Pod 更換 IP 失聯,即服務發現
- 通過 label selector 關聯 Pod
Service 作業原理
Service 是由 kube-proxy 組件加上 iptables/LVS 共同實作,
說白了就是通過 kube-proxy 生成了一堆 iptables 規則,通過 iptables 規則來轉發資料,
iptables 轉發:
- K8S 默認的轉發設定,
- 選擇后端 Pod 為隨機選擇,
- 當 Pod 沒有回應,連接會失敗,并沒有健康檢查機制,
- 需要配合 Pod 就緒探測器來確保訪問到健康的 Pod,
- 當集群規模達到上萬個服務時,iptables 轉發效率會顯著降低,
LVS轉發:
- 基于內核哈希表,性能強大,具有更高網路吞吐量,
- 適用于 Pod 量級大,轉發規則更多的大規模集群,
- LVS 支持更多的 Pod 負載均衡調度演算法,
- LVS 只負責負載均衡和代理功能,剩余的包過濾和SNAT等操作還是需要 iptables 處理,但這些操作規則數量不會因 Pod 數量的增加而增加,
- 也叫 IPVS ,
Service 的默認作業方式
創建 Pod 和 默認Service,進行默認作業狀態的測驗,
先創建3個 Pod
cat nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
創建一個默認型別的 Service,名稱為 nginx-service
cat nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
protocol: TCP
port: 80
是 service 在集群內部的VIP埠
targetPort: 80
是 Pod 的埠
執行創建
kubectl apply -f nginx.yaml
kubectl apply -f nginx-service.yaml
查看運行情況
[root@master01 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-d46f5678b-cldf4 1/1 Running 0 21m 192.10.137.153 work03 <none> <none>
nginx-deployment-d46f5678b-lnxh9 1/1 Running 0 21m 192.10.205.252 work01 <none> <none>
nginx-deployment-d46f5678b-th8xq 1/1 Running 0 21m 192.10.75.89 work02 <none> <none>
[root@master01 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP 192.20.150.26 <none> 80/TCP 13m
查看名稱為 nginx-service 的 service 成功掛載的后端 Pod
[root@master01 ~]# kubectl get endpoints nginx-service
NAME ENDPOINTS AGE
nginx-service 192.10.137.153:80,192.10.205.252:80,192.10.75.89:80 14m
可以看到我們創建的名為 nginx-service 的 Service 后端掛載了3個 Pod
給3個 Pod 寫入內容,訪問 Pod 時回傳自身的主機名
kubectl exec nginx-deployment-d46f5678b-cldf4 -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html';
kubectl exec nginx-deployment-d46f5678b-lnxh9 -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html';
kubectl exec nginx-deployment-d46f5678b-th8xq -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html';
我們訪問 Service IP 看看
[root@master01 ~]# curl 192.20.150.26
nginx-deployment-d46f5678b-th8xq
[root@master01 ~]# curl 192.20.150.26
nginx-deployment-d46f5678b-cldf4
[root@master01 ~]# curl 192.20.150.26
nginx-deployment-d46f5678b-lnxh9
可以看到 Service 成功將請求代理到了后端的一組 Pod,并且進行了流量的分配,
這是 Service 的默認作業型別,只能在集群所屬的節點上訪問到,離開集群后無法被訪問到,
這種作業型別叫做 ClusterIP,
Service 對外提供服務的三種方式
上一節可以看到,Service 默認不對集群外部提供服務,那么如何才能在集群外部訪問到呢,有三種方案,
externalIPs 方式
Service 中配置可以 externalIPs,IP 為本集群中 work 節點宿主機 IP,
apiVersion: v1
kind: Service
,,,
,,,
spec:
,,,
,,,
externalIPs:
- 192.168.10.16
- 192.168.10.17
在 192.168.10.16/17 上執行 ss -lntp 可以看到 Service 定義的暴露埠,
在集群外部訪問 192.168.10.16/17 上 Service 暴露的埠即可,
NodePort 方式
改造 nginx-service.yaml,增加一行 type: NodePort
cat nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
protocol: TCP
創建 service
kubectl apply -f nginx-service.yaml
查看運行情況
[root@master01 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service NodePort 192.20.167.221 <none> 80:30913/TCP 13m
引數 PORT(S) 80:30913/TCP,其中 30913 就是用來集群外部訪問的埠,
可以訪問任何一臺物理宿主機的 ip:30913 來訪問到 Pod,
30913 是 K8S 從固定范圍 30000-32767 中選擇的,也可以通過引數 nodePort 指定固定埠,
可選擇范圍可以通過 kube-apiserver 的 –service-node-port-range 引數來修改,
[root@master01 ~]# ss -nltp | grep 30913
LISTEN 0 128 *:30913 *:*
可以看到宿主機上監聽了 30913 埠,
測驗
在沒有運行 K8S 集群的機器上訪問 K8S 宿主機
[root@192-168-10-18 ~]# curl 192.168.10.12:30913
nginx-deployment-d46f5678b-2pmts
[root@192-168-10-18 ~]# curl 192.168.10.12:30913
nginx-deployment-d46f5678b-zv8m4
[root@192-168-10-18 ~]# curl 192.168.10.12:30913
nginx-deployment-d46f5678b-2pmts
可以看到從集群外部可以成功訪問到 Pod 中內容,并且為隨機分配,
原理
kube-proxy 在宿主機上創建了 iptables 規則,對宿主機 IP:30913 的訪問將被轉發到 Service IP,然后 Service 再通過自己的 iptables 規則分發到 Pod
LoadBalancer 方式
NodePort 方式中,如果要正式對外提供服務,我們需要在集群外部再創建一個高可用的負載均衡器,以方便把流量轉發到宿主機開放的埠上,如果宿主機開放埠發生了變更,我們需要手工修改前端負載均衡器,
公有云的 LoadBalancer 自動化了這一程序,
LoadBalancer 這種方式應用于公有云,提交一個 type: LoadBalancer 的 Service 創建申請后,公有云會幫我們創建一個負載均衡器,該負載均衡器會把請求直接分發給 Pod,同時 Pod IP 發生變化后,會自動更新到負載均衡器上,
其他:Headless Service
通過指定 spec.clusterIP 的值為 "None" 可以創建 Headless Service,
Headless Service 不會分配 Cluster IP,kube-proxy 不會處理它們, 而且平臺也不會為它們進行負載均衡和路由,
定義了 selector 的無頭服務,Endpoint 控制器會在 API 中創建 Endpoints 記錄, 并且修改 DNS 配置回傳 A 記錄,通過這個地址,請求可以直接到達 Service 的后端 Pod 上,
結束語
Service 對 IP 資訊易變的 Pod 提供了服務發現、負載均衡等管理功能,同時提供了外部訪問的能力,從而使外部用戶能夠穩定的訪問到運行在集群內部的 Pod 提供的服務,
上面說的三種作業方式有如下問題:
- ClusterIP 方式默認作業在集群內部,使用引數 externalIPs 可指定哪個及誒按暴露埠,但無法進行7層的URL跳轉等控制
- NodePort 方式下,全部節點都會暴露該埠,但一個埠只能對應一個業務,適合業務比較少的環境或者測驗環境,業務多了以后無法有效管理
- LoadBalance 方式只適合于現有的公有云平臺,無法用于自建集群,同時還需要額外費用
這些問題導致無法直接應用于生產環境中,
如果想提供給自建集群的生產環境使用,需要在 Service 前面再加一層 Ingress Controller,
聯系我
微信公眾號:zuolinux_com

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/78493.html
標籤:其他
