kube-state-metrics CronJob 指标详解:从 14 个 kube_cronjob_* 指标到调度解析源码原理
2026/9/17 20:10:04 网站建设 项目流程

kube-state-metrics CronJob 指标详解:从 14 个 kube_cronjob_* 指标到调度解析源码原理

【免费下载链接】kube-state-metricsAdd-on agent to generate and expose cluster-level metrics.项目地址: https://gitcode.com/GitHub_Trending/ku/kube-state-metrics

本文围绕 kube-state-metrics 官方的 CronJob 指标文档(docs/metrics/workload/cronjob-metrics.md)展开,完整覆盖全部 14 个kube_cronjob_*指标的含义、标签与稳定性状态,并结合 internal/store/cronjob.go 的源码实现深入讲解下一调度时间的计算逻辑、时区处理、非法 cron 调度的容错机制,以及如何通过--metric-annotations-allowlist/--metric-labels-allowlist等启动参数控制标签类指标的暴露。读完本文,你将能够正确查询这些指标构建 CronJob 监控告警,并理解指标背后的取数与解析原理。

1. kube_cronjob_* 指标全览

CronJob 指标全部为 Gauge 类型,用于回答"CronJob 是否还在正常调度、下一次调度何时发生、上次运行是否成功"这类问题。文档中列出的完整指标清单如下:

指标名类型说明标签状态
kube_cronjob_annotationsGauge将 Kubernetes annotation 转换为 Prometheus 标签,由--metric-annotations-allowlist控制cronjobnamespaceannotation_CRONJOB_ANNOTATIONEXPERIMENTAL
kube_cronjob_infoGaugeCronJob 基本信息(调度表达式、并发策略、时区)cronjobnamespacescheduleconcurrency_policytimezoneSTABLE
kube_cronjob_labelsGauge将 Kubernetes label 转换为 Prometheus 标签,由--metric-labels-allowlist控制cronjobnamespacelabel_CRONJOB_LABELSTABLE
kube_cronjob_createdGauge创建时间的 Unix 时间戳cronjobnamespaceSTABLE
kube_cronjob_next_schedule_timeGauge下一次调度时间cronjobnamespaceSTABLE
kube_cronjob_schedule_invalidGauge调度表达式(在其配置的时区下)无法解析时为 1cronjobnamespaceEXPERIMENTAL
kube_cronjob_status_activeGauge当前正在运行的 Job 数量cronjobnamespaceSTABLE
kube_cronjob_status_last_schedule_timeGauge上次成功调度的 Unix 时间戳cronjobnamespaceSTABLE
kube_cronjob_status_last_successful_timeGauge上次成功完成的 Unix 时间戳cronjobnamespaceSTABLE
kube_cronjob_spec_suspendGauge是否已挂起(1/0)cronjobnamespaceSTABLE
kube_cronjob_spec_starting_deadline_secondsGauge错过调度时间后启动 Job 的宽限秒数cronjobnamespaceSTABLE
kube_cronjob_metadata_resource_versionGauge对象资源版本号cronjobnamespaceSTABLE
kube_cronjob_spec_successful_job_history_limitGauge保留的成功 Job 数量上限cronjobnamespaceEXPERIMENTAL
kube_cronjob_spec_failed_job_history_limitGauge保留的失败 Job 数量上限cronjobnamespaceEXPERIMENTAL

所有指标都带有两个固定基础标签:cronjob=<cronjob-name>namespace=<cronjob-namespace>。从源码结构看,这一点由 internal/store/cronjob.go 中的wrapCronJobFunc统一注入:

var ( descCronJobLabelsName = "kube_cronjob_labels" descCronJobLabelsDefaultLabels = []string{"namespace", "cronjob"} ) func wrapCronJobFunc(f func(*batchv1.CronJob) *metric.Family) func(interface{}) *metric.Family { return func(obj interface{}) *metric.Family { cronJob := obj.(*batchv1.CronJob) metricFamily := f(cronJob) for _, m := range metricFamily.Metrics { m.LabelKeys, m.LabelValues = mergeKeyValues( descCronJobLabelsDefaultLabels, []string{cronJob.Namespace, cronJob.Name}, m.LabelKeys, m.LabelValues, ) } return metricFamily } }

