Service(服务)

概述

参考:

Service 资源可以将一组运行在 Pods 上的应用程序暴露为网络服务,这是一种抽象的功能,说白了 Service 资源实现 服务发现,执行访问 POD 的任务、4 层代理 等功能

为什么要使用 Service?

Deployment 可以部署多个副本,每个 Pod 都有自己的 IP,外界如何访问这些副本呢?通过 Pod 的 IP 吗?要知道 Pod 很可能会被频繁地销毁和重启,它们的 IP 会发生变化,用 IP 来访问不太现实。答案是 Service。Service 作为访问 Pod 的接入层来使用。service 就像 lvs 的 director 一样,充当一个调度器的作用。

Service 定义了外界访问一组特定 Pod 的方式。Service 有自己的 IP 和 PORT ,Service 为 Pod 提供了负载均衡。

可以把 Service 想象成负载均衡功能的前端,该 Service 下的 Pod 是负载均衡功能的后端。通过其自动创建的 ipvs 或者 iptables 的规则,访问 Service 的 IP:PORT,然后转发数据到后端的 Pod

Endpoints

注意:在 service 与 pod 中间,还有一个中间层,这个中间层就是 Endpoints 资源。

Endpoints 是一个由 IP 和 PORT 组成的 endpoint 列表,Endpoints 的这些 IP 和 PORT 来自于由 Service 的标签选择器匹配到的 pod 资源(也可在 service 不适用标签选择器的时候,手动指定 endpoints 的 IP 与 PORT)。默认情况下,创建 Service 资源时,会自动创建同名的 Endpoints 资源。从抽象角度看,service 所关联的每一个 pod 其实都是 Endpoints 资源列表中的一个 endpoint

Service 的实现

Service 是 k8s 中的一种资源,但是如果想要实现 Service 资源定义的那些内容,则需要 kube-proxy 这个程序,实际上,创建一个 service,就是让 kube-proxy 创建一系列 iptables 或者 ipvs 规则

  • userspace:在 1.1.0 版本之前使用该模型,由于需要把报文转到内核空间再回到用户空间过于低效
  • iptables:通过 Kube-proxy 监听 Pod 变化在宿主机上生成并维护。由于 kube-proxy 需要在总之循环里不断得刷新 iptables 规则来确保它们始终是正确的,这样当 Host 上有大量 Pod 时,会产生极多 Iptables 规则,大量占用 Host 的 CPU 资源,这时候 ipvs 模型就可以解决该问题
  • ipvs:将负载均衡与代理功能从 iptables 手中接过来,一些辅助性并且数量不多的(比如包过滤、SNAT 等)操作依然由 iptables 完成。如果想要启用 ipvs 工作模型,那么需要在/etc/sysconfig/kubelet 该配置文件中加入 KUBE_PROXY_MODE=ipvs 这一行,且给 linux 装载 ipvs 模块和连接跟踪模块

有的 CNI 插件,比如 Cilium、等等,其项目内部也基于自身的应用场景,做出了可以替代 kube-proxy 的程序,以更适用于本身的逻辑。

Publishing Service(发布 Service)

发布 Service 是指将 Service 暴露出去以供其他客户端访问他,并将请求转给其所关联的后端 Pod。Service 有多种类型,不同的类型对应不同的发布 Service 方式,默认的类型为 ClusterIP

  • ClusterIP # 通过集群的内部 IP 暴露 Service。该 Service 只能被暴露在进群内部,集群外部无法访问。
    • Service 暴露的集群内部 IP 由 kube-controller-manager 程序的 --service-cluster-ip-range 标志控制。
  • NodePort # 在集群中每个节点 IP 的静态端口上暴露 Service 给集群外部,每个节点上将会创建到 Service 的 CluseterIP 的路由条目,以便我们从集群外部访问 NodePort 类型的 Service
  • LoadBalancer # 使用云提供商的负载均衡器暴露 Service 给集群外部。
  • ExternalName #

我们还可以使用 Kubernetes 的 Ingress 资源暴露 Service。

ClusterIP:仅用于 kubernetes 集群内通信

