k8s-Service(服务)
在k8s的世界里,虽然每个Pod都会被分配一个单独的IP地址,这个IP地址会随时Pod的销毁而消失。
这就引出一个问题:如果有一组Pod组成一个集群来提供服务,那么如何来访问它们呢?k8s的Service(服务)就是用来解决这个问题的核心概念。
一个Service可以看作一组提供相同服务的Pod的对外访问接口。Service作用于哪些Pod是通过Label Selector来定义的。
1. 对Service的定义
对Service的定义同样使用Yaml或Json格式的配置文件来完成。以redis-slave服务的定义为例:
apiVersion: v1
kind: Service
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
ports:
- port: 6379
selector:
name: redis-slave
通过该定义,k8s将会创建一个名为“redis-slave”的服务,并在6379端口上监听。
spec.selector的定义表示该Service将包含所有具有"name=redis-slave"的Label的Pod。
在Pod正常启动后,系统将会根据Service定义创建出与Pod对应的Endpoint(端点)对象,以建立起Service与后端Pod的对应关系。
随着Pod的创建、销毁,Endpoint对象也将被更新。Endpoint对象主要有Pod的IP地址和容器所需监听的端口号组成。
2.Service的内部访问方式: Pod的IP地址和Service的Cluster IP地址
Pod的IP地址是Docker Daemon根据docker0网桥的IP地址段进行分配的,但Service的Cluster IP地址是k8s系统中的虚拟IP地址,由系统动态分配。
Service的Cluster IP地址相对于Pod的IP地址来说相对稳定,Service被创建时即被分配一个IP地址,在销毁该Service之前,这个IP地址都不会再变化了。
而Pod在k8s集群中生命周期较短,可能被ReplicationContrller销毁、再次创建,新创建的Pod将会分配一个新的IP地址。
3.Service的内部访问方式: 外部访问Service
由于Service对象在Cluster IP Range池中分配到的IP只能在内部访问,所以其他Pod都可以无障碍地访问到它。
如果这个Service作为前端服务,准备为集群外的客户端提供服务,我们就需要给这个服务提供公共IP了。
k8s支持两种对外提供服务的Service的type定义:NodePort和LoadBalancer。
3.1. NodePort
在定义Service时指定spec.type=NodePort,并指定spec.ports.nodePort的值,系统就会在k8s集群中的每个Node上打开一个主机上的真实端口号。这样,能够访问Node的客户端都就能通过这个端口号访问到内部的Service了。
以php-frontend service的定义为例,nodePort=80,这样,在每一个启动了该php-frontend Pod的Node节点上,都会打开80端口。
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
name: frontend
spec:
type: NodePort
ports:
- port: 80
nodePort: 30001
selector:
name: frontend
3.2. LoadBalancer
如果云服务商支持外接负载均衡器,则可以通过spec.type=LoadBalaner定义Service,同时需要指定负载均衡器的IP地址。使用这种类型需要指定Service的nodePort和clusterIP。例如:
apiVersion: v1
kind: Service
metadata: {
"kind" "Service",
"apiVersion": "v1",
"metadata": {
"name": "my-service"
},
"spec": {
"type": "LoadBalaner",
"clusterIP": "10.0.171.239",
"selector": {
"app": "MyApp"
},
"ports": [
{
"protocol": "TCP",
"port": 80,
"targetPort": 9376,
"nodePort": 30061
}
],
},
"status": {
"loadBalancer": {
"ingress": [
{
"ip": "146.148.47.155"
}
]
}
}
}
在这个例子中,status.loadBalancer.ingress.ip设置的146.146.47.155为云服务商提供的负载均衡器的IP地址。
之后,对该Service的访问请求将会通过LoadBalancer转发到后端Pod上去,负载分发的实现方式则依赖于云服务上提供的LoadBalancer的实现机制。