k8s权威指南笔记:Service

Posted by keys961 on April 3, 2022

1. Service定义

Service定义文档:

  • https://kubernetes.io/zh/docs/concepts/services-networking/service/

  • https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#service-v1-core

2. Service概念与原理

2.1. Service概念

Service用途:

  1. 给一组Pod提供一个抽象、稳定的网络地址

  2. 实现负载均衡/路由,透明化后端Pod地址(Endpoint)

例如,有2个Tomcat的Pod组成的Deployment,包含2个Pod的地址,需要直接和它们通信,负载均衡需要自己做。

之后抽象出1个Service,抽象出一个稳定地址,访问该地址,自动负载均衡且不需要知道Pod的地址。

2.2. 负载均衡机制

a. kube-proxy代理

通过每个Node等kube-proxy进程代理。

提供三种模式:

  • user-space:代理在用户空间实现,效率最低

  • iptables:基于内核的iptables规则进行转发路由,需要通过readiness probe更新规则

  • ipvs:基于内核netlink接口的IPVS规则,效率最高,需启动IPVS模块,否则退回iptables模式

路由规则支持:round-robin, least connection, dst hashing, src hashing, shortest expected delay等

b. 会话保持机制

一段时间内(会话内),将某个源IP的请求路由到同个Pod上。

Pod定义中,使用sessionAffinity设置(为ClientIP),并设置会话过期时间。

2.3. 端口设置

Service可以指定多个端口,以暴露在外。端口的通信协议可不同。

端口列表中每个端口的3个不同字段:

  • port:服务暴露给集群内部访问的端口,配合ClusterIP:port使用

  • targetPort:映射到Pod上的端口,默认和port相同

    • 如:Service的port80,Pod服务的端口是8080,则targetPort8080
  • nodePort:绑定在Node IP上的端口,以便外部访问,配合NodeIP:nodePort使用

2.4. 路由到外部服务

一般情况下,Service将一组Pod合起来,将请求直接转发到Pod上。

也可直接路由到外部服务,即Service -> Service,不需配置Label Selector(selector)。

步骤:

  1. 定义外部Service的Endpoints对象,即指名外部Service的IP和端口列表

  2. 本服务引用步骤1的Endpoints对象

2.5. 发布服务

通过设置spec.type,将Service暴露给外部使用。共4种方法:

  • ClusterIP:默认,集群内部IP暴露,仅内部访问

  • NodePort:通过物理节点Node的IP暴露,即通过nodeIP:nodePort访问

  • LoadBalancer:通过云服务商的负载均衡器,向外部暴露服务。服务上云后,负载均衡器会自动将外网IP请求路由到ClusterIPNodePort服务上

  • ExternalName:通过DNS到CNAME机制,将Service的内部域名转换为externalName外部域名来访问

也可以用Ingress暴露,见后文。

2.6. 支持的网络协议

  • TCP:默认,任意可用

  • UDP:大多可用,若spec.typeLoadBalancer(见2.5节),取决于云服务商

  • SCTP:需要插件,若spec.typeLoadBalancer,取决于云服务商

  • HTTP:取决于云服务商,也可以用Ingress直接暴露

  • PROXY:取决于云服务商

2.7. 服务发现

实现机制有2类:

  1. 环境变量:创建Pod后,自动设置Service的地址、端口等环境变量

    • 格式类似<servicename>_SERVICE_HOST<servicename>_SERVICE_PORT
  2. DNS(推荐):使用CoreDNS服务器提供域名解析

    • A/AAAA记录:

      • 格式为<servicename>.<namespace>.svc.<clusterdomain>

      • “普通”服务返回一个IP地址,“无头”服务返回1组Pod的IP地址列表

    • SRV记录:

      • 设置命名端口后,每个端口都有一条SRV记录,解析为端口号和域名

      • 格式为_<portname>._<protocol>.<servicename>.<namespace>.svc.<clusterdomain>

      • “普通”服务返回1个结果,“无头”服务返回1组Pod的结果

2.8. Headless Service

不提供ClusterIP,需要设置clusterIP: None

外部应用从DNS获取一组Pod的Endpoint列表,需要自行实现负载均衡。

若指定了Label Selector,则扫描符合条件的Pod,加入Endpoint列表中。

若没指定Label Selector,则不会创建Endpoint列表,而是:

  • spec.typeExternalName,则直接转换为externalName外部域名

  • 若Service定义中有同名的Endpoint定义,则转换为该Endpoint定义中的列表