每个 Service 创建完成后一般都会有一个 cluster-ip(headless 类型的 service 没有),这个 IP 是 Kubernetes 集群的专用 IP,是一种虚拟 IP,可以把它当做 lvs 中的 vip。只不过这些 IP 并不能直接访问到,而是在 Service 创建完成后,在 iptables 或者 ipvs 规则中所使用的 IP。Kubernetes 创建完成后,cluster-ip 默认的使用范围是 10.96.0.0/12

  • headless:无头服务,当不需要使用负载均衡和单一服务 IP 的时候,可以给 ClusterIP 设为 None。kube-proxy 不使用这些服务并且平台(platform)没有负载均衡和代理
    • headless 由于没有 cluster-ip,所以是通过域名的方式来让外部访问到该 service 的 endpoint 的,如果有 cluster-ip 的话,则 ServiceName.NameSpaceName.svc.cluster.local 的域名会解析到该 service 的 cluster-ip 上,如果是 headless 的话,域名解析的结果则是所有 endpoint 的 ip,客户端每次向此 headless 类型的 service 发起的请求,将直接接入到各个 endpoint 上,不再由 service 资源进行代理转发,而是由 DNS 服务器收到查询请求时以轮训的方式返回各个 endpoint 的 IP。

NodePort:用于从集群外部访问 Service

通过 kube-proxy 添加 iptables 规则,把流量通过主机的 port 转发到 Service 的 port 上。

NodePort 建立在 ClusterIP 类型之上,NodePort 会将宿主机的 port 与 service 的 port 所关联,这样就可以将 service 乃至其 endpoint 都可以让集群外部直接访问。如果定义 NodePort 时不指定,则会随机选择宿主机上的 30000 至 32767 之间的一个端口作为 NodePort 的 PORT。

比如下图画红框的部分就表示冒号前是 service 的 port,冒号后是宿主机上的 port,当访问宿主机的 port 的时候,该访问请求会被 iptables 或者 ipvs 规则转发到 service 的 port 上,然后转交给其 endpoint

但是,请注意!NodePort 有个很致命的确定,kube-proxy 无法绑定 NodePort 案例

LoadBalancer:一般当 kubernetes 部署在云上时使用

LoadBalancer 建立在 NodePort 之上,可以将实现 Service 资源的默认负载均衡器(i.e.kube-proxy)替换为其他的负载均衡器。这时可以直接通过负载均衡器暴露的 IP + PORT 直接访问 Service 后端关联的 Pod。

现阶段,想让 Service 对接外部负载均衡器,在指定 Service 的 spec.type 字段的值为 LoadBalancer 以外,还需要配置 metadata.annotations 字段,以便让外部负载均衡器的控制器获取 Service 信息以便对自己进行配置。

当我们创建了一个 LoadBalancer 类型的 Service 后,该 Sevice 对象将会获得一个 External-IP,通常这个 IP 是由外部负载均衡器的控制器提供的:

kubectl get svc nginx
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
nginx   LoadBalancer   10.100.126.91   192.168.0.101   80:31555/TCP   4s

此时,通过 LB 控制器,可以让 LB 自动关联到 Service 后端的 Pod 上,通常来说 192.168.0.101 是 LB 设备的 VIP,访问 192.168.0.101 的 31555 端口会自动负载均衡到 nginx Service 后端的 Pod

常见的负载均衡器:

ExternalName:把集群外部的服务引入到集群内部

External IPs(外部 IP)

为 Service 对象配置 spec.externalIPs 字段后,会在节点上创建一个 ipvs 条目,Director 为 externalIPs 的值,RealServer 为 Service 关联的后端 Pod 的 IP。此时,只要为客户端配置一条路由规则,目的地址是 ExternalIP 的包都转发给 K8S 的节点,就可以从集群外部访问 Service 了。

注意:externalIPs 不受 Kubernetes 管理

image.png External IP 还会在使用 LoadBalancer 类型的 Service 时,自动被公有云厂商的 LB 填充,通常都是将 ingress-controller-nginx 的 Service 配置为 LoadBalancer 类型以对接公有云厂商的 LB。

手动指定 Endpoints

不指定 selector,手动创建一个与 Service 同名的 Endpoints,这样就能实现手动指定该 Service 所关联的 Endpoints