Kubernetes-Service介紹(一)-基本概念


前言

本篇是Kubernetes第八篇,大家一定要把環境搭建起來,看是解決不了問題的,必須實戰。Pod篇暫時應該還缺少兩篇,等Service和存儲相關內容介紹以后,補充剩下的兩篇,有狀態的Pod會涉及這兩塊的內容。

Kubernetes系列文章:
  1. Kubernetes介紹
  2. Kubernetes環境搭建
  3. Kubernetes-kubectl介紹
  4. Kubernetes-Pod介紹(-)
  5. Kubernetes-Pod介紹(二)-生命周期
  6. Kubernetes-Pod介紹(三)-Pod調度
  7. Kubernetes-Pod介紹(四)-Deployment

為什么需要Service

在應用創建一個Nginx的Pod集合,由3個Pod組成,每個容器的端口端口號都是80;

  1. 編輯nginx-deployment.yaml;
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: backend
  replicas: 3
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        resources:
          limits:
            memory: "128Mi"
            cpu: "128m"
        ports:
        - containerPort: 80
  1. 創建Deployment資源;
kubectl apply -f nginx-deployment.yaml
  1. 查看Pod的IP;
kubectl get pods -o wide
image.png
image.png
  1. 通過IP訪問Pod;
curl 10.100.1.92:80
image.png
image.png

這樣就會存在問題,由於Pod的生命是有限的,如果Pod重啟IP有可能會發生變化。如果我們的服務都是將Pod的IP地址寫死,Pod的IP發生變化以后,后端服務也將會不可用。當然我們可以通過手動更新如nginx的upstream配置來改變后端的服務IP。也可以通過Consul,ZooKeeper、etcd等工具,把我們的服務注冊到這些服務發現中心,然后讓這些工具動態的去更新Nginx的配置就可以了,我們完全不用去手工的操作了。

img
img

Kubernetes提供了Service對象,它定義了一組Pod的邏輯集合和一個用於訪問它們的策略,這個概念和微服務非常類似。一個Serivce下面包含的Pod集合一般是由Label Selector來決定的。這樣就可以不用去管后端的Pod如何變化,只需要指定Service的地址就可以了,這廝因為我們在中間添加了一層服務發現的中間件,Pod銷毀或者重啟后,把這個Pod的地址注冊到這個服務發現中心去。Service的這種抽象就可以幫我們達到這種解耦的目的。

Service原理初探

我們以前面創建的nginx為案例,通過創建一個Service來給3個Pod做負載:

  1. 創建Service方式有兩種,一種是通過kubectl expose命令來創建,另外一個種通過yaml文件來創建,這里我們采用yaml方式來創建,創建一個nginx-service.yaml文件;
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  #定義后端pod標簽為app=backend
  selector:
    app: backend
  ports:
  #service端口號
  - port: 80
    #pod的端口號
    targetPort: 80
  1. 創建Service對象;
kubectl apply -f nginx-service.yaml
  1. 查看Service的IP地址;
kubectl get svc
  1. 通過ServiceIP和端口號訪問;
curl 10.96.165.211:80
image.png
image.png
  1. 查看Service的Endpoint的列表;
kubectl describe svc nginx-service
image.png
image.png

這里我們可以初步看出來Service的服務發現離不開Endpoint對象,Endpoint是Kubernetes中的一個資源對象,存儲在Etcd中,用來記錄一個Service對應的所有Pod的訪問地址。Service配置Selector,Endpoint Controller才會自動創建對應的Endpoint對象;否則不會創建Endpoint對象。Endpoint Controller主要有以下作用:

  1. 負責生成和維護所有endpoint對象的控制器
  2. 負責監聽Service和對應Pod的變化,監聽到Service被刪除,則刪除和該Service同名的Endpoint對象;監聽到新的Service被創建,則根據新建Service信息獲取相關Pod列表,然后創建對應Endpoint對象;監聽到Service被更新,則根據更新后的Service信息獲取相關Pod列表,然后更新對應Endpoint對象;監聽到Pod事件,則更新對應的Service的Endpoint對象,將Pod IP記錄Eendpoint中;

Endpoint完成服務發現,真正從服務IP到后端Pod的負載均衡的實現則是由每個Node節點上的kube-proxy負責實現的,kube-proxy會監聽Service和Endpoints的更新並調用其代理模塊在主機上刷新路由轉發規則,從而實現動態跟新服務列表,整體訪問情況可以參考下圖,這是第一代kube-proxy實現方式,現在的實現方式有所調整,但是我覺得這個是最容易讓人明白的方式。

image.png
image.png

下圖是第二代或者第三代實現方式,實現方式:

img
img

Service負載均衡

Service路由轉發都是由 kube-proxy 組件來實現的,Service 僅以一種 ClusterIP 的形式存在,kube-proxy 將Service訪問請求轉發到后端的多個Pod的實例上,kube-proxy 的路由轉發規則是通過其后端的代理模塊實現的,kube-proxy 的代理模塊目前有三種實現方案:

userspace

在userspace(用戶空間代理)模式下,kube-proxy進程是一個真實的TCP/UDP代理,負責從Service到Pod的訪問流量的轉發,如下圖: img

userspace模式下kube-proxy通過API Server的Watch接口實時跟蹤Service與Endpoint的變更信息,來實現動態更新iptables規則;對每個Service它都為其所在Node節點開放一個端口,作為其服務代理端口;發往該端口的請求會采用一定的策略轉發給與該服務對應的后端Pod實體。kube-proxy同時會在本地節點設置 iptables 規則,這個規則用於捕獲通過Cluter IP和Port訪問Service請求,並將這些轉發到對應的端口上,如果采用DNS形式,前面還有DNS解析層。

img
img

