【云原生】Kubernetes之Service概述

96 阅读6分钟

为何需要 Service

Kubernetes 中 Pod 是随时可以消亡的(节点故障、容器内应用程序错误等原因)。如果使用  运行您的应用程序,Deployment 将会在 Pod 消亡后再创建一个新的 Pod 以维持所需要的副本数。每一个 Pod 有自己的 IP 地址,然而,对于 Deployment 而言,对应 Pod 集合是动态变化的。

这个现象导致了如下问题:

  • 如果某些 Pod(假设是 'backends')为另外一些 Pod(假设是 'frontends')提供接口,在 'backends' 中的 Pod 集合不断变化(IP 地址也跟着变化)的情况下,'frontends' 中的 Pod 如何才能知道应该将请求发送到哪个 IP 地址?

Kubernetes Service

Kubernetes 中 Service 是一个 API 对象,通过 kubectl + YAML 或者 Kuboard,定义一个 Service,可以将符合 Service 指定条件的 Pod 作为可通过网络访问的服务提供给服务调用者。

Service 是 Kubernetes 中的一种服务发现机制:

  • Pod 有自己的 IP 地址
  • Service 被赋予一个唯一的 dns name
  • Service 通过 label selector 选定一组 Pod
  • Service 实现负载均衡,可将请求均衡分发到选定这一组 Pod 中

例如,假设有一个无状态的图像处理后端程序运行了 3 个 Pod 副本。这些副本是相互可替代的(前端程序调用其中任何一个都可以)。在后端程序的副本集中的 Pod 经常变化(销毁、重建、扩容、缩容等)的情况下,前端程序不应该关注这些变化。

Kubernetes 通过引入 Service 的概念,将前端与后端解耦

创建 Service

Kubernetes Servies 是一个 RESTFul 接口对象,可通过 yaml 文件创建。

例如,假设您有一组 Pod:

  • 每个 Pod 都监听 9376 TCP 端口
  • 每个 Pod 都有标签 app=MyApp
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
    

上述 YAML 文件可用来创建一个 Service:

  • 名字为 my-service
  • 目标端口为 TCP 9376
  • 选取所有包含标签 app=MyApp 的 Pod