2.9. Endpoint Slices

将一组完整Pod的Endpoint列表分成若干个分片,解决大集群扩展问题。

为某个Service所有,类似于将网络分成若干子网分开管理:Service -> Endpoint Slice -> Endpoint。

原始情况:

  • Service要存储每个Pod的Endpoint,这些存储于etcd中,成本高、apiserver压力大且有容量限制

  • kube-proxy需要维护的iptables或ipv的规则增多

  • Pod的Endpoint更改时,都要传输到每个kube-proxy上,带宽浪费

Endpoint Slices下:

  • Pod的Endpoint更改时,只需更新一个分片,开销急剧变小

  • 为基于Node拓扑的服务路由提供支持

Endpoint Slice分片:基于nodeNamezone

Endpoint Slice分片平衡:

  1. 移除和修改已有Endpoint Slice的Endpoint

  2. 对1中已修改的Endpoint Slice,用新Endpoint填满

  3. 剩余的,添加到未修改的Endpoint Slice,或创建新的,优先考虑创建新的

3. DNS服务

3.1. CoreDNS服务器

目前使用CoreDNS实现,为一个Pod,里面为1个coredns容器。架构图如下:

图片.png

部署可参考:[使用 CoreDNS 进行服务发现 Kubernetes](https://kubernetes.io/zh/docs/tasks/administer-cluster/coredns/)

3.2. Node本地DNS缓存

Node节点本地拥有NodeLocal DNSCache,优先使用本地,然后使用CoreDNS查询。可缓解CoreDNS服务器压力,降低查询延迟、网络竞争,提升性能。

部署可参考:[在 Kubernetes 集群中使用 NodeLocal DNSCache Kubernetes](https://kubernetes.io/zh/docs/tasks/administer-cluster/nodelocaldns/)

3.3. Pod DNS

a. 域名规则

规则为:<pod-ip>.<servicename>.<namespace>.pod.<clusterdomain>

若Pod没有由Service暴露出来,则<servicename>省略。

b. 自定义hostname和subdomain

Pod的定义文件中,可设置hostname,替换掉规则中的pod-ip

也可设置subdomain,替换掉<servicename>。若需要将其暴露在外,需要创建Headless Service,其name要设置为对应的subdomain

c. DNS策略

配置dnsPolicy设置,有4种:

  • Default:继承宿主机

  • ClusterFirst:优先使用k8s集群的DNS服务(如CoreDNS)

  • ClusterFirstWithHostNet:若Pod开启hostNetwork直接使用宿主机网卡通信,开启它可使用k8s集群的DNS服务

  • None:忽略,需要手动配置DNS,见下d

d. 自定义DNS配置

dnsPolicyNone时,在dnsConfig设置,包含下面3个参数:

  • nameservers:DNS服务器列表

  • searches:用于搜索的域名后缀

  • options:其它可选参数,如ndot,timeout

4. Ingress

Ingress将集群内服务暴露到集群外,并进行服务到流量路由,规则由Ingress对象控制。

其包含了:

  • Ingress Controller:进行服务暴露和流量路由

  • Ingress:定义路由规则

外部访问Ingress Controller暴露的端口即可。

图片.png

4.1. Ingress Controller

作用:

  • 进行服务暴露和流量路由

  • 自动监听apiserver并更新路由规则

可选nginx、HAProxy、云服务提供等方案。

部署:选择一个方案,如nginx-ingress,构建Deployment或DaemonSet

  • nginx-ingress: https://kubernetes.github.io/ingress-nginx/deploy/

4.2. Ingress

用于定义路由规则。

包含2种设置:

  1. 路由规则(spec.rules):包括host域名、http.paths服务路径,每个路径还需要设置backend,即对应的服务名和端口号

    • 路径需要设置路径类型(pathType),可选精确、前缀,或由哪个IngressClass控制,通配符也可匹配
  2. 默认后端设置(defaultBackend):在不匹配任何服务时,路由到此,也可在Ingress Controller中设置

4.3. IngressClass

k8s可部署多个Ingress Controller,而Ingress归哪个Ingress Controller管,需要IngressClass。

IngressClass指定了Ingress Controller的名字,Ingress应用它即可归对应的Ingress Controller管。

系统有一个默认的IngressClass,当Ingress没指定IngressClass时使用。