Kubelet Probe Manager
在 Kubernetes 中,系统和应用程序的健康检查任务是由 kubelet 来完成的,本文主要讨论 kubelet 中 probemanager 相关的实现原理。
如果你对 k8s 的各种 probe 如何使用还不了解,可以看下我之前写的这篇K8S 中的健康检查机制,是从实践的角度介绍的。
statusManager
在 kubelet 初始化的时候,会创建 statusManager 和 probeManager,这两个都是和 pod 状态相关的逻辑,在kubelet 原理解析一:pod 管理文章中有提到,statusManager 负责维护状态信息,并把 Pod 状态及时更新到 Api-Server,
但是它并不负责监控 pod 状态的变化,而是提供对应的接口供其他组件调用,比如 probeManager。probeManager 会定时去监控 pod 中容器的健康状况,一旦发现状态发生变化,就调用 statusManager 提供的方法更新 pod 的状态。
|
|
Golang
statusManager 代码位于:pkg/kubelet/status/status_manager.go
|
|
Golang
Start() 方法是在 kubelet 运行的时候调用的,它会启动一个 goroutine 执行更新操作:
|
|
Golang
这个 goroutine 就能不断地从两个 channel 监听数据进行处理:syncTicker 是个定时器,也就是说它会定时保证 apiserver 和自己缓存的最新 pod 状态保持一致;podStatusChannel 是所有 pod 状态更新发送到的地方,调用方不会直接操作这个 channel,而是通过调用上面提到的修改状态的各种方法,这些方法内部会往这个 channel 写数据。
m.syncPod 根据参数中的 pod 和它的状态信息对 apiserver 中的数据进行更新,如果发现 pod 已经被删除也会把它从内部数据结构中删除。
probeManager
probeManager 负责 检测 pod 中容器的健康状态,目前有三种 probe:
- liveness: 让 Kubernetes 知道你的应用程序是否健康,如果你的应用程序不健康,Kubernetes 将删除 Pod 并启动一个新的替换它(与 RestartPolicy 有关)。Liveness 探测可以告诉 Kubernetes 什么时候通过重启容器实现自愈。
- readiness: readiness 与 liveness 原理相同,不过 Readiness 探针是告诉 Kubernetes 什么时候可以将容器加入到 Service 负载均衡中,对外提供服务。
- startupProbe:1.16 开始支持的新特性,检测慢启动容器的状态,具体参考 startup-probes
并不是所有的 pod 中的容器都有健康检查的探针,如果没有,则不对容器进行检测,默认认为容器是正常的。在每次创建新 pod 的时候,kubelet 都会调用 probeManager.AddPod(pod) 方法,它对应的实现在 pkg/kubelet/prober/prober_manager.go 文件中:
|
|
Golang
在这个方法里,kubelet 会遍历 pod 中所有的 container,如果配置了 probe,就创建一个 worker,并异步处理这次探测
|
|
Golang
worker 开始 run 之后,会调用 doProbe 方法
|
|
Golang
liveness 检测结果会存放在 resultsManager,它把结果保存在缓存中,并发送到 m.updates 管道。而管道消费者是 kubelet 中的主循环 syncLoopIteration。
|
|
Golang
liveness 检测如果不通过,pod 就会重启,由 kubelet 的 sync 循环处理即可。但 readness 检测失败不能重启 pod,因此 readness 的逻辑是:
|
|
Golang
proberManager 启动的时候,会运行一个 goroutine 定时读取 readinessManager 管道中的数据,并根据数据调用 statusManager 去更新 apiserver 中 pod 的状态信息。
负责 Service 逻辑的组件获取到了这个状态,就能根据不同的值来决定是否需要更新 endpoints 的内容,也就是 service 的请求是否发送到这个 pod。
Probe 方法
上面是 probemanager 的主要逻辑,我们接下来看下真正执行探测任务的 probe 方法
|
|
Golang
probe 主方法调用 pb.runProbeWithRetries 方法,传入 containerid、类型、重试次数等。
exec 方法
调用 runtimeService 的 ExecSync 方法进入容器执行命令,回收结果,如果退出码为 0 ,就认为探测成功。
|
|
Golang
HTTP 方法
标准的 http 探测模板,如果 400 > code >= 200,则认为成功。不支持 https
|
|
Golang
TCP 方法
gRPC 或 FTP 服务一般会使用 TCP 探测,尝试在指定端口上建立 TCP 连接。
如果 socket 连接能成功,则返回成功。
func DoTCPProbe(addr string, timeout time.Duration) (probe.Result, string, error) {
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
// Convert errors to failures to handle timeouts.
return probe.Failure, err.Error(), nil
}
err = conn.Close()
if err != nil {
klog.Errorf("Unexpected error closing TCP probe socket: %v (%#v)", err, err)
}
return probe.Success, "", nil
}
参考
-
No backlinks found.