当前位置:   article > 正文

Kubernetes Service详解

kubernetes service

Service是Kubernetes实现微服务架构的核心概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求以负载均衡的方式分发到后端的各个容器应用上。

Service 概述

Kubernetes Service用于为一组提供服务的Pod抽象一个稳定的网络访问地址,是Kubernetes实现微服务的核心概念。简单来说,Service负责Pod的逻辑分组并提供其访问策略。通过Service的定义设置的访问地址是DNS域名格式的服务名称,对于客户端应用来说,网络访问方式并没有改变(DNS域名的作用等价于主机名、互联网域名或IP地址)。
Service还提供了负载均衡器功能,将客户端请求负载分发到后端提供具体服务的各个Pod上。
Service主要用于提供网络服务,通过Service的定义,能够为客户端应用提供稳定的访问地址(域名或IP地址)和负载均衡功能,以及屏蔽后端Endpoint的变化,是Kubernetes实现微服务的核心资源。Service实现的是微服务架构中的几个核心功能:全自动的服务注册、服务发现、服务负载均衡等。

Service原理

Service主要用于提供网络服务,通过Service的定义,能够为客户端应用提供稳定的访问地址(域名或IP地址)和负载均衡功能,以及屏蔽后端Endpoint的变化,是Kubernetes实现微服务的核心资源。
在介绍Service时,一个重要的概念是ClusterIP。在Kubernetes中,每个Service都具有一个全局唯一的虚拟IP地址——ClusterIP地址。当Service被创建后,Kubernetes就会自动为它分配一个可用的ClusterIP地址,而且在Service的整个生命周期中,它的ClusterIP地址都不会改变,客户端可以通过这个虚拟IP地址+服务的端口直接访问该服务。因为每个服务都变成具备唯一IP地址的通信节点,所以服务间通信问题就变成了基础的TCP/UDP网络通信问题。注意:
(1) ClusterIP只是一个虚拟IP,没有一个"实体网络对象"与之对应,所以ClusterIP地址无法被Ping通。ClusterIP地址只能与Service Port组成一个具体的服务访问端点,单独的ClusterIP不具备TCP/IP通信的基础。
(2) ClusterIP仅属于Kubernetes集群这个封闭的空间,集群外的节点要访问这个通信端口,则需要做一些额外的工作。
客户端应用通过Service ClusterIP + Service Port的方式,可以实现服务的访问,那么请求又是如何进入到具体地Pod呢?这就需要提到Endpoint。Endpoint表示一个Service对应的所有Pod副本的访问地址,其拓扑关系如下图所示:

请添加图片描述
可以说,Endpoint负责Service和Pod的映射,辅助从Service路由到Pod。具体来说,Kubernetes中使用 Endpoints Controller来监听Service和对应的Pod副本的变化,如果监测到Service被删除,则删除和该Service同名的Endpoints对象。如果监测到新的Service被创建或者修改,则根据该Service信息获得相关的Pod列表,然后创建或者更新Service对应的Endpoints对象。如果监测到Pod的事件,则更新它所对应的Service的Endpoints对象(增加、删除或者修改对应的Endpoint条目)。
虽然使用Endpoint,可以完成Service到Pod的映射,但是因为一个Service只有一个Endpoint。在大规模应用场景(一个Service由多个Pod,如超过5000个),会给集群内的网络带宽带来巨大的压力,从而影响集群的整体性能。为了解决上述问题,Kubernetes引入了EndpointSlice资源对象。EndpointSlice可以看成单个Endpoint的分片。引入EndpointSlice后,Service的服务规模可以提升至少10倍以上(理论上,一个Service可以包含10万个以上的Pod)。
相比通过Service ClusterIP + Service Port的方式访问服务,一种更友好的方式是通过域名的方式访问服务。为支持域名访问,需要使用到集群DNS服务。在Kubernetes中,集群DNS服务是一种Addon(附加组件)。当前主流的集群DNS服务是CoreDNS。DNS服务通过维护服务域名到Service ClusterIP + Service Port的映射,确保可以通过域名访问服务。
同时,Kubernetes使用DNS服务作为服务注册表,这样一旦新的服务创建后,Kubernetes就会将该服务自动注册到集群DNS中。当客户端应用需要访问某服务时,就可以通过访问DNS服务实现服务发现。

