K8s有损发布问题探究

简介: 应用发布过程往往出现流量有损,本次文章内容通过提出问题、问题分析和解决方案,EDAS在面对上述问题时,提供了无侵入式的解决方案,无需更改程序代码或参数配置,在EDAS控制台即可实现应用无损上下线。

问题提出

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

image.png


image.png

因为应用发布会伴随流量有损,所以我们往往需要将发布计划移到业务低谷期,并严格限制应用发布的持续时间,尽管如此,还是不能完全避免发布带来的风险,有时甚至不得不选择停机发布。EDAS作为一个通用应用管理系统,应用发布是其最基本的功能之一,而K8s 应用是EDAS中最普遍的应用的形态,下文将通过对EDAS客户真实场景的归纳,从K8s的流量路径入手,分析有损发布产生的原因,并提供实用的解决方案。



流量路径分析

K8s中,流量通常可以从以下几种路径进入到应用Pod中,每条路径大相径庭,流量损失的原因各不相同。我们将分情况探究每种路径的路由机制,以及Pod变更对流量路径的影响。

LB Service流量

image.png

通过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流量

image.png

通过Nginx Ingress提供的SLB访问应用时,流量路径核心组件为Ingress Controller,它不但作为代理服务器负责将流量转发到后端服务的Pod中,还负责根据Endpoint更新网关代理的路由规则。

在应用发布时,Ingress Controller会监听Endpoint的变化,并更新Ingress网关路由后端,流量进入后会根据流量特征转发到匹配规则上游,并根据上游后端列表选择一个后端将请求转发过去。

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


微服务流量

image.png

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

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


原因分析与通用解决方案

应用发布过程其实是新Pod上线和旧Pod下线的过程,当流量路由规则的更新与应用Pod上下线配合出现问题时,就会出现流量损失。我们可以将应用发布中造成的流量损失归类为上线有损和下线有损,总的来看,上线和下线有损的原因如下,后文将分情况做更深入讨论:

  • 上线有损:新Pod上线后过早被加入路由后端,流量被过早路由到了未准备好的Pod。
  • 下线有损:旧Pod下线后路由规则没有及时将后端移除,流量仍路由到了停止中的Pod。


上线有损分析与对策

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

image.png

如果在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数据如下图所示:

image.png

根据Pod接收流量的QPS曲线可以看出,在Pod启动后没有直接均摊线上的流量,而是在设定的预热时长120秒内,每秒处理的流量呈线性增长趋势,并在120秒后趋于稳定,符合流量预热的预期效果。

下线有损分析与对策

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

image.png

从图中我们可以看到,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时,若负载均衡器路由规则没有及时更新,那么负载均衡器仍然会将流量转发到原节点上,而这条路径没有可用后端,导致流量有损。


image.png

此时,在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,此时就会出现流量损失。


image.png

解决方案:

  • 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。

image.png

对于微服务应用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)

image.png

  • Nginx Ingress支持配置“nginx.ingress.kubernetes.io/service-upstream”注解

image.png

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

image.png

除此之外,EDAS还提供了多种流量网关管理方式,如Nginx Ingress、ALB Ingress、云原生网关,也为应用的发布提供了多种部署方式,如分批发布、金丝雀发布,还提供了不同维度的可观测手段,如Ingress监控、应用监控。在EDAS平台管理应用,能够轻松实现多种部署场景下的无损上下线。

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
Kubernetes API Docker
k8s教程(pod篇)-容器获取pod信息(Downward API)
k8s教程(pod篇)-容器获取pod信息(Downward API)
2463 0
|
Java 关系型数据库 MySQL
文件在线压缩与解压|基于Springboot实现文件在线压缩与解压
文件在线压缩与解压|基于Springboot实现文件在线压缩与解压
342 0
|
机器学习/深度学习 编解码 计算机视觉
【论文速递】CVPR2022-Blind2Unblind:具有可见盲点的自监督图像去噪
【论文速递】CVPR2022-Blind2Unblind:具有可见盲点的自监督图像去噪
|
Kubernetes 网络协议 Java
程序技术好文:记一次k8spod频繁重启的优化之旅
程序技术好文:记一次k8spod频繁重启的优化之旅
770 0
|
Kubernetes 监控 网络协议
在K8S中,如果因为网络原因导致Pod异常,该如何排查?
在K8S中,如果因为网络原因导致Pod异常,该如何排查?
|
存储 大数据 数据挖掘
【数据新纪元】Apache Doris:重塑实时分析性能,解锁大数据处理新速度,引爆数据价值潜能!
【9月更文挑战第5天】Apache Doris以其卓越的性能、灵活的架构和高效的数据处理能力,正在重塑实时分析的性能极限,解锁大数据处理的新速度,引爆数据价值的无限潜能。在未来的发展中,我们有理由相信Apache Doris将继续引领数据处理的潮流,为企业提供更快速、更准确、更智能的数据洞察和决策支持。让我们携手并进,共同探索数据新纪元的无限可能!
498 11
|
存储 Kubernetes 负载均衡
kubernetes容器网络
K8S容器网络的原理及实践
2108 2
kubernetes容器网络
|
Rust 安全 C++
游戏引擎的未来:是Rust成为新王,还是C++仍占鳌头?
【8月更文挑战第31天】随着游戏行业的快速发展,对高性能、安全且易维护的游戏引擎需求日益增长。虽然C++长期占据主导地位,但Rust语言凭借其内存安全和高性能的特点,逐渐成为游戏引擎开发的新选择。Rust通过所有权机制和强大的类型系统,在保证内存安全的同时实现了与C++相当的性能,有助于提前发现潜在错误。尽管Rust在生态系统成熟度和学习曲线上仍面临挑战,其在游戏引擎领域的潜力正逐渐被认可。随着Rust社区的发展和工具链的完善,Rust有望成为游戏引擎开发的重要选项。
680 0
|
JavaScript
Vue配置路由导航守卫实现用户登录和退出(Vue2.x)
Vue配置路由导航守卫实现用户登录和退出(Vue2.x)
Vue配置路由导航守卫实现用户登录和退出(Vue2.x)
|
Docker 容器
http: server gave HTTP response to HTTPS client解决方案
http: server gave HTTP response to HTTPS client解决方案
2442 0

热门文章

最新文章