也就是说,无论某个指标的生成函数自己声明了哪些标签,最终都会与namespacecronjob两个标签合并输出,这保证了按命名空间/CronJob 维度做 PromQL 聚合时的标签一致性。

2. 指标逐个解析

2.1 kube_cronjob_info:调度语义的载体

kube_cronjob_info的取值恒为 1,真正的信息量在其三个附加标签中:

kube_cronjob_info{concurrency_policy="Forbid",cronjob="my-cronjob",namespace="ns1",schedule="0 */6 * * *",timezone="Asia/Shanghai"} 1

源码中对应逻辑(internal/store/cronjob.gokube_cronjob_info生成函数)直接读取j.Spec.Schedulej.Spec.ConcurrencyPolicy,并在Spec.TimeZone为空时回退为字符串"local"

timeZone := "local" if j.Spec.TimeZone != nil { timeZone = *j.Spec.TimeZone }

注意测试用例中出现了timezone="local"的输出(见 internal/store/cronjob_test.go),这印证了未显式设置spec.timeZone时标签值为local,而非空串。

2.2 kube_cronjob_next_schedule_time:判断"调度是否延迟"的核心指标

该指标描述为:下一次计划调度的 Unix 时间戳,取值为lastScheduleTime之后的下一次触发时间;若从未调度过,则从 CronJob 创建时间起算。官方建议用它来判断 Job 是否延迟。

其计算链路在 internal/store/cronjob.go 中分两层:

func parseSchedule(schedule string, timeZone *string) (cron.Schedule, error) { if timeZone != nil { schedule = fmt.Sprintf("CRON_TZ=%s %s", *timeZone, schedule) } sched, err := cron.ParseStandard(schedule) if err != nil { return nil, fmt.Errorf("failed to parse cron job schedule '%s': %w", schedule, err) } return sched, nil } func getNextScheduledTime(schedule string, lastScheduleTime *metav1.Time, createdTime metav1.Time, timeZone *string) (time.Time, error) { sched, err := parseSchedule(schedule, timeZone) if err != nil { return time.Time{}, err } if !lastScheduleTime.IsZero() { return sched.Next(lastScheduleTime.Time), nil } if !createdTime.IsZero() { return sched.Next(createdTime.Time), nil } return time.Time{}, errors.New("createdTime and lastScheduleTime are both zero") }

可以推断出三个关键行为:

  1. 调度解析使用第三方 cron 库github.com/netresearch/go-cron),并支持标准 5 段 cron 表达式(cron.ParseStandard)。
  2. 时区通过CRON_TZ=前缀注入表达式,即 CronJob 的spec.timeZone会直接参与下一次触发时间的计算。
  3. 两个基准时间的优先级:优先status.lastScheduleTime,其次creationTimestamp

两个值得注意的边界条件同样体现在源码中:

  • Suspended 的 CronJob 不输出该指标if err == nil && (j.Spec.Suspend == nil || !*j.Spec.Suspend)。已挂起的 CronJob 不再产生下一次调度,因此指标缺失(而非输出 0),这一点在 internal/store/cronjob_test.go 的SuspendedCronJob1用例中得到验证——期望输出里只有kube_cronjob_spec_suspend=1,没有next_schedule_time行。
  • 解析失败不输出、也不崩溃:单个调度表达式无法解析时,跳过next_schedule_time,同时由kube_cronjob_schedule_invalid以值 1 暴露解析失败,与 Kubernetes CronJob controller 的容错态度一致。

2.3 kube_cronjob_schedule_invalid 与 IANA 时区数据库内嵌

kube_cronjob_schedule_invalid(EXPERIMENTAL)在"CronJob 的 schedule 在其配置的时区下无法解析"时输出 1。源码直接复用parseSchedule做检测:

if _, err := parseSchedule(j.Spec.Schedule, j.Spec.TimeZone); err != nil { ms = append(ms, &metric.Metric{Value: 1}) }

一个容易被忽视的实现细节:internal/store/cronjob.go 顶部显式内嵌了 IANA 时区数据库:

// Embed the IANA time zone database into the binary so that named time // zones (e.g. a CronJob's spec.timeZone of "Asia/Singapore") resolve even // when running from a minimal/distroless image that ships no tzdata. _ "time/tzdata"