Service创建

尽管客户端应用可以直接通过Pod的IP地址和端口号访问提供的服务,但是,提供服务的容器应用通常是分布式的,且Pod副本的数量可能在运行过程中动态改变(如动态扩缩),且单个Pod的IP地址也可能发生变化(如发生了故障恢复)。
对客户端应用来说,要实现动态感知服务后端实例的变化,以及将请求发送到多个后端实例的负载均衡机制,都会大大增加客户端系统实现的复杂度。Kubernetes的Service就是用于解决这些问题的核心组件。通过Service的定义,可以对客户端应用屏蔽后端Pod实例数量及Pod IP地址的变化,通过负载均衡策略实现请求到后端Pod实例的转发,为客户端应用提供一个稳定的服务访问入口地址。
可以使用kubectl expose命令创建Service,也可以通过YAML文件来创建Service。这里更推荐后者看,因为基于文件的方式更加易于管理。示例如下:

apiVersion: v1
kind: Service
metadata:
  name: demo-service
spec:
  selector:
    app: demo-service
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在上述定义中,ports属性的-port指定了Service的端口号为80,-targetPort则用来指定后端Pod的容器端口号,selectors属性定义Pod所拥有的label:app=demo-service。
在Kubernetes中,使用Endpoints表示一个Service对应的所有Pod副本的访问地址,而Endpoints Controller就是负责生成和维护所有 Endpoints对象的控制器。
Service不仅具有标准网络协议的IP地址,还可以以DNS域名的形式存在。Service的域名表示方法为<servicename>.<namespace>.svc. <clusterdomain>,servicename为服务的名称,namespace为其所在 namespace的名称,clusterdomain为Kubernetes集群设置的域名后缀。
相比于IP+端口的访问方式,基于DNS域名的访问方式更加优雅,且更符合现有网络访问方式。(Service的虚拟IP无法保证固定,且可读性较差)

Service的负载均衡

当一个Service对象在Kubernetes集群中被定义出来时,集群内的客户端应用就可以通过服务IP访问到具体的Pod容器提供的服务了。从服务IP到后端Pod的负载均衡机制,则是由每个Node上的kube-proxy负责实现的。

基于 kube-proxy的代理模式

当客户端应用访问服务时,从服务IP到后端Pod的负载均衡机制,是由每个Node上的kube-proxy负责实现的。kube-proxy提供了多种代理模式,可以通过启动参数–proxy-mode设置,具体代理模式如下:
(1) userspace模式:用户空间模式,由kube-proxy完成代理的实现,效率最低,不再推荐使用。
(2) iptables模式:kube-proxy通过设置Linux Kernel的iptables规则,实现从Service到后端Endpoint列表的负载分发规则,效率很高。但是,如果某个后端Endpoint在转发时不可用,此次客户端请求就会得到失败的响应。对于上述场景,可以通过为Pod设置readinessprobe(服务健康探针)来保证只有达到ready状态的 Endpoint才会被设置为Service的后端Endpoint。
(3) ipvs模式:kube-proxy通过设置Linux Kernel的netlink接口设置IPVS规则,转发效率和支持的吞吐率都是最高的。ipvs模式要求Linux Kernel启用IPVS模块,如果操作系统未启用IPVS内核模块,kube-proxy则会自动切换至iptables模式。同时,ipvs模式支持更多的负载均衡策略,具体如下所述:

rr: round-robin,轮询策略  
lc: least connection,最小连接数策略  
dh: destination hashing,目的地址哈希策略  
sh: source hashing,源地址哈希策略  
sed: shortest expected delay,最短期望延时策略  
nq: never queue,永不排队策略  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(4) kernelspace模式:Windows Server上的代理模式。

会话保持机制

除了kube-proxy提供的代理模式,Sevice还支持通过设置sessionAffinity实现基于客户端IP的会话保持机制的负载均衡策略。如果某个客户端来源IP发起的请求首次转发到后端的某个Pod上,之后从相同的客户端IP发起的请求都将被转发到相同的后端Pod上,配置参数为service.spec.sessionAffinity,示例如下:

apiVersion: v1
kind: Service
metadata:
  name: demo-service
spec:
  sessionAffinity: ClientIP        # 启用基于客户端IP的会话保持机制
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800        # 设置会话保持的最长时间,这里是10800s(3h)
  selector:
    app: demo-service
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Endpoint Slice(端点分片)

