只要将 pod 调度到某个节点,该节点上的 Kubelet 就会运行 pod 的容器, 从此只要该 pod 存在, 容器就会保持运行。 如果容器的主进程崩溃,Kubelet 将重启容器。

4.1.1 介绍存活探针

Kubernetes 可以通过存活探针 (liveness probe) 检查容器是否还在运行。 可以为 pod 中的每个容器单独指定存活探针。 Kubernetes 将定期执行探针,如果探测失败则重新启动容器。

Kubernetes 有以下三种探测容器的机制:

  • HTTP GET探针对容器的 IP 地址(你指定的端口和路径)执行 HTTP GET 请求。如果探测器收到响应,并且响应状态码不代表错误(换句话说,如果HTTP响应状态码是2xx或3xx), 则认为探测成功。如果服务器返回错误响应状态 码或者根本没有响应,那么探测就被认为是失败的,容器将被重新启动。

例如:http-get http://:8080/

  • TCP套接字探针尝试与容器指定端口建立TCP连接。如果连接成功建立,则探测成功。否则,容器重新启动。
  • Exec探针在容器内执行任意命令,并检查命令的退出状态码。如果状态码 是 0, 则探测成功。所有其他状态码都被认为失败。

4.1.2 创建基于HTTP的存活探针

实验环境准备:修改Node.js应用程序代码app.js——在第五个请求之后,给每个请求返回HTTP状态码500 (Internal Server Error)。重新构建镜像,并上传到本地仓库。

  1. cd /root/dockerfile
  2. cat >app.js <<'EOF'
  3. const http = require('http');
  4. const os = require('os');
  5. console.log("Kubia server starting...");
  6. var requestCount = 0;
  7. var handler = function(request, response) {
  8. console.log("Received request from " + request.connection.remoteAddress);
  9. requestCount++;
  10. if (requestCount > 5) {
  11. response.writeHead(500);
  12. response.end("I'm not well. Please restart me!");
  13. return;
  14. }
  15. response.writeHead(200);
  16. response.end("You've hit " + os.hostname() + "\n");
  17. };
  18. var www = http.createServer(handler);
  19. www.listen(8080);
  20. EOF
  21. cat >Dockerfile <<'EOF'
  22. FROM node:7
  23. ADD app.js /app.js
  24. ENTRYPOINT ["node", "app.js"]
  25. EOF
  26. docker build -t kubia-unhealthy .
  27. docker tag kubia-unhealthy 10.0.0.10:5000/luksa/kubia-unhealthy
  28. docker push 10.0.0.10:5000/luksa/kubia-unhealthy

创建一个包含HTTP GET存活探针的新pod,下面的代码清单是pod 的YAML。
image.png

该pod的描述文件定义了 一 个httpGet存活探针,该探针告诉Kubernetes定期在端口8080和路径 / 上执行HTTP GET 请求,以确定该容器是否健康。这些请求在容器运行后立即开始。经过五次这样的请求(或实际的客户端请求)后,你的应用程序开始返回HTTP状态码500, Kubernetes会认为探测失败并重启容器。

  1. cd /root/k8s/
  2. cat >kubia-liveness-probe.yaml <<'EOF'
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: kubia-liveness
  7. spec:
  8. containers:
  9. - image: luksa/kubia-unhealthy
  10. name: kubia
  11. livenessProbe:
  12. httpGet:
  13. path: /
  14. port: 8080
  15. EOF
  16. kubectl create -f ./kubia-liveness-probe.yaml

4.1.3 使用存活探针

创建pod后,大约一分半钟后,容器将重启。RESTARTS列显示pod的容器已被重启一次(如果你再等一分半钟,它会再次重启,然后无限循环下去)。
image.png

获取崩溃容器的应用日志

当你想知道为什么前一个容器终止,或你想看到的是前一个容器的日志,而不是当前容器的。可以通过添加—previous选项来完成。

Usage:

