SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(优雅上下线)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
云原生网关 MSE Higress,422元/月
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
简介: 本篇是《SpringCloud 应用在 Kubernetes 上的最佳实践》系列文章的第八篇,主要介绍了如何做到流量的无损上/下线。更多相关文章阅读可查看文末。

头图.png

作者 | 骄龙

导读:本篇是《SpringCloud 应用在 Kubernetes 上的最佳实践》系列文章的第八篇,主要介绍了如何做到流量的无损上/下线。更多相关文章阅读可查看文末。

前言

上篇我们讲的是发布回滚过程,尤其是在 Kubernetes 的回滚过程中,原生有提供 Rollout 到上一个版本的能力,能保证我们在发布过程中遇到问题时快速回退的能力。然而在每一次上线的过程中,我们最难处理的就是正在运行中的流量,如何做到流量的无损上/下线,是一个系统能保证 SLA 的关键。

介绍

什么是优雅上线?就如下面这个房子一样,未建好的房子,人住进去会有危险,房子应该建好,装修好,人才能住进去。

1.jpeg

那么如何做到优雅上线呢?我们先来看一个 WEB 应用的加载过程,就像上面造房子一样,是个漫长的过程:

2.png

应用的加载是漫长的,在加载过程,服务是不可预期的;如过早地打开 Socket 监听,则客户端可能感受到漫长的等待;如果数据库、消息队列、REDIS 客户端未完成初始化,则服务可能因缺少关键的底层服务而异常。

所以在应用准备完成后,才接入服务,即做到优雅上线。当然应用上线后,也可能因如数据库断连等情况引起服务不可用;或是准备完成了,但在上线前又发生数据库断连,导致服务异常。为了简化问题,后面两种情况作为一个应用自愈的问题来看待。

什么是优雅下线?与建房子相反就像下面的危房一样,人住在里面很危险,人应该先从房子出来,然后推掉房子。

3.jpeg

那么如何做到优雅下线呢?我们先来看一个 WEB 应用的停止过程:

4.png

所以关闭服务接入(转移服务接入),完成正在处理的服务,清理自身占用的资源后退出即做到优雅下线。

如何实现优雅下线

从上面介绍看,似乎不难,但事实上,很少有系统真正实现了优雅上下线。因为软件本身由无数各种各样相互依赖的结构组成,每个结构都使用一些资源,污染一些资源;通常在设计之初优雅上下线也不被作为优先考虑的需求,所以对于下线的过程,通常都没被充分考虑,在设计上通常要求:

  • 结构(组件)应形成层次关系;
  • 用户线程需能收到停止信号并响应退出;否则使用 daemon 线程;
  • 结构应按依赖关系自下向上构建:就像建房子一样,自内向外构建而成;
  • 结构应按依赖关系自上向下销毁:就像拆房子一样,自外向内拆解。

优雅下线实现路径

大致分为一个完整的过程,需要经历一下四个关键的节点,如下图:

5.png

  • 接收信号:停止信号可能从进程内部触发(比如 Crash 场景),如果自退出的话基本上无法保证优雅下线;所以能保证优雅下线的前提就是需要正确处理来自进程外部的信号;
  • 停止流量接收:由于在停止之前,我们会有一些正在处理的请求,贸然退出会对这些请求产生损耗。但是在这段时间之内我们绝不能再接收新的业务请求,如果这是一个后台任务型(消息消费型或任务调度型)的程序,也要停止接收新的消息和任务。对于一个普通的 WEB 场景,这一块不同的场景实现的方式也会不一样,下面的 Srping Cloud 应用的下线流程会详细讲解;
  • 销毁资源:常见的是一些系统资源,也包括一些缓存、锁的清理、同时也包括线程池、关闭阻塞中的的 IO 操作,等到我们这些服务器资源销毁之后,就可以通知主线程退出。

Spring Cloud 应用

