本章内容包括:
- 创建服务资源,利用单个地址访问一组pod
- 发现集群中的服务
- 将服务公开给外部客户端
- 从集群内部连接外部服务
- 控制pod与服务关联
- 排除服务故障
上一章学习了如何通过ReplicaSet
以及类似的资源部署运行pod的事。尽管特定的pod可以独立的应对外部刺激,但现在大多数的应用都需要根据外部请求做出响应。例如,就微服务而言,通常需要对集群内部其他应用,以及来自集群外部客户端的HTTP请求做出响应。
pod需要一种寻找其他pod的方法来使用其他pod提供的服务。如果没有kubernetes,系统管理员需要在应用的配置文件中明确指出服务的精确的IP地址或者主机名来配置每个客户端应用,但在kubernetes中不适用这种方法:
- pod是短暂的:它们随时会启动或者关闭,无论是为了给其他pod提供空间而从节点中移除,或者是pod的副本数增加或减少,以及节点异常导致的pod变动。
- kubernetes会在pod启动前给已经调度到节点上的pod分配IP地址,因此客户端不可能提前知道提供服务pod的IP地址。
- 水平伸缩意味着多个pod可以提供相同的服务,每个pod都有自己的地址。
为了解决以上问题,kubernetes提供了一种解决方法----服务(service)。
1 服务介绍
kubernetes服务是一种为一组功能相同的pod提供单一不变的接入点的资源。当服务存在时,它的IP和端口不会改变。客户端通过IP地址和端口号建立连接,这些连接会被路由到提供该服务的任意一个pod上。通过这种方式,客户端不需要知道每个提供服务的pod的地址,这样这些pod就能被随意的在集群中增加或删除。
举个例子,现在有三个pod对外提供前端服务,一个pod为前端提供后端服务,结合上服务如下图所示:
2 创建服务
服务的后端可能不止一个pod。服务的连接对所有的后端pod是负载均衡的,可以使用标签选择器来指定哪些pod属于同一组。
创建服务最简单的办法就是使用kubectl expose
,但推荐使用yaml描述文件来创建服务。
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- port: 80 # 服务可用端口
targetPort: 8080 # 转发到的容器端口
protocol: TCP # 默认TCP,支持TCP/UDP/SCTP
selector:
app: kubia # 转到具有指定标签的pod上,不指定将无法转发流量
以上的yaml创建了一个叫kubia的Service资源,对外开放80端口,将外部请求转发到具有app: kibia
标签的pod上的8080端口。
创建好后,我们来查看一下服务的信息
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 13d
创建好之后,看到SVC有一个集群地址,类型是ClusterIP
,这个因为只是集群地址,所以只能集群内部访问。
测试服务
可以通过以下几种方法向服务发送请求:
- 创建一个pod,它将请求发送到服务的集群IP并记录响应。可以通过查看pod日志检查服务的响应。
- 使用ssh远程登录到一个Kubernetes节点上使用curl命令。
- 通过
kubectl exec
命令在一个已经存在的pod上执行curl命令。
在运行的容器中执行命令
# -- 代表这kubectl命令项的结束
# 如果不适用--,那么后面的-s会被解析成kubectl的选项,意思为连接到一个http://10.96.0.1的API服务器
[root@master ~]# kubectl exec kubia-97qvk -- curl -s http://10.96.0.1
You've hit kubi-gzwik
以上命令在执行时的顺序为:
- 利用Kubernetes执行curl命令,curl命令向一个后端有三个pod服务的IP发送了HTTP请求;
- Kubernetes服务代理截取该连接,在三个pod中随机选取一个pod,将请求转发给它;
- Node.js在pod中运行处理请求,并返回带有pod名称的HTTP响应;
- curl命令向标准输出打印返回值,该返回值被kubectl截取并打印到主机的标准输出。
会话亲和性
如果执行多次同样的命令,每次都会随机向不同的pod请求。因为服务代理通常将每个连接随机指向选择后端pod中的一个,即使连接来自于同一个客户端。如果想要把同一个客户端IP产生的请求每次都指向同一个pod,可以设置服务的sessionAffinity
属性为ClientIP
(默认为None)。
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600 # 亲和性过期时间
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
Kubernetes服务并不支持基于cookie的亲和性,因为服务不在HTTP层面上工作。服务处理TCP和UDP包,并不关心其中的载荷内容,而cookie是HTTP协议中的一部分。Web服务器可能会使用长连接导致就算不设置会话亲和性,请求也每次会打到同一个pod上。
同一个服务暴露多个端口
创建的服务可以暴露一个端口也可以暴露多个端口。创建一个有多端口的服务时,必须给每个端口指定名字。
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
selector:
app: kubia
这样创建的服务就能把80端口的流量转发给容器8080端口,443端口转发给8443端口。标签选择器应用基于整个服务,不能对每个端口做单独的配置。如果不同的pod有不同的端口映射关系,需要创建两个服务。
使用命名的端口
可以在pod的定义中给容器端口命名,这样就能在服务的声明中使用名字来指定特定的端口号,这样会使得服务更加清晰。
apiVersion: V1
kind: pod
metadata:
name: kubia
spec:
containers: