025.掌握Service-SVC基礎使用

  • 2020 年 3 月 11 日
  • 筆記

一 Service簡介

1.1 Service概念

Service是Kubernetes的核心概念,通過創建Service,可以為一組具有相同功能的容器應用提供一個統一的入口地址,並且將請求負載分發到後端的各個容器應用上。

1.2 Service定義詳解

  1 apiVersion: v1		#必須,api版本    2 kind: Service		#必須,類型為Service    3 metadata:			#必須,元數據    4   name: string		#必須,Service名稱    5   namespace: string		#必須,命名空間,默認為default    6   labels:			#自定義標籤屬性列表    7     - name: string    8   annotations:		#自定義註解屬性列表    9     - name: string   10 spec:			#必須,詳細描述   11   selector: []		#必須,Label Selector配置   12   type: ClusterIP		#必須,Serice類型,詳見如下   13   sessionAffinity: string	#虛擬服務IP地址,當選擇type=ClusterIP時,若不指定,則系統進行自動分配;當type=LoadBalancer時,需要指定   14   ports:			#Service需要暴露的端口列表   15   - name: string		#端口名稱   16     protocol:		#端口協議,支持TCP和UDP,默認為TCP   17     port: int		#服務監聽的端口號   18     targetPort: 8080	#需要轉發到後端Pod的端口號   19     nodePort: int		#當spec.type=NodePort時,指定映射到物理機的端口號   20 status:			#當spec.type=LoadBalancer時,設置外部負載均衡的地址,用於公有雲   21   loadBalancer:		#外部負載均衡器   22     ingress:		#外部負載均衡器   23       ip: string		#外部負載均衡器的IP地址   24       hostname: string	#外部負載均衡器的主機名

spec.type:Service的類型,指定Service的訪問方式,默認為ClusterIP。

  • ClusterIP:虛擬的服務IP地址,該地址用於Kubernetes集群內部的Pod訪問,在Node上kube-proxy通過設置的iptables規則進行轉發;
  • NodePort:使用宿主機的端口,使能夠訪問各Node的外部客戶端通過Node的IP地址和端口號就能訪問服務;
  • LoadBalancer:使用外接負載均衡器完成到服務的負載分發,需要在spec.status.loadBalancer字段指定外部負載均衡器的IP地址,並同時定義nodePort和clusterIP,用於公有雲。

二 Service基本使用

2.1 Service的基本用法

一般來說,對外提供服務的應用程序需要通過某種機制來實現,對於容器應用最簡便的方式就是通過TCP/IP機制及監聽IP和端口號來實現。

示例:定義一個提供Web服務的RC,由兩個Tomcat容器副本組成,每個容器都通過containerPort設置提供服務的端口號為8080。
[root@k8smaster01 study]# cat webapp-rc.yaml

  1 apiVersion: v1    2 kind: ReplicationController    3 metadata:    4   name: webapp    5 spec:    6   replicas: 2    7   template:    8     metadata:    9       name: webapp   10       labels:   11         app: webapp   12     spec:   13       containers:   14       - name: webapp   15         image: tomcat   16         ports:   17         - containerPort: 8080

[root@k8smaster01 study]# kubectl create -f webapp-rc.yaml

[root@k8smaster01 study]# kubectl get pods -l app=webapp -o yaml | grep podIP

podIP: 172.24.9.88

podIP: 172.24.9.199

[root@k8smaster01 study]# curl 172.24.9.88:8080

clipboard

直接通過Pod的IP地址和端口號可以訪問到容器應用內的服務,但是Pod的IP地址是不可靠的,例如當Pod所在的Node發生故障時,Pod將被Kubernetes重新調度到另一個Node,Pod的IP地址將發生變化。

如果容器應用本身是分佈式的部署方式,通過多個實例共同提供服務,就需要在這些實例的前端設置一個負載均衡器來實現請求的分發。Kubernetes中的Service就是用於解決這些問題的核心組件。