一个 Spring boot 应用通常由应用本身和一系列的 Starter 组成,对于 Spring boot 体系,需要了解如下核心概念:

  • Starter:提供一系列的模块,由 Spring boot 核心通过 auto-configuration 机制加载;
  • Bean:一切皆 Bean,starter 模块的加载产生各种 Bean;
  • Context:Bean 的容器,容器拥有生命周期,Bean 需要感知生命周期事件;
  • LifeCycle:生命周期管理接口;
  • ApplicationEvent:模块之间,模块与容器之间,通过发送或监听事件来达到互相通讯的目的。

所以对于应用上下线这个主题,我们应尽可能利用其丰富的原生事件机制,Spring Cloud 中内置的 Starter 机制针对整个生命周期管理的过程有了很好的封装。

Spring Cloud 应用的优雅上线

Spring Cloud 启动过程触发回调及事件如下,详细介绍见 application-events-and-listeners,简单罗列如下:

6.png

Spring 自身及其组件大量基于这些事件构建,如响应 WebServerInitializedEvent 事件向服务注册中心注册服务,对于应用一般可利用:

  • InitializingBean or @PostConstruct:在 Bean 装配完后,被回调,如完成数据源初始化连接;
  • ApplicationReadyEvent、ApplicationRunner、CommandLineRunner:如开始监听消息队列,处理消息;注册到SLB等;先通过配置禁用服务的自动注册,在这里做手动服务注册。

Spring Cloud 应用的优雅下线

Spring Cloud 本身可以作为一个应用单独存在,也可以是依附在一个微服务集群中,同时还能作为反向代理架构中的一个网关。不同的场景,需要用到的方法也不一样,我们就常用的三种场景针对性的加以说明。

场景一:直接访问 WEB 服务

7.png

客户端直接访问 WEB 应用,在这个用例下,优雅下线需要做的事情有:

  • 正在处理的请求完成处理
  • 应用自身完成安全下线并正常退出
  • 客户端感知到连接异常

Spring-boot 从 2.3 开始内置了 WEB 应用优雅下线的能力,需配置如下,具体介绍参见 graceful-shutdown

server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=20s

其实现方式:

  • 首先关闭 socket 监听,等待正在处理的所有请求完成:具体可见 WebServerGracefulShutdownLifecycle,通过 getPhase 返回最大值,达到早于 WEB 容器关闭执行的目的;
  • 然后触发 WEB 容器关闭:具体可见 WebServerStartStopLifecycle。

但其实,对于未被 WEB 容器完全接收的请求,客户端仍会收到连接被重置的异常,只是这个时间窗口极小。该需求从提出到实现的时间跨度较长,感兴趣的可参见 github 上的讨论

场景二:经由反向代理的服务优雅下线

8.jpeg

因为实例前面还有反向代理,相比上个场景,需要新增“反向代理下线”这个处理流程。即若应用已经下线,但反向代理未摘除该应用实例时客户端将感知到失败。一般采取的策略有:

  • 反向代理支持失败转移到其它应用实例;
  • 在关闭应用前,如将健康探测接口返回不健康以及等待足够的超时,让反向代理感知并摘除实例的路由信息。

对于仍在使用 2.3 以前版本的 Spring Cloud 应用,可参见一个方案,实现方式:

  • 使用自身的 shutdownHook 替换 Spring 的 shutdownHook;
  • 先改变 health 状态,等待一段时间,让反向代理感知并摘除实例的路由信息。

场景三:在微服务集群中下线单个服务

9.jpeg

在优雅关闭 Spring Cloud 应用自身之前,我们除了完成场景一之中的目标之外,还需要将自身节点从注册中心中下线。目前在 Spring Cloud 中针对注册中心下线的场景暂未提供开箱即用的方法,下面介绍两种可能的实现方案:

方案 1:先通过脚本、或通过监听 ContextClosedEvent 反注册服务摘除流量;等待足够时间,如使用 ribbon 负载均衡器,需要长于配置的刷新时间;对于基于 HTTP 的服务,若 Spring Cloud 版本小于 2.3,则时间需加上预期的请求处理时间;

