K8s 探针

kubernetes porbes (探针)

k8s 探针

node-server.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const { createServer } = require('http');
var url = require("url");
const os = require('os');

const HOST = '0.0.0.0';
const PORT = 8080;

var healthVar = true;

const server = createServer((req, resp) => {

  const path = url.parse(req.url).pathname;
  console.log("Request for " + path + " received.");

  if (path === '/healthz') {
    if (healthVar === true) {
        resp.writeHead(200, { 'Content-Type': 'text/plain' });
        resp.end('nodejs http server is health.');
    } else {
        resp.writeHead(500, { 'Content-Type': 'text/plain' });
        resp.end('nodejs http server is unhealth.');
    }
  } else if (path === '/shutdown') {
    healthVar = false;
    resp.writeHead(200, { 'Content-Type': 'text/plain' });
    resp.end('nodejs http server is shutdown.');
  } else {
    resp.writeHead(200, { 'Content-Type': 'text/plain' });
    console.log('server is working...');
    resp.end('hello nodejs http server');
  }
});

server.listen(PORT, HOST, (error) => {
  if (error) {
    console.log('Something wrong: ', error);
    return;
  }
  console.log(`server is listening on http://${HOST}:${PORT} ...`, ' PID = ', process.pid);
});

/** 改造部分 关于进程结束相关信号可自行搜索查看*/
function close(signal) {
    console.log(`收到 ${signal} 信号开始处理`);
    server.close(() => {
        console.log(`服务停止 ${signal} 处理完毕`);
        process.exit(0);
    });
}

process.on('SIGTERM', close.bind(this, 'SIGTERM'));
process.on('SIGINT', close.bind(this, 'SIGINT'));
/** 改造部分 */
1
2
3
4
5
FROM node:lts-alpine3.18
COPY node-server.js /node/
WORKDIR /node/
EXPOSE 8080
ENTRYPOINT ["node", "/node/node-server.js"]

构建镜像

1
nerdctl build -t porbe-server:v1 .

发布镜像到个人仓库

1
2
3
4
5
6
# 登录
nerdctl login harbor.nihility.cn
# 打标签
nerdctl tag porbe-server:v1 harbor.nihility.cn/library/porbe-server:v1
# 推送镜像
nerdctl push harbor.nihility.cn/library/porbe-server:v1

探针参考文档

kubernetes 探针 [Probes] 分为:活动/活性(Liveness)、就绪(Readiness)和启动(Startup)探针。

Kubernetes(通常简称为 K8s)中的探针是一种用于监测容器内部应用程序状态的机制。

探针有助于确保容器中的应用程序正常运行,并可以在应用程序出现问题时采取适当的措施,如重新启动容器或将流量切换到其它实例。

Kubernetes支持以下类型的探针:

  1. Liveness Probe(存活探针):Liveness 探针用于检测应用程序是否仍然在运行。如果 Liveness 探针检测到应用程序崩溃或无响应,Kubernetes 将重新启动容器。这有助于确保应用程序的高可用性。
  2. Readiness Probe(就绪探针):Readiness 探针用于确定应用程序是否已准备好接受流量。如果 Readiness 探针失败,Kubernetes 将不会将流量路由到该容器,直到应用程序准备好为止。这有助于确保在应用程序启动或重启时不会向其发送流量。
  3. Startup Probe(启动探针):Startup 探针是一种相对较新的探针类型,用于确定应用程序是否已经启动。它与 Liveness Probe 的主要区别在于,它只在容器启动后的初始一段时间内运行。这有助于处理应用程序启动期间的特定问题。

探针的类型、检查方式和配置选项可以根据应用程序的需求进行调整,以确保容器的可用性和稳定性。

Kubernetes 提供了活性探针来检测和修复服务长时间运行最终转换为不可用状态情况,重启服务。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox:1.36
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
  • periodSeconds :kubelet 应每 5 秒执行一次活性探测。
  • initialDelaySeconds: kubelet 在执行第一次探测之前应该等待 5 秒。

