Kubernetes服务发现

Service

在 K8s 里面,服务发现与负载均衡就是 K8s Service。下图就是在 K8s 里 Service 的架构,K8s Service 向上提供了外部网络以及 pod 网络的访问,即外部网络可以通过 service 去访问,pod 网络也可以通过 K8s Service 去访问。

向下,K8s 对接了另外一组 pod,即可以通过 K8s Service 的方式去负载均衡到一组 pod 上面去,这样相当于解决了前面所说的复发性问题,或者提供了统一的访问入口去做服务发现,然后又可以给外部网络访问,解决不同的 pod 之间的访问,提供统一的访问地址。
在这里插入图片描述

Kubernetes 在设计之初就充分考虑了针对容器的服务发现与负载均衡机制,提供了Service 资源,并通过 kube-proxy 配合 cloud provider 来适应不同的应用场景。随着kubernetes 用户的激增,用户场景的不断丰富,又产生了一些新的负载均衡机制。目前,kubernetes 中的负载均衡大致可以分为以下几种机制,每种机制都有其特定应用场景:

  • Service:直接用 Service 提供 cluster 内部的负载均衡,并借助 cloud provider 提 供的 LB 提供外部访问。

  • Ingress Controller:还是用 Service 提供 cluster 内部的负载均衡,但是通过自定义Ingress Controller 提供外部访问。

  • Service Load Balancer:把 load balancer 直接跑在容器中,实现 Bare Metal 的Service Load Balancer。

  • Custom Load Balancer:自定义负载均衡,并替代 kube-proxy,一般在物理部署Kubernetes 时使用,方便接入公司已有的外部服务。

Service 是对一组提供相同功能的 Pods 的抽象,并为它们提供一个统一的入口。借助Service,应用可以方便的实现服务发现与负载均衡,并实现应用的零宕机升级。

Service 通过标签来选取服务后端,一般配合 Replication Controller 或者 Deployment来保证后端容器的正常运行。这些匹配标签的 Pod IP 和端口列表组成 endpoints,由kube-proxy 负责将服务 IP 负载均衡到这些endpoints 上。

在创建 Service 的时候,也可以不指定 Selectors,用来将 service 转发到 kubernetes集群外部的服务(而不是Pod)。

四种类型与三类协议

Service 有四种类型

  • ClusterIP:默认类型,自动分配一个仅 cluster 内部可以访问的虚拟 IP。

  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 :NodePort 来访问该服务。如果 kube-proxy 设置了 – nodeport-addresses=10.240.0.0/16 (v1.10 支持),那么仅该 NodePort 仅对 设置在范围内的 IP 有效。

  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 :NodePort。(需要借助外部能力来实现负载均衡

  • ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过spec.externlName 设定)。需要 kube-dns 版本在 1.7 以上。

Service、Endpoints 和 Pod 支持三种类型的协议:

  • TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
  • UDP(User Datagram Protocol,用户数据报协议)是一种无连接的传输层协议,用于不可靠信息传送服务。
  • SCTP(Stream Control Transmission Protocol,流控制传输协议),用于通过IP网传输SCN(Signaling Communication Network,信令通信网)窄带信令消息。

Service工作原理

  • kube-proxy 负责将 service 负载均衡到后端 Pod 中,如下图所示:

    说明:

    默认情况下,使用ipvs模式。如果在yaml中定义期望使用iptables模式,如果物理机没有打开该模式,则会回退到ipvs模式。

    iptables使用NAT等技术将virtualIP的流量转发至endpoint中。

在这里插入图片描述

为什么不使用 DNS 轮询

时不时会有人问到为什么 Kubernetes 依赖代理(kube-proxy)将入站流量转发到后端。那其他方法呢? 例如,是否可以依靠轮询进行DNS域名解析?