方案 2:客户端支持连接感知重试,如重试,实现方案可参考Spring-retry,针对连接异常 RemoteConnectFailureException 做重试。

针对 Eureka 中的场景,有一个很好的参考的例子,请参见:https://home1-oss.github.io/home1-oss-gitbook/release/docs/oss-eureka/GRACEFUL_SHUTDOWN.html

Kubernetes 下的机制

Kubernetes 中针对应用的的管控提供了丰富的手段,正常的情况它提供了应用生命周期中的灵活扩展点,同时也支持自己扩展它的 Operator 自定义上下线的流程。

10.jpeg

抛开实现成本,以下线的情况来说,一个 Kubernetes 应用实例下线之前,管控程序会向 POD 发送一个 SIGTERM 的信号,应用响应时除了额外响应这一个信号之外,还能触发一段自定义的 PreStop 的挂在脚本,代码样例如下:

yaml
lifecycle:                   
      preStop:                   
        exec:                    
          command:               
          - sh
          - -c
          - "sleep 5"

上面的例子一点特殊说明:因服务控制面刷新与 POD 收到 SIGTERM 同时发生,所以这里通过 sleep 5 让服务控制面先完成刷新,应用进程再响应 SIGTERM 信号。

Spring Cloud 与 Kubernetes 的结合

Kubernetes 会根据健康检查的情况来更新服务(Service)列表,其中如果 Liveness 失败,则会触发容器重建,这是一个相对很重的操作;若 Readiness 失败,则 Kubenetes 则默认不会将路由服务流量到相应的容器;基于这一机理,Spring Cloud 2.3 开始,也做了原生的的支持,具体参见 liveness-and-readiness-probes-with-Spring-boot,这些健康检查端点可对接 Kubnetes 相应的 probe:

  • /actuator/health/liveness
  • /actuator/health/readiness

同时,Spring Boot 内置了相应的 API、事件、Health Check 监控,部分代码/配置片段如下:

java
// Available as a component in the application context
ApplicationAvailability availability;
LivenessState livenessState = availabilityProvider.getLivenessState();
ReadinessState readinessState = availabilityProvider.getReadinessState();
....
// 对于应用,也可以通过API,发布相应的事件,来改变应用的状态
AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
// 同时,应用监控也可影响这健康状态,将监控与健康关联,在K8S体系下,可以实现如离群摘除,应用自愈的能力
// application.properties
management.endpoint.health.group.liveness.include=livenessProbe,cacheCheck

回到 Spring Cloud 应用 在微服务集群中下线单个服务 的章节中,我们的应用如果跑在 Kuberntes 中,如果我们使用了原生的 Kubernetes 机制去管理应用生命周期的话,只需要发布一个应用事件 (LivenessState.BROKEN) 即可实现优雅下线的能力。

EDAS提供内置的优雅上下线能力

通过上面两部分了解了 Spring Cloud 和 K8s 中的机制,EDAS 基于原生的机制,衍生出来了自己的方法,除了最大化利用这些能力:主动更新 Liveness、Readiness、Ribbon 服务列表之外,我们还提供了无代码侵入的开箱即用的能力,列举如下:

后续

这一章节之后,和发布相关的内容都已经更新完毕,下一章节我们要开始高可用部分的能力,高可用也是系统保障 SLA 的关键部分,简单的理解是流量洪峰到来如何保证系统不会受到影响?当然我们还有一部分要达成的是洪峰退去之后资源是否存在浪费?敬请期待 ...

