关于阿里云:K8s-有损发布问题探究

38次阅读

共计 5819 个字符,预计需要花费 15 分钟才能阅读完成。

作者: 魁予

问题提出

流量有损是在利用公布时的常见问题,其景象通常会反馈到流量监控上,如下图所示,公布过程中服务 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 注解“nginx.ingress.kubernetes.io/service-upstream”反对配置为“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 反对配置注解:

nginx.ingress.kubernetes.io/service-upstream

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

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

正文完
 0