YAZONG 我的开源

Kubernetes(六)(迁移Kubernetes前准备)(6.4)kubernetes的服务发现

  , , ,
0 评论0 浏览

运行在K8S的服务之间怎样来通信,如何访问到彼此?服务没有固定的IP,运行方式跟以前不同。

服务之间的通信、访问和发现必须是要解决的问题。

一共有三种场景。各种通信情况就迎刃而解了。

第一种:集群内部。

第二种:集群内访问集群外的服务。

第三种:集群外访问集群内部的服务。

面对这些通信问题,K8S提供了哪些技术方案,来支持这些问题?

集群内部

=============第一种情况的第一种1.1方案:DNS+ClusterIP

image.png

具体业务肯定运行在容器POD中:PODA。

业务要访问集群内的一个服务:PODB。

image.png

在PODA知道PODB的IP,直接访问,但是PODB的IP肯定是不稳定的,经常会变化。

K8S提出一个概念,叫service,service里可以有一个clusterIP(这里的service类型是clusterIP),这个service可以指向多个POD(通过label selector去选择集群内的POD),这个PODA就可以通过service的虚拟IP,通过service自带的负载均衡去访问PODB的各个实例,这样的话,在PODA里面,就可以去写上service的IP,serviceIP是一个相对固定的IP地址,不管PODB如何重启,只要service没有被删掉,那么它的IP就是永远不变的。

image.png

如果在程序中布满了serviceIP,显然也不是特别优雅的方式,所以K8S又提供了一层DNS,DNS可以让我们通过service的名字去访问到service,比如这个service叫SerB,这个DNS就可以解析到这个serviceIP,从而实现对PODB的访问。然后这个PODA就可以通过DNS直接在程序里面配置SerB的名字,通过这个名字就可以访问到PODB了。

=============第一种情况的第二1.2种方案:HeadlessService

image.png

还有一种情况,当service的负载均衡方式不满足我们的需求,当需要自定义负载均衡的策略,或者是多个实例,多个实例之间它们有交互的时候,比如说一些集中化的应用,要彼此之间访问通讯,去确定一个主节点,它们就需要知道当前的服务列表具体由哪些POD,在这种情况下,上述1.1就不满足需求了。

比如有一个PODC,也有很多的实例,K8S有一个HeadlessService,这个HeadlessService的特点是并不会帮助你去轮询或负载均衡的方式去访问具体的POD,而是会把具体的POD列表返回给你,当PODA去访问HeadlessService的时候,HeadlessService会把POD具体的ENDPOINTS(一系列的PODC)返回给客户端PODA,PODA拿到列表之后,可以自己进行负载均衡或者其他的一些策略。

集群内访问集群外的服务

这是第二种情况的第一种2.1方案:最直观、最简单的方式:PODA去访问mysql,固定的IP+PORT。

image.png

这是第二种情况的第二种2.2方案:还可以像访问其他集群的内部的服务一样,去访问mysql。去定义一个service,并不会通过label selector去选择集群内的POD,这是一个空的service,手工的去定义一个EndPoint对象,EndPoint的名字跟service是一样的,所以EndPoint会和service绑定在一起,然后POD可以通过DNS,找到serviceIP,找到对应的EndPoint,EndPoint中配置的是一个具体的外部服务的地址。

所以说当PODA通过这种方式访问mysql的时候,在配置DNS里面直接写上名字就可以了,当mysql的信息变化的时候,只需要修改EndPoint的内容就可以了,而应用程序是不需要做修改的,mysql对PODA来说,它并不能感知到mysql是集群外部的服务,就跟访问集群内部的服务一样,没啥区别。

集群外访问集群内部的服务

非常常见,也是非常重要的情况。

==================这是第三种情况的第一种3.1方案:

image.png

在集群外部有一客户端,要访问集群内部的服务,有一个服务PODD,可能有多个实例,外部服务通过NodePort,NodePort是一种service的类型,NodePort除了会有一个ClusterIP之外,会在每一个物理节点都暴露一个端口出来,比如说大框是一个物理节点,会把NodePort暴露在这个物理节点上,在集群上的任意一个节点上都会有这个端口,比如这个8080。

比如还有个节点Node,当Client请求这个物理节点的8080的时候,也会把这个请求通过8080端口转发到最终的服务PODD上面。

这种方式以及用的很少了,除非极特殊的服务会用,因为在每一个物理节点上都会有一个这个8080端口,对于客户端来说,也比较麻烦,虽然说哪个都可以访问,但是不知道会访问哪个,并且会有资源的浪费,多一层资源转发,当服务多的时候,端口还可能不够用,所以在生产环境中,很少会使用。

=================这是第三种情况的第二种3.2方案:

image.png

当client访问PODE时,也会在访问一个物理节点的端口,类似于NodePort,比如8080,这里叫做hostport,和NodePort的区别是,并不是在每一个节点上都会有这样一个端口,它只是一个简单的端口映射,也就是说服务PODE运行在哪台机器上,就会在哪台机器上开启一个这个端口,比如只有一个实例,那么只能在一台机器上有这么一个端口,其他机器不会有,客户端在请求这个服务的时候,只能去访问有这个实例的机器的端口,而NodePort不一样,哪怕只有一个实例,也会在所有的物理节点上都有8080端口监听,这是hostport和NodePort最主要的区别。

==================这是第三种情况的第三种3.3方案:

image.png

==================这是第三种情况的第四种3.4方案:

还有一种非常常见的情况是,用浏览器browser去访问一个域名的时候,要把这个域名解析到K8S集群中的一个服务,此时该怎么办呢?

image.png

通过现有的服务发现方案,有一种方式,在这个集群中的任意一个节点上,部署一个nginx,配置好配置文件,把对应的域名和POD的IP-PORT配置上,这个时候请求到nginx,nginx在K8S集群内部,nginx就可以访问到所有的POD的IP-PORT,所以就可以正常的请求到结果了,但这里面的问题,POD经常变,域名可能经常的增加,每次修改还要修改配置文件和重启,这样的过程比较复杂和麻烦,但这种场景和需求是切实存在的,所以K8S确实考虑到了这个问题。

K8S提出了一个概念叫Ingress,首先可以配置这个ingress的配置,配置域名”api.mooc.com”,在这个域名下要请求的路径path->/api,当请求到这个路径的时候,要把这个请求发给谁,可以配置一个service->/api-service,类似于这种的信息,这个信息是K8S需要我们自己提供的,要告诉Ingress在哪个域名、路径下解析到哪个service,建立起这个关系,通过IngressController去完成这样一个真正域名的解析和转发,这个IngressController也可以自己去实现,毕竟在K8S集群中运行,可以拿到所有service后面对应的POD,可以订阅K8S的一些事件,去监听Ingress、POD的变化,比如内置一个nginx,去 同步自动修改nginx的配置 ,就可以实现这个七层的服务发现的方案了。

当然这个IngressController并不需要我们自己实现,Ingress社区以及有了成熟的实现,比如IngressNginx、IngressGC等。

总体结构

image.png

上述就是K8S预先准备好的,在各种场景下的,服务发现的方案和技术,只有知道这些,才有能力去考虑,服务该以什么样的方式运行在K8S集群之中,彼此之间进行什么样的通讯,通过什么样的方式去交互,这些都是业务迁移之前必须掌握的内容。


标题:Kubernetes(六)(迁移Kubernetes前准备)(6.4)kubernetes的服务发现
作者:yazong
地址:https://blog.llyweb.com/articles/2022/11/10/1668055865146.html