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
直接通過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
提示:如上所示,訪問沒有標籤選擇器的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
提示:由上可知,通過解析SVC的地址,直接解析出來的為Pod的IP。