关于 Service,您还需要了解:

  • Kubernetes 将为该 Service 分配一个 IP 地址(ClusterIP 或 集群内 IP),供 Service Proxy 使用(参考虚拟 IP 和 Service proxy

  • Kubernetes 将不断扫描符合该 selector 的 Pod,并将最新的结果更新到与 Service 同名 my-service 的 Endpoint 对象中。

虚拟 IP 和服务代理

Kubernetes 集群中的每个节点都运行了一个 kube-proxy,负责为 Service(ExternalName 类型的除外)提供虚拟 IP 访问。

为何不使用 round-robin DNS

许多用户都对 Kubernetes 为何使用服务代理将接收到的请求转发给后端服务,而不是使用其他途径,例如:是否可以为 Service 配置一个 DNS 记录,将其解析到多个 A value(如果是 IPv6 则是 AAAA value),并依赖 round-robin(循环)解析?

Kubernetes 使用在 Service 中使用 proxy 的原因大致有如下几个:

  • 一直以来,DNS 软件都不确保严格检查 TTL(Time to live),并且在缓存的 dns 解析结果应该过期以后,仍然继续使用缓存中的记录
  • 某些应用程序只做一次 DNS 解析,并一直使用缓存下来的解析结果
  • 即使应用程序对 DNS 解析做了合适的处理,为 DNS 记录设置过短(或者 0)的 TTL 值,将给 DNS 服务器带来过大的负载

版本兼容性

Kubernetes 支持三种 proxy mode(代理模式),他们的版本兼容性如下:

代理模式Kubernetes 版本是否默认
User space proxy modev1.0 +
Iptables proxy modev1.1 +默认
Ipvs proxy modev1.8 +

User space 代理模式

在 user space proxy mode 下:

  • kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
  • kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 打开一个随机端口
  • kube-proxy 安装 iptables 规则,将发送到该 Service 的 ClusterIP(虚拟 IP)/ Port 的请求重定向到该随机端口
  • 任何发送到该随机端口的请求将被代理转发到该 Service 的后端 Pod 上(kube-proxy 从 Endpoint 信息中获得可用 Pod)
  • kube-proxy 在决定将请求转发到后端哪一个 Pod 时,默认使用 round-robin(轮询)算法,并会考虑到 Service 中的 SessionAffinity 的设定

如下图所示:

Kubernetes教程:Service user space

Iptables 代理模式 默认模式

在 iptables proxy mode 下:

  • kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件

  • kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 安装 iptable 规则

  • iptables 将发送到 Service 的 ClusterIP / Port 的请求重定向到 Service 的后端 Pod 上

    • 对于 Service 中的每一个 Endpoint,kube-proxy 安装一个 iptable 规则
    • 默认情况下,kube-proxy 随机选择一个 Service 的后端 Pod

如下图所示:

Kubernetes教程:Service iptables proxy

iptables proxy mode 的优点:

  • 更低的系统开销:在 linux netfilter 处理请求,无需在 userspace 和 kernel space 之间切换
  • 更稳定

与 user space mode 的差异:

  • 使用 iptables mode 时,如果第一个 Pod 没有响应,则创建连接失败
  • 使用 user space mode 时,如果第一个 Pod 没有响应,kube-proxy 会自动尝试连接另外一个后端 Pod

您可以配置 Pod 就绪检查(readiness probe)确保后端 Pod 正常工作,此时,在 iptables 模式下 kube-proxy 将只使用健康的后端 Pod,从而避免了 kube-proxy 将请求转发到已经存在问题的 Pod 上。

IPVS 代理模式

在 IPVS proxy mode 下:

  • kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
  • kube-proxy 根据监听到的事件,调用 netlink 接口,创建 IPVS 规则;并且将 Service/Endpoint 的变化同步到 IPVS 规则中
  • 当访问一个 Service 时,IPVS 将请求重定向到后端 Pod

Kubernetes教程:Service IPVS proxy

IPVS 模式的优点

IPVS proxy mode 基于 netfilter 的 hook 功能,与 iptables 代理模式相似,但是 IPVS 代理模式使用 hash table 作为底层的数据结构,并在 kernel space 运作。这就意味着

  • IPVS 代理模式可以比 iptables 代理模式有更低的网络延迟,在同步代理规则时,也有更高的效率
  • 与 user space 代理模式 / iptables 代理模式相比,IPVS 模式可以支持更大的网络流量

IPVS 提供更多的负载均衡选项:

  • rr: round-robin
  • lc: least connection (最小打开的连接数)
  • dh: destination hashing
  • sh: source hashing
  • sed: shortest expected delay
  • nq: never queue

Headless Services

“Headless” Service 不提供负载均衡的特性,也没有自己的 IP 地址。创建 “headless” Service 时,只需要指定 .spec.clusterIP 为 "None"。

“Headless” Service 可以用于对接其他形式的服务发现机制,而无需与 Kubernetes 的实现绑定。

对于 “Headless” Service 而言:

  • 没有 Cluster IP
  • kube-proxy 不处理这类 Service
  • Kubernetes不提供负载均衡或代理支持

DNS 的配置方式取决于该 Service 是否配置了 selector:

  • 配置了 Selector

    Endpoints Controller 创建 Endpoints 记录,并修改 DNS 配置,使其直接返回指向 selector 选取的 Pod 的 IP 地址

  • 没有配置 Selector

    Endpoints Controller 不创建 Endpoints 记录。DNS服务返回如下结果中的一种:

    • 对 ExternalName 类型的 Service,返回 CNAME 记录
    • 对于其他类型的 Service,返回与 Service 同名的 Endpoints 的 A 记录