问题提出

流量有损是在利用公布时的常见问题,其景象通常会反馈到流量监控上,如下图所示,公布过程中服务RT忽然升高,造成局部业务响应变慢,给用户的最直观体验就是卡顿;或是申请的500谬误数突增,在用户侧可能感触到服务降级或服务不可用,从而影响用户体验。

因为利用发布会随同流量有损,所以咱们往往须要将公布打算移到业务低谷期,并严格限度利用公布的持续时间,尽管如此,还是不能完全避免公布带来的危险,有时甚至不得不抉择停机公布。EDAS作为一个通用利用管理系统,利用公布是其最根本的性能之一,而K8s 利用是EDAS中最广泛的利用的状态,下文将通过对EDAS客户实在场景的演绎,从K8s的流量门路动手,剖析有损公布产生的起因,并提供实用的解决方案。

流量路径分析

K8s中,流量通常能够从以下几种门路进入到利用Pod中,每条门路天壤之别,流量损失的起因各不相同。咱们将分状况探索每种门路的路由机制,以及Pod变更对流量门路的影响。

LB Service流量

通过LoadBalancer类型Service拜访利用时,流量门路中外围组件是LoadBalancer和ipvs/iptables。LoadBalancer负责接管K8s集群内部流量并转发到Node节点上,ipvs/iptables负责将节点接管到的流量转发到Pod中。外围组件的动作由CCM(cloud-controller-manager)和kube-proxy驱动,别离负责更新LoadBalancer后端和ipvs/iptables规定。

在利用公布时,就绪的Pod会被增加到Endpoint后端,Terminating状态的Pod会从Endpoint中移除。kube-proxy组件会更新各节点的ipvs/iptables规定,CCM组件监听到了Endpoint的变更后会调用云厂商API更新负载均衡器后端,将Node IP和端口更新到后端列表中。流量进入后,会依据负载均衡器配置的监听后端列表转发到对应的节点,再由节点ipvs/iptables转发到理论Pod。

Service反对设置externalTrafficPolicy,依据该参数的不同,节点kube-proxy组件更新ipvs/iptables列表及CCM更新负载均衡器后端的行为会有所不同:

  • Local模式:CCM 仅会将指标服务所在节点增加入负载平衡后端地址列表。流量达到该节点后仅会转发到本节点的Pod中。
  • Cluster模式:CCM会将所有节点都增加到负载平衡后端地址列表。流量达到该节点后容许被转发到其余节点的Pod中。

Nginx Ingress流量

通过Nginx Ingress提供的SLB拜访利用时,流量门路外围组件为Ingress Controller,它岂但作为代理服务器负责将流量转发到后端服务的Pod中,还负责依据Endpoint更新网关代理的路由规定。

在利用公布时,Ingress Controller会监听Endpoint的变动,并更新Ingress网关路由后端,流量进入后会依据流量特色转发到匹配规定上游,并依据上游后端列表抉择一个后端将申请转发过来。

默认状况下,Controller在监听到Service的Endpoint变更后,会调用Nginx中的动静配置后端接口,更新Nginx网关上游后端列表为服务Endpoint列表,即Pod的IP和端口列表。因而,流量进入Ingress Controller后会被间接转发到后端Pod IP和端口。

微服务流量

应用微服务形式拜访利用时,外围组件为注册核心。Provider启动后会将服务注册到注册核心,Consumer会订阅注册核心中服务的地址列表。

在利用公布时,Provider启动后会将Pod IP和端口注册到注册核心,下线的Pod会从注册核心移除。服务端列表的变更会被消费者订阅,并更新缓存的服务后端Pod IP和端口列表。流量进入后,消费者会依据服务地址列表由客户端负载平衡转发到对应的Provider Pod中。

起因剖析与通用解决方案

利用公布过程其实是新Pod上线和旧Pod下线的过程,当流量路由规定的更新与利用Pod高低线配合呈现问题时,就会呈现流量损失。咱们能够将利用公布中造成的流量损失归类为上线有损和下线有损,总的来看,上线和下线有损的起因如下,后文将分状况做更深刻探讨:

  • 上线有损:新Pod上线后过早被退出路由后端,流量被过早路由到了未筹备好的Pod。
  • 下线有损:旧Pod下线后路由规定没有及时将后端移除,流量仍路由到了进行中的Pod。

上线有损剖析与对策

K8s中Pod上线流程如下图所示:

