使用 Kubernetes 和 Spring Boot 的自我修复应用程序-Java快速入门教程
1. 简介
在本教程中,我们将讨论Kubernetes 的探测,并演示如何利用Actuator 的健康指示器来准确查看应用程序的状态。
出于本教程的目的,我们将假设一些预先存在的SpringBootActuator,Kubernetes和Docker的经验。
2. Kubernetes 探针
Kubernetes定义了两个不同的探针,我们可以用来定期检查一切是否按预期工作:活动性和就绪性。
2.1. 活力和准备
借助活动性和就绪性探测器,Kubelet可以在检测到异常时立即采取行动,并最大限度地减少我们应用程序的停机时间。
两者都以相同的方式配置,但它们具有不同的语义,Kubelet根据触发哪个操作执行不同的操作:
- 就绪 – 就绪情况验证我们的Pod是否已准备好开始接收流量。当所有容器都准备好时,我们的Pod就准备好了
- 活动性– 与准备相反,活动性检查我们的Pod是否应该重新启动。它可以选取我们的应用程序正在运行但处于无法取得进展状态的用例;例如,它处于死锁状态
我们在容器级别配置两种探测类型:
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: k8s.gcr.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 1
successThreshold: 1
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 1
successThreshold: 1
我们可以配置许多字段来更精确地控制探测器的行为:
- initialDelaySeconds 初始延迟秒 – 创建容器后,等待n秒再启动探测
- periodSeconds–此探测器应运行的频率,默认为 10 秒;最小值为 1 秒
- timeoutSeconds–我们在探测器超时之前等待的时间,默认为 1 秒;最小值再次为 1 秒
- failureThreshold 失败阈值 –在放弃之前尝试n次。在准备就绪的情况下,我们的 Pod 将被标记为未就绪,而在活动的情况下放弃意味着重新启动Pod。此处的默认值为 3 次失败,最小为 1 次
- successThreshold成功阈值 – 这是探测在失败后被视为成功的最小连续成功数。它默认为 1 成功,其最小值也是 1
在这种情况下,我们选择了tcp探针,但是,我们也可以使用其他类型的探针。
2.2. 探头类型
根据我们的用例,一种探头类型可能比另一种更有用。例如,如果我们的容器是 Web 服务器,则使用http探测器可能比tcp探测器更可靠。
幸运的是,Kubernetes有三种不同类型的探针可供我们使用:
- exec–在我们的容器中执行bash指令。例如,检查特定文件是否存在。如果指令返回失败代码,则探测失败
- tcpSocket – 尝试使用指定的端口与容器建立tcp连接。如果无法建立连接,则探测失败
- httpGet–向在容器中运行并侦听指定端口的服务器发送HTTP GET请求。任何大于或等于 200 且小于 400 的代码都表示成功
请务必注意,除了我们前面提到的字段之外,HTTP探测还有其他字段:
- host– 要连接的主机名,默认为我们 pod 的 IP
- scheme– 应该用于连接的方案,HTTP 或HTTPS,默认值为 HTTP
- path – 在 Web 服务器上访问的路径
- httpHeaders– 要在请求中设置的自定义标头
- port – 要在容器中访问的端口的名称或编号
3.Spring Actuator和 Kubernetes 自我修复功能
现在我们已经大致了解了Kubernetes如何能够检测我们的应用程序是否处于损坏状态,让我们看看如何利用Spring 的Actuator来密切关注我们的应用程序,还密切关注它的依赖关系!
出于这些示例的目的,我们将依靠Minikube。
3.1. Actuator及其健康指标
考虑到Spring 有许多 HealthIndicators可供使用,反映我们应用程序对Kubernetes 探针的一些依赖项的状态就像将Actuator依赖项添加到我们的 pom 中一样简单.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3.2. 活体示例
让我们从一个应用程序开始,该应用程序将正常启动,并在30秒后过渡到损坏状态。
我们将通过创建一个HealthIndicator来模拟中断状态,该指示器验证布尔变量是否为true。我们将变量初始化为true,然后安排一个任务在 30 秒后将其更改为false:
@Component
public class CustomHealthIndicator implements HealthIndicator {
private boolean isHealthy = true;
public CustomHealthIndicator() {
ScheduledExecutorService scheduled =
Executors.newSingleThreadScheduledExecutor();
scheduled.schedule(() -> {
isHealthy = false;
}, 30, TimeUnit.SECONDS);
}
@Override
public Health health() {
return isHealthy ? Health.up().build() : Health.down().build();
}
}
有了我们的健康指标,我们需要对应用程序进行 docker化:
FROM openjdk:8-jdk-alpine
RUN mkdir -p /usr/opt/service
COPY target/*.jar /usr/opt/service/service.jar
EXPOSE 8080
ENTRYPOINT exec java -jar /usr/opt/service/service.jar
接下来,我们创建我们的Kubernetes模板:
apiVersion: apps/v1
kind: Deployment
metadata:
name: liveness-example
spec:
...
spec:
containers:
- name: liveness-example
image: dbdock/liveness-example:1.0.0
...
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 3
failureThreshold: 1
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 20
timeoutSeconds: 2
periodSeconds: 8
failureThreshold: 1
我们使用指向Actuator运行状况终结点的httpGet探测器。对应用程序状态(及其依赖项)的任何更改都将反映在部署的运行状况上。
将我们的应用程序部署到Kubernetes 后,我们将能够看到两个探测器的运行情况:大约 30 秒后,我们的Pod将被标记为未就绪并从轮换中删除;几秒钟后,Pod重新启动。
我们可以看到我们的Pod执行kubectl 的事件描述了 pod 的 liveness-example:
Warning Unhealthy 3s (x2 over 7s) kubelet, minikube Readiness probe failed: HTTP probe failed ...
Warning Unhealthy 1s kubelet, minikube Liveness probe failed: HTTP probe failed ...
Normal Killing 0s kubelet, minikube Killing container with id ...
3.3. 准备示例
让我们在不同的用例中使用它:假设我们的应用程序需要一点时间才能接收流量。例如,它需要将文件加载到内存中并验证其内容。
这是一个很好的示例,说明我们何时可以利用就绪情况探测。
让我们修改上一个示例中的HealthIndicator和Kubernetes模板,并使它们适应此用例:
@Component
public class CustomHealthIndicator implements HealthIndicator {
private boolean isHealthy = false;
public CustomHealthIndicator() {
ScheduledExecutorService scheduled =
Executors.newSingleThreadScheduledExecutor();
scheduled.schedule(() -> {
isHealthy = true;
}, 40, TimeUnit.SECONDS);
}
@Override
public Health health() {
return isHealthy ? Health.up().build() : Health.down().build();
}
}
我们将变量初始化为false,40 秒后,任务将执行并将其设置为true。
接下来,我们使用以下模板对应用程序进行 dockerization 和部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: readiness-example
spec:
...
spec:
containers:
- name: readiness-example
image: dbdock/readiness-example:1.0.0
...
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 40
timeoutSeconds: 2
periodSeconds: 3
failureThreshold: 2
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 100
timeoutSeconds: 2
periodSeconds: 8
failureThreshold: 1
虽然相似,但我们需要指出探针配置中的一些更改:
- 由于我们知道我们的应用程序需要大约 40 秒才能准备好接收流量,因此我们将就绪探测的初始延迟秒数增加到 40 秒
- 同样,我们将liveness探测器的initialDelaySeconds增加到 100 秒,以避免被Kubernetes 过早杀死。
如果在 40 秒后仍未完成,则仍有大约 60 秒的时间完成。之后,我们的活动探测器将启动并重新启动Pod。