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_annotations | Gauge | 将 Kubernetes annotation 转换为 Prometheus 标签,由--metric-annotations-allowlist控制 | cronjob、namespace、annotation_CRONJOB_ANNOTATION | EXPERIMENTAL |
kube_cronjob_info | Gauge | CronJob 基本信息(调度表达式、并发策略、时区) | cronjob、namespace、schedule、concurrency_policy、timezone | STABLE |
kube_cronjob_labels | Gauge | 将 Kubernetes label 转换为 Prometheus 标签,由--metric-labels-allowlist控制 | cronjob、namespace、label_CRONJOB_LABEL | STABLE |
kube_cronjob_created | Gauge | 创建时间的 Unix 时间戳 | cronjob、namespace | STABLE |
kube_cronjob_next_schedule_time | Gauge | 下一次调度时间 | cronjob、namespace | STABLE |
kube_cronjob_schedule_invalid | Gauge | 调度表达式(在其配置的时区下)无法解析时为 1 | cronjob、namespace | EXPERIMENTAL |
kube_cronjob_status_active | Gauge | 当前正在运行的 Job 数量 | cronjob、namespace | STABLE |
kube_cronjob_status_last_schedule_time | Gauge | 上次成功调度的 Unix 时间戳 | cronjob、namespace | STABLE |
kube_cronjob_status_last_successful_time | Gauge | 上次成功完成的 Unix 时间戳 | cronjob、namespace | STABLE |
kube_cronjob_spec_suspend | Gauge | 是否已挂起(1/0) | cronjob、namespace | STABLE |
kube_cronjob_spec_starting_deadline_seconds | Gauge | 错过调度时间后启动 Job 的宽限秒数 | cronjob、namespace | STABLE |
kube_cronjob_metadata_resource_version | Gauge | 对象资源版本号 | cronjob、namespace | STABLE |
kube_cronjob_spec_successful_job_history_limit | Gauge | 保留的成功 Job 数量上限 | cronjob、namespace | EXPERIMENTAL |
kube_cronjob_spec_failed_job_history_limit | Gauge | 保留的失败 Job 数量上限 | cronjob、namespace | EXPERIMENTAL |
所有指标都带有两个固定基础标签: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 } }也就是说,无论某个指标的生成函数自己声明了哪些标签,最终都会与namespace、cronjob两个标签合并输出,这保证了按命名空间/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.Schedule、j.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") }可以推断出三个关键行为:
- 调度解析使用第三方 cron 库(
github.com/netresearch/go-cron),并支持标准 5 段 cron 表达式(cron.ParseStandard)。 - 时区通过
CRON_TZ=前缀注入表达式,即 CronJob 的spec.timeZone会直接参与下一次触发时间的计算。 - 两个基准时间的优先级:优先
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_active | len(j.Status.Active) | 恒输出,可为 0 |
kube_cronjob_status_last_schedule_time | j.Status.LastScheduleTime.Unix() | 字段为 nil 时不输出该指标 |
kube_cronjob_status_last_successful_time | j.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_suspend:boolFloat64(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_created:CreationTimestamp.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"} 13. 启用与配置:启动参数与采集机制
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 是理解各指标"何时输出、何时缺失"的最佳依据,其中几个代表性场景:
- 时区参与计算:
ActiveRunningCronJobWithTZ1(timeZone: "Asia/Shanghai")与同名无时区对象使用相同的lastScheduleTime和0 */6 * * *,两者计算出的next_schedule_time相差恰好 4 小时(UTC 与东八区的时差),TestGetNextScheduledTime用UTC与Asia/Shanghai两个用例显式验证了这一差值。 - 未调度过的新 CronJob:
ActiveCronJob1NoLastScheduled的LastScheduleTime为 nil,next_schedule_time从creationTimestamp起算(25 * * * *调度、下一分钟为 25 分)。 - Suspend 为 nil 的回退:
ActiveCronJobNilSuspend用例验证省略suspend字段时按 false 处理(kube_cronjob_spec_suspend=0),且正常输出next_schedule_time。 - Suspended 状态:
SuspendedCronJob1用例中next_schedule_time序列缺失、kube_cronjob_spec_suspend=1、status_active=0。 - 非法调度/非法时区:
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_info、kube_cronjob_spec_*)、运行状态(kube_cronjob_status_*)、调度健康度(kube_cronjob_next_schedule_time、kube_cronjob_schedule_invalid)与元数据(kube_cronjob_created、kube_cronjob_metadata_resource_version、kube_cronjob_annotations/labels)五个维度。理解其实现时,重点把握三点:namespace/cronjob基础标签由wrapCronJobFunc统一注入;next_schedule_time由parseSchedule+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),仅供参考