如果在Pod上线时,不对Pod中服务进行可用性查看,这会使得Pod启动后过早被增加到Endpoint后端,后被其余网关控制器增加到网关路由规定中,那么流量被转发到该Pod后就会呈现连贯被回绝的谬误。因而,健康检查尤为重要,咱们须要确保Pod启动实现再让其摊派线上流量,防止流量损失。K8s为Pod提供了readinessProbe用于校验新Pod是否就绪,设置正当的就绪探针对利用理论的启动状态进行查看,进而可能管制其在Service后端Endpoint中上线的机会。

基于Endpoint流量场景

对于基于Endpoint管制流量门路的场景,如LB Service流量和Nginx Ingress流量,配置适合的就绪探针查看就可能保障服务健康检查通过后,才将其增加到Endpoint后端摊派流量,以防止流量损失。例如,在Spring Boot 2.3.0以上版本中减少了健康检查接口/actuator/health/readiness和/actuator/health/liveness以反对配置利用部署在K8S环境下的就绪探针和存活探针配置:

readinessProbe:   ...  httpGet:    path: /actuator/health/readiness    port: ${server.port}

微服务流量场景

对于微服务利用,服务的注册和发现由注册核心治理,而注册核心并没有如K8s就绪探针的查看机制。并且因为JAVA利用通常启动较慢,服务注册胜利后所需资源均依然可能在初始化中,比方数据库连接池、线程池、JIT编译等。如果此时有大量微服务申请涌入,那么很可能造成申请RT过高或超时等异样。

针对上述问题,Dubbo提供了提早注册、服务预热的解决方案,性能概述如下:

  • 提早注册性能容许用户指定一段时长,程序在启动后,会先实现设定的期待,再将服务公布到注册核心,在期待期间,程序有机会实现初始化,防止了服务申请的涌入。
  • 服务预热性能容许用户设定预热时长,Provider在向注册中⼼注册服务时,将⾃身的预热时⻓、服务启动工夫通过元数据的模式注册到注册中⼼中,Consumer在注册中⼼订阅相干服务实例列表,依据实例的预热时长,联合Provider启动工夫计算调用权重,以管制刚启动实例调配更少的流量。通过小流量预热,可能让程序在较低负载的状况下实现类加载、JIT编译等操作,以反对预热完结后让新实例稳固均摊流量。

咱们能够通过为程序减少如下配置来开启提早注册和服务预热性能:

dubbo:    provider:        warmup: 120000        delay: 5000

配置以上参数后,咱们通过为Provider利用扩容一个Pod,来查看新Pod启动过程中的QPS曲线以验证流量预热成果。QPS数据如下图所示:

依据Pod接管流量的QPS曲线能够看出,在Pod启动后没有间接均摊线上的流量,而是在设定的预热时长120秒内,每秒解决的流量呈线性增长趋势,并在120秒后趋于稳定,合乎流量预热的预期成果。

下线有损剖析与对策

在K8s中,Pod下线流程如下图所示:

从图中咱们能够看到,Pod被删除后,状态被endpoint-controller和kubelet订阅,并别离执行移除Endpoint和删除Pod操作。而这两个组件的操作是同时进行的,并非咱们预期的按程序先移除Endpoint后再删除Pod,因而有可能会呈现在Pod曾经接管到了SIGTERM信号,但依然有流量进入的状况。

K8s在Pod下线流程中提供了preStop Hook机制,能够让kubelet在发现Pod状态为Terminating时,不立刻向容器发送SIGTERM信号,而容许其做一些进行前操作。对于上述问题的通用计划,能够在preStop中设置sleep一段时间,让SIGTERM提早一段时间再发送到利用中,能够防止在这段时间内流入的流量损失。此外,也能容许已被Pod接管的流量持续解决实现。

下面介绍了在变更时,因为Pod下线和Endpoint更新机会不合乎预期程序可能会导致的流量有损问题,在利用接入了多种类型网关后,流量门路的复杂度减少了,在其余路由环节也会呈现流量损失的可能。

LB Service流量场景

在应用LoadBalancer类型Service拜访利用时,配置Local模式的externalTrafficPolicy能够防止流量被二次转发并且可能保留申请包源IP地址。

利用公布过程中,Pod下线并且曾经从节点的ipvs列表中删除,然而由CCM监听Endpoint变更并调用云厂商API更新负载均衡器后端的操作可能会存在提早。如果新Pod被调度到了其余的节点,且原节点不存在可用Pod时,若负载均衡器路由规定没有及时更新,那么负载均衡器依然会将流量转发到原节点上,而这条门路没有可用后端,导致流量有损。