使用Endpoint时,一个Service只有一个Endpoint。且kube-proxy 会在每个Node上运行,并监控 Endpoint 资源的任何更新。如果Endpoint资源中有一个端口发生更改,那么整个对象都会分发到 kube-proxy 的每个实例。
另一方面,Endpoint资源会限制Service的服务规模。一般存储在 etcd(Kubernetes使用etcd作为后端存储)中的对象默认大小限制为1.5MB。在某些情况下,它会将 Endpoint 资源限制为 5000 个 Pod IP。对于大多数用户而言,这没什么关系,但是对于接近这个大小的 Service 而言,就有大问题了。
举例来说,如果一个Service有5000个 Pod,且需要一个大小为1.5MB的Endpoint资源。那么,当该列表中的某个网络端点发生了变化,那么就要将这个完整的 Endpoint 资源分发给集群中的每个节点。在具有3000个节点的大型集群中,这会是个很大的问题。每次更新将跨集群发送4.5GB的数据(1.5MB*3000,即 Endpoint 大小*节点个数),并且每次端点更新都要发送这么多数据。想象一下,如果进行一次滚动更新,共有 5000 个 Pod 全部被替换,那么传输的数据量将超过 22 TB(4.5GB*5000,即一次更新大小*更新次数)。这不仅对集群内的网络带宽浪费巨大,而且对Master的冲击非常大,会影响Kubernetes集群的整体性能。
为了解决上述问题,Kubernetes引入了EndpointSlice资源对象。EndpointSlice 可以看成单个Endpoint的分片。 EndpointSlice通过对Endpoint进行分片管理来实现降低Master和各Node之间的网络传输数据量及提高整体性能的目标。EndpointSlice根据Endpoint所在Node的拓扑信息进行分片管理,示例如下图所示:
请添加图片描述
在上图中,假设所有的Pod均归属同一个Service,那么原本由一个EndPoint管理的多个Point,现在变成由多个EndPointSlice分别管理部分。且EndPointSlice还支持Node拓扑的划分。大规模集群中,管理员应对不同Zone或不同Region的Node设置相关的topology标签,用于为Node设置拓扑信息。
默认情况下,Endpoint Slice中最多包含100个Endpoint,如需修改,则可以通过Controller Manager服务的启动参数–max-endpoints-per-slice设置,但上限不能超过1000个(考虑到通信成本)。

基于拓扑敏感的服务路由机制

基于拓扑敏感的服务路由机制就是实现基于Node拓扑的流量路由,如将发送到某个服务的流量优先路由到与客户端相同Node的Endpoint上,或者路由到与客户端相同Zone的那些Node的Endpoint上。
在默认情况下,发送到一个Service的流量会被均匀转发到每个后端 Endpoint,但无法根据更复杂的拓扑信息设置复杂的路由策略。基于拓扑敏感的服务路由机制的引入就是为了实现基于Node拓扑的服务路由,允许Service创建 者根据来源Node和目标Node的标签来定义流量路由策略。
通过对来源(source)Node和目标(destination)Node标签的匹配,用户可以根据业务需求对Node进行分组,设置有意义的指标值来标识“较近”或者“较远”的属性。以公有云环境来说,通常有区域(Zone或Region) 的划分,云平台倾向于把服务流量限制在同一个区域内,这通常是因为跨区域网络流量会收取额外的费用。
服务拓扑机制需要通过设置kube-apiserver和kube-proxy服务的启动参数–feature-gates="ServiceTopology=true,EndpointSlice=true"进行启用 (需要同时启用EndpointSlice功能),然后就可以在Service资源对象上通过定义topologyKeys字段来控制到Service的流量路由了。在Service中设置拓扑敏感的路由示例如下:

apiVersion: v1
kind: Service
metadata:
  name: demo-service
