原文发表于kubernetes中文社区,为作者原创翻译 ,原文地址
更多kubernetes文章,请多关注kubernetes中文社区
目录
背景:容器被广泛使用
优雅停机( Gracefully Shutdown )
存活状态( Liveness )
就绪状态( Readiness )
1.如何更新状态?
2.如何获取应用状态?
示例:Spring Boot 2.3.0配置Readiness和Liveness
代码示例
测试:Readiness和Liveness
Spring Boot团队最近发布了2.3.0版本,该版本具有许多增强功能,升级功能。(具体可以参考,Spring Boot 2.3.0 发行说明)。
在早些时候,Spring团队就已经宣布2.3版本将专注于Kubernetes。考虑到这一点,本文我们将尝试探索其中一些功能。
众所周知,容器是独立的单元,它是将应用程序及其依赖项打包为一个单元。因此,应用程序可以实现从一个环境快速可靠地迁移运行到另一个环境。*只要不同环境中存在兼容的容器运行时,就可以满足容器的可移植性,。
容器是操作系统虚拟化的一种,它包含运行应用程序所需的一切:可执行二进制文件,类库,配置文件,配置文件和系统库。与服务器虚拟化相比,容器不为每个虚拟环境提供单独的操作系统,容器使用主机上的操作系统。
由于对容器的前所未有的使用,对诸如Kubernetes和Docker Swarm之类的容器编排和管理工具的需求也在增加。这些工具通过许多功能简化了我们管理容器的工作,这些功能包括:
几乎所有功能都围绕应用程序(容器)提供以下方面的状态:
让我们详细研究它们中的每一个,以及如何在启动Spring Boot 2.3.0中实现它们。
通常,优雅停机意味着在关闭应用程序之前,应设置超时期限,以允许仍在进行中的请求操作完成。在此超时期间,将不允许新请求。这将使你在应用请求处理方面保持一致,即没有未处理请求。
Spring Boot 2.3.0.RELEASE引入了Graceful Shutdown的功能。其中所有四个嵌入式Web服务器(Tomcat,Undertow,Netty和Jetty)都为响应式和基于Servlet的Web应用程序提供优雅停机功能。优雅停机是关闭应用程序上下文的一部分,并且在SmartLifecycle bean停止的最早阶段执行。
应用程序在宽限期内停止新请求的恰当方式,取决于所使用的服务器。根据官方文档Tomcat,Jetty和Reactor Netty将在网络层停止接受请求。Undertow将接受请求,但立即会以HTTP 503(服务不可用)来响应。
请注意,Tomcat 9.0.33或更高版本,才具备优雅停机功能。
在Spring Boot 2.3.0中,优雅停机非常容易实现,并且可以通过在应用程序配置文件中设置两个属性来进行管理。
例如:
Properties 文件
# Enable gracefule shutdown
server.shutdown=graceful
# Allow grace timeout period for 20 seconds
spring.lifecycle.timeout-per-shutdown-phase=20s
# Force enable health probes. Would be enabled on kubernetes platform by default
management.health.probes.enabled=true
现在,当我们配置了优雅停机时,可能会有两种可能性:
就应用程序而言,存活状态是指应用程序的状态是否正常。如果存活状态不正常,则意味着应用程序本身已损坏,无法恢复。在Kubernetes中,如果存活探针检测失败,则kubelet将杀死Container,并且Container将接受其重新启动策略。如果容器未提供存活探针,则默认状态为“ Success ”。
Spring Boot 2.3.0引入了org.springframework.boot.availability.LivenessState。可用状态为
就绪状态,指的是应用程序是否已准备好接受并处理客户端请求。出于任何原因,如果应用程序尚未准备好处理服务请求,则应将其声明为繁忙,直到能够正常响应请求为止。如果“Readiness”状态尚未就绪,则不应将流量路由到该实例。
例如,在Kubernetes中,如果就绪探针失败,则 Endpoints 控制器将从与Endpoints中删除Pod的IP地址。设置就绪状态为“Failure”。如果容器未提供就绪探针,则默认状态为“Success”。
Spring Boot在ReadinessState的帮助下引入了就绪状态。可以设置的值有:
现在,当我们了解概念后,脑海中会有两个问题。
Spring Boot选择了Spring应用程序事件模型来更改可用性状态。Spring Boot还配置了ApplicationAvailabilityBean类型的Bean,它是ApplicationAvailability接口的实现。该bean监听这些事件并保持最新状态。因此,我们使用ApplicationAvailability来获取应用程序的状态
// Available as a component in the application context
ApplicationAvailability availability;
LivenessState livenessState = availability.getLivenessState();
ReadinessState readinessState = availability.getReadinessState();
我们还可以使用AvailabilityChangeEvent来更新状态。
最常见的用例是部署支持探针的Web应用程序。Spring boot Actuator是唯一需要的依赖项。因为Spring Boot Actuator已经公开了应用程序运行状况的端点( endpoint )。
启动Spring Boot 2.3.0 Actuator还将在Health指示器中公开可用性状态。这些指标将全部显示在“/actuator/health*”上。这些健康状况端点也可以作为单独的HTTP端点使用:“ /actuator/health/liveness ”和“ /actuator/health/readiness ”。
在Kubernetes平台上运行时,默认情况下这些指标( indicators)包括在actuator/health的endpoint中,而在另一个平台上则不可用。但是,你始终可以通过将management.health.probes.enabled属性设置为true来覆盖此行为。
Properties 文件
# Force enable health probes. Enabled on Kubernetes platform by default
management.health.probes.enabled=true
请注意,所有与可用性相关的组件都org.springframework.boot.availability软件包的一部分。
在我们编写代码之前,请记住
出于演示的目的,我将在单个RestController中操作,并使用HTTP GET端点调用它们。在理想情况下,这些将由负责管理应用程序不同组件的bean完成。例如缓存,如果缓存失败,则你的缓存bean可以发布ReadinessState.REFUSING_TRAFFIC以拒绝请求到达此应用程序。
下面的代码仅用于演示如何根据需要更改状态。不能在生产中使用。
package org.sk.ms.probes;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* This is sample code to display how to use probes available in spring boot 2.3.0. Not to be used in production. This must be updated by {@link Component} beans for example caching or connection revalidators
*/
@RestController
public class ExampleController {
private final Logger logger = org.slf4j.LoggerFactory.getLogger(ExampleController.class);
@Autowired
private ApplicationEventPublisher eventPublisher;
public ExampleController(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@GetMapping("/complete-normally")
public String completeNormally() throws Exception {
return "Hello from Controller";
}
@GetMapping("/i-will-sleep-for-30sec")
public String destroy() throws Exception {
logger.info("------------------ Sleeping for 30 sec");
Thread.sleep(30000);
return "sleep complete";
}
@GetMapping("/readiness/accepting")
public String markReadinesAcceptingTraffic() {
AvailabilityChangeEvent.publish(eventPublisher, this, ReadinessState.ACCEPTING_TRAFFIC);
return "Readiness marked as ACCEPTING_TRAFFIC";
}
@GetMapping("/readiness/refuse")
public String markReadinesRefusingTraffic() {
AvailabilityChangeEvent.publish(eventPublisher, this, ReadinessState.REFUSING_TRAFFIC);
return "Readiness marked as REFUSING_TRAFFIC";
}
@GetMapping("/liveness/correct")
public String markLivenessCorrect() {
AvailabilityChangeEvent.publish(eventPublisher, this, LivenessState.CORRECT);
return "Liveness marked as CORRECT";
}
@GetMapping("/liveness/broken")
public String markLivenessBroken() {
AvailabilityChangeEvent.publish(eventPublisher, this, LivenessState.BROKEN);
return "Liveness marked as BROKEN";
}
}
检查可用端点的初始值:
我们将宽限期设置为20秒。而且没有正在进行中的请求。观察日志。
我们有20秒的宽限期。让我们发出一个需要30秒才能完成的请求,然后尝试停止该应用程序。
在这种情况下,应用程序将因错误而关闭。注意观察日志。
默认情况下,存活状态是由Spring应用程序上下文设置的。
存活状态,一旦标记为 correct ,它将反映在运行状况端点中,并且容器管理器将知道此实例。
例如,你的应用程序依赖于缓存,并且无法刷新缓存,应用程序的存活状态就会被标记为 broken ,容器管理器会基于配置采取适当的措施处理。
一旦你的应用程序准备好处理请求,可以将就绪状态设置为REFUSING_TRAFFIC。
例如,在缓存更新时,应该将应用程序标记为REFUSING_TRAFFIC,否则可能会处理过时的数据。在这种情况下,容器管理器应停止向该实例发送流量。
但请记住,恢复后应将状态标记回ACCEPTING_TRAFFIC,以便可以将流量再次路由到该实例。
你可以在此GitHub仓库找到以上示例的完整代码。
译文链接: https://dzone.com/articles/configuring-graceful-shutdown-readiness-and-livene