这意味着即使 kube-state-metrics 运行在没有 tzdata 的极简/distroless 镜像中,spec.timeZone: "Asia/Singapore"这类具名时区也能正确解析,不会误报schedule_invalid=1。internal/store/cronjob_test.go 的TestCronJobStoreScheduleParsing专门覆盖了三类解析失败场景(*/120 4-22 * * *步长超范围、0 1 */32,1-7 * 3越界列表、Invalid/Zone非法时区),以及具名时区正常解析的回归用例。

2.4 状态类指标:status_active / last_schedule_time / last_successful_time

三个状态指标分别映射status子资源:

指标数据来源缺失行为
kube_cronjob_status_activelen(j.Status.Active)恒输出,可为 0
kube_cronjob_status_last_schedule_timej.Status.LastScheduleTime.Unix()字段为 nil 时不输出该指标
kube_cronjob_status_last_successful_timej.Status.LastSuccessfulTime.Unix()字段为 nil 时不输出该指标

从源码结构看,后两者都遵循"指针为空则整个 Family 输出空 Metrics 列表"的约定,因此在 Prometheus 中新建、尚未运行过的 CronJob 上查不到这两个序列,告警规则中应使用absent()or组合来兜底。

2.5 Spec 类指标:suspend / starting_deadline_seconds / history limits / created / resource_version

  • kube_cronjob_spec_suspendboolFloat64(j.Spec.Suspend != nil && *j.Spec.Suspend),即 null 按 API 默认 false 处理输出 0,true输出 1。
  • kube_cronjob_spec_starting_deadline_seconds:仅当Spec.StartingDeadlineSeconds != nil时输出,值为秒数(测试用例中使用 300)。
  • kube_cronjob_spec_successful_job_history_limit/kube_cronjob_spec_failed_job_history_limit(均为 EXPERIMENTAL):同为 nil 敏感输出,仅当字段显式设置时出现。
  • kube_cronjob_createdCreationTimestamp.Unix(),零值时间戳不输出。
  • kube_cronjob_metadata_resource_version:将metadata.resourceVersion作为数值输出,可用于感知对象变更。

2.6 标签类指标:annotations 与 labels

kube_cronjob_annotations(EXPERIMENTAL)与kube_cronjob_labels(STABLE)将 Kubernetes 元数据中的 annotation/label 键值转成 Prometheus 标签(前缀分别为annotation_label_),但默认不暴露。源码中的门槛检查非常直接:

if len(allowAnnotationsList) == 0 { return &metric.Family{} // 未配置 allowlist 时不输出任何样本 } annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", j.Annotations, allowAnnotationsList)

启用方式是在启动参数中配置白名单(详见下节),且该列表按资源维度(复数名cronjobs)解析,由 internal/store/builder.go 把配置透传给生成函数:

"cronjobs": func(b *Builder) []cache.Store { return b.buildStoresFunc( cronJobMetricFamilies(b.allowAnnotationsList["cronjobs"], b.allowLabelsList["cronjobs"]), &batchv1.CronJob{}, createCronJobListWatch, b.useAPIServerCache, b.objectLimit, ) }

输出示例(来自测试用例,allowlist 只放行了app.k8s.io/owner):

kube_cronjob_annotations{annotation_app_k8s_io_owner="@foo",cronjob="ActiveRunningCronJob1",namespace="ns1"} 1

3. 启用与配置:启动参数与采集机制

3.1 资源开关

cronjobs--resources参数的默认列表中,即默认开启 CronJob 指标采集。完整默认值见 docs/developer/cli-arguments.md 的--resources说明(certificatesigningrequests,configmaps,cronjobs,daemonsets,...,volumeattachments)。如果启用了多资源分片部署,可参考 examples/daemonsetsharding/deployment.yaml 中的部署清单方式。

3.2 --metric-annotations-allowlist 与 --metric-labels-allowlist

这两个参数控制 2.6 节两个标签类指标的暴露,格式要点(摘自 docs/developer/cli-arguments.md):

  • 按"复数资源名 + 逗号分隔的键列表"声明,例如只放行 CronJob 上的ownerannotation:
--metric-annotations-allowlist '=cronjobs=[owner]'
  • 也可以为多资源同时配置,如'=namespaces=[kubernetes.io/team],cronjobs=[app.k8s.io/owner]'
  • 每个资源可用单个*放行任意键('=cronjobs=[*]'),但官方明确警告其性能影响严重;完整的通配*仅当它是列表第一项时生效。
  • 未配置时,kube_cronjob_annotations/kube_cronjob_labels完全不出现在 /metrics 输出中(注意:kube_cronjob_labels的默认输出并不包含额外 label 键——基础标签cronjob/namespace由所有指标统一携带,与白名单机制无关)。

3.3 数据采集方式

从 internal/store/cronjob.go 的createCronJobListWatch看,CronJob 数据经由BatchV1().CronJobs(ns).List/Watch建立 informer 本地缓存,支持--namespace过滤(ns)与字段选择器(fieldSelector)。因此上述所有指标都反映 informer 缓存中最新的 CronJob 对象状态,指标本身不产生对 API Server 的额外轮询压力;对象规模较大时可通过--object-limit限制单资源缓存上限(见 builder 中b.objectLimit的传递)。

4. 测试用例揭示的行为边界

internal/store/cronjob_test.go 是理解各指标"何时输出、何时缺失"的最佳依据,其中几个代表性场景:

  1. 时区参与计算ActiveRunningCronJobWithTZ1timeZone: "Asia/Shanghai")与同名无时区对象使用相同的lastScheduleTime0 */6 * * *,两者计算出的next_schedule_time相差恰好 4 小时(UTC 与东八区的时差),TestGetNextScheduledTimeUTCAsia/Shanghai两个用例显式验证了这一差值。
  2. 未调度过的新 CronJobActiveCronJob1NoLastScheduledLastScheduleTime为 nil,next_schedule_timecreationTimestamp起算(25 * * * *调度、下一分钟为 25 分)。
  3. Suspend 为 nil 的回退ActiveCronJobNilSuspend用例验证省略suspend字段时按 false 处理(kube_cronjob_spec_suspend=0),且正常输出next_schedule_time
  4. Suspended 状态SuspendedCronJob1用例中next_schedule_time序列缺失、kube_cronjob_spec_suspend=1status_active=0
  5. 非法调度/非法时区TestCronJobStoreScheduleParsing确认解析失败时只输出schedule_invalid=1,进程不会因单个坏对象而崩溃。

5. 监控与告警思路

基于上述指标,可以构造几类典型的 PromQL 检查(示例,请结合自身集群实际标签调整):

  • 调度延迟检测kube_cronjob_next_schedule_time < time()持续一段时间说明下次触发点已过去但尚未调度(未挂起时);
  • 失败时区/调度表达式kube_cronjob_schedule_invalid == 1
  • 长期无成功记录time() - kube_cronjob_status_last_successful_time > 期望周期,注意该指标在新建 CronJob 上不存在,需要处理序列缺失;
  • 挂起感知kube_cronjob_spec_suspend == 1可与上述延迟告警做unless排除,避免误报;
  • 运行堆积kube_cronjob_status_active > 1(配合concurrency_policy != "Allow"kube_cronjob_info)可能意味着上一次 Job 未正常结束。

小结

kube-state-metrics 的 CronJob 指标族以 14 个 Gauge 指标覆盖了调度配置(kube_cronjob_infokube_cronjob_spec_*)、运行状态(kube_cronjob_status_*)、调度健康度(kube_cronjob_next_schedule_timekube_cronjob_schedule_invalid)与元数据(kube_cronjob_createdkube_cronjob_metadata_resource_versionkube_cronjob_annotations/labels)五个维度。理解其实现时,重点把握三点:namespace/cronjob基础标签由wrapCronJobFunc统一注入;next_schedule_timeparseSchedule+getNextScheduledTime基于lastScheduleTime(或creationTimestamp)与CRON_TZ前缀的时区表达式计算,并通过内嵌time/tzdata保证具名时区在极简镜像中可用;标签类指标默认关闭,需经--metric-annotations-allowlist/--metric-labels-allowlist白名单显式开启。相关源码与测试分别位于 internal/store/cronjob.go、internal/store/cronjob_test.go、internal/store/builder.go,参数细节可查阅 docs/developer/cli-arguments.md。

【免费下载链接】kube-state-metricsAdd-on agent to generate and expose cluster-level metrics.项目地址: https://gitcode.com/GitHub_Trending/ku/kube-state-metrics

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询