spec:
  ports:
    - port: 80            # 指定Service的端口,用于集群内部访问
      protocol: TCP       # 指定协议
      targetPort: 80      # 指定后端Pod的容器端口
  selector:
    app: demo-service     # 指定关联Pod的标签
  topologyKeys:
    - "kubernetes.io/hostname"           # 优先将流量路由到相同Node,如果没有,则匹配下一条规则
    - "topology.kubernetes.io/zone"      # 优先将流量路由到相同Zone,如果没有,则匹配下一条规则
    - "topology.kubernetes.io/region"    # 优先将流量路由到相同Region,如果没有,则匹配下一条规则
    - "*"                                # 以上规则都没有匹配,将请求路由到集群内任意可用的Endpoint上
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上述示例中,topologyKeys字段设置的是一组Node标签列表,按顺序匹配Node完成流量的路由转发,流量会被转发到标签匹配成功的Node上。如果按第1个标签找不到匹配的Node,就尝试匹配第2个标签,以此类推。如果全部标签都没有匹配的Node,则请求将被拒绝。将topologyKeys配置为"*"表示任意拓扑,它只能作为配置列表中的最后一个才有效。如果完全不设置topologyKeys字段,或者将其值设置为空,就相当于没有启用服务拓扑功能。
注意,服务拓扑和externalTrafficPolicy=Local是不兼容的,所以一个Service不能同时使用这两种特性。但是,在同一个Kubernetes集群中,启用服务拓扑的Service和设置externalTrafficPolicy=Local特性的Service是可以同时存在的。

Headless Service

在某些应用场景中,客户端应用不需要通过Kubernetes内置的Service实现负载均衡功能,或者需要自行完成对服务后端各实例的服务发现机制,或者需要自行实现负载均衡功能,此时可以通过创建Headless Service来实现(直译成无头服务–不是很好,还行改进)。
所有的Headless Service都没有入口访问地址(无ClusterIP 地址),因此kube-proxy也不会为其创建负载转发规则。但是服务名(DNS域名)的解析机制取决于该Headless Service是否设置了Label Selector。
Headless Service降低了微服务与Kubernetes系统的耦合性。应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。

配置Label Selector

如果Headless Service设置了Label Selector,Kubernetes则将根据 Label Selector查询后端Pod列表,自动创建Endpoint列表,将服务名(DNS域名)的解析机制设置为:当客户端访问该服务名时,得到的是全部Endpoint列表(而不是一个确定的IP地址)。使用示例如下:

apiVersion: v1
kind: Service
metadata:
  name: demo-headless-service
spec:
  ports:
    - port: 80                      # 指定Service的端口,用于集群内部访问
      protocol: TCP                 # 指定协议
      targetPort: 80                # 指定后端Pod的容器端口
  selector:
    app: demo-headless-service      # 指定关联Pod的标签
  clusterIP: None                   # 无ClusterIP地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上述示例中,如果当客户端通过DNS服务名"demo-headless-service"和服务端口号访问该Headless Service(URL=demo-headless-service:80)时,将得到该Service后端Endpoint列表,这样客户端程序就可根据返回的Endpint列表自行决定如何操作,如轮询访问各个Endpoint、或随机访问各个Endpoint。

未配置Label Selector

如果Headless Service没有设置Label Selector,则Kubernetes将不会自动创建对应的Endpoint列表。DNS系统会根据下列条件尝试对该服务名设置DNS记录:
(1) 如果Service的类型为ExternalName,则对服务名的访问将直接被DNS系统转换为Service设置的外部名称(externalName)
(2) 如果系统中存在与Service同名的Endpoint定义,则服务名将被解析为Endpoint定义中的列表,适用于非ExternalName类型的Service。

将Service暴露到集群外部

Kubernetes为Service创建的ClusterIP地址是对后端Pod列表的一层抽象,对于集群外部来说并没有意义,因为集群外部无法通过ClusterIP访问服务。但有些Service是需要对集群外部提供服务的,为此,Kubernetes提供了多种机制将Service暴露出去,供集群外部的客户端访问。这可以通过Service资源对象的类型字段"type"进行设置。

ClusterIP

Kubernetes默认会自动设置Service的虚拟IP地址,仅可被集群内部的客户端应用访问。当然,用户也可手工指定一个 ClusterIP地址,不过需要确保该IP在Kubernetes集群设置的ClusterIP地址范围内(通过kube-apiserver 服务的启动参数–service-cluster-ip-range设置),并且没有被其他Service使用。因为是手动设置,且无法保证设置的IP不重复,所以不推荐这种方式。

NodePort

将Service的端口号映射到每个Node的一个端口号上,这样集群中的任意Node都可以作为Service的访问入口地址,即NodeIP:NodePort。示例如下:

