K8s 环境无损上下线接入检查 SOP

本文介绍在K8s环境下接入MSE服务治理无损上下线之前需要进行的检查内容。 

前提条件

已经开通MSE微服务治理,并准备使用无损上下线功能。

无损上线

检查项一:是否需要开启延迟注册

检查原因:开启延迟注册后,可以防止服务未就绪的情况下,提前注册到注册中心,导致消费者调用报错。

检查内容:代码中是否存在异步初始化的情况,并且应用需要在这些初始化动作完成才能接收流量。

bean 创建过程中存在 new thread、线程池(各种 ExecutorService)提交任务进行初始化Springboot ApplicationRunner/CommandLineRunner 中进行初始化监听了 ContextRefreshedEvent、ApplicationReadyEvent、ApplicationStartedEvent 后进行初始化。

处理建议:如果存在上述初始化行为,则应该评估服务注册是否需要在这些行为完成后再进行。如果确认需要,则需要开启延迟注册。

检查项二:是否存在监听 InstancePreRegisteredEvent 事件的情况

检查原因:该事件的监听行为如果抛出非受检异常,可能会导致应用注册失败。

检查内容:代码中是否有监听 InstancePreRegisteredEvent 事件的 Listener。

处理建议:如果有,则需要检查是否使用 try catch 块抓住所有异常,否则可能会导致应用注册失败,导致线上流量跌 0。

检查项三:是否有外部网关、SLB 调用来的流量

检查原因:如果应用接受的流量,有部分是直接来自于集群外部的请求,那么这个应用设置的预热时长无法生效。因为 mse agent 无法这些流量发起方对当前应用进行小流量预热。

检查内容:梳理应用调用链路,如果接入了 ARMS,可以在 ARMS 上查看调用当前应用的流量都来自于哪里。

处理建议:如果有来自外部负载均衡的流量,需要将工作负载的 minready 时长进行适当延长(可以考虑将其取值为服务预热时长保持一致),尽量保障服务预热完成后,再开始接收 svc 流量检查。

检查项四:是否存在自定义 loadbalancer

检查原因:如果应用有自定义的 loadbalancer,则会导致该应用调用其他应用时,无法实现对其他应用的小流量预热。因为 mse agent 小流量预热是在 springcloud 默认的 loadbalancer 里面做的增强。

检查内容:在代码中搜索是否有自定义的 springcloud loadbalancer,这些类一般都直接或间接实现了 ReactiveLoadBalancer,可以通过 IDEA 直接查询该接口在项目中是否有实现类。

处理建议:如有项目中有自定义的 loadbalancer,不建议使用,具体可以联系研发侧。

检查项五:K8s 工作负载是否已经配置 MSE 就绪检查

检查原因:MSE 就绪检查在 springcloud、dubbo 微服务注册后才会通过。如果 K8s 就绪检查不能做到在微服务注册后才通过,可能会导致应用发版结束后,线上的应用都尚未注册,导致线上没有可用提供者,进而导致流量跌 0 的问题。

检查内容:查看工作负载的 yaml配置,确认就绪检测(Readiness)位置是否已经配置了 mse 的 55199 端口,路径为 /readiness 的就绪检查

处理建议:如果没有配置,则需要进行配置。如果配置了自定义的就绪检查,并且该自定义就绪检查目的是为检查 pod 是否开放 tomcat 端口,则可以直接替换为 mse 55199/readiness。

无损下线

检查项一:检查集群中是否能存在 arms-pilot 或者 mse-pilot

检查原因:arms-pilot 和 mse-pilot 属于比较古早的 pilot 组件,现在已经统一被 ack-onepilot 组件替代。

检查内容:在集群中搜索是否存在 arms-pilot 和 mse-pilot 命名空间。

处理建议:如果有,则需要将这些命名空间下的资源全部删除,后续只使用 ack-onepilot 组件即可。

检查项二:集群中 ack-onepilot 版本是否大于 4.0.0

检查原因:当 pilot 版本低于 4.0.0 的情况下,pilot 存在比较明显的性能问题,在集群进行大规模 pod 启停时,可能会导致部分 pod 接入 agent 失败。

