Kubernetes 容器的最佳实践:健康检查

Kubernetes 容器的最佳实践:健康检查

TL博士

  • 要实现容器和微服务的高可观察性,日志和主要指标是不够的。
  • 为了更快地恢复并提高弹性,应用程序应应用高可观测性原则 (HOP)。
  • 在应用程序级别,NOP 需要:正确的日志记录、密切监控、健全性检查和性能/转换跟踪。
  • 使用检查作为 NOR 的元素 就绪探针 и 活性探针 Kubernetes。

什么是健康检查模板?

在设计关键任务和高可用性应用程序时,考虑容错等方面非常重要。 如果应用程序能够从故障中快速恢复,则该应用程序被认为具有容错能力。 典型的云应用程序使用微服务架构 - 每个组件都放置在单独的容器中。 并且为了确保在设计集群时k8s上的应用程序是高可用的,需要遵循一定的模式。 其中包括健康检查模板。 它定义了应用程序如何向 k8s 传达其运行状况良好的信息。 这不仅是有关 pod 是否正在运行的信息,还包括有关它如何接收和响应请求的信息。 Kubernetes 对 Pod 健康状况了解得越多,它在流量路由和负载平衡方面做出的决策就越明智。 因此,高可观察性原则允许应用程序及时响应请求。

高可观测性原则(HOP)

高可观测性原理是其中之一 设计容器化应用程序的原则。 在微服务架构中,服务并不关心它们的请求是如何处理的(这是正确的),但重要的是它们如何从接收服务接收响应。 例如,为了验证用户身份,一个容器向另一个容器发送 HTTP 请求,并期望得到某种格式的响应 - 仅此而已。 PythonJS 也可以处理请求,Python Flask 可以响应。 容器就像黑匣子,彼此隐藏着内容。 然而,NOP 原则要求每个服务公开多个 API 端点,以指示其健康状况以及其准备情况和容错状态。 Kubernetes 请求这些指标是为了考虑路由和负载平衡的后续步骤。

精心设计的云应用程序使用标准 I/O 流 STDERR 和 STDOUT 记录其主要事件。 接下来是辅助服务,例如 filebeat、logstash 或 Fluentd,将日志传送到集中监控系统(例如 Prometheus)和日志收集系统(ELK 软件套件)。 下图显示了云应用程序如何根据健康测试模式和高可观测性原则工作。

Kubernetes 容器的最佳实践:健康检查

如何在 Kubernetes 中应用健康检查模式?

k8s 开箱即用,使用其中一个控制器(部署, 副本集, 守护进程集, 有状态集 等等)。 控制器发现 Pod 由于某种原因掉落后,会尝试重新启动它或将其移动到另一个节点。 然而,pod 可能会报告它已启动并正在运行,但它本身却无法运行。 让我们举个例子:您的应用程序使用 Apache 作为 Web 服务器,您将该组件安装在集群的多个 pod 上。 由于库配置不正确,所有对应用程序的请求都会响应代码 500(内部服务器错误)。 在检查交付时,检查 Pod 的状态会给出成功的结果,但客户的想法不同。 我们将这种不良情况描述如下:

Kubernetes 容器的最佳实践:健康检查

在我们的示例中,k8s 确实 功能检查。 在这种类型的验证中,kubelet 不断检查容器中进程的状态。 一旦他了解到该过程已停止,他将重新启动它。 如果只需重新启动应用程序即可解决该错误,并且该程序设计为在出现任何错误时关闭,那么您只需遵循 NOP 和运行状况测试模式进行进程运行状况检查即可。 唯一遗憾的是,并不是所有的错误都可以通过重启来消除。 在这种情况下,k8s 提供了 2 种更深层次的方法来识别 pod 的问题: 活性探针 и 就绪探针.

活性探针

在这期间 活性探针 kubelet 执行 3 种类型的检查:不仅确定 pod 是否正在运行,还确定它是否准备好接收并充分响应请求:

  • 设置对 pod 的 HTTP 请求。 响应必须包含 200 到 399 范围内的 HTTP 响应代码。因此,代码 5xx 和 4xx 表示 Pod 存在问题,即使进程正在运行也是如此。
  • 要使用非 HTTP 服务(例如 Postfix 邮件服务器)测试 pod,您需要建立 TCP 连接。
  • 对 Pod(内部)执行任意命令。 如果命令完成码为 0,则认为检查成功。

这是如何工作的一个例子。 下一个 pod 定义包含一个对 HTTP 请求抛出 500 错误的 NodeJS 应用程序。为了确保容器在收到此类错误时重新启动,我们使用 livenessProbe 参数:

apiVersion: v1
kind: Pod
metadata:
 name: node500
spec:
 containers:
   - image: magalix/node500
     name: node500
     ports:
       - containerPort: 3000
         protocol: TCP
     livenessProbe:
       httpGet:
         path: /
         port: 3000
       initialDelaySeconds: 5