获取pod中前一个运行的容器实例的日志
kubectl logs —previous

可以通过查看kubectl describe的内容来了解为什么必须重启容器,如下面的代码清单所示。
image.png
image.png
退出代码为137, 这有特殊的含义——表示该进程由外部信号终止。数字137是两个数字的总和:128+x, 其中x是终止进程的信号编号。在这个例子中,x等于9, 这是SIGKILL的信号编号,意味着这个进程被强行终止。

注意:当容器被强行终止时,会创建一个全新的容器——而不是重启原来的容器。

4.1.4 配置存活探针的附加属性

image.png
kubectl describe的输出结果中,还包括了存活探针的附加信息
http-get http://8080/ 表示存活探针的机制;
delay=0s 表示容器启动后立即开始探测;
timeout=1s 表示容器必须在1秒内进行响应,不然这次探测记作失败;
period=10s 表示每10秒探测一次容器;
#success=1 表示探测只要成功1次则认为容器健康;
#failure=3 表示探测连续3次失败则重启容器;

使用 initialDelaySeconds 设置探测容器健康的初始延迟:

  1. livenessProbe:
  2. httpGet:
  3. path: /
  4. port: 8080
  5. initialDelaySeconds: 15 #Kubernetes会在第一次探测前等待 15 秒

如果没有设置初始延迟,探针将在启动时立即开始探测容器, 这通常会导致探测失败, 因为应用程序还没准备好开始接收请求。 如果失败次数超过阈值,在应用程序能正确响应请求之前, 容器就会重启。

提示:务必记得设置一个初始延迟(initialDelaySeconds)来说明应用程序的启动时间。

很多场合都会看到这种情况,用户很困惑为什么他们的容器正在重启。 但是如果使用kubectl describe, 他们会看到容器以退出码137(SIGKILL)或143(SIGTERM)结束, 并告诉他们该pod是被迫终止的。 此外,pod事件列表将显示容器因liveness probe失败而被终止。 如果你在pod启动时看到这种情况,那是因为未能适当设置initialDelaySeconds。

4.1.5 创建有效的存活探针

对于在生产中运行的pod, 一定要定义一个存活探针。没有探针的话,Kubernetes无法知道你的应用是否还活着。 只要进程还在运行, Kubernetes会认为容器是健康的。

存活探针应该检查什么

但为了更好地进行存活检查, 需要将探针配置为请求特定的URL路径(例如health), 并让应用从内部对内部运行的所有重要组件执行状态检查, 以确保它们都没有终止或停止响应。

提示:请确保/health HTTP 端点不需要认证,否则探剧会一直失败,导致你的容器无限重启。

一定要确保只检查应用程序的内部。例如, 当服务器无法连接到后端数据库时, 前端Web服务器的存活探针不应该返回失败。 如果问题的原因在数据库中,重启Web服务器容器不会解决问题。

保持探针轻量

存活探针不应消耗太多的计算资源, 并且运行不应该花太长时间 。 默认情况下,探测器执行的频率相对较高, 而且必须在一秒之内执行完毕。探针的CPU时间计入容器的CPU时间配额, 因此使用重量级的存活探针将减少主应用程序进程可用的CPU时间。

提示:如果你在容器中运行Java应用程序, 请确保使用HTTP GET存活探针,而不是启动全新JVM以获取存活信息的Exec探针。 任何基于JVM或类似的应用程序也是如此,它们的启动过程需要大量的计算资源。

无须在探针中实现重试循环

探针的失败阈值(#failure=3)是可配置的, 并且通常在容器被终止之前探针必须失败多次。 但即使你将失败阙值设置为1 , Kubernetes为了确认一次探测的失败,会尝试若干次 。 因此在探针中自己实现重试循环是浪费精力。

存活探针小结:

容器崩溃或其存活探针失败时通过重启容器来保持运行,这项任务是由承载pod的节点上的Kubelet执行的,而Master node上运行的Control Plane 组件不会参与此过程。