- 云原生
- 后端
- 运维
【免费下载链接】Terminus
Open-Source Personal Cloud OS for Always-On Agents
导读:本文基于 Terminus 开源仓库中
olares-cluster技能套件的 cronjob 参考文档,完整讲解olares-cli cluster cronjob的六个核心动词(list/get/yaml/jobs/suspend/resume),并结合 CLI 源码(cli/cmd/ctl/cluster/cronjob)与单元测试逐层剖析其 API 调用链、安全约束与底层实现原理。读完你将掌握:如何通过命令快速查看计划任务、定位 CronJob 派生的 Job、安全地暂停与恢复调度,以及这些命令背后 K8sapis/batch/v1资源模型的真实工作机制。
一、前置知识:CronJob 在 Olares 运行时视图中的定位
olares-cli cluster cronjob(别名cronjobs/cj)属于olares-cli cluster命令树,面向 Olares ControlHub 的 K8s 运行时视图。在动手之前,需要先加载两个前置技能文档:
- 共享入口 olares-cli shared rules:负责技能路由、活动 profile 选择、平台入口点以及认证 proceed/stop 门禁;
- 父级技能 cluster (per-user K8s view):定义了身份由活动 profile 决定、可见性与授权由服务端决定、变更类动词必须确认等横切原则。
CronJob 对应 Kubernetes 的apis/batch/v1API 组,与一次性运行的cluster job属于同一 group/version,但二者是两个独立的命令包。从 root.go 的实现注释可以看到拆分原因:
- 尽管二者都位于
apis/batch/v1,但动词集合不同——只有 CronJob 拥有suspend/resume,只有 Job 拥有rerun; - Olares 的 SPA(单页应用)也将二者建模为兄弟实体(
jobType枚举中cronjobs与jobs并列); - 历史上 CronJob 曾位于 K8s 1.25 之前才存在的
apis/batch/v1beta1,现在 Olares 集群只提供 v1 端点,CLI 不再引用旧路径。
简言之:面向"计划任务"(每晚备份、定期清理)用
cronjob;面向"一次性批处理"(迁移、建索引)用job。
二、动词总览:六个核心操作一览
| Verb | 用途 | 端点与机制 |
|---|---|---|
list | 跨命名空间或单命名空间列表,列:NAMESPACE / NAME / SCHEDULE / SUSPEND / ACTIVE / LAST-SCHEDULE / AGE | /kapis/resources.kubesphere.io/v1alpha3/cronjobs或/kapis/.../namespaces/<ns>/cronjobs |
get <ns/name> | 单对象纵向摘要:ConcurrencyPolicy+ Active Jobs + Job Template Selector | /apis/batch/v1/namespaces/<ns>/cronjobs/<name> |
yaml <ns/name> | 完整 K8s 原生 YAML(保留服务端返回的全部字段) | 与get相同端点,但以原始字节转 YAML |
jobs <ns/name> | 两步式列出该 CronJob 派生的子 Job,--limit N截断显示条数 | GET CronJob 取 UID 与模板 labels → 列出 Jobs → 客户端按 ownerReferences 过滤 |
suspend <ns/name> | 变更类(破坏性)。PATCH{"spec":{"suspend":true}},application/merge-patch+json;已挂起时无操作短路 | PATCH/apis/batch/v1/namespaces/<ns>/cronjobs/<name> |
resume <ns/name> | 非破坏性。PATCH{"spec":{"suspend":false}};已激活时无操作短路;无--yes(重新启用不算破坏性) | 同 PATCH 端点,body 相反 |
所有身份参数遵循"<namespace>/<name>"或-n <ns> <name>两种写法(由clusteropts.SplitNsName解析),全部端点位于https://control-hub.<terminus>之下,认证由活动 profile 透明处理(options.go 中 Prepare 的实现会经 refreshingTransport 自动注入并轮换 access token)。
三、list:一眼看清所有计划任务
# 跨所有可见命名空间列出计划任务 olares-cli cluster cronjob list # 限定单个命名空间 + label 过滤 + 自定义分页 olares-cli cluster cronjob list -n user-system-alice -l app=backup --limit 50 --page 1list的常用标志(来自 list.go):
| 标志 | 默认值 | 说明 |
|---|---|---|
-n, --namespace | 空(所有可见命名空间) | 指定后走namespaces/<ns>/cronjobs端点,表格出现 NAMESPACE 列 |
-l, --label | 空 | K8s label-selector 语法,如app=foo,服务端过滤 |
--limit | 100 | KubeSphere 分页的每页大小(--all模式下即排空循环的页大小) |
--page | 1 | 1 起始的页码;--page N>1与--all互斥 |
--all | false | 排空所有页直到取尽 |
-o, --output | table | table或json |
-q, --quiet | false | 抑制输出,仅以退出码表示成败 |
--no-headers | false | 省略表头,便于脚本化 |
表格各列语义由 types.go 的三个渲染函数决定:
- SUSPEND:
True/False,仅当对象极老且spec.suspend未设置时显示-(suspendLabel); - LAST-SCHEDULE:
"<age> ago"的人类可读相对时间,控制器尚未写入lastScheduleTime(即创建后从未触发)时显示-(lastScheduleLabel); - ACTIVE:当前活跃(正在运行)Job 的数量,来自
status.active。
--all与--page>1的互斥由 pagination.go 的 Validate 强制,返回可读错误而非 cobra 通用报错;当单页数据少于总量时,stderr 会打印(showing items A-B of TOTAL — pass --page N or --all to see more)提示(PrintPageHint)。
四、get 与 yaml:单对象细节与原始定义
4.1 get —— 纵向摘要
# 完整摘要:调度 + 并发策略 + 活跃 Job + 模板选择器 olares-cli cluster cronjob get user-system-alice/nightly-backup # 脚本友好的 JSON 形态 olares-cli cluster cronjob get user-system-alice/nightly-backup -o json表模式下输出为纵向 key/value 摘要(get.go 的 renderGetTable),包含字段:
Schedule(cron 表达式,如0 2 * * *)、Time Zone(spec.timeZone非空时显示,该字段为 batch/v1 在 K8s 1.27 起的 GA 能力);Suspend、Concurrency Policy(Allow/Forbid/Replace,仅在服务端填充时显示);Starting Deadline(startingDeadlineSeconds秒数)、Successful Jobs History Limit/Failed Jobs History Limit(历史 Job 保留上限);Active Jobs(status.active中的对象名,按字典序稳定排序,便于重复运行 diff);Last Schedule、Last Successful(均以相对时间呈现)、Created、Age;Job Template Selector(由spec.jobTemplate.metadata.labels推导出的key=value,key=value形式选择器,见下文 jobs 部分)。
API 版本细节值得注意:get走的是 K8s 原生端点/apis/batch/v1/...。源码注释 get.go 指出:SPA 的getCornJobsDetail历史上曾传apis/batch/v1beta1(来自其API_VERSIONS.cronjobs常量),但该版本已在 Kubernetes 1.25 移除,Olares 集群只提供 v1 端点,因此 CLI 明确使用 v1。对应的回归测试 get_test.go 将buildGetPath固定为/apis/batch/v1/namespaces/<ns>/cronjobs/<name>,并验证了命名空间、点号名称以及含斜杠名称的百分号转义(防止路径注入到其他资源)。
同一
buildGetPath被get/yaml/suspend/resume四个动词共享——suspend/resume是对get同一 URL 的 PATCH,因此只要测试钉住这个 helper,四个动词就不会各自漂移。
4.2 yaml —— 字节级保真的 K8s 原生 YAML
olares-cli cluster cronjob yaml user-system-alice/nightly-backupyaml与get使用同一端点,差异在于:yaml通过 yaml.go 将服务端返回的原始 JSON 字节经sigs.k8s.io/yaml直接转为 YAML,因此服务端返回的任何字段都会被保留——包括cronjob get的轻量类型 CronJob 未建模的未知字段。该动词刻意不接受--output(YAML 本身就是目的),需要 JSON 时请使用get -o json;仅提供--quiet标志。
五、jobs:两步式定位 CronJob 派生的 Job
# 查看该 CronJob 最近派生的 10 个 Job olares-cli cluster cronjob jobs user-system-alice/nightly-backup --limit 10这是本命令树中最具实现深度的动词。文档明确说明:没有"直接列出某 CronJob 所属 Job"的服务端端点,CLI 必须自行推导父子关系。而当前仓库源码(jobs.go)已演进为"labels 预过滤 + ownerReferences UID 权威判定"的两步式/三步式流程:
- GET CronJob,读取
metadata.uid(权威匹配键)与spec.jobTemplate.metadata.labels(优化键); - 列出候选 Job:若 jobTemplate 带 labels,则用推导出的
labelSelector=<derived>请求/apis/batch/v1/namespaces/<ns>/jobs让 apiserver 预收窄(节省大命名空间下的网络字节);若无 labels(kubectl/yaml 手写 CronJob 的常见情况),则改为无过滤地列出该命名空间下全部 Job; - 客户端按 ownerReferences 过滤:仅保留满足
Controller=true && UID==parentUID && (Kind 为空或为 "CronJob")的 Job(isChildOfCronJob)。
其中metadata.ownerReferences才是 K8s 原生的父子绑定事实来源——CronJob 控制器会为每个派生 Job 打上指向父 CronJob UID 的controller=trueOwnerReference,无论用户是否在模板里设置 labels。选择器推导只作为"尽力而为"的优化:Job 之间可能共享 labels(或与同命名空间手工创建的 Job 撞 label),因此 UID 校验是最终权威。
实现上的三个细节值得展开:
- 不按 Name 匹配:CronJob 被删除后用同名重建会获得全新 UID,按 Name 匹配会把子 Job 错误归属给新父对象;
- 拒绝非 controller 引用:
Controller=false的 ownerRef 只是第三方操作者写入的"软引用",即使 UID 碰巧一致也不能视为父级; - 空 Kind 被容忍:部分 apiserver 内部引用会省略 Kind 字段,直接拒绝会导致漏掉合法子 Job。
该判定谓词由 jobs_test.go 的TestIsChildOfCronJob以 8 个用例逐项钉死:无 ownerRefs 的手工 Job、UID 匹配/不匹配、非 controller 引用、外来 Kind、空 Kind、多 owner 扫描、空 parentUID 永不匹配。
版本演进提示:文档中描述的"jobTemplate 无 labels 时报错"行为,属于该动词的早期实现(源码注释明确指出之前曾依赖 SPA 的 label 重建技巧,在 KubeSphere/SPA UI 创建的 CronJob 上可行,但遇到 kubectl/yaml 手写 CronJob 就会失败)。当前源码已修复为 ownerReferences 方案,不再因缺少 labels 而报错,而是退化为无预过滤的全量列出再本地过滤。阅读较旧版本的文档或技能说明时,应以此仓库源码为准。
--limit N控制过滤后展示的条数上限(默认 100,0表示不限),而非 apiserver 请求大小;表格列与cluster job list完全一致(NAME / COMPLETIONS / STATUS / AGE),并复用job.Job.StatusLabel()渲染 STATUS(Complete/Failed/Suspended/Running/Failing/Pending,见 job/types.go),保证同一个挂起 Job 在两个命令树里不会出现"Suspended / Pending"两种读法。截断时 stderr 提示(showing 5 of 17 matched — pass --limit N to see more)。
六、suspend 与 resume:安全地暂停与恢复调度
# 暂停调度(会确认;后续计划触发被跳过) olares-cli cluster cronjob suspend user-system-alice/nightly-backup # 脚本化时用 --yes 跳过确认 olares-cli cluster cronjob suspend user-system-alice/nightly-backup --yes # 重新启用 olares-cli cluster cronjob resume user-system-alice/nightly-backupsuspend与resume共享同一 PATCH 通路(suspend.go 的 runToggle),二者唯一差别是 suspend 值(true/false)与提示文案:
- GET 当前对象,读取
spec.suspend现值与resourceVersion; - 无操作短路:若目标状态与当前状态一致,打印
cronjob <ns>/<name> is already suspended/active — no change(quiet 模式静默),跳过 PATCH——省下一次 API 往返与一次无谓的 resourceVersion 递增;JSON 模式仍输出合成的toggleResult(operation/namespace/cronjob/suspend/resourceVersion); - 确认门禁:仅
suspend包裹在ConfirmDestructive中——挂起生产调度虽然可逆但可见地打断(下一次触发不会发生),所以脚本必须显式传--yes;resume在调用处以assumeYes=true传入,不弹确认(重新启用非破坏性,控制器下一个 tick 就会追赶); - PATCH:
Content-Type: application/merge-patch+json,body 为{"spec":{"suspend":true}}或{"spec":{"suspend":false}},与 SPA 的 Suspend 开关等价。
确认机制的底层实现在 cli/pkg/cliutil/confirm.go:--yes直接放行;stdin 非 TTY 时报错(宁可中断脚本也不在无人审阅时执行破坏操作);回答y/yes放行,其余任何输入或 EOF 返回aborted by user。
安全约束(父级 SKILL 的 Mutating 安全契约)
- 挂起只停止未来的调度触发,已运行的 Job 不受影响——正在执行的备份/清理任务会正常跑完;
suspend是"暂停调度"而非"删除"——通过resume可完全逆转,对用户沟通时必须用"pause schedule"措辞;- **变更类动词遵循"先确认、脚本用
--yes、最终由服务端决定"**的契约(见 olares-cluster SKILL.md 的 Safety and escalation)。
七、Agent 使用注意(判断调度是否真的在触发)
技能文档给出了面向 Agent 的关键判别套路,核心是两步式问答:
- 先
get查看SUSPEND、LastScheduleTime与Active; - 再
jobs查看最近派生的 Job。
如果SUSPEND=true但ACTIVE仍有近期 Job,说明这些 Job 是在暂停之后才结束的——它们会正常跑完,但不会有新的派生。
另一个高价值提醒:长时间挂起后 resume 会触发一次追赶式触发。CronJob 控制器在调度被跳过多次后,会根据ConcurrencyPolicy与StartingDeadlineSeconds补触发一次。因此对一个已挂起数小时的 CronJob 执行resume前,务必先向用户预警这次 catch-up fire(例如每天 02:00 的备份在挂起 6 小时后恢复,可能立即补跑一次)。
对应"停止每晚运行"这类需求,优先 suspend 而非 delete:挂起保留调度定义与全部历史(lastScheduleTime、历史 Job 记录),删除则不可逆。
八、常见错误速查
| 症状 | 原因 | 处理 |
|---|---|---|
jobTemplate has no labels — cannot derive selector(旧版jobs行为) | 早期实现要求 jobTemplate 必须带 labels 才能推导选择器 | 当前版本已改为 ownerReferences UID 判定、不再依赖 labels;如遇旧版行为,可先用cluster cronjob yaml人工检查模板与子 Job 的metadata.ownerReferences |
cronjob already suspended/already active | 无操作短路 | 提示性通知,无需处理;JSON 模式返回当前resourceVersion |
cronjob get返回 404 | 该 profile 下不存在该 CronJob,或命名空间不可见 | 用cronjob list核对ns/name;CLI 已使用正确的apis/batch/v1group,若对象确实存在可对照cluster application list确认命名空间可见性 |
非 TTY 下suspend报 stdin 错误 | 确认机制拒绝无人审阅的破坏操作 | 脚本化时显式传--yes |
九、底层实现速览:从命令到 K8s API 的调用链
olares-cli cluster cronjob <verb> └─ cmdutil.Factory(活动 profile 解析 + refreshingTransport 认证) └─ clusterclient.Client(指向 https://control-hub.<terminus>) ├─ list → GET /kapis/resources.kubesphere.io/v1alpha3[/namespaces/<ns>]/cronjobs?limit&page ├─ get → GET /apis/batch/v1/namespaces/<ns>/cronjobs/<name> ├─ yaml → GET 同上(原始字节)→ sigs.k8s.io/yaml ├─ jobs → GET cronjob → GET /apis/batch/v1/namespaces/<ns>/jobs[?labelSelector] → 客户端 ownerReferences 过滤 └─ suspend/resume → GET(RV 感知)→ ConfirmDestructive → PATCH application/merge-patch+json关键源码文件索引:
- 命令装配与别名:root.go
- 类型模型与列渲染辅助:types.go
- 列表与分页:list.go、pagination.go
- 单对象详情:get.go、yaml.go
- 子 Job 定位与 UID 过滤:jobs.go、jobs_test.go
- 挂起/恢复共享通路:suspend.go、resume.go
- 确认机制:confirm.go
- 端点回归测试:get_test.go
相关阅读:cluster job(一次性 Job)参考 提供了 CronJob 子 Job 的进一步操作(
pods/events/rerun);olares-cluster 技能总览 定义了本命令树完整的动词索引与安全契约。
- 云原生
- 后端
- 运维
【免费下载链接】Terminus
Open-Source Personal Cloud OS for Always-On Agents
相关推荐
Terminus olares-cli cluster middleware 命令详解:基于 olares-cli 的 Olares 托管中间件只读盘点
Terminus olares cli cluster middleware 命令详解:基于 olares cli 的 Olares 托管中间件只读盘点 本指南
云原生后端运维Terminus 集群容器 exec 实战:在 Olares 中使用 olares-cli 安全执行容器内命令
Terminus 集群容器 exec 实战:在 Olares 中使用 olares cli 安全执行容器内命令 本文面向 Terminus 开源仓库中的 ola
云原生后端运维Olares 集群 Job 运维指南:用 olares-cli cluster job 管理一次性批处理任务
Olares 集群 Job 运维指南:用 olares cli cluster job 管理一次性批处理任务 olares cli cluster job (别
云原生后端运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考