0. 前言

kubernetes 提供三种探针,配置探针(Liveness),就绪探针(Readiness)和启动(Startup)探针判断容器健康状态。其中,存活探针确定什么时候重启容器,就绪探针确定容器何时准备好接受流量请求,启动探针判断应用容器何时启动。

本文通过分析 kubelet 源码了解 kubernetes 的探针是怎么工作的。

1. kubelet probeManager

kubelet 中的 probeManager 模块提供了探针服务,直接分析 probeManager

// kubernetes/pkg/kubelet/kubelet.go
func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,...) (*Kubelet, error) {
...
klet.livenessManager = proberesults.NewManager()
klet.readinessManager = proberesults.NewManager()
klet.startupManager = proberesults.NewManager() ...
if kubeDeps.ProbeManager != nil {
klet.probeManager = kubeDeps.ProbeManager
} else {
klet.probeManager = prober.NewManager(
klet.statusManager,
klet.livenessManager,
klet.readinessManager,
klet.startupManager,
klet.runner,
kubeDeps.Recorder)
}
...
}

NewMainKubelet 中初始化 probeManager。其中,probeManager 包括三种探针 statusManagerlivenessManagerreadinessManager

kubelet 处理 pod 时,会将 pod 添加到 probeManager

// kubernetes/pkg/kubelet/kubelet.go
func (kl *Kubelet) SyncPod(ctx context.Context, updateType kubetypes.SyncPodType, pod, mirrorPod *v1.Pod, podStatus *kubecontainer.PodStatus) (isTerminal bool, err error) {
...
// Ensure the pod is being probed
kl.probeManager.AddPod(pod)
...
} // kubernetes/pkg/kubelet/prober/prober_manager.go
func (m *manager) AddPod(pod *v1.Pod) {
...
key := probeKey{podUID: pod.UID}
for _, c := range append(pod.Spec.Containers, getRestartableInitContainers(pod)...) {
key.containerName = c.Name if c.StartupProbe != nil {
...
} if c.ReadinessProbe != nil {
key.probeType = readiness
if _, ok := m.workers[key]; ok {
klog.V(8).ErrorS(nil, "Readiness probe already exists for container",
"pod", klog.KObj(pod), "containerName", c.Name)
return
}
w := newWorker(m, readiness, pod, c)
m.workers[key] = w
go w.run()
} if c.LivenessProbe != nil {
...
}
}
}

manager.AddPod 中包含三种探针的处理逻辑,这里以 ReadinessProbe 探针为例进行分析。首先,创建 ReadinessProbe 的 worker,接着开启一个协程运行该 worker:

// kubernetes/pkg/kubelet/prober/worker.go
func (w *worker) run() {
...
probeLoop:
// doProbe 进行探针检测
for w.doProbe(ctx) {
// Wait for next probe tick.
select {
case <-w.stopCh:
break probeLoop
case <-probeTicker.C:
case <-w.manualTriggerCh:
// continue
}
}
} func (w *worker) doProbe(ctx context.Context) (keepGoing bool) {
...
// Note, exec probe does NOT have access to pod environment variables or downward API
result, err := w.probeManager.prober.probe(ctx, w.probeType, w.pod, status, w.container, w.containerID)
if err != nil {
// Prober error, throw away the result.
return true
}
...
}

进入 worker.probeManager.prober.probe 查看探针是怎么探测 container 的:

// kubernetes/pkg/kubelet/prober/prober.go
// probe probes the container.
func (pb *prober) probe(ctx context.Context, probeType probeType, pod *v1.Pod, status v1.PodStatus, container v1.Container, containerID kubecontainer.ContainerID) (results.Result, error) {
var probeSpec *v1.Probe
switch probeType {
case readiness:
probeSpec = container.ReadinessProbe
case liveness:
probeSpec = container.LivenessProbe
case startup:
probeSpec = container.StartupProbe
default:
return results.Failure, fmt.Errorf("unknown probe type: %q", probeType)
} if probeSpec == nil {
klog.InfoS("Probe is nil", "probeType", probeType, "pod", klog.KObj(pod), "podUID", pod.UID, "containerName", container.Name)
return results.Success, nil
} result, output, err := pb.runProbeWithRetries(ctx, probeType, probeSpec, pod, status, container, containerID, maxProbeRetries)
...
} // runProbeWithRetries tries to probe the container in a finite loop, it returns the last result
// if it never succeeds.
func (pb *prober) runProbeWithRetries(ctx context.Context, probeType probeType, p *v1.Probe, pod *v1.Pod, status v1.PodStatus, container v1.Container, containerID kubecontainer.ContainerID, retries int) (probe.Result, string, error) {
var err error
var result probe.Result
var output string
for i := 0; i < retries; i++ {
result, output, err = pb.runProbe(ctx, probeType, p, pod, status, container, containerID)
if err == nil {
return result, output, nil
}
}
return result, output, err
} func (pb *prober) runProbe(ctx context.Context, probeType probeType, p *v1.Probe, pod *v1.Pod, status v1.PodStatus, container v1.Container, containerID kubecontainer.ContainerID) (probe.Result, string, error) {
timeout := time.Duration(p.TimeoutSeconds) * time.Second
if p.Exec != nil {
klog.V(4).InfoS("Exec-Probe runProbe", "pod", klog.KObj(pod), "containerName", container.Name, "execCommand", p.Exec.Command)
command := kubecontainer.ExpandContainerCommandOnlyStatic(p.Exec.Command, container.Env)
return pb.exec.Probe(pb.newExecInContainer(ctx, container, containerID, command, timeout))
} if p.HTTPGet != nil {
req, err := httpprobe.NewRequestForHTTPGetAction(p.HTTPGet, &container, status.PodIP, "probe")
...
} if p.TCPSocket != nil {
...
} if p.GRPC != nil {
...
}
...
}

到这里我们可以看到,根据探针的不同类型执行不同的方法,对于用命令行探测的探针,执行 prober.exec.Probe 方法,对于 http 类型的探针,执行 httpprobe.NewRequestForHTTPGetAction 类型的方法,等等。

2. 小结

本文从 kubelet 源码层面介绍了 kubernetes 中探针的检测逻辑,力图做到知其然,知其所以然。


Kubernetes:kubelet 源码分析之探针的更多相关文章

  1. kubelet源码分析——关闭Pod

    上一篇说到kublet如何启动一个pod,本篇讲述如何关闭一个Pod,引用一段来自官方文档介绍pod的生命周期的话 你使用 kubectl 工具手动删除某个特定的 Pod,而该 Pod 的体面终止限期 ...

  2. kubelet源码分析——监控Pod变更

    前言 前文介绍Pod无论是启动时还是关闭时,处理是由kubelet的主循环syncLoop开始执行逻辑,而syncLoop的入参是一条传递变更Pod的通道,显然syncLoop往后的逻辑属于消费者一方 ...

  3. Kubernetes client-go 源码分析 - Reflector

    概述入口 - Reflector.Run()核心 - Reflector.ListAndWatch()Reflector.watchHandler()NewReflector()小结 概述 源码版本: ...

  4. Kubernetes Deployment 源码分析(二)

    概述startDeploymentController 入口逻辑DeploymentController 对象DeploymentController 类型定义DeploymentController ...

  5. kubelet源码分析(version: git tag 1.7.6)

    一.概述 kubelet源码入口:cmd/kubelet/kubelet.go main() cmd/kubelet/app 包中的Run函数: 查看先参数,kubelet.KubeletDeps t ...

  6. client-go客户端自定义开发Kubernetes及源码分析

    介绍 client-go 是一种能够与 Kubernetes 集群通信的客户端,通过它可以对 Kubernetes 集群中各资源类型进行 CRUD 操作,它有三大 client 类,分别为:Clien ...

  7. Kubernetes Deployment 源码分析(一)

    概述Deployment 基础创建 DeploymentReplicaSet滚动更新失败回滚历史版本回滚其他特性小结 概述 Deployment 是最常用的 Kubernetes 原生 Workloa ...

  8. kubelet源码分析——kubelet简介与启动

    kubelet是k8s集群中一个组件,其作为一个agent的角色分布在各个节点上,无论是master还是worker,功能繁多,逻辑复杂.主要功能有 节点状态同步:kublet给api-server同 ...

  9. kubelet源码分析——启动Pod

    前文说到Kubelet启动时,调用到kubelet.Run方法,里面最核心的就是调用到kubelet.syncLoop.它是一个循环,这个循环里面有若干个检查和同步操作,其中一个是地在监听Pod的增删 ...

  10. Kubernetes client-go 源码分析 - ListWatcher

    概述ListWatch 对象的创建GetterListWatchList() & Watch() 概述 源码版本信息 Project: kubernetes Branch: master La ...

