解决 Prometheus 监控 Kubernetes Job 误报的坑

解决 Prometheus 监控 Kubernetes Job 误报的坑

作者:阳明 2022-03-06 09:51:04

云计算

云原生 要想做到监控报警准确,其实我们只需要去获取同一个 CronJob 触发的一组 Job 的最后一次任务,只有该 Job 在执行失败的时候才触发报警即可。

昨天在 Prometheus 课程辅导群里面有同学提到一个问题,是关于 Prometheus 监控 Job 任务误报的问题(已经同步到社区网站),大概的意思就 CronJob 控制的 Job,前面执行失败了会触发报警,后面生成的新的 Job 可以正常执行后,但是还是会收到前面的报警:

这是因为一般在执行 Job 任务的时候我们会保留一些历史记录方便排查问题,所以如果之前有失败的 Job 了,即便稍后会变成成功的,那么之前的 Job 也会继续存在,而大部分直接使用 kube-prometheus 安装部署的话使用的默认报警规则是kube_job_status_failed > 0,这显然是不准确的,只有我们去手动删除之前这个失败的 Job 任务才可以消除误报,当然这种方式是可以解决问题的,但是不够自动化,一开始没有想得很深入,想去自动化删除失败的 Job 来解决,但是这也会给运维人员带来问题,就是不方便回头去排查问题。下面我们来重新整理下思路解决下这个问题。

CronJob 会在计划的每个执行时间创建一个 Job 对象,可以通过 .spec.successfulJobsHistoryLimit 和 .spec.failedJobsHistoryLimit 属性来保留多少已完成和失败的 Job,默认分别为3和1,比如下面声明一个 CronJob 的资源对象:

apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
--c
-date;
restartPolicy: OnFailure

根据上面的资源对象规范,Kubernetes 将只保留一个失败的 Job 和一个成功的 Job:

NAMECOMPLETIONSDURATIONAGE
hello-41117063560/12m10d
hello-41117063561/15s5s

要解决上面的误报问题,同样还是需要使用到 kube-state-metrics 这个服务,它通过监听 Kubernetes APIServer 并生成有关对象状态的指标,它并不关注单个 Kubernetes 组件的健康状况,而是关注内部各种对象的健康状况,例如 Deployment、Node、Job、Pod 等资源对象的状态。这里我们将要使用到以下几个指标:

  • kube_job_owner:用来查找 Job 和触发它的 CronJob 之间的关系。
  • kube_job_status_start_time:获取 Job 被触发的时间。
  • kube_job_status_failed:获取执行失败的任务。
  • kube_cronjob_spec_suspend:过滤掉挂起的作业。

下面是一个指标示例,其中包含 CronJob 触发运行的hello 任务生成的标签:

kube_job_owner{job_name="hello-1604875860", namespace="myNamespace", owner_is_controller="true", owner_kind="CronJob", owner_name="hello"} 1
kube_job_status_start_time{job_name="hello-1604875860", namespace="myNamespace"} 1604875874
kube_job_status_failed{job_name="hello-1604875860", namespace="myNamespace", reason="BackoffLimitExceeded"} 1
kube_cronjob_spec_suspend{cronjob="hello",job="kube-state-metrics", namespace="myNamespace"} 0

要想做到监控报警准确,其实我们只需要去获取同一个 CronJob 触发的一组 Job 的最后一次任务,只有该 Job 在执行失败的时候才触发报警即可。

由于 kube_job_status_failed 和 kube_job_status_start_time 指标中并不包含所属 CronJob 的标签,所以第一步需要加入这个标签,而 kube_job_owner 指标中的 owner_name 就是我们需要的,可以用下面的 promql 语句来进行合并:

max(
kube_job_status_start_time
* ON(job_name, namespace) GROUP_RIGHT()
kube_job_owner{owner_name!= ""}
)
BY (job_name, owner_name, namespace)

这里我们使用 max 函数是因为我们可能会因为 HA 运行多个 kube-state-metrics,所以用 max 函数来返回每个 Job 任务的一个结果即可。假设我们的 Job 历史记录包含 2 个任务(一个失败,另一个成功),结果将如下所示:

{job_name="hello-1623578940", namespace="myNamespace", owner_name="hello"} 1623578959
{job_name="hello-1617667200", namespace="myNamespace", owner_name="hello"} 1617667204

