Service服务发现
微服务意味着存在更多的独立服务,但他们并非独立的个体,而是存在着复杂的依赖关系且彼此之间通常需要进行非常频繁的交互和通信的群体。然而,建立通信之前,服务和服务之间该如何获知彼此的地址呢?在K8S系统上,service为Pod中的服务类应用提供了一个稳定的访问入口,但Pod客户端中的应用如何得知某个特定Service资源的IP和端口呢?这个时候就需要引入服务发现的机制
服务发现概述
简单来说,服务发现就是服务或者应用之间互相定位的过程。不过,服务发现并非新概念,传统的单体应用架构时代也会用到,只不过单体应用的动态性不强,更新和重新发布的频度较低,通常以月甚至以年计,基本上不会进行自动伸缩,因此服务发现的概念无须显性强调。在传统的单体应用网络位置发生变化时,由IT运维人员手工更新一下相关的配置文件基本上就能解决问题。但在微服务应用场景中,应用呗拆分成众多的小服务,他们按需创建且变动频繁,配置信息基本无法事先写入配置文件中并及时跟踪和反映动态变化,因此服务发现的重要性便随之凸显 服务发现机制的基本实现,一般是实现部署好一个网络位置较为稳定的服务注册中心(也称为服务总线),服务提供者(服务端)向注册中心注册自己的位置信息,并在变动后及时予以更新,相应的,服务消费者则周期性的从注册中心获取服务提供者的最新位置信息从而”发现”要访问的目标服务资源。复杂的服务发现机制还能够让服务提供者提供其描述信息,状态信息及资源使用信息等,以供消费者实现更为复杂的服务选择逻辑 实践中,根据服务发现过程的实现方式,服务发现还可分为两种类型:客户端发现和服务端发现
- 客户端发现:由客户端到服务注册中心发现其依赖到的服务的相关信息,因此,它需要内置特定的服务发现程序和发现逻辑
- 服务端发现:这种方式需要额外用到一个称为中央路由器或者服务均衡器的组件;服务消费者将请求发往中央路由器或者负载均衡器,由他们负责查询服务注册中心获取服务提供者的位置信息,并将服务消费者的请求转发给服务提供者
K8S支持两种基本的服务发现模式—环境变量和DNS
服务发现方式:环境变量
创建Pod资源时,kubelet会将其所属名称空间内的每个活动的Service对象以一系列环境变量的方式注入其中。它支持使用K8S Server环境变量以及与docker的links兼容的变量
- K8S Server环境变量:K8S为每个Service资源生成包含以下形式的环境变量在内的一系列环境变量,在同一名称空间中创建的Pod对象都会自动拥有这些变量
- {SVCNAME}_SERVICE_HOST
- {SVCNAME}_SERVICE_PORT
- 注意:如果SVCNAME中使用了连接线,那么K8S会在定义为环境变量时将其转换为下划线
- Docker Link形式的环境变量:Docker使用—link选项实现容器连接时所设置的环境变量形式,具体使用方式请参考Docker的相关文档。在创建Pod对象时,K8S也会将与此形式兼容的一系列环境变量注入Pod对象中
例如,在Service资源myapp-svc创建后创建的Pod对象中查看可用的环境变量,其中以MYAPP_SVC_SERVICE开头的表示k8s service环境变量,名称中不包含“SERVICE”字符串的环境变量为Docker link形式的环境变量
/ # printenv |grep MYAPP
MYAPP_SVC_PORT_80_TCP_ADDR=10.99.97.97
MYAPP_SVC_PORT_80_TCP_PORT=80
MYAPP_SVC_PORT_80_TCP_PROTO=tcp
MYAPP_SVC_PORT_80_TCP=tcp://10.99.97.97:80
MYAPP_SVC_SERVICE_HOST=10.99.97.97
MYAPP_SVC_PORT=tcp://10.99.97.97:80
MYAPP_SVC_SERVICE_PORT=80
/ #
基于环境变量的服务发现其功能简单、易用,但存在一定的局限性,例如,仅有那些与创建的Pod对象在同一名称空间中且实现存在的Service对象的信息才会以环境变量的形式注入,那些处于非同一名称空间,或者是在Pod资源创建之后才创建的Service对象的相关环境变量则不会被添加。而基于DNS的发现机制并不存在此类限制
ClusterDNS和服务发现
K8S系统之上用于名称解析服务发现的ClusterDNS是集群的核心附件之一,集群中创建的每个Service对象,都会尤其自动生成相关的资源记录。默认情况下,集群内各Pod资源会自动配置其作为名称解析服务器,并在其DNS搜索列表中包含它所属的名称空间的域名后缀 无论是使用kubeDNS还是CoreDNS,它们提供的基于DNS的服务发现发现解决方案都会负责解析以下资源记录(resource record)类型以实现服务发现
- 拥有clusterIP的Service资源,需要具有以下类型的资源记录
- A记录:..svc.. IN A
- SRV记录:.…svc.. IN SRV ..svc.
- PTR记录:....in-addr.arpa. IN PTR ..svc.
- Headless类型的Service资源,需要具有以下类型的资源记录
- A记录:..svc.. IN A
- SRV记录:.…svc.. IN SRV …svc.
- PTR记录:....in-addr.arpa. IN PTR …svc.
- ExternaIname类型的Service资源,需要具有CNAME类型的资源记录
- CNAME记录:..svc.. IN CNAME
名称解析和服务发现是K8S系统许多功能得以实现的基础服务,它通常是集群安装完成后应该立即部署的附加组件。如果使用kubeadm初始化集群,它会自动进行部署
服务发现方式:DNS
创建Service资源对象时,ClusterDNS会为它自动创建资源记录用于名称解析和服务注册,于是,Pod资源可直接使用标准的DNS名称来访问这些Service资源。每个Service对象相关的DNS记录包含如下两个
- {SVCNAME}.{NAMESPACE}.{CLUSTER_DOMAIN}
- {SVCNAME}.{NAMESPACE}.svc.{CLUSTER_DOMAIN}
在前面的部署章节中,有一个参数用于指定--cluster-dns
用于指定集群DNS服务的工作地址--cluster-domain
用于指定集群使用的本地域名,因此,系统初始化时默认会将--cluster.local.
和主机所在域作为DNS的本地域名,这些信息会在Pod创建时以DNS配置的相关信息注入它的/etc/resolv.conf配置文件中。例如,使用交互式进入Pod资源查看其配置
[root@k8s-master01 nginx]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-8858448d-97x7n 1/1 Running 0 3d3h
myapp-8858448d-dj2tr 1/1 Running 0 3d3h
myapp-8858448d-jr2wn 1/1 Running 0 3d3h
myapp-8858448d-qs76p 1/1 Running 0 3d3h
myapp-8858448d-xbjcc 1/1 Running 0 3d3h
[root@k8s-master01 nginx]# kubectl exec -ti myapp-8858448d-97x7n -- /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.99.110.110
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options ndots:5
/ #
上述search参数中指定的DNS各搜索域,是以次序指定的几个域名后缀,具体所示如下
- {NAMESPAE}.svc.{CLUSTER_DOMAIN};如default.svc.cluster.local
- svc.{CLUSTER_DOMAIN};如:svc.cluster.local
- {CLUSTER_DOMAIN};如:cluster.local
- {WORK_NODE_DOMAIN}: 如:.
例如,在前面创建的Pod中尝试使用请求解析myapp-svc的相关DNS记录
/ # nslookup myapp-svc.default
nslookup: can't resolve '(null)': Name does not resolve
Name: myapp-svc.default
Address 1: 10.99.97.97 myapp-svc.default.svc.cluster.local
/ #
/ # curl myapp-svc
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
/ #