检查内容:在集群中找到 ack-onepilot 命名空间,查看 ack-onepilot 组件的镜像版本,是否大于 4.0.0。

处理建议:如果小于 4.0.0,则需要给 pilot 组件进行升级,建议升级到最新的 5.1.1,升级可以直接在 ACK 组件管理中进行升级。升级前需要注意,检查一下集群中的应用接入 ARMS 时,是否通过配置 label 的方式来接入 ARMS ,如果是配置 annotation 方式接入 ARMS 的情况下,建议先在每次版本迭代时,逐步将 annotation 方式接入改为 label 方式接入。所有应用改完后,再给 pilot 升级。pilot 升级过程中,不可以进行应用的发布、扩容等等操作,需要等 pilot 升级完毕后再进行应用变更操作。

检查项三:K8s 应用工作负载是否配置了自定义的 preStop

检查原因:如果配置了自定义的 preStop,那么 MSE 无损下线不能保证可以完整的执行,除非自定义的 preStop 中有 sleep 操作,并且 sleep 时间有数十秒。

检查内容:查看工作负载的 yaml,确认停止前行为(preStop)是否配置。

处理建议:如果进行了相关配置,则需要检查自定义 preStop 中是否有 sleep 操作,没有 sleep 操作,则在 preStop 尾部命令加上 sleep 30 即可。如 preStop 配置的是 echo stopped > /tmp/test.text,则需要修改为:echo stopped > /tmp/test.text; sleep 30

检查项四:工作负载是否将/tmp 目录挂载了持久卷

检查原因:无损下线流程在执行时,会在 /tmp 目录下生成 prestop 文件,mse agent 会读取该文件,如果该文件存在且存在配置,则会触发服务注销操作。如果一个 pod 的 /tmp 目录是挂载的,那么可能有其他 pod (或者可能是这个 pod 自己)向 /tmp 目录下生成一个 prestop 文件,当 pod 启动后,会读取到该 prestop 文件,立刻进行服务注销。

检查内容:检查工作负载的配置,是否对 /tmp 目录挂载了持久卷。

处理建议:如果挂载了持久卷,需要取消挂载,否则可能出现异常情况。

检查项五:工作负载是否设置了 hostNetwork

检查原因:无损下线流程在执行时,会调用 54199/offline 接口来进行无损下线,如果工作负载设置了 hostNetwork,那就会导致 pod 使用的是 node 的网络空间,这样该 node 上其他使用 hostNetwork 并且接入无损下线的 pod 也会收到这个无损下线请求,进而导致其他 pod 意外下线。

检查内容:检查工作负载的网络模式设置。

处理建议:不建议使用 hostNetwork,如果一定要使用,需要为这些 pod 配置 profiler.micro.service.http.server.enable=false 环境变量或系统参数,然后手工为这些 pod 配置 preStop 内容:echo stop > /tmp/prestop;wget http://127.0.0.1:54199/offline 2>/tmp/null;sleep 30;exit 0

检查项六:应用之间的调用是否使用了 K8s svc

检查原因:无损下线目前只支持走注册中心场景下的无损上线、下线,不支持 K8s svc。

检查内容:检查应用(包括网关应用的路由配置)的服务提供者配置,是否全部走 nacos 进行发现。包括 feign client 中直接配置了 url以及RestTemplate 没有加 @LoadBalanced 注解。

处理建议:最好全部改为走注册中心做发现,feign client 不配置 url 直连,以及 RestTemplate 应该加上 @LoadBalanced注解。

检查项七:rocketmq 客户端支持性检查

检查原因:无损下线 rocketmq 目前只支持 remoting 协议客户端,不支持 grpc 协议客户端。

检查内容:检查项目中 rocketmq 客户端使用方式。

处理建议:如果项目使用 grpc 协议客户端,可以考虑更换为 remoting 协议客户端,然后使用 MSE 无损下线来实现消息的优雅停机。若经过评估无法更换客户端类型,则需要考虑自行通过改造停机脚本实现 grpc 协议客户端的优雅停机。