命令返回 0 表示容器存活,非 0 表示节点损坏不可访问。

启动等待 5s 执行活性探针检测,在每 5s 检测一次服务活性状态,检测失败重启容器。

1
2
3
4
Warning  Unhealthy  43s (x6 over 2m8s)   kubelet  Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal   Killing    43s (x2 over 118s)   kubelet  Container liveness failed liveness probe, will be restarted
Normal   Created    13s (x3 over 2m39s)  kubelet  Created container liveness
Normal   Started    13s (x3 over 2m39s)  kubelet  Started container liveness

另一种活性探测使用 HTTP GET 请求。 pod-liveness-http.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: harbor.nihility.cn/library/porbe-server:v1
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3
  • periodSeconds: kubelet 每 3s 执行一次活性探针检测。
  • initialDelaySeconds: 告诉 kubelet 在执行首次活性探针检测时需先等待 3s 。

kubelet 发起 HTTP GET 请求,任何大于或等于 200 且小于 400 的状态码都表示成功。其它状态码表示失败。

1
2
3
Normal   Created    11s (x2 over 74s)  kubelet Created container liveness
Warning  Unhealthy  11s (x3 over 17s)  kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
Normal   Killing    11s                kubelet Container liveness failed liveness probe, will be restarted

第三种类型的活性探测使用 TCP 套接字。通过此配置,kubelet 将尝试在指定端口上打开容器的套接字。如果可以建立连接,则容器被认为是健康的,如果不能建立连接,则被认为是失败的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: harbor.nihility.cn/library/porbe-server:v1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10

这个配置文件同时使用了 readiness 和 liveness 探针。

  • readiness:kubelet 将在容器启动后 15 秒发送第一个就绪探测。将尝试连接到 goproxy 容器上 8080 端口,当探针成功,这个 Pod 将会标记为准备。kubelet 将继续每 10 秒运行一次此检查。
  • liveness:同就绪探针一样,如果活性探测失败,容器将重新启动。

探针有许多字段,您可以使用它们来更精确地控制启动、活动和就绪检查的行为:

  • initialDelaySeconds:在容器运行之后,启动、活动或就绪探测已初始化前的延迟秒数。如果定义了启动探测,则在启动探测成功之前,活动性和就绪性探测延迟不会开始。如果 periodSeconds 的值大于 initialDelaySeconds,则 initialDelaySeconds 将被忽略。默认为 0 秒,最小值为 0。
  • periodSeconds:执行探测的频率(以秒为单位)。默认为 10 秒。最小值为 1。
  • timeoutSeconds: 探测超时后的秒数。默认为 1 秒。最小值为 1。
  • successThreshold: 探测失败后被视为成功的最小连续成功次数。默认为 1。活性和启动探针必须为 1。最小值为 1。
  • failureThreshold: 当探测连续失败failureThreshold次后,Kubernetes认为整体检查失败:容器未 就绪/健康/存活。对于启动或活性探测的情况,如果至少 failureThreshold 探测失败,Kubernetes 会将容器视为不健康并触发该特定容器的重新启动。

http-porbes 参考文档

可以在 HttpGet 请求中配置额外参数:

  • path: HTTP 服务器上的访问路径,默认为“/”。
  • httpHeaders: 要在请求中设置的自定义标头,HTTP 允许重复标头。
  • port: 容器上要访问的端口的名称或编号,数字必须在 1 到 65535 范围内。

参考文档

诀窍是使用相同的命令、HTTP 或 TCP 检查来设置启动探测,并使用足够长的 failureThreshold * periodSeconds 来涵盖最坏情况的启动时间。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

由于启动探针的存在,应用程序将有最多 5 分钟 (30 * 10 = 300 秒) 的时间来完成启动。

一旦启动探针成功一次,活性探针就会接管以提供对容器死锁的快速响应。

如果启动探测从未成功,容器将在 300 秒后被终止,并受 pod 的 restartPolicy 约束。

相关内容