相关文章推荐:

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1天前
|
人工智能 运维 监控
容器服务Kubernetes场景下可观测体系生产级最佳实践
阿里云容器服务团队在2024年继续蝉联Gartner亚洲唯一全球领导者象限,其可观测体系是运维的核心能力之一。该体系涵盖重保运维、大规模集群稳定性、业务异常诊断等场景,特别是在AI和GPU场景下提供了全面的观测解决方案。通过Tracing、Metric和Log等技术,阿里云增强了对容器网络、存储及多集群架构的监控能力,帮助客户实现高效运维和成本优化。未来,结合AI助手,将进一步提升问题定位和解决效率,缩短MTTR,助力构建智能运维体系。
|
24天前
|
人工智能 Kubernetes 安全
赋能加速AI应用交付,F5 BIG-IP Next for Kubernetes方案解读
赋能加速AI应用交付,F5 BIG-IP Next for Kubernetes方案解读
59 13
|
23天前
|
Kubernetes 算法 调度
阿里云 ACK FinOps成本优化最佳实践
本文源自2024云栖大会梁成昊演讲,讨论了成本优化策略的选择与实施。文章首先介绍了成本优化的基本思路,包括优化购买方式、调整资源配置等基础策略,以及使用弹性、资源混部等高级策略。接着,文章详细探讨了集群优化和应用优化的具体方法,如使用抢占式实例降低成本、通过资源画像识别并优化资源配置,以及利用智能应用弹性策略提高资源利用效率。
|
23天前
|
Kubernetes 容灾 调度
阿里云 ACK 高可用稳定性最佳实践
本文整理自2024云栖大会刘佳旭的演讲,主题为《ACK高可用稳定性最佳实践》。文章探讨了云原生高可用架构的重要性,通过Kubernetes的高可用案例分析,介绍了ACK在单集群高可用架构设计、产品能力和最佳实践方面的方法,包括控制面和数据面的高可用策略、工作负载高可用配置、企业版容器镜像服务高可用配置等内容,旨在帮助企业构建更加可靠和高效的应用运行环境。
|
23天前
|
存储 Kubernetes 关系型数据库
阿里云ACK备份中心,K8s集群业务应用数据的一站式灾备方案
本文源自2024云栖大会苏雅诗的演讲,探讨了K8s集群业务为何需要灾备及其重要性。文中强调了集群与业务高可用配置对稳定性的重要性,并指出人为误操作等风险,建议实施周期性和特定情况下的灾备措施。针对容器化业务,提出了灾备的新特性与需求,包括工作负载为核心、云资源信息的备份,以及有状态应用的数据保护。介绍了ACK推出的备份中心解决方案,支持命名空间、标签、资源类型等维度的备份,并具备存储卷数据保护功能,能够满足GitOps流程企业的特定需求。此外,还详细描述了备份中心的使用流程、控制台展示、灾备难点及解决方案等内容,展示了备份中心如何有效应对K8s集群资源和存储卷数据的灾备挑战。
|
2月前
|
存储 Java Nacos
Spring Cloud+Nacos+KMS 动态配置最佳实践
本文讲述了 Spring Cloud 应用中结合 Nacos 实现了运行期配置动态更新的功能,以及在此基础上结合 KMS 在不改动代码的情况下对应用使用的敏感配置进行保护,解决将配置迁移到 Nacos 中可能存在的数据安全顾虑,并对其底层工作原理做了简单介绍。
505 14
|
2月前
|
存储 运维 Kubernetes
K8s业务迁移最佳实践: 灵活管理资源备份与调整策略,实现高效简便的应用恢复
在当今快速变化的云原生领域,Kubernetes(K8s)集群的运维面临着诸多挑战,其中灾备与业务迁移尤为关键。ACK备份中心支持丰富的资源调整策略,在数据恢复阶段即可自动适配目标集群环境,确保业务无缝重启。
|
2月前
|
Kubernetes 监控 API
深入解析Kubernetes及其在生产环境中的最佳实践
深入解析Kubernetes及其在生产环境中的最佳实践
61 1
|
2月前
|
监控 持续交付 Docker
Docker 容器化部署在微服务架构中的应用有哪些?
Docker 容器化部署在微服务架构中的应用有哪些?
|
2月前
|
监控 持续交付 Docker
Docker容器化部署在微服务架构中的应用
Docker容器化部署在微服务架构中的应用

相关产品

  • 容器服务Kubernetes版