这与任何其他 pod 定义没有什么不同,但我们添加了一个对象 .spec.containers.livenessProbe... 范围 httpGet 接受 HTTP GET 请求发送到的路径(在我们的示例中是 /,但是在战斗场景中可能会出现类似的情况 /api/v1/status)。 另一个 livenessProbe 接受一个参数 initialDelaySeconds,指示验证操作等待指定的秒数。 之所以需要延迟,是因为容器需要时间来启动,而重新启动后,它会在一段时间内不可用。

要将此设置应用于集群,请使用:

kubectl apply -f pod.yaml

几秒钟后,您可以使用以下命令检查 pod 的内容:

kubectl describe pods node500

在输出的最后,找到 那是什么.

正如您所看到的,livenessProbe 发起了一个 HTTP GET 请求,容器生成了错误 500(这是它被编程要做的事情),并且 kubelet 重新启动了它。

如果您想知道 NideJS 应用程序是如何编程的,这里是使用的 app.js 和 Dockerfile:

app.js

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(500, { "Content-type": "text/plain" });
    res.end("We have run into an errorn");
});

server.listen(3000, function() {
    console.log('Server is running at 3000')
})

Dockerfile

FROM node
COPY app.js /
EXPOSE 3000
ENTRYPOINT [ "node","/app.js" ]

需要注意的是:livenessProbe 仅在失败时才会重新启动容器。 如果重新启动无法纠正阻止容器运行的错误,kubelet 将无法采取措施纠正问题。

就绪探针

readinessProbe 的工作方式与 livenessProbe 类似(GET 请求、TCP 通信和命令执行),但故障排除操作除外。 检测到故障的容器不会重新启动,但会与传入流量隔离。 想象一下,其中一个容器正在执行大量计算或负载很重,导致响应时间增加。 对于 livenessProbe,会触发响应可用性检查(通过 timeoutSeconds 检查参数),之后 kubelet 会重新启动容器。 启动后,容器开始执行资源密集型任务并再次重新启动。 这对于需要响应速度的应用程序至关重要。 例如,一辆汽车在路上等待服务器的响应,响应被延迟,汽车就发生了事故。

我们来编写一个 redinessProbe 定义,将 GET 请求响应时间设置为不超过两秒,应用程序将在 5 秒后响应 GET 请求。 pod.yaml 文件应如下所示:

apiVersion: v1
kind: Pod
metadata:
 name: nodedelayed
spec:
 containers:
   - image: afakharany/node_delayed
     name: nodedelayed
     ports:
       - containerPort: 3000
         protocol: TCP
     readinessProbe:
       httpGet:
         path: /
         port: 3000
       timeoutSeconds: 2

让我们用 kubectl 部署一个 pod:

kubectl apply -f pod.yaml

让我们等几秒钟,然后看看 readinessProbe 是如何工作的:

kubectl describe pods nodedelayed

在输出的末尾,您可以看到一些事件是相似的 这个.

可以看到,当检查时间超过2秒时,kubectl并没有重启pod。 相反,他取消了请求。 传入的通信被重定向到其他正在工作的 Pod。

请注意,现在 Pod 已卸载,kubectl 会再次将请求路由到它:对 GET 请求的响应不再延迟。

为了进行比较,下面是修改后的 app.js 文件:

var http = require('http');

var server = http.createServer(function(req, res) {
   const sleep = (milliseconds) => {
       return new Promise(resolve => setTimeout(resolve, milliseconds))
   }
   sleep(5000).then(() => {
       res.writeHead(200, { "Content-type": "text/plain" });
       res.end("Hellon");
   })
});

server.listen(3000, function() {
   console.log('Server is running at 3000')
})

TL博士
在云应用程序出现之前,日志是监控和检查应用程序运行状况的主要手段。 然而,没有办法采取任何纠正措施。 日志在今天仍然有用;需要收集它们并将其发送到日志收集系统以分析紧急情况并做出决策。 [例如,所有这些都可以在不使用 monit 的云应用程序的情况下完成,但使用 k8s 就变得容易多了:) – 编者注。 ]

如今,必须几乎实时进行更正,因此应用程序不再必须是黑匣子。 不,它们应该显示允许监控系统查询和收集有关流程状态的有价值数据的端点,以便它们可以在必要时立即响应。 这称为性能测试设计模式,它遵循高可观察性原则(HOP)。

Kubernetes 默认提供 2 种类型的健康检查:readinessProbe 和 livenessProbe。 两者都使用相同类型的检查(HTTP GET 请求、TCP 通信和命令执行)。 他们针对 Pod 中的问题做出的决策有所不同。 livenessProbe 重新启动容器,希望错误不再发生,而 readinessProbe 将 pod 与传入流量隔离,直到问题原因得到解决。

正确的应用程序设计应该包括这两种类型的检查,并确保它们收集足够的数据,特别是在引发异常时。 它还应该显示必要的 API 端点,为监控系统(Prometheus)提供重要的健康指标。

来源: habr.com

添加评论