Pod的讲解终于告一段落了,现在我们来一起学习Service。
Service是Kubernetes的核心概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上。本章对Service的使用进行详细说明,包括Service的负载均衡机制、如何访问Service、Headless Service、DNS服务的机制和实践、Ingress 7层路由机制等。
Service的yaml定义文件完整内容如下:
apiVersion: v1 // Required
kind: Service // Required
metadata: // Required
name: string // Required
namespace: string // Required
labels:
- name: string
annotations:
- name: string
spec: // Required
selector: [] // 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
属性名称 | 取值类型 | 是否必选 | 取值说明 |
---|---|---|---|
apiVersion | string | 是 | v1 |
kind | string | 是 | Service |
metadata | object | 是 | 元数据 |
metadata.name | string | 是 | Service名称 |
metadata.namespace | string | 是 | 命名空间,不指定默认为default |
metadata.labels[] metadata.annotation[] |
list list |
自定义标签属性列表 自定义注释属性列表 |
|
spec | object | 是 | 详细描述 |
spec.selector[] | list | 是 | label selector配置,将选择具有指定label标签的Pod 作为管理范围 |
spec.type | string | 是 | Service的类型,指定Service的访问方式,默认值为ClusterIP。 1.Cluster IP:虚拟的服务IP地址,该地址用于K8s集群内部的Pod访问,在Node上kube-prox通过设置的optables规则进行转发。 2.NodePort: 使用宿主机的端口,使能够访问各Node的外部客户端通过Node的IP地址和端口号就能访问服务。 3. LoadBalancer: 使用外接负载均衡器完成到服务的负载分发,需要在spec.status.loadBalancer字段指定外部负载均衡器的IP地址,并同时定义nodePort和clusterIP,用于公有云环境。 |
spec.clusterIP | string | 虚拟服务IP地址,当type=clusterIP时,如果不指定。这系统进行自动分配,也可以手工指定:当type=loadBalancer时,这需要指定 | |
spec.sessionAffinity | string | 是否支持Session,可选值为ClusterIP,默认值为空。 clusterIP: 表示同一个客户端的访问请求都转发到同一个后端Pod |
|
spec.ports[] | list | Service需要暴露的端口列表 | |
spec.ports[].name | string | 端口名称 | |
spec.ports[].protocol | string | 端口协议,支持TCP和UDP。默认为TCP | |
spec.ports[].port | init | 服务监听的端口号 | |
spec.ports[].targePort | int |
需要转发到后端Pod的端口号 | |
spec.ports[].nodePort | int |
当spec.type=NodePort时,指定映射到物理机的端口号 |
|
status | object | 当spec.type=LoadBalancer时,设置外部负载均衡的地址,用于公有云环境 | |
status.loadBalancer | object | 外部负载均衡器 | |
status.loadBalancer.ingress | object | 外部负载均衡器 | |
status.loadBalancer.ingress.ip | string | 外部负载均衡器的IP地址 | |
status.loadBalancer.ingress.hostname | string | 外部负载均衡器的主机名 |
一般来说,对外提供服务的应用程序需要通过某种机制来实现,对于容器应用最简便的方式就是通过TCP/IP机制及监听IP和端口号来实现。例如,定义一个提供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
创建该RC之后获取该Pod的IP地址
kubectl get pod -l app=webapp -o yaml | grep podIP
可以直接通过这两个Pod的IP地址和端口号访问Tomcat服务:
直接通过Pod的IP地址和端口号可以访问到容器应用内的服务,但是Pod的IP地址是不可靠的,例如当Pod所在的Node发生故障时,Pod将被Kubernetes重新调度到另一个Node,Pod的IP地址将发生变化。更重要的是,如果容器应用本身是分布式的部署方式,通过多个实例共同提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。Kubernetes中的Service就是用于解决这些问题的核心组件。
以前面创建的webapp应用为例,为了让客户端应用访问到两个Tomcat Pod实例,需要创建一个Service来提供服务。Kubernetes提供了一种快速的方法,即通过kubectl expose命令来创建Service:
kubectl expose rc webapp
查看新创建的Service,可以看到系统为它分配了一个虚拟的IP地址(ClusterIP),Service所需的端口号则从Pod中的containerPort复制而来:
接下来就可以通过Service的IP地址和Service的端口号访问该Service了:
这里,对Service地址10.111.79.254:8080的访问被自动负载分发到了后端两个Pod之一。
除了使用kubectl expose命令创建Service,我们也可以通过配置文件定义Service,再通过kubectl create命令进行创建。例如对于前面的webapp应用,我们可以设置一个Service,代码如下:
apiVersion: v1
kind: Service
metadata:
name: webapp1
spec:
ports:
- port: 8081
targetPort: 8080
selector:
app: webapp
Service定义中的关键字段是ports和selector。本例中ports定义部分指定了Service所需的虚拟端口号为8081,由于与Pod容器端口号8080不一样,所以需要再通过targetPort来指定后端Pod的端口号。selector定义部分设置的是后端Pod所拥有的label:app=webapp。
目前Kubernetes提供了两种负载分发策略:RoundRobin和SessionAffinity,具体说明如下:
◎ RoundRobin:轮询模式,即轮询将请求转发到后端的各个Pod上。
◎ SessionAffinity:基于客户端IP地址进行会话保持的模式,即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上。
在默认情况下,Kubernetes采用RoundRobin模式对客户端请求进行负载分发,但我们也可以通过设置service.spec.sessionAffinity=ClientIP来启用SessionAffinity策略。这样,同一个客户端IP发来的请求就会被转发到后端固定的某个Pod上了。
通过Service的定义,Kubernetes实现了一种分布式应用统一入口的定义和负载均衡机制。Service还可以进行其他类型的设置,例如设置多个端口号、直接设置为集群外部服务,或实现为Headless Service(无头服务)模式。
有时一个容器应用也可能提供多个端口的服务,那么在Service的定义中也可以相应地设置为将多个端口对应到多个应用服务。在下面的例子中,Service设置了两个端口号,并且为每个端口号都进行了命名:
apiVersion: v1
kind: Service
metadata:
name: webapp2
spec:
ports:
- port: 8080
targetPort: 8080
name: web
- port: 8005
targetPort: 8005
name: management
selector:
app: webapp
另一个例子是两个端口号使用了不同的4层协议—TCP和UDP:
apiVersion: v1
kind: Service
metadata:
name: webapp3
spec:
ports:
- port: 8080
protocol: UDP
name: tdp
- port: 8080
protocol: TCP
name: tcp
selector:
app: webapp
在某些环境中,应用系统需要将一个外部数据库作为后端服务进行连接,或将另一个集群或Namespace中的服务作为服务的后端,这时可以通过创建一个无Label Selector的Service来实现:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
通过该定义创建的是一个不带标签选择器的Service,即无法选择后端的Pod,系统不会自动创建Endpoint,因此需要手动创建一个和该Service同名的Endpoint,用于指向实际的后端访问地址。创建Endpoint的配置文件内容如下:
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 8080
访问没有标签选择器的Service和带有标签选择器的Service一样,请求将会被路由到由用户手动定义的后端Endpoint上。
小结:
本节内容到此结束,是不是觉得很简单呢?
觉得简单,说明我们已经入k8s的门了,继续加油!
多多点关注哦~