userspace該模式下最大的問題是,Service的請求會先從用戶空間進入內核iptables,然后再回到用戶空間,最后在由kube-proxy完成后端Endpoints的選擇和代理工作,這樣需要用戶空間和內核態一直來回切換,這樣帶來的系統開銷會很大。

iptables

Kubernetes從1.2版本開始,將iptables作為kube-proxy的默認模式。iptables模式下的kube-proxy不再起到Proxy的作用,其核心功能:通過API Server的Watch接口實時跟蹤Service與Endpoint的變更信息,並更新對應的iptables規則,Client的請求流量則通過iptables的NAT機制“直接路由”到目標Pod。

img
img

與第1代的userspace模式相比,iptables模式完全工作在內核態,不用再經過用戶態的kube-proxy中轉,因而性能更強。iptables模式雖然實現起來簡單,但存在無法避免的缺陷:在集群中的Service和Pod大量增加以后,iptables中的規則會急速膨脹,導致性能顯著下降,在某些極端情況下甚至會出現規則丟失的情況,並且這種故障難以重現與排查。

IPVS
img
img

Kubernetes從1.8版本開始引入第3代的IPVS(IPVirtualServer)模式,IPVS在Kubernetes1.11中升級為GA穩定版。iptables與IPVS雖然都是基於Netfilter實現的,但因為定位不同,二者有着本質的差別:iptables是為防火牆而設計的;IPVS則專門用於高性能負載均衡,並使用更高效的數據結構(Hash表),允許幾乎無限的規模擴張。由此解決掉了iptables的弊病。

與iptables相比,IPVS擁有以下明顯優勢:

  1. 為大型集群提供了更好的可擴展性和性能;

  2. 支持比iptables更復雜的復制均衡算法(最小負載、最少連接、加權等);

  3. 支持服務器健康檢查和連接重試等功能;

  4. 可以動態修改ipset的集合,即使iptables的規則正在使用這個集合;

由於IPVS無法提供包過濾、airpin-masqueradetricks(地址偽裝)、SNAT等功能,因此在某些場景(如NodePort的實現)下還要與iptables搭配使用。

在IPVS模式下,kube-proxy又做了重要的升級,即使用iptables的擴展ipset,而不是直接調用iptables來生成規則鏈。iptables規則鏈是一個線性的數據結構,ipset則引入了帶索引的數據結構,因此當規則很多時,也可以很高效地查找和匹配。

會話保持機制

Service支持通過設置sessionAffinity實現基於客戶端的IP的會話保持,首次將某個客戶端來源的IP發起請求轉發到后端某個Pod上,之后從相同的客戶端IP發起的請求都被轉發到相同的Pod上,配置參數為spec.sessionAffinity,也可以設置會話保持的最長時間,在此之后重新設置訪問規則,通過配置spec.sessionAffinityConfig.clientIP.timeoutSeconds來實現,可以參考以下配置:

Service支持通過設置sessionAffinity實現基於客戶端的IP的會話保持,首次將某個客戶端來源的IP發起請求轉發到后端某個Pod上,之后從相同的客戶端IP發起的請求都被轉發到相同的Pod上,配置參數為spec.sessionAffinity,也可以設置會話保持的最長時間,在此之后重新設置訪問規則,通過配置spec.sessionAffinityConfig.clientIP.timeoutSeconds來實現,可以參考以下配置:

  apiVersion: v1
  kind: Service
  metadata:
    name: nginx-service
  spec:
    sessionAffinity: ClientIP
    sessionAffinityConfig:
      clientIP:
        timeoutSeconds: 1000
    #定義后端pod標簽為app=backend
    selector:
      app: backend
    ports:
    #service端口號
    - port: 80
      #pod的端口號
      targetPort: 80

Service類型

Service支持的類型也就是Kubernetes 中將服務暴露的方式,默認有四種 ClusterIP、NodePort、LoadBalancer、ExternelName,下面會詳細介紹每種類型Service的使用場景。

ClusterIP

ClusterIP類型的Service是Kubernetes集群默認的服務暴露方式,它只能用於集群內部通信,可以被各 Pod 訪問,也可以手動指定ClusterIP,不過要確保該IP在Kubernetes集群設置ClusterIP的范圍內部,並且沒有被其他Service使用,整個訪問流程如下:

img
img

集群內部整體結構可參考以下模型:

img
img
NodePort

對於我們來說,不全是集群內訪問,也需要集群外業務訪問。那么ClusterIP就滿足不了了。NodePort是一種對外提供訪問的方式,NodePort類型的Service可以讓Kubemetes集群每個節點上保留一個相同的端口,外部訪問連接首先訪問節點IP:Port,然后將這些連接轉發給服務對應的Pod。整體的訪問流程:

image.png
image.png

集群內部整體結構可參考以下模型:

img
img
LoadBalancer

LoadBalancer類型的Service其實是NodePort類型Service的擴展,通過一個特定的LoadBalancer訪問Service,這個LoadBalancer將請求轉發到節點的NodePort,可以理解為端口的Nginx負載均衡器。

LoadBalancer本身不是屬於Kubernetes的組件,這部分通常是由具體廠商(雲服務提供商)提供,不同廠商的Kubernetes集群與LoadBalancer的對接實現各不相同,被提供的負載均衡器的信息將會通過Service的status.loadBalancer字段被發布出去。

整體結構可參考以下模型:

image.png
image.png
ExternalName

ExternalName類型的服務用於將集群外的服務定義為Kubernetes集群的Service,並且通過externalName字段指定外部服務的地址,可以是域名也可以是IP格式。集群內部的客戶端可以通過這個Service訪問外部服務,這種類型的服務沒有后端Pod,因此也不需要設置Label Selector。

整體結構可參考以下模型:

img
img

結束

歡迎大家點點關注,點點贊!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM