Kubernetes之服務發現及負載Services


Service 概述

kubernetes 中的pod是有生生滅滅的,時刻都有可能被新的pod所代替,而不可復活(pod的生命周期)。一旦一個pod生命終止,通過ReplicaSets動態創建和銷毀pod(Pod的動態擴縮容,滾動升級 等)。 每個pod都有自己的IP,這IP隨着pod的生生滅滅而變化,不能被依賴。這樣導致一個問題,如果這個POD作為后端(backend)提供一些功能供給一些前端POD(frontend),在kubernete集群中是如何實現讓這些前台能夠持續的追蹤到這些后台的?所以之間需要一個服務作為后端的服務負載------service

Kubernetes Service 是一個定義了一組Pod的策略的抽象,這些被服務標記的Pod都是(一般)通過label Selector實現的

舉個例子,考慮一個圖片處理 backend,它運行了3個副本。這些副本是可互換的 —— frontend 不需要關心它們調用了哪個 backend 副本。 然而組成這一組 backend 程序的 Pod 實際上可能會發生變化,frontend 客戶端不應該也沒必要知道,而且也不需要跟蹤這一組 backend 的狀態。 Service 定義的抽象能夠解耦這種關聯。

Service 實現的三種方式

在 Kubernetes 集群中,每個 Node 運行一個 kube-proxy 進程。kube-proxy 負責為 Service 實現了一種 VIP(虛擬 IP)的形式,而不是 ExternalName 的形式,在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但並不是默認的運行模式。 從 Kubernetes v1.2 起,默認就是 iptables 代理。在Kubernetes v1.8.0-beta.0中,添加了ipvs代理。在 Kubernetes v1.0 版本,Service 是 “4層”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用來表示 “7層”(HTTP)服務。

kube-proxy 這個組件始終監視着apiserver中有關service的變動信息,獲取任何一個與service資源相關的變動狀態,通過watch監視,一旦有service資源相關的變動和創建,kube-proxy都要轉換為當前節點上的能夠實現資源調度規則(例如:iptables、ipvs)

 userspace 代理模式

這種模式,當客戶端Pod請求內核空間的service iptables后,把請求轉到給用戶空間監聽的kube-proxy 的端口,由kube-proxy來處理后,再由kube-proxy打請求轉給內核空間的 service iptalbes,再由service iptalbes根據請求轉給各節點中的的service pod。由此可見這個模式有很大的問題,由客戶端請求先進入內核空間的,又進去用戶空間訪問kube-proxy,由kube-proxy封裝完成后再進去內核空間的iptables,再根據iptables的規則分發給各節點的用戶空間的pod。這樣流量從用戶空間進出內核帶來的性能損耗是不可接受的

 

 

iptables 代理模式

客戶端IP請求時,直接求情本地內核service ip,根據iptables的規則求情到各pod上,因為使用iptable NAT來完成轉發,也存在不可忽視的性能損耗。另外,如果集群中存在上萬的Service/Endpoint,那么Node上的iptables rules將會非常龐大,性能還會再打折扣。

 

 

ipvs 代理模式

客戶端IP請求時,直接求情本地內核service ipvs,根據ipvs的規則求情到各pod上。kube-proxy會監視Kubernetes Service對象和Endpoints,調用netlink接口以相應地創建ipvs規則並定期與Kubernetes Service對象和Endpoints對象同步ipvs規則,以確保ipvs狀態與期望一致。訪問服務時,流量將被重定向到其中一個后端Pod。

與iptables類似,ipvs基於netfilter 的 hook 功能,但使用哈希表作為底層數據結構並在內核空間中工作。這意味着ipvs可以更快地重定向流量,並且在同步代理規則時具有更好的性能。此外,ipvs為負載均衡算法提供了更多選項,例如:

  • rr:輪詢調度
  • lc:最小連接數
  • dh:目標哈希
  • sh:源哈希
  • sed:最短期望延遲
  • nq:不排隊調度

注意: ipvs模式假定在運行kube-proxy之前在節點上都已經安裝了IPVS內核模塊。當kube-proxy以ipvs代理模式啟動時,kube-proxy將驗證節點上是否安裝了IPVS模塊,如果未安裝,則kube-proxy將回退到iptables代理模式。

 

 

如果某個服務后端pod發生變化,標簽選擇器適應的pod有多一個,適應的信息會立即放映到apiserver上,而kube-proxy一定可以watch到etc中的信息變化,而將他立即轉為ipvs或者iptables中的規則,這一切都是動態和實時的,刪除一個pod也是同樣的原理。

 service 定義

kubectl explain svc.spec

  • ports   建立哪些端口,暴露的端口是哪些
  • selector  把哪些容器通過這個service暴露出去
  • type  有四種  (ExternalName  ClusterIP  NodePort LoadBalancer) 默認是ClusterIP

ports 的定義

kubectl explain svc.spec.ports

  • name 指定的port的名稱
  • nodePort  指定節點上的端口
  • port  暴露給服務的端口
  • targetPort 容器的端口
  • protocol 執行協議(TCP or UDP)

 ClusterIP方式

apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: default
spec:
  selector:
    app: redis
    role: log-store
  type: ClusterIP
  ports:
  - port: 6379
    targetPort: 6379

查看一下詳細

