为什么需要 Service:
每个Pod被创建后,都会有它自己的IP,但由于Pod存在生命周期,经常会被创建与销毁,一旦重新创建POD,该POD的IP就会发生变化。所以,这些POD应用需要对外提供一个统一的访问入口,即固定IP,外部才能正常访问,这个统一的访问入口就是service。
Service 定义:
我们可以将多个POD划分到同一个逻辑组中,并统一向外提供服务,这个逻辑组就是一个service。POD是通过Label Selector加入到指定的service中。Service相当于是一个负载均衡器,用户请求会先到达service,再由service转发到它内部的某个POD上(默认以round-robin方式转发)。
请求(集群内部或外部) -> Service -> POD1,POD2,...
一、Service四种类型
k8s中共有4种类型的service,不同service类型是通过 services.spec.type 字段来指定。
- ClusterIP:用于集群内部访问。该类型会为service分配一个IP,集群内部请求先到达service,再由service转发到其内部的某个POD上。
- NodePort:用于集群外部访问。该类型会将Service的Port映射到集群的每个Node节点上,然后在集群之外,就能通过Node节点上的映射端口访问到这个Service。
- LoadBalancer:用于集群外部访问。该类型是在所有Node节点前又挂了一个负载均衡器,作为集群外部访问的统一入口,外部流量会先到达LoadBalancer,再由它转发到集群的node节点上,通过nodePort再转发给对应的service,最后由service转发到后端Pod中。
- ExternalName:创建一个DNS别名(即CNAME)并指向到某个Service Name上,也就是为某个Service Name添加一条CNAME记录,当有请求访问这个CNAME时会自动解析到这个Service Name上。
Headless Service:
Service作为访问入口,它可以用 IP 或 service_name(要借助于内部DNS解析)这两种方式来访问。当创建好service时,系统会自动为Service分配一个IP,我们也可以通过services.spec.clusterIP 字段手动指定一个IP,但若这个字段设置为None,系统就不会再为这个service分配IP,后面就只能通过service_name来访问,而这种没有IP的service,就被称为Headless Service。
也就是,Headless表示这个service没有分配IP信息,只能通过service_name来访问。
service字段说明:
services.spec.
clusterIP: 设置service的IP。若省略该字段,默认由系统随机分配
ports:
port: 设置service的访问端口
targetPort: 设置service下的后端POD端口
nodePort: 设置映射到node节点上的端口。当type为NodePort或LoadBalancer时,需要为service指定一个映射在Node节点上的端口
selector: 设置标签选择器。POD根据匹配此标签来加入到此service下
type: 定义service类型,可选值有ClusterIP(默认), NodePort, LoadBalancer, ExternalName
sessionAffinity: 可选值是ClientIP或None。默认值为None,表示以round-robin方式将请求转发到后端Pod,若值为ClientIP,表示保持连接,请求始终会被调度到同一个Pod上
1. ClusterIP类型
需要将 services.spec.type 设置为 ClusterIP,该类型会为service分配一个IP,这个IP只能在集群内部访问,用户请求先到达service,再由service转发到其内部的某个POD上。
默认service是以round-robin方式将请求转发到后端Pod,若始终要转发到同一个POD上,需要设置services.spec.sessionAffinity。
请求(集群内部)---> service_ip/service_name:8080 -> POD1:80,POD2:80,POD3:80
示例1:带有IP的Service
第一步:创建一个ClusterIP类型的Service(手动指定IP或省略ClusterIP字段)
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector: #设置标签选择器。POD通过匹配此label来选择是否要加入到这个Service下
app: myweb01
type: ClusterIP #设置service类型为ClusterIP
ClusterIP: 172.17.202.174 #为service指定一个IP,这个IP在集群内部可访问。若省略该字段,系统会自动分配一个IP
ports:
- name: nginx
port: 8080
protocol: TCP
targetPort: 80
sessionAffinity: None
第二步:创建POD(加入到此service中)
apiVersion: apps/v1
kind: Deployment
metadata:
name: svc01-pod-demo
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myweb01
template:
metadata:
labels:
app: myweb01
spec:
containers:
- name: myweb01
image: nginx:1.7.9
ports:
- name: http
containerPort: 80
第三步:查看Service
$ kubectl get svc my-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 172.17.202.174 <none> 8080/TCP 4s
$ kubectl describe svc my-service
Name: my-service
Namespace: default
Labels: <none>
Annotations: ...
Selector: app=myweb01 #service会通过匹配此label,选择哪些POD加入到这个service下
Type: ClusterIP
IP: 172.17.2.14
Port: nginx 8080/TCP
TargetPort: 80/TCP
Endpoints: 172.16.1.12:80,172.16.2.17:80,172.16.2.45:80 #表示当前已加入到此service下的POD
Session Affinity: None
Events: <none>
第四步:集群内部访问Service
- 直接访问Service的IP;
- 直接访问Service的Name(依靠DNS解析):同一个Namespace下直接通过servicename访问,跨Namespace访问需要加上namespace,如{servicename}.{namespace};
- 通过环境变量访问:将service的一些IP、端口等信息,通过环境变量方式传到Pod里;
集群内的Pod和Node
|
| curl 172.17.2.14:8080
| curl my-service:8080
| curl $service_name:$service_port
|
——————↓———————
| my-service |
| 172.17.2.14 |
'——————↓———————'
|
____________________|_________________________
| / | \ |
| / | \ |
| POD1 | POD3 |
| 172.16.1.12:80 | 172.16.2.17:80 |
| POD2 |
| 172.16.2.45:80 |
|