apiVersion: v1
kind: Service
metadata:
  name: demo-service
spec:
  type: NodePort          # 指定服务类型为NodePort
  ports:
    - port: 80            # 指定Service的端口,用于集群内部访问
      protocol: TCP       # 指定协议
      targetPort: 80      # 指定后端Pod的容器端口
      nodePort: 30001     # 对外暴露的端口,注意端口范围,用于集群外部访问
  selector:
    app: demo-service     # 指定关联Pod的标签
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在默认情况下,Node的kube-proxy会在全部网卡(0.0.0.0)上绑定NodePort端口号。但是在很多环境中,一台主机会配置多块网卡,所以还需将其绑定到特定网卡上。kube-proxy可以通过设置特定的IP地址将NodePort绑定到特定的网卡上,而无须绑定在全部网卡上,其设置方式为配置启动参数"–nodeport-addresses",指定需要绑定的网卡IP地址,多个地址之间使用逗号分隔。

LoadBalancer

LoadBalancer用来将Service映射到一个已存在的负载均衡器的IP地址上,通常在公有云环境中使用。云服务商提供LoadBalancer可以直接将流量转发到后端Pod上,无需再通过kube-proxy提供的负载均衡机制进行流量转发,且负载分发机制依赖于云服务商的具体实现。示例如下:

apiVersion: v1
kind: Service
metadata:
  name: demo-service
spec:
  type: LoadBalancer      # 指定服务类型为LoadBalancer
  ports:
    - port: 80            # 指定Service的端口,用于集群内部访问
      protocol: TCP       # 指定协议
      targetPort: 80      # 指定后端Pod的容器端口
  selector:
    app: demo-service     # 指定关联Pod的标签
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在服务创建成功之后,云服务商会在Service的定义中补充LoadBalancer的IP地址(status字段):

status:
  loadBalancer:  
    ingress:  
      - ip: 192.0.2.127
  • 1
  • 2
  • 3
  • 4

或者通过"kubectl describe service xxx-service"的命令方式确认loadBalancer的IP。

ExternalName

ExternalName类型的服务用于将集群外的服务定义为Kubernetes的集群的Service,并且通过externalName字段指定外部服务的地址,可以使用域名或IP格式。集群内的客户端应用通过访问这个Service就能访问外部服务了。这种类型的Service没有后端Pod,所以无须设置Label Selector。示例如下:

apiVersion: v1
kind: Service
metadata:
  name: search-service
spec:
  type: ExternalName              # 指定服务类型
  externalName: www.baidu.com     # 指定外部服务的地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

通过上述定义后,集群内的其他服务,就可以通过 ..svc.的方式访问www.baidu.com了。

Ingress 简介

除了使用Service的type将服务暴露给集群外部,还可以通过Ingress。Kubernetes通过引入Ingress资源对象,实现将Kubernetes集群外的客户端请求路由到集群内部的服务上,同时提供7层(HTTP和HTTPS)路由功能。
Kubernetes使用了一个Ingress策略定义和一个具体提供转发服务的Ingress Controller,两者结合,实现了基于灵活Ingress策略定义的服务路由功能。需要注意的是,Ingress只能以HTTP和HTTPS提供服务,对于使用其他网络协议的服务,可以通过设置Service的类型(type)为NodePort或LoadBalancer 对集群外部的客户端提供服务。
使用Ingress进行服务路由时,Ingress Controller基于Ingress规则将客户端请求直接转发到Service对应的后端Endpoint(Pod)上,这样会跳过kube-proxy设置的路由转发规则,以提高网络转发效率。

Ingress Controller

Ingress Controller需要实现基于不同HTTP URL向后转发的负载分发规则,并可以灵活设置7层负载分发策略。目前Ingress Controller已经有许多实现方案,如Nginx、HAProxy、Kong、Istio 等开源软件的实现,以及公有云GCE、Azure、AWS等提供的Ingress应用网关,用户可以参考官方网站根据业务需求选择适合的Ingress Controller。
在Kubernetes中,Ingress Controller会持续监控API Server的/ingress 接口(即用户定义的到后端服务的转发规则),动态感知集群中Ingress的规则变化(无需手动绑定Ingress Controller和Ingress资源对象)。当/ingress接口后 端的服务信息发生变化时,Ingress Controller会自动更新其转发规则。
注意,Ingress Controller 不同于Deployment 控制。因为 Ingress Controller 不直接运行为kube-controller-manager的一部分,它仅仅是Kubernetes集群的一个插件,类似于CoreDNS,需要在集群上单独部署。

