为何需要 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 mode | v1.0 + | |
| Iptables proxy mode | v1.1 + | 默认 |
| Ipvs proxy mode | v1.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的设定
如下图所示:
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
如下图所示:
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
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 记录