【Kubernetes】Service 简介

87 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情

一、前言

Service 其实是一个抽象的概念,通过虚拟 IP(VIP) 映射出指定的端口,将客户端发来的请求进行代理并转发到后端具有相同 LabelPods 中。

它是 Kubernetes 常用的向外部暴露内部服务的方式。

Tips: 想要集群外也可以访问:

  1. LoadBalancer:提供一个公网的 IP
  2. NodePort:使用 NATService的端口暴露出去
  3. IngressKubernetes 提供的一种路由转发机制。

(1)Service的作用

Service 通过标签选择器 Label 可以选择具有相同标签的 Pod 形成一个集合。

三大作用:

  • 对外暴露流量
  • 做负载均衡(load balancing
  • 服务发现(service-discovery

(2)Service的工作原理

  1. Service 根据标签选择器查找 Pod
  2. 创建和 Service 同名的 Endpoints 对象(Endpoints 是组成 Service 的一组 IP 地址和端口 Node 资源)
  3. Pod 地址发生变化时,Endpoints 也会随之发生变化,Service 接收请求后通过 Endpoints 查找请求转发的目标地址。

如下是 Service 工作原理图:

2022-04-1800-26-48.png

Endpoints Controller 的主要用处是负责生成和维护所有 Endpoints 对象,通过监听 Service 和对应 Pods 的变化,及时更新 ServiceEndpoints 对象。

Service 被创建后 Endpoints Controller 会监听符合标签选择器对应的 Pod 的状态,当 Pod 处于 Running 并准备就绪时,Endpoints Controller 就会将 Pod ip 记录到 Endpoints 对象中,所以 Service 发现就是通过 Endpoints 实现的。

具体到每个 Node 上会使用 kube-proxy 实现负载均衡,kube-proxy 会根据 ServiceEndpoints 的变化更新每个 Node 节点上 IptablesIpvs 中保存的路由转发规则。



二、Service 的类型

根据 type 类型分为 4 种模式:

  1. ClusterIP:这是 Kubernetes 的默认方式。这其中根据是否会生成 ClusterIP 又分为普通 ServiceHeadless Service

    普通 Service:创建的 Service 会分配一个集群内部可访问的固定虚拟 IP,这是最常使用的方式。 Headless Service:创建的 Service 不会分配固定的虚拟 IP,同样也不会通过 kube-proxy 做反向代理和负载均衡,主要通过 DNS 提供稳定的网络 ID 进行访问,通常用于 StatefulSet 中。

  2. NodePort:使用 ClusterIP,并且将 Serviceport 端口映射到集群中每个 Node 节点的相同端口 port,这样在集群外部访问服务可以直接使用 nodeIP:nodePort 进行访问。

  3. LoadBalancer:在 NodePort 的基础上(也就是拥有 ClusterIPnodePort),还会向所处的公有云申请负载均衡器 LB(负载均衡器的后端直接映射到各 Node 节点的 nodePort 上),这样就实现了通过外部的负载均衡器访问服务。

  4. ExternalName:这是 Service 的一种特例形式。主要用于解决运行在集群外部的服务问题,这种方式下会返回外部服务的别名来为集群内部提供服务。上述提到的 3 种模式主要依赖于 kube-proxy,而这种模式依赖于 kube-dns 的层级。

举个栗子:

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: test
spec:
  type: ExternalName
  externalName: www.donald.com

当使用上面的 YAML 文件创建服务以后,DNS 服务会给 <service-name>.<namespace>.svc.cluster.local(在这里就为 my-service.test.svc.cluster.local)创建一个 CNAME 记录,并向其中写入 www.donald.com

当查询服务 my-service.test.svc.cluster.local 时,集群中的 DNS 服务就会返回映射的 www.donald.com

Service 涉及到的 3 种 Port

  • port:指 Service 暴露在 ClusterIP 上的端口 port,在 Kubernetes 集群内部就是通过 ClusterIP:port 访问服务的。
  • nodePort:指节点 Node 开放的端口,从 Kubernetes 集群外部可以通过 nodeIP:nodePort 访问服务。nodePort 的范围是 30000-32767,设置端口时不能够与已经在使用的有冲突。
  • targetPort:指 Pod 开放的端口,外部访问的请求经过 port 端口和 nodePort 端口,以及 kube-proxy 服务最终转发到后端 Pod 的 targetPort 端口,然后进入到 Pod 中的容器内进行处理。

4 种 IP

  • ClusterIP:这是一个虚拟地址(VIP),没有实际的网络设备承载这个地址,它的底层实现原理是依靠 kube-proxy 通过 iptables 规则重定向到 Node 节点的端口上,然后再负载均衡到后端 Pod。具体过程为:kube-proxy 发现新 Service,在 Node 节点上打开一个任意的 nodePort 端口,创建对应的 iptables 规则或是 IPVS 规则,重定向 Service 的 ClusterIP:port 到新建的 nodePort 端口上,然后开始处理关于新 Service 的连接访问。
  • NodeIP:上面提到的 ClusterIP 由于是虚拟的,只能用于 Kubernetes 集群内部的访问。当集群需要提供外部服务,这时就需要为服务提供公共 IP,通过定义 YAML 文件指定 Service 类型 spec.type=NodePort,然后集群会在所有 Node 节点上开放一个指定的 nodePort,集群外部通过 nodeIP:nodePort 就可以访问服务了。
  • PodIP:当每个新 Pod 创建时,集群都会先使用 gcr.io/google_containers/pause 镜像创建一个容器,然后再创建其它要求的容器。在 Pod 内部其它容器都使用 container 网络模式,并指定其值为 pause 容器的 ID(即:network_mode: "container: pause 容器 ID"),这样 Pod 内所有容器将共享 pause 容器的网络,所有其他容器在 Pod 内部都处于同一个网络模式下,可以彼此间直接通信,而与外部通信都会经过 pause 容器进行代理,因此 pause 容器 IP 才是真正实际意义上的 PodIP。
  • ExternalIP:外部 IP,通过负载均衡(LB)方式发布服务的话,也就是 LoadBalancer Service 类型,公有云提供的负载均衡器的访问地址。

2022-04-1800-32-23.png

Service 服务发现

Service 服务发现目前有两种类型:环境变量和 DNS。通常而言更加推荐使用后一种,也就是 DNS

  • 环境变量

当新创建 Pod 时,kubelet 会向该 Pod 中注册所有已经创建的与 Service 相关的环境变量。注意这里就存在一个问题,当某个特定的 Service 晚于 Pod 的创建,那么先创建的 Pod 就不会注册该 Service 的环境变量。因此我们更加推荐使用 DNS 来进行服务发现。

  • DNS

通过在集群中部署 CoreDNS 服务(在老版本的 Kubernetes 集群中使用的是 KubeDNS)来实现集群内部 Pod 通过 DNS 方式进行通讯。

CoreDNS 是以插件方式集成到 Kubernetes 集群中作为默认的 DNS 服务,这样可以更加灵活方便的进行拓展。