现在我们知道每个 Job 的所有者了,接着我们需要找出最后执行的任务,我们可以通过按 owner_name 标签聚合结果来实现这一点:

max(
kube_job_status_start_time
* ON(job_name,namespace) GROUP_RIGHT()
kube_job_owner{owner_name!=""}
)
BY (owner_name)

上面这条语句会找到每个 owner(也就是 CronJob)最新的任务开始时间,然后再和上面的语句进行合并,保留开始时间相同的记录即为最新执行的 Job 任务了:

max(
kube_job_status_start_time
* ON(job_name,namespace) GROUP_RIGHT()
kube_job_owner{owner_name!=""}
)
BY (job_name, owner_name, namespace)
== ON(owner_name) GROUP_LEFT()
max(
kube_job_status_start_time
* ON(job_name,namespace) GROUP_RIGHT()
kube_job_owner{owner_name!=""}
)
BY (owner_name)

结果将显示每个 CronJob 最后执行的作业,并且仅显示最后一个:

{job_name="hello-1623578940", namespace="myNamespace", owner_name="hello"} 1623578959

为了增加可读性我们还可以将 job_name、owner_name 标签替换为 job 和 cronjob,这样更容易看明白:

label_replace(
label_replace(
max(
kube_job_status_start_time
* ON(job_name,namespace) GROUP_RIGHT()
kube_job_owner{owner_name!=""}
)
BY (job_name, owner_name, namespace)
== ON(owner_name) GROUP_LEFT()
max(
kube_job_status_start_time
* ON(job_name,namespace) GROUP_RIGHT()
kube_job_owner{owner_name!=""}
)
BY (owner_name),
"job", "$1", "job_name", "(.+)"),
"cronjob", "$1", "owner_name", "(.+)")

现在将会看到类似于下面的结果:

{job="hello-1623578940", cronjob="hello", job_name="hello-1623578940", namespace="myNamespace", owner_name="hello"} 1623578959

由于上面的查询语句比较复杂,如果每次报警评估的时候都去进行一次实时计算会对 Prometheus 产生非常大的压力,这里我们可以借助记录规则来实现类离线计算的方式,大大提高效率,创建如下所示的记录规则,用来表示获取每个 CronJob 最后执行的作业记录:

- record: job:kube_job_status_start_time:max
expr: |
label_replace(
label_replace(
max(
kube_job_status_start_time
* ON(job_name,namespace) GROUP_RIGHT()
kube_job_owner{owner_name!=""}
)
BY (job_name, owner_name, namespace)
== ON(owner_name) GROUP_LEFT()
max(
kube_job_status_start_time
* ON(job_name,namespace) GROUP_RIGHT()
kube_job_owner{owner_name!=""}
)
BY (owner_name),
"job", "$1", "job_name", "(.+)"),
"cronjob", "$1", "owner_name", "(.+)")

现在我们知道了 CronJob 最近开始执行的 Job 了,那么想要过滤出失败的,则再使用 kube_job_status_failed 指标就可以了:

- record: job:kube_job_status_failed:sum
expr: |
clamp_max(job:kube_job_status_start_time:max, 1)
* ON(job) GROUP_LEFT()
label_replace(
(kube_job_status_failed > 0),
"job", "$1", "job_name", "(.+)"
)

这里使用 clamp_max 函数将 job:kube_job_status_start_time:max 的结果转换为一组上限为 1 的时间序列,使用它来通过乘法过滤失败的作业,得到包含一组最近失败的 Job 任务,这里我们也添加到名为 kube_job_status_failed:sum 的记录规则中。

最后一步就是直接为失败的 Job 任务添加报警规则,如下所示:

- alert: CronJobStatusFailed
expr: |
job:kube_job_status_failed:sum
* ON(cronjob, namespace) GROUP_LEFT()
(kube_cronjob_spec_suspend == 0)

为避免误报,我们已将挂起的任务排除在外了。到这里我们就解决了 Prometheus 监控 CronJob 的任务误报的问题,虽然 kube-prometheus 为我们内置了大量的监控报警规则,但是也不能完全迷信,有时候并不一定适合实际的需求。

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/285309.html<

(0)
管理的头像管理
上一篇2025-05-14 20:06
下一篇 2025-05-14 20:07

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注