Service示例:以如上webapp應用為例,為了讓客戶端應用訪問到兩個Tomcat Pod實例,可以創建一個Service來提供服務。Kubernetes提供了一種快速的方法,即通過kubectl expose命令來創建Service。
[root@k8smaster01 study]# kubectl expose rc webapp

[root@k8smaster01 study]# kubectl get svc | grep -E ‘NAME|webapp’

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

webapp ClusterIP 10.10.10.51 <none> 8080/TCP 45s

[root@k8smaster01 study]# curl 10.10.10.51:8080 #測試訪問

提示:如上Service地址10.10.10.51:8080的訪問被自動負載分發到後端兩個Pod。

Service示例2:通過Service配置文件暴露服務。

[root@k8smaster01 study]# vi webappsvc.yaml

  1 apiVersion: v1    2 kind: Service    3 metadata:    4   name: webappservice    5 spec:    6   ports:    7   - port: 8081    8     targetPort: 8080    9   selector:   10     app: webapp

[root@k8smaster01 study]# kubectl create -f webappsvc.yaml

[root@k8smaster01 study]# kubectl get svc | grep -E ‘NAME|webappser’

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

webappservice ClusterIP 10.10.10.83 <none> 8081/TCP 22s

提示:如上Service定義中的關鍵字段是ports和selector。本例中ports定義部分指定了Service所需的虛擬端口號為8081,由於與Pod容器端口號8080不一樣,所以需要再通過targetPort來指定後端Pod的端口號。selector定義部分設置的是後端Pod所擁有的label:app=webapp。

[root@k8smaster01 study]# curl 10.10.10.83:8081 #訪問測試

  • Service負載分發策略:RoundRobin和SessionAffinity

    • RoundRobin:輪詢模式,即輪詢將請求轉發到後端的各個Pod上。
    • SessionAffinity:基於客戶端IP地址進行會話保持的模式,即第1次將某個客戶端發起的請求轉發到後端的某個Pod上,之後從相同的客戶端發起的請求都將被轉發到後端相同的Pod上。

在默認情況下,Kubernetes採用RoundRobin模式對客戶端請求進行負載分發,同時可以通過設置service.spec.sessionAffinity=ClientIP來啟用SessionAffinity策略。這樣,同一個客戶端IP發來的請求就會被轉發到後端固定的某個Pod上了。

通過Service的定義,Kubernetes實現了一種分佈式應用統一入口的定義和負載均衡機制。Service還可以進行其他類型的設置,例如設置多個端口號、直接設置為集群外部服務,或實現為Headless Service(無頭服務)模式。

2.2 多端口Service

有時一個容器應用也可能提供多個端口的服務,那麼在Service的定義中也可以相應地設置為將多個端口對應到多個應用服務。

示例1:如下,Service設置了兩個端口號,並且為每個端口號都進行了命名。

[root@k8smaster01 study]# vi twoportservice.yaml

  1 apiVersion: v1    2 kind: Service    3 metadata:    4   name: webapp    5 spec:    6   ports:    7   - port: 8080    8     targetPort: 8080    9     name: web   10   - port: 8005   11     targetPort: 8005   12     name: management   13   selector:   14     app: webapp

[root@k8smaster01 study]# vi kubednsservice.yaml

  1 apiVersion: v1    2 kind: Service    3 metadata:    4   name: kube-dns    5   namespace: kube-system    6   labels:    7     k8s-app: kube-dns    8     kubernetes.io/cluster-service: "true"    9     kubernetes.io/name: "KubeDNS"   10 spec:   11   selector:   12     k8s-app: kube-dns   13   clusterIP: 169.169.0.100   14   ports:   15   - name: dns   16     port: 53   17     protocol: UDP   18   19   - name: dns-tcp   20     port: 53   21     protocol: TCP

2.3 外部服務Service