$ kubectl describe svc redis
Name:                   redis
Namespace:              default
Labels:                 <none>
Annotations:            kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"ports":[{"port":6379,"targetPort":6379}...
Selector:               app=redis,role=log-store
Type:                   ClusterIP
IP:                     10.43.164.114
Port:                   <unset> 6379/TCP
Endpoints:              10.42.0.219:6379
Session Affinity:       None
Events:                 <none>

資源記錄格式:

SVC_NAME.NS_NAME.DOMAIN.LTD.

默認的service的a記錄  svc.cluster.local.

剛創建的service的a記錄  redis.default.cluster.local.

 

NodePort方式

apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  selector:
    app: myapp
    release: dev
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
$ kubectl describe svc myapp
Name:                   myapp
Namespace:              default
Labels:                 <none>
Annotations:            field.cattle.io/publicEndpoints=[{"addresses":["172.16.138.170"],"port":30080,"protocol":"TCP","serviceName":"default:myapp","allNodes":true}]
                        kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"ports":[{"nodePort":30080,"port":80,"ta...
Selector:               app=myapp,release=dev
Type:                   NodePort
IP:                     10.43.162.175
Port:                   <unset> 80/TCP
NodePort:               <unset> 30080/TCP
Endpoints:              10.42.0.218:80,10.42.1.107:80,10.42.3.210:80
Session Affinity:       None
Events:                 <none>

#可以看到他負責均衡的效果
$  for a in  {1..10}; do    curl http://172.16.138.170:30080/hostname.html  && sleep 1s; done
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-7shh9
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-7shh9
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-7shh9
myapp-deploy-869b888f66-vwgj2
myapp-deploy-869b888f66-7shh9
myapp-deploy-869b888f66-4l4cv

LoadBalancer類型

 使用支持外部負載均衡器的雲提供商的服務,設置 type 的值為 "LoadBalancer",將為 Service 提供負載均衡器。 負載均衡器是異步創建的,關於被提供的負載均衡器的信息將會通過 Servicestatus.loadBalancer 字段被發布出去。

來自外部負載均衡器的流量將直接打到 backend Pod 上,不過實際它們是如何工作的,這要依賴於雲提供商。 在這些情況下,將根據用戶設置的 loadBalancerIP 來創建負載均衡器。 某些雲提供商允許設置 loadBalancerIP。如果沒有設置 loadBalancerIP,將會給負載均衡器指派一個臨時 IP。 如果設置了 loadBalancerIP,但雲提供商並不支持這種特性,那么設置的 loadBalancerIP 值將會被忽略掉。

 

ExternalName 類型

提供訪問發布服務的,像使用集群內部一樣使用外部服務。

 

會話粘性(常說的會話保持)

kubectl explain svc.spec.sessionAffinity

支持ClientIP和None 兩種方式,默認是None(隨機調度) ClientIP是來自於同一個客戶端的請求調度到同一個pod中

apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  selector:
    app: myapp
    release: dev
  sessionAffinity: ClientIP
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080

查看來自同一客戶端的請求始終訪問同一個Pod

$ kubectl describe svc myapp
Name:            myapp
Namespace:        default
Labels:            <none>
Annotations:        field.cattle.io/publicEndpoints=[{"addresses":["172.16.138.170"],"port":30080,"protocol":"TCP","serviceName":"default:myapp","allNodes":true}]
            kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"ports":[{"nodePort":30080,"port":80,"ta...
Selector:        app=myapp,release=dev
Type:            NodePort
IP:            10.43.162.175
Port:            <unset>    80/TCP
NodePort:        <unset>    30080/TCP
Endpoints:        10.42.0.218:80,10.42.1.107:80,10.42.3.210:80
Session Affinity:    ClientIP
Events:            <none>

$ for a in  {1..10}; do    curl http://172.16.138.170:30080/hostname.html  && sleep 1s; done
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv
myapp-deploy-869b888f66-4l4cv

Headless service(就是沒有Cluster IP 的Service )

有時不需要或不想要負載均衡,以及單獨的 Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值為 "None" 來創建 Headless Service。它會給一個集群內部的每個成員提供一個唯一的DNS域名來作為每個成員的網絡標識,集群內部成員之間使用域名通信

這個選項允許開發人員自由尋找他們自己的方式,從而降低與 Kubernetes 系統的耦合性。 應用仍然可以使用一種自注冊的模式和適配器,對其它需要發現機制的系統能夠很容易地基於這個 API 來構建。

對這類 Service 並不會分配 Cluster IP,kube-proxy 不會處理它們,而且平台也不會為它們進行負載均衡和路由。 DNS 如何實現自動配置,依賴於 Service 是否定義了 selector。

apiVersion: v1
kind: Service
metadata:
  name: myapp-headless
  namespace: default
spec:
  selector:
    app: myapp
    release: dev
  clusterIP: "None"
  ports:
  - port: 80
    targetPort: 80

驗證

$ dig -t A myapp-headless.default.svc.cluster.local. @10.42.0.5

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A myapp-headless.default.svc.cluster.local. @10.42.0.5
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55062
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;myapp-headless.default.svc.cluster.local. IN A

;; ANSWER SECTION:
myapp-headless.default.svc.cluster.local. 30 IN    A 10.42.0.218
myapp-headless.default.svc.cluster.local. 30 IN    A 10.42.1.107
myapp-headless.default.svc.cluster.local. 30 IN    A 10.42.3.210

;; Query time: 2 msec
;; SERVER: 10.42.0.5#53(10.42.0.5)
;; WHEN: Fri Aug 31 11:40:46 EDT 2018
;; MSG SIZE  rcvd: 106

 


免責聲明!

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



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