zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Kubernetes 服务发现之Service

Kubernetes服务 Service 发现
2023-09-14 09:01:47 时间

Service概念


   Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod发生变更,应用程序就会被更新。对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP 的网桥的方式访问 Service,再由 Service 重定向到 backend Pod。 

     在Kubernetes中,每个节点都安装了kube-proxy,kube-proxy通过kubernetes中固有的watch请求方法持续监听apiserver。一旦有service资源发生变动(增删改查), kube-proxy可以及时转化为能够调度到后端Pod节点上的规则这个规则可以是iptables也可以是ipvs,取决于service实现方式。 

Kubernetes 三大IP

  • Node Network 节点网络地址是配置在节点网络之上

  • Pod Network Pod网络地址是配置在Pod网络之上节点网络和Pod网络都是配置在某个设备之上,可以是硬件也可以是虚拟网络

  • Cluster Network(svc network) virtual IP svc ip没有配置在某个网络接口上,它只是存在service的规则当中Service 工作原理

 运行在Pod中的应用是向客户端提供服务的守护进程,比如 nginx、tomcat、etcd等等,它们都是受控于控制器的资源对象,存在生命周期,我们知道Pod资源对象在自愿或非自愿终端后,只能被重构的Pod对象所替代,属于不可再生类组件。而在动态和弹性的管理模式下,Service为该类Pod对象提供了一个固定、统一的访问接口和负载均衡能力。

  其实,就是说Pod存在生命周期,有销毁,有重建,无法提供一个固定的访问接口给客户端。并且为了同类的Pod都能够实现工作负载的价值,由此Service资源出现了,可以为一类Pod资源对象提供一个固定的访问接口和负载均衡,类似于阿里云的负载均衡或者是LVS的功能。

  但是要知道的是,Service和Pod对象的IP地址,一个是虚拟地址,一个是Pod IP地址,都仅仅在集群内部可以进行访问,无法接入集群外部流量。而为了解决该类问题的办法可以是在单一的节点上做端口暴露(hostPort)以及让Pod资源共享工作节点的网络名称空间(hostNetwork)以外,还可以使用 NodePort 或者是 LoadBalancer 类型的Service资源,或者是有 7层负载均衡能力的 Ingress资源。

  Service是Kubernetes的核心资源类型之一,Service资源基于标签选择器将一组Pod定义成一个逻辑组合,并通过自己的IP地址和端口调度代理请求到组内的Pod对象,如下图所示:

  Service对象的IP地址也称为Cluster IP,它位于为Kubernetes集群配置指定专用的IP地址范围之内,是一种虚拟的IP地址,它在Service对象创建之后保持不变,并且能够被同一集群中的Pod资源所访问。Service端口用于接受客户端请求,并将请求转发至后端的Pod应用的相应端口,这样的代理机制,也称为端口代理,它是基于TCP/IP 协议栈的传输层。

 

Service的实现模型


  在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。

      kube-proxy 这个组件始终监视着apiserver中有关service的变动信息,获取任何一个与service资源相关的变动状态,通过watch监视,一旦有service资源相关的变动和创建,kube-proxy都要转换为当前节点上的能够实现资源调度规则(例如:iptables、ipvs)。

1、userspace代理模式(放弃)  

    userpace在这种模式下,kube-proxy监视Kubernetes主服务器以添加和删除Service和Endpoint对象。对于每个服务,它都会在本地节点上打开一个端口(随机选择)。与此代理端口的任何连接都代理到服务的后端Pod。SessionAffinity在决定使用哪个后端Pod时,kube-proxy会考虑服务的设置。最后,用户控件代理安装iptables规则,以获取服务clusterIP和流量port。规则将流量重定向到代理后端Pod的端口。

简单点来说Service的请求先从用户空间进入内核iptables转发到这个端口,然后再回到用户空间,由kube-proxy完成后端endpoint的选择和代理,这样流量会有从用户空间进入内核的过程,效率低,有明显的性能瓶颈。

 2、 iptables代理模式(目前保留)

  目前默认的方案,完全以内核iptables的nat方式实现service负载均衡。该方式在大规模情况下存在一些性能问题;首先,iptables没有增量更新的功能,更新一条规则需要整体flush,更新时间长,这段时间之内流量会有不同程度的影响;此外,iptables规则串行匹配,没有预料到Kubernetes这种在一个机器上会有很多规则的情况,流量需要经过所有规则的匹配后在进行转发,对时间和内存都是极大的小号,尤其在大规模情况下对性能的影响十分明显。

 3、ipvs代理模式(推荐)

   与iptables、userspace 模式一样,kube-proxy 依然监听Service以及Endpoints对象的变化, 不过它并不创建反向代理, 也不创建大量的 iptables 规则, 而是通过netlink 创建ipvs规则,并使用k8s Service与Endpoints信息,对所在节点的ipvs规则进行定期同步; netlink 与 iptables 底层都是基于 netfilter 钩子,但是 netlink 由于采用了 hash table 而且直接工作在内核态,在性能上比 iptables 更优。同时,ipvs负载均衡除了简单rr规则还有很多选择,适合在大型集群中使用,而缺点是带来了额外的配置维护操作。

      与iptables类似,ipvs基于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项,例如:

  • rr:轮询调度

  • lc:最小连接数

  • dh:目标哈希

  • sh:源哈希

  • sed:最短期望延迟

  • nq:不排队调度

注意:ipvs模式假定在运行kube-proxy之前,在节点上都已经安装了IPVS内核模块。当kube-proxy以ipvs代理模式启动时,kube-proxy将验证节点上是否安装了IPVS模块,如果未安装,则kube-proxy将回退到iptables代理模式。

      如果某个服务后端pod发生变化,标签选择器适应的pod有多一个,适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息变化,而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理。如图:

 

Service的定义


1、清单创建Service

[root@k8s-master01 ~]# kubectl explain svc
KIND:     Service
VERSION:  v1

DESCRIPTION:
     Service is a named abstraction of software service (for example, mysql)
     consisting of local port (for example 3306) that the proxy listens on, and
     the selector that determines which pods will answer requests sent through
     the proxy.

FIELDS:
   apiVersion  <string>
   kind  <string>
   metadata  <Object>
   spec  <Object>
   status  <Object>   

其中重要的4个字段:

apiVersion:
kind:
metadata:
spec:
  clusterIP: 可以自定义,也可以动态分配
  ports:(与后端容器端口关联)
  selector:(关联到哪些pod资源上)
  type:服务类型

2、service的类型

对一些应用(如 Frontend)的某些部分,可能希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。

Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。

Kubernetes ServiceTypes 的取值以及行为如下:

  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。

  • NodePort:通过每个 Node 上的 IP 和 静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。

  • LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务 和 ClusterIP 服务。

  • ExternalName:通过返回 CNAME 和 它的值,可以将服务映射到 externalName 字段的内容(例如 foo.bar.example.com)。没有任何类型代理被创建,这只有 Kubernetes 1.7 或 更高版本的 kube-dns 才支持。