在某些環境中,應用系統需要將一個外部數據庫、另一個集群或Namespace中的服務作為服務的後端,則可通過創建一個無Label Selector的Service來實現。

[root@k8smaster01 study]# vi noselectorservice.yaml

  1 apiVersion: v1    2 kind: Service    3 metadata:    4   name: my-service    5 spec:    6   ports:    7   - protocol: TCP    8     port: 80    9     targetPort: 80

[root@k8smaster01 study]# kubectl create -f noselectorservice.yaml

如上定義創建的是一個不帶標籤選擇器的Service,即無法選擇後端的Pod,系統不會自動創建Endpoint,因此需要手動創建一個和該Service對應的Endpoint,用於指向實際的後端訪問地址。

如下所示的Endpoint的定義文件:

[root@k8smaster01 study]# vi noselectorendpoint.yaml

  1 apiVersion: v1    2 kind: Endpoints    3 metadata:    4   name: my-service    5 subsets:    6 - addresses:    7   - IP: 47.96.145.131    8   ports:    9   - port: 80

[root@k8smaster01 study]# kubectl create -f noselectorendpoint.yaml

[root@k8smaster01 study]# kubectl get svc | grep -E ‘NAME|my-service’

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

my-service ClusterIP 10.10.10.211 <none> 80/TCP 3s

[root@k8smaster01 study]# curl 10.10.10.211

clipboard

提示:如上所示,訪問沒有標籤選擇器的Service和帶有標籤選擇器的Service一樣,請求將會被路由到由用戶手動定義的後端Endpoint上。

三 Headless Service

3.1 無頭服務簡介

在某些應用場景中,若需要人為指定負載均衡器,不使用Service提供的默認負載均衡的功能,或者應用程序希望知道屬於同組服務的其他實例。Kubernetes提供了Headless Service來實現這種功能,即不為Service設置ClusterIP(入口IP地址),僅通過Label Selector將後端的Pod列表返回給調用的客戶端。

此場景中,Service就不再具有一個特定的ClusterIP地址,對其進行訪問將獲得包含Label「app=nginx」的全部Pod列表,然後客戶端程序自行決定如何處理這個Pod列表。

例如,StatefulSet就是使用Headless Service為客戶端返回多個服務地址的。

對於「去中心化」類的應用集群,Headless Service非常適合。

3.2 Nginx場景實驗

通過對Headless Service搭建Nginx集群,從而自動實現應用集群的創建。

[root@k8smaster01 study]# vi nginx-service.yaml #創建Service

  1 apiVersion: v1    2 kind: Service    3 metadata:    4   labels:    5     name: nginx-svc    6   name: nginx-svc    7 spec:    8   ports:    9   - protocol: TCP   10     port: 80   11     targetPort: 80   12   selector:   13     name: nginx-demo			#定義selector   14   clusterIP: None

[root@k8smaster01 study]# kubectl create -f nginx-service.yaml

[root@k8smaster01 study]# vi nginx-deployment.yaml

  1 apiVersion: apps/v1    2 kind: Deployment    3 metadata:    4   labels:    5     name: nginx-demo    6   name: nginx-demo    7 spec:    8   replicas: 2    9   selector:   10     matchLabels:   11       name: nginx-demo   12   template:   13     metadata:   14       labels:   15         name: nginx-demo   16     spec:   17       containers:   18       - name: nginx   19         image: nginx:1.7.9   20         ports:   21         - containerPort: 80   22           name: web

[root@k8smaster01 study]# kubectl create -f nginx-deployment.yaml

[root@k8smaster01 study]# kubectl create -f nginx-service.yaml

[root@k8smaster01 study]# kubectl get svc -o wide

[root@k8smaster01 study]# kubectl get pods -o wide

[root@k8smaster01 study]# nslookup nginx-svc.default.svc.cluster.local 10.10.190.170

clipboard

提示:由上可知,通過解析SVC的地址,直接解析出來的為Pod的IP。