Ingress资源对象

Ingress资源对象是一组基于DNS名称(host)或URL路径把请求转发到指定的Service的规则。Ingress用于将集群外部的请求流量转发到集群内部的服务。需要注意的是,Ingress资源对象本身不能进行"路由转发",只是一组规则的集合。完成套接字监听及流量转发的组件则是Ingress Controller。注意,在Ingress资源对象创建前,需要先确保需要指定的Service正确运行,且Ingress中对路径的定义需要与Service提供的访问路径一致,否则将被转发到一个不存在的路径上,从而引发错误。Ingress资源对象定义示例如下:

apiVersion: v1
kind: Ingress
metadata:
  name: demo-ingress
spec:
  rules:
  - host: "test.web.com"
    http:
      paths:
      - path: /login                 # 指定路径
        pathType: Prefix             # 指定路径匹配规则,Prefix表示前缀匹配
        backend:
          service:
            name: demo-serviceb      # 服务的元数据名称
            port:
              number: 80             # 服务的端口
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上面的Ingress资源声明中,包含了一个转发规则:将对test.web.com/login的资源请求,转发给一个服务名为demo-service的Service资源。
这样,当客户端应用访问est.web.com/login资源时,Ingress Controller 就能根据Ingress资源对象中声明的转发规则,将流量转发到具体的Service。

IngressClass资源对象

在一个Kubernetes集群内,可以部署多个不同类型的Ingress Controller,此时需要在Ingress资源对象上注明该策略由哪个Controller管理。Kubernetes从1.18版本开始引入一个新的资源对象IngressClass对其进行规范定义。在IngressClass中除了可以设置Ingress的管理Controller,还可以配置更加丰富的参数信息(通过parameters字段进行设置)。IngressClass资源对象定义的示例如下:

apiVersion: v1
kind: IngressClass
metadata:
  name: demo-ingress-class             # 指定IngressClass元数据的名称
spec:
  controller: demo-ingress-controller  # 待匹配Ingress Controller的元数据名称
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在IngressClass资源对象中指定了对应的Ingress Controller后,接下来就是在Ingress中指定需要匹配的IngressClass。示例如下:

apiVersion: v1
kind: Ingress
metadata:
  name: demo-ingress
spec:
  ingressClassName: demo-ingress-class      # 指定需要匹配的IngressClass的元数据名称
  rules:
  - host: "test.web.com"
    http:
      paths:
      - path: /login
        pathType: Prefix
        backend:
          service:
            name: demo-serviceb
            port:
              number: 80
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可见,在Ingress资源对象中,只需指定spec.ingressClassName到具体的IngressClass就可以了,这样就间接完成了Ingress和Ingress Controller的绑定。
如果在系统中存在多个默认的IngressClass,则在创建Ingress资源时必须指定ingressClassName,否则系统将无法判断使用哪个默认的IngressClass。管理员通常应确保在一个集群中只有一个默认的IngressClass。

Ingress的TLS安全设置

Kubernetes支持为Ingress设置TLS安全访问机制,通过为Ingress的host(域名)配置包含TLS私钥和证书的Secret进行支持。这里不展开讨论,有兴趣的同学,可以参考《Kubernetes权威指南 从Docker到Kubernetes实践全接触》一书,自行学习。
需要说明的是,TLS的功能特性依赖于Ingress Controller的具体实现,不同Ingress Controller的实现机制可能不同。

DNS服务

作为服务发现机制的基本功能,在集群内需要能够通过服务名对服务进行访问,这就需要一个集群范围内的DNS服务来完成从服务名到ClusterIP地址的解析。

CoreDNS服务

伴随着Kubernetes的发展,当前主流的DNS服务是CoreDNS服务。这里重点介绍下CoreDNS服务,其他DNS服务还请读者自行学习。
部署CoreDNS服务时需要创建3个资源对象:1个ConfigMap、1个Deployment和1个Service。在启用了RBAC的集群中,还可以设置ServiceAccount、ClusterRole、ClusterRoleBinding对CoreDNS容器进行权限设置。其中,ConfigMap用来设置CoreDNS的主配置文件Corefile的内容,如定义各种域名的解析方式和使用的插件,等等;Deployment用来设置CoreDNS容器应用的内容,如定义副本的数量,资源限制、水平扩缩,等等;Service是DNS服务的配置。