有以下几个原因:

  • DNS 实现的历史由来已久,它不遵守记录 TTL,并且在名称查找结果到期后对其进行缓存。
  • 有些应用程序仅执行一次 DNS 查找,并无限期地缓存结果 [pod是经常出生入死的资源对象]。
  • 即使应用和库进行了适当的重新解析,DNS 记录上的 TTL 值低或为零也可能会给 DNS 带来高负载,从而使管理变得困难。

服务发现

各种类型的 Service 对源 IP 的处理方法不同:

  • ClusterIP Service:使用 iptables 模式,集群内部的源 IP 会保留(不做 SNAT)。如果 client 和 server pod 在同一个 Node 上,那源 IP 就是 client pod 的 IP 地址;如果在不同的 Node 上,源 IP 则取决于网络插件是如何处理的,比如使用 flannel时,源 IP 是 node flannel IP 地址。

  • NodePort Service:默认情况下,源 IP 会做 SNAT,server pod 看到的源 IP 是Node IP。为了避免这种情况,可以给 service 设置spec.ExternalTrafficPolicy=Local (1.6-1.7 版本设置 Annotation service.beta.kubernetes.io/external-traffic=OnlyLocal ),让 service只代理本地 endpoint 的请求(如果没有本地 endpoint 则直接丢包),从而保留源IP。

  • LoadBalancer Service:默认情况下,源 IP 会做 SNAT,server pod 看到的源 IP是 Node IP。设置 service.spec.ExternalTrafficPolicy=Local 后可以自动从云平台负载均衡器中删除没有本地 endpoint 的 Node,从而保留源 IP。

Headless 服务

  • Headless 服务即不需要 Cluster IP 的服务,即在创建服务的时候指定spec.clusterIP=None 。这类Service并不会分配Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。
  • service 有一个特别的形态就是 Headless Service。service 创建的时候可以指定 clusterIP:None,告诉 K8s 说我不需要 clusterIP(就是刚才所说的集群里面的一个虚拟 IP),然后 K8s 就不会分配给这个 service 一个虚拟 IP 地址,它没有虚拟 IP 地址怎么做到负载均衡以及统一的访问入口呢?
  • 它是这样来操作的:pod 可以直接通过 service_name 用 DNS 的方式解析到所有后端 pod 的 IP 地址,通过 DNS 的 A 记录的方式会解析到所有后端的 Pod 的地址,由客户端选择一个后端的 IP 地址,这个 A 记录会随着 pod 的生命周期变化,返回的 A 记录列表也发生变化,这样就要求客户端应用要从 A 记录把所有 DNS 返回到 A 记录的列表里面 IP 地址中,客户端自己去选择一个合适的地址去访问 pod。

Ingress

Service 虽然解决了服务发现和负载均衡的问题,但它在使用上还是有一些限制,比如:

  • 只支持 4 层负载均衡,没有 7 层功能。
  • 对外访问的时候,NodePort 类型需要在外部搭建额外的负载均衡。
  • 而 LoadBalancer 要求 kubernetes 必须跑在支持的 cloud provider 上面 。

Ingress 就是为了解决这些限制而引入的新资源,主要用来将服务暴露到 cluster 外面,并且可以自定义服务的访问策略。比如想要通过负载均衡器实现不同子域名到不同服务的访问。

为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

说明:

Ingress 本身并不会自动创建负载均衡器,cluster 中需要运行一个 ingress controller 来根据 Ingress 的定义来管理负载均衡器。目前社区提供了 nginx 和 gce 的参考实现。

Traefik 提供了易用的 Ingress Controller,使用方法见 https://docs.traefik.io/user-guide/kubernetes/。

Service Load Balancer

在 Ingress 出现以前,Service Load Balancer 是推荐的解决 Service 局限性的方式。Service Load Balancer 将 haproxy 跑在容器中,并监控 service 和 endpoint 的变化,通过容器 IP 对外提供 4 层和 7 层负载均衡服务。