随机推荐

  1. OpenHarmony有氧拳击之设备端开发

    一.简介 在一个风和日丽,阳光明媚的下午,码农们都像往常一样正在专注地码代码.突然前面的小哥哥站起来,手握开发板,来回出拳.这是怎么回事? 原来这是一款拳击互动游戏,本文将带你一同解开其中的奥秘.开发 ...

  2. 一图读懂HUAWEI HiAI Foundation

    作为华为端侧AI的创新开放平台,HMS Core的HUAWEI HiAI Foundation开放AI算力,助力AI应用高效开发,同时联合多领域打造AI生态,实现日调用600亿次的突破,助力AI生态繁 ...

  3. Stable diffusion 初学者指南

    1. Stable diffusion 初学者指南 想掌握Stable Diffusion AI技术吗? 这份初学者指南专为完全没接触过Stable Diffusion或任何AI图像生成器的新手设计. ...

  4. css实现带背景颜色的小三角

    <div id="first"> <p>带背景颜色的小三角实现是比较简单的</p> <span id="top"> ...

  5. sql 语句系列(列举非索引外键)[八百章之第九章]

    列举非索引外键 列举出那些外键没有添加索引. 目的: 1.减少锁. 2.外键添加索引,提示了查询性能,因为要与父表做连接查询做笛卡尔积. 下面只要会复制即可,没有会去从新写一遍的. select fk ...

  6. 重新点亮linux 命令树————二进制安装[十一八]

    前言 简单介绍一下二进制安装 正文 wget https://openresty.org/download/openresty-1.15.8.1.tar.gz tar -zxf openresty-V ...

  7. NG 转发配置

    ng的用途就不用说了,反向代理么,都知道,不过以前一直不太理解怎没配,现在终于理解点了 1.下载ng,如图: 2.先解压,解压后的路径不建议有空格和中文,其次配置环境变量,加到系统path 3.启动n ...

  8. Windows家庭版开启远程桌面的方法

    一.背景介绍 Windows家庭版提示不支持远程桌面,如下图Windows11家庭版: 本文将介绍一种Windows家庭版开启远程桌面的方法,分为开启远程桌面设置.配置问题排查及解决: 安装远程桌面补 ...

  9. 简述Linux磁盘IO

    1.什么是磁盘 在讲解磁盘IO前,先简单说下什么是磁盘.磁盘是可以持久化存储的设备,根据存储介质的不同,常见磁盘可以分为两类:机械磁盘和固态磁盘. 1.1 机械磁盘 第一类,机械磁盘,也称为硬盘驱动器 ...

  10. 鸿蒙HarmonyOS实战-ArkUI组件(Canvas)

    一.Canvas Canvas组件是一种图形渲染组件,它提供了一个画布(canvas),开发者可以在上面绘制各种图形.文本等.Canvas组件通常用于创建游戏.数据可视化等需要动态绘制图形的应用程序. ...