此时,在Pod的preStop中配置sleep尽管可能让Pod在LoadBalancer后端更新前失常运行一段时间,但却无奈保障kube-proxy在CCM移除LoadBalancer后端前不删除节点中ipvs/iptables规定的。场景如上图所示,在Pod下线过程中,申请门路2曾经删除,而申请门路1还没有及时更新,即便sleep可能让Pod持续提供一段时间服务,但因为转发规定的不残缺,流量没有被转发到Pod就曾经被抛弃了。

解决方案:

设置externalTrafficPolicy为Cluster可能防止流量下线损失。因为Cluster模式下集群所有节点均被退出负载均衡器后端,且节点中ipvs保护了集群所有可用Pod列表,当本节点中不存在可用Pod时,能够二次转发到其余节点上的Pod中,然而会导致二次转发损耗,并且无奈保留源IP地址。
设置Pod原地降级,通过为Node打特定标签的形式,设置新Pod依然被调度到本节点上。那么该流程无需调用云厂商API进行负载均衡器后端更新,流量进入后会转发到新Pod中。

Nginx Ingress流量场景

对于Nginx Ingress,默认状况下流量是通过网关间接转发到后端PodIP而非Service的ClusterIP。在利用公布过程中,Pod下线,并由Ingress Controller监听Endpoint变更并更新到Nginx网关的操作存在提早,流量进入后依然可能被转发到已下线的Pod,此时就会呈现流量损失。

解决方案:

  • Ingress注解“http://nginx.ingress.kubernet...”反对配置为“true”或“false”,默认为“false”。设置该注解为“true”时,路由规定应用ClusterIP为Ingress上游服务地址;设置为“false”时,路由规定应用Pod IP为Ingress上游服务地址。因为Service的ClusterIP总是不变的,当Pod高低线时,无需思考Nginx网关配置的变更,不会呈现上述流量下线有损问题。但须要留神的是,当应用该个性时流量负载平衡均由K8s管制,一些Ingress Controller个性将会生效,如会话放弃、重试策略等。
  • 在Pod的preStop设置sleep一段时间,让Pod接管SIGTERM信号前期待一段时间,容许接管并解决这段时间内的流量,防止流量损失。

微服务流量场景

在Provider利用公布的过程中,Pod下线并从注册核心登记,但消费者订阅服务端列表变更存在肯定的提早,此时流量进入Consumer后,若Consumer仍没有刷新serverList,依然可能会拜访到已下线的Pod。

对于微服务利用Pod的下线,服务注册发现是通过注册核心而非不依赖于Endpoint,上述endpoint-controller移除Endpoint并不能实现Pod IP从注册核心下线。仅仅在preStop中sleep依然无奈解决消费者serverList缓存刷新提早问题。为了旧Pod可能优雅下线,在preStop中须要首先从注册核心下线,并可能解决曾经接管的流量,还须要保障下线前消费者曾经将其客户端缓存的Provider实例列表刷新。下线实例能够通过调用注册核心接口,或在程序中调用服务框架所提供的接口并设置到preStop以达到上述成果,在EDAS中能够间接应用http://localhost:54199/offline:lifecycle:

  preStop:     exec:       command:         - /bin/sh         - -c         - curl http://localhost:54199/offline; sleep 30;

企业级一站式解决方案

下面咱们对利用公布过程中三种罕用流量门路的流量有损问题进行了起因剖析并给出了解决方案。总的来说,为了保障流量无损,须要从网关参数和Pod生命周期探针和钩子来保障流量门路和Pod高低线的默契配合。EDAS在面对上述问题时,提供了无侵入式的解决方案,无需更改程序代码或参数配置,在EDAS控制台即可实现利用无损高低线。如下图所示:

  • LB Service反对配置内部流量策略(externalTrafficPolicy)

  • Nginx Ingress反对配置“http://nginx.ingress.kubernet...”注解

  • 微服务利用配置无损上线参数

除此之外,EDAS还提供了多种流量网关治理形式,如Nginx Ingress、ALB Ingress、云原生网关,也为利用的公布提供了多种部署形式,如分批公布、金丝雀公布,还提供了不同维度的可观测伎俩,如Ingress监控、利用监控。在EDAS平台治理利用,可能轻松实现多种部署场景下的无损高低线。

原文链接

本文为阿里云原创内容,未经容许不得转载。