Node本地DNS缓存服务

为了提高整个集群的DNS域名解析的性能,Kubernetes支持了Node本地DNS缓存功能。使用Node本地DNS缓存的好处如下:
(1) 如果命中Node本地DNS缓存,则可以减少对DNS服务的访问,从而减少跨主机查询的网络延时。
(2) 提供Node级别DNS解析请求的度量(Metrics)和可见性(visibility)。
(3)可以启用负缓存(Negative caching)功能,减少对集群DNS服务的查询数量。
部署Node本地DNS缓存服务后,在客户端Pod内对服务名的解析没有变化,仍然可以直接通过服务名访问其他服务。只是在解析域名时,先通过Node本地DNS缓存进行域名解析,当缓存中不存在该域名时,再将请求转发到集群DNS服务进行解析。

服务注册与发现机制

服务注册机制

Kubernetes使用DNS作为服务注册表,为了满足这一需要,每个Kubernetes集群都会在kube-system命名空间中用Pod的形式运行一个DNS服务,通常称之为集群DNS。然后应用到Kubernetes中的Service会自动注册到集群DNS之中,注册过程大致如下:
(1) 向API Server用POST方式提交一个新的Service定义;
(2) 这个请求需要经过认证、鉴权以及其它的准入策略检查过程之后才会放行;
(3) Service得到一个ClusterIP(虚拟IP,VIP),并保存到集群数据仓库;
(4) 在集群范围内传播Service配置;
(5) 集群 DNS 服务得知该 Service 的创建,据此创建必要的 DNS 记录。
在Kubernetes中DNS是一种Addon(附加组件)。Kubernetes从v1.13开始CoreDNS成为默认DNS服务,CoreDNS的特点是效率更高,资源占用率更小。CoreDNS实现了一个控制器,会对API Server进行监听,一旦发现有新建的Service对象,就创建一个从Service名称映射到ClusterIP的域名记录。这样Service就不必自行向DNS进行注册,CoreDNS 控制器会关注新创建的Service对象,并实现后续的DNS过程。DNS中注册的名称就是metadata.name,而ClusterIP则由Kubernetes自行分配。

服务发现机制

Kubernetes中服务发现机制是指客户端应用在一个Kubernetes集群中如何获知后端服务的访问地址。Kubernetes提供了两种机制供客户端应用以固定的方式获取后端服务的访问地址:环境变量方式和DNS方式。
1、环境变量方式
在一个Pod运行起来的时候,系统会自动为其容器运行环境注入所有集群中有效Service的信息。Service的相关信息包括服务IP、服务端口号、各端口号相关的协议等,通过{SVCNAME}_SERVICE_HOST和{SVCNAME}_SERVICE_PORT格式进行设置。然后,客户端应用就能够根据Service相关环境变量的命名规则,从环境变量中获取需要访问的目标服务的地址。
2、DNS方式
Service在Kubernetes系统中遵循DNS命名规范,Service的DNS域名表示方法为<servicename>.<namespace>.svc.<clusterdomain>,其中servicename为服务的名称,namespace为其所在namespace的名称,clusterdomain为Kubernetes集群设置的域名后缀(例如cluster.local)。
对于客户端应用来说,DNS域名格式的Service名称提供的是稳定、不变的访问地址,可以大大简化客户端应用的配置,是Kubernetes集群中推荐的使用方式。
当Service以DNS域名形式进行访问时,就需要在Kubernetes集群中存在一个DNS服务器来完成域名到ClusterIP地址的解析工作了。目前由CoreDNS作为Kubernetes集群的默认DNS服务器提供域名解析服务。

参考

《Kubernetes权威指南 从Docker到Kubernetes实践全接触》 龚正 吴治辉 闫健勇 编著
《深入剖析 Kubernetes》 张磊 著
https://zhuanlan.zhihu.com/p/245165617 使用 EndpointSlice 扩展 Kubernetes 网络

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/189794
推荐阅读
相关标签
  

闽ICP备14008679号