K8S實戰(十)| Service

前言

Pod 已經成功運行起來了,可是有兩個問題。html

一是這些 Pod 沒法從集羣外部直接訪問到,二是 Pod 出現故障自愈後,IP 會發生變化。前端

如何解決這兩個問題,這裏有一個很是重要的概念:Servicenode

更新歷史

Service 的意義和特色

  1. 對一組 Pod 提供負載均衡(工做在 TCP/UDP 4 層)
  2. 防止 Pod 更換 IP 失聯,即服務發現
  3. 經過 label selector 關聯 Pod

Service 工做原理

Service 是由 kube-proxy 組件加上 iptables/LVS 共同實現。
說白了就是經過 kube-proxy 生成了一堆 iptables 規則,經過 iptables 規則來轉發數據。linux

iptables 轉發:nginx

  1. K8S 默認的轉發設置。
  2. 選擇後端 Pod 爲隨機選擇。
  3. 當 Pod 沒有響應,鏈接會失敗,並無健康檢查機制。
  4. 須要配合 Pod 就緒探測器來確保訪問到健康的 Pod。
  5. 當集羣規模達到上萬個服務時,iptables 轉發效率會顯著下降。

LVS轉發:算法

  1. 基於內核哈希表,性能強大,具備更高網絡吞吐量。
  2. 適用於 Pod 量級大,轉發規則更多的大規模集羣。
  3. LVS 支持更多的 Pod 負載均衡調度算法。
  4. LVS 只負責負載均衡和代理功能,剩餘的包過濾和SNAT等操做仍是須要 iptables 處理,但這些操做規則數量不會因 Pod 數量的增長而增長。
  5. 也叫 IPVS 。

Service 的默認工做方式

建立 Pod 和 默認Service,進行默認工做狀態的測試。後端

先建立3個 Podapi

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 提供的服務。

上面說的三種工做方式有以下問題:

  1. ClusterIP 方式默認工做在集羣內部,使用參數 externalIPs 可指定哪一個及誒按暴露端口,但沒法進行7層的URL跳轉等控制
  2. NodePort 方式下,所有節點都會暴露該端口,但一個端口只能對應一個業務,適合業務比較少的環境或者測試環境,業務多了之後沒法有效管理
  3. LoadBalance 方式只適合於現有的公有云平臺,沒法用於自建集羣,同時還須要額外費用

這些問題致使沒法直接應用於生產環境中。

若是想提供給自建集羣的生產環境使用,須要在 Service 前面再加一層 Ingress Controller。

聯繫我

微信公衆號:zuolinux_com

微信掃碼關注

相關文章
相關標籤/搜索