社区提供的 Service Load Balancer 支持四种负载均衡协议:TCP、HTTP、HTTPS 和 SSL TERMINATION,并支持 ACL 访问控制。

说明:

Service Load Balancer 已不再推荐使用,推荐使用 Ingress Controller。

Custom Load Balancer

虽然 Kubernetes 提供了丰富的负载均衡机制,但在实际使用的时候,还是会碰到一些 复杂的场景是它不能支持的,比如:

  • 接入已有的负载均衡设备
  • 多租户网络情况下,容器网络和主机网络是隔离的,这样 kube-proxy 就不能正常工作

这个时候就可以自定义组件,并代替 kube-proxy 来做负载均衡。基本的思路是监控 kubernetes 中 service 和 endpoints 的变化,并根据这些变化来配置负载均衡器。比如weave flux、nginx plus、kube2haproxy 等。

集群的外部访问

Service 的 ClusterIP 是 Kubernetes 内部的虚拟 IP 地址,无法直接从外部直接访问。但如果需要从外部访问这些服务该怎么办呢,一般有以下四种方法

  • 使用 NodePort 服务在每台机器上绑定一个端口,这样就可以通过 :NodePort 来访问该服务。

  • 使用 LoadBalancer 服务借助 Cloud Provider 创建一个外部的负载均衡器,并将请求转发到 :NodePort 。该方法仅适用于运行在云平台之中的Kubernetes 集群。对于物理机部署的集群,可以使用 MetalLB 实现类似的功能。

  • 使用 Ingress Controller 在 Service 之上创建 L7 负载均衡并对外开放。

  • 使用 ECMP 将 Service ClusterIP 网段路由到每个 Node,这样可以直接通过ClusterIP 来访问服务,甚至也可以直接在集群外部使用 kube-dns。这一方法一般用在物理机部署的情况下。

Kubernetes 服务发现架构

K8s 分为 master 节点和 worker 节点:

  • master 里面主要是 K8s 管控的内容;
  • worker 节点里面是实际跑用户应用的一个地方。
    在这里插入图片描述

在 K8s master 节点里面有 APIServer,就是统一管理 K8s 所有对象的地方,所有的组件都会注册到 APIServer 上面去监听这个对象的变化。

这里面最关键的有三个组件:

  • Cloud Controller Manager,负责去配置 LoadBalancer 的一个负载均衡器给外部去访问;
  • Coredns,就是通过 Coredns 去观测 APIServer 里面的 service 后端 pod 的一个变化,去配置 service 的 DNS 解析,实现可以通过 service 的名字直接访问到 service 的虚拟 IP,或者是 Headless 类型的 Service 中的 IP 列表的解析;
  • kube-proxy ,它通过监听 service 以及 pod 变化,然后实际去配置集群里面的 node pod 或者是虚拟 IP 地址的一个访问。

实际访问链路是什么样的呢?

  • 比如说从集群内部的一个 Client Pod3 去访问 Service。Client Pod3 首先通过 Coredns 这里去解析出 ServiceIP,Coredns 会返回给它 ServiceName 所对应的 service IP 是什么,这个 Client Pod3 就会拿这个 Service IP 去做请求,它的请求到宿主机的网络之后,就会被 kube-proxy 所配置的 iptables 或者 IPVS 去做一层拦截处理,之后去负载均衡到每一个实际的后端 pod 上面去,这样就实现了一个负载均衡以及服务发现。
  • 对于外部的流量,比如通过公网访问的一个请求。它是通过外部的一个负载均衡器 Cloud Controller Manager 去监听 service 的变化之后,去配置的一个负载均衡器,然后转发到节点上的一个 NodePort 上面去,NodePort 也会经过 kube-proxy 的一个配置的一个 iptables,把 NodePort 的流量转换成 ClusterIP,紧接着转换成后端的一个 pod 的 IP 地址,去做负载均衡以及服务发现。
    这就是整个 K8s 服务发现以及 K8s Service 整体的结构。
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