Service 是 k8s 的核心概念,通過創建Service,可以為一組具有相同功能的容器應用提供一個統一的入口地址,並且將請求負載分發到后端的各個容器應用上。
Service 的定義
Service YAML格式的定義文件如下:
apiVersion: v1 // Required
kind: Service // Required
metadata: // Required
name: string // Required
namespace: string // Required
labels:
- name: string
annotations:
- name: string
spec: // Required
selectors: [] // Required
type: string // Required
clusterIP: string
sessionAffinity: string
ports:
- name: string
protocol: string
port: int
targetPort: int
nodePort: int
status:
loadBalancer:
ingress:
ip: string
hostname: string
各屬性的說明:
Service的基本用法
一般k8s的Pod都會以RC或者Deployment對外進行發布,並使用TCP/IP+Port的方式使得外部可以訪問內部得服務。例如一個提供Web服務RC,由兩個tomcat容器組成,每個容器都通過containerPort設置服務得端口號為8080。
# webapp-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: webapp
spec:
replicas: 2
template:
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080 ## containerPort 要和 鏡像暴露得端口要一直。
創建該RC:
kubectl apply -f webapp-rc.yaml
創建成功之后:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webapp-nfrzq 1/1 Running 0 96s 10.244.1.44 k8s-node1 <none> <none>
webapp-sxlmk 1/1 Running 0 96s 10.244.0.73 k8s-master <none> <none>
這樣就可以使用Pod得IP+Port得方式訪問Tomcat服務了:
# curl 10.244.1.44:8080
# curl 10.244.0.73:8080
但是以這種方式訪問服務是有問題得,首先我們需要知道每個Pod得地址,如果該Pod故障,就需要切換使用另一個Pod得IP。所以k8s中Service組件就是用於解決這些問題的。
Service的簡單使用
創建Service有兩種方法,使用命令kubectl export
或者根據定義文件創建。
先來使用命令創建Service,例如為之前的RC提供一個Service,使用命令如下:
[root@k8s-master service]# kubectl expose rc webap
[root@k8s-master service]# kubectl get svc webapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp ClusterIP 10.107.38.14 <none> 8080/TCP 27s
創建Service后就可以直接使用Service的IP:Port的格式訪問服務,請求會被自動負載分發到后端的Pod上。
我們還可以使用yaml定義文件創建Service,定義文件如下:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8081
targetPort: 8080
selector:
app: webapp
創建該Service:
[root@k8s-master service]# kubectl create -f webapp-svc.yaml
service/webapp created
[root@k8s-master service]# kubectl get svc webapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp ClusterIP 10.109.63.40 <none> 8081/TCP 7s
目前k8s提供了兩種負載分發策略:
- RoundRobin:輪詢模式,即輪詢將請求轉發到后端的各個Pod上。
- SessionAffinity:基於客戶端IP地址進行會話保持的模式,即第1次將某個客戶端發起的請求轉發到后端的某個Pod上,之后從相同的客戶端發起的請求都將被轉發到后端相同的Pod上。
默認情況下,k8s采用RoundRobin模式,通過設置參數service.spec.sessionAffinity=ClientIP來啟用SessionAffinity策略。
多端口Service
Service 支持設置多個端口對應到多個應用服務,配置文件如下:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8080
targetPort: 8080
name: web
- port: 8005
targetPort: 8005
name: management
selector:
app: webapp
也可以指定協議:
...
spec:
ports:
- port: 53
procotol: UDP
name: dns
- port: 53
procotol: TCP
name: dns-tcp
...
外部服務service
外部服務Service可以將k8s集群外的應用納入到集群服務,可以通過創建一個無Label Selector的Service實現:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- port: 8080
procotol: TCP
targetPort: 80
同時還需要創建一個和該Service同名的Endpoint資源,用於指向實際的后端訪問地址,配置文件如下:
apiVersion: v1
kind: Endpoint
metadata:
name: my-service
subsets:
- address:
- IP: 1.2.3.4
ports:
- port: 80
Headless Service
k8s支持用戶自定義負載均衡的策略,k8s提供了Headless Service來實現這種功能,即不為Service設置ClusterIP(入口IP地址),僅通過Label Selector將后端的Pod列表返回給調用的客戶端。例如:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 80
clusterIP: None
selector:
app: nginx
這樣,Service就不再具有一個特定的clusterIP,對其訪問將獲得包含Label “app=nginx”的全部Pod列表,然后客戶端程序自行決定如何處理這個Pod列表。StatefulSet就是使用Headless Service為客戶端返回多個服務地址的,而且Headless Service十分適用於“去中心化”類的應用集群。
從集群外部訪問Pod或者Service
由於Pod和Service都是k8s集群范圍內的虛擬概念,所以集群外的客戶端無法通過Pod的IP地址或者Service的虛擬IP地址訪問。因此可以將Pod或者Service的端口號映射到宿主機,讓容器外部的客戶端也可以訪問。
將容器應用的端口號映射到物理機
1.設置容器級別的hostPort
# pod-hostport.yaml
apiVersion: v1
kind: Pod
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080
hostPort: 9000
創建Pod
kubectl create -f pod-hostport.yaml
創建完成后就可以訪問k8s node的IP:Port。
2.設置Pod級別的hostNetwork=true
該Pod中所有容器的端口號都將被直接映射到物理機上。在設置hostNetwork=true時需要注意,在容器的ports定義部分如果不指定hostPort,則默認hostPort等於containerPort,如果指定了hostPort,則hostPort必須等於containerPort的值:
## pod-hostnetwork.yaml
apiVersion: v1
kind: Pod
metadata:
name: webapp
labels:
app: webapp
spec:
hostNetwork: true
containers:
- name: webapp
image: tomcat
imagePullPolicy: Never
ports:
- containerPort: 8080
將Service的端口號映射到物理機
1.設置nodePort映射到物理機
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
nodePort: 31080 # 默認有效值 30000 - 32767
selector:
app: webapp
對該Service的訪問也將被負載分發到后端的多個Pod上。
2.設置LoadBalancer映射到雲平台
這種用法僅用於在公有雲服務提供商的雲平台上設置Service的場景。 在下面的例子中, status.loadBalancer.ingress.ip設置的146.148.47.155為雲服務商提供的負載均衡器的IP地址。 對該Service的訪問請求將會通過LoadBalancer轉發到后端Pod上, 負載分發的實現方式則依賴於雲服務商提供的LoadBalancer的實現機制:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
nodePort: 30061
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155