news 2026/9/23 15:59:59

Salt 调度执行模块 salt.modules.schedule 完全指南:管理与运维 minion 定时任务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Salt 调度执行模块 salt.modules.schedule 完全指南:管理与运维 minion 定时任务

Salt 调度执行模块 salt.modules.schedule 完全指南:管理与运维 minion 定时任务

【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址: https://gitcode.com/gh_mirrors/sa/salt

导读

本文围绕 Salt(gh_mirrors/sa/salt)中的salt.modules.schedule执行模块展开,系统讲解如何通过saltCLI 在 minion 上查看、新增、修改、删除、启停定时任务,并结合底层调度器实现说明其工作原理。读者学完后可以熟练使用schedule.*系列命令管理单台或集群中的计划任务,并理解调度任务的持久化机制、pillar/opts 两种来源以及事件驱动的工作方式。

说明:关联文档 doc/ref/modules/all/salt.modules.schedule.rst 是 Sphinx 的automodule自动文档入口,其全部技术内容实际来自模块源码 salt/modules/schedule.py 的 docstring 与实现。本文以该模块为主线,结合源码与测试展开。

一、模块概览与适用前提

salt.modules.schedule用于"管理 minion 上的 Salt 调度任务"(Module for managing the Salt schedule on a minion),自 Salt 2014.7.0 版本引入。它本质上是管理 Salt 内置调度器(scheduler)的用户接口:真正的调度循环实现在 salt/utils/schedule.py 的Schedule类中,而本模块通过向 minion 事件总线(event bus)发射manage_schedule事件来驱动调度器的增删改查。

依赖要求

  • 模块 docstring 明确指出要求 minion 上安装 python-dateutil,否则when(按指定时间执行)与range(时间段)相关能力不可用。
  • 源码中通过 try/except 检测依赖:salt/modules/schedule.py:
try: import dateutil.parser as dateutil_parser _WHEN_SUPPORTED = True _RANGE_SUPPORTED = True except ImportError: _WHEN_SUPPORTED = False _RANGE_SUPPORTED = False
  • 若要在调度任务中使用cron表达式,底层调度器还需要croniter库(见 salt/utils/schedule.py 的导入逻辑),该依赖缺失时cron选项会报错。

代理 minion 支持

模块顶部声明__proxyenabled__ = ["*"],意味着该模块可以被所有类型的 proxy minion 加载使用(如网络设备代理 minion),这是通过代理方式纳管网络设备时同样可以管理定时任务的依据。

二、调度任务的两种来源:opts 与 pillar

调度任务可以来自两个位置,理解这一点是正确使用本模块的前提:

  1. opts(配置)来源:来自 minion 配置文件或minion.d/*.conf中的schedule段,以及通过模块命令新增后被持久化的任务。命令执行后默认会持久化到 minion 配置目录下 minion.d 中的_schedule.conf文件(该文件由 Salt 自动创建,文件名以下划线开头,属于 Salt 内部使用文件,详见 minion 配置文档)。
  2. pillar 来源:来自 master 下发的 pillar 数据中的schedule段。来自 pillar 的任务不会被持久化到本地_schedule.conf,因为其生命周期由 pillar 管理。

这一点可以从源码中得到印证:模块的deletemodifyenable_jobdisable_jobpostpone_jobskip_jobmovecopy等函数在操作时都会先判断任务属于where="opts"还是where="pillar",针对 pillar 中的任务会明确设置persist: False(例如 salt/modules/schedule.py),避免把 pillar 任务写入本地配置文件造成冲突。

三、任务字段白名单与通用选项

模块顶部定义了调度任务的字段白名单SCHEDULE_CONF(salt/modules/schedule.py),list输出任务时会只保留这些字段,其余字段被过滤。完整字段如下:

字段含义
name任务名(调度器内部用于识别任务)
function要执行的执行模块函数,如test.pingstate.apply
args/kwargs传给 function 的位置参数列表与关键字参数字典
seconds/minutes/hours/days间隔周期(可组合使用,_seconds为内部归一化后的秒数)
when指定具体时间点执行(依赖 dateutil,支持hh:mm或 ISO 格式列表)
once/once_fmt只执行一次,once_fmt指定时间格式(默认%Y-%m-%dT%H:%M:%S
croncron 表达式(依赖 croniter)
range仅在指定时间段内执行(依赖 dateutil)
splay随机延时间隔,可为整数或{start: n, end: m}范围字典
maxrunning最大并行运行实例数,默认 1
enabled任务是否启用,默认 True
jid_include是否把任务执行记录到 minion 的 job cache,默认 True
returner结果返回器
return_config/return_kwargs返回器的配置选项与参数
metadata随任务结果一起返回的元数据
run_on_startminion 启动/调度器重启时是否立即执行一次
until/after任务生效的截止/起始时间
skip_during_range/run_after_skip_range在指定时间段内跳过执行,以及跳过结束后是否补跑
return_job任务结束后是否发 job 返回事件

注意:模块别名映射为__func_alias__ = {"list_": "list", "reload_": "reload"},因此 CLI 上使用schedule.listschedule.reload即可。

四、任务生命周期管理:增删改查与启停

以下命令全部通过salt远程执行,目标可以是单个 minion 或一组 minion。

1. 查看任务:schedule.list

列出 minion 上当前所有调度任务:

# 列出全部任务(默认隐藏内部任务、显示禁用任务) salt '*' schedule.list # 显示所有任务(含 Salt 内部以 __ 开头的任务) salt '*' schedule.list show_all=True # 隐藏被禁用的任务 salt '*' schedule.list show_disabled=False # 仅查看 opts(配置)来源的任务 salt '*' schedule.list where=opts # 仅查看 pillar 来源的任务 salt '*' schedule.list where=pillar

实现要点(salt/modules/schedule.py):

  • 默认输出 YAML 格式,可通过return_yaml=False改为返回 Python 字典;
  • __开头的任务属于 Salt 内部自动添加(如 mine 更新),默认隐藏,show_all=True时显示;
  • 任务未显式声明enabled时默认视为启用;
  • 每个任务会附加saved: True/False字段,标识该任务是否已持久化到 minion 配置(即存在于_schedule.conf);
  • 支持offline=True离线模式:不经过事件总线,直接从_schedule.conf文件读取(minion 未运行时也能查看)。

2. 新增任务:schedule.add

# 每 3600 秒执行一次 test.ping salt '*' schedule.add job1 function='test.ping' seconds=3600 # 带参数执行 cmd.run salt '*' schedule.add job2 function='cmd.run' job_args="['date >> /tmp/date.log']" seconds=60 # 离线模式(minion 未运行时直接把任务写入 _schedule.conf) salt '*' schedule.add job1 function='test.ping' seconds=3600 offline=True

常用参数组合示例:

# 每 5 分钟执行,带 1~10 秒随机抖动 salt '*' schedule.add check_disk function='disk.usage' minutes=5 splay='{start: 1, end: 10}' # 每天 02:30 执行,基于 when(需 dateutil) salt '*' schedule.add nightly function='state.apply' when='02:30am' # 按 cron 表达式每周一凌晨执行(需 croniter) salt '*' schedule.add weekly function='pkg.upgrade' cron='0 3 * * 1' # 通过 pillar 下发任务(不持久化到本地) salt '*' schedule.add pillar_job function='test.ping' seconds=60 where=pillar

关键行为(源码 salt/modules/schedule.py):

  • 时间参数冲突校验seconds/minutes/hours/days不能与whencron混用;whencron也不能同时使用,违反时返回错误信息。
  • job_args必须是 list,job_kwargs必须是 dict,否则校验失败。
  • 若任务已存在则直接报错"already exists"。
  • 支持test=True试运行模式,只提示"would be added"而不真正添加。
  • 默认persist=True,任务会持久化到_schedule.conf;设置为persist=False则只对当前运行中的调度器生效。

3. 修改任务:schedule.modify

# 修改 job1 的执行周期 salt '*' schedule.modify job1 function='test.ping' seconds=7200 # 离线修改 salt '*' schedule.modify job1 function='test.ping' seconds=7200 offline=True

实现要点(salt/modules/schedule.py):

  • 目标任务不存在时报错Job X does not exist in schedule.
  • 未指定的字段沿用原任务值(function缺省时自动取原值);
  • 比较新旧任务内容,若完全一致则返回Job X in correct state(幂等);
  • 变更时会先在返回结果的changes中记录oldnew两份任务定义;
  • 支持test=True试运行,以及offline=True离线写文件。

4. 删除任务:schedule.delete 与 schedule.purge

# 删除单个任务 salt '*' schedule.delete job1 # 删除全部用户任务(跳过以 __ 开头的内部任务与全局 enabled 标志) salt '*' schedule.purge # 离线删除 salt '*' schedule.delete job1 offline=True
  • delete只针对单个任务,来自 pillar 的任务删除时不会持久化(salt/modules/schedule.py);
  • purge遍历所有非内部任务逐个删除,同样支持testoffline模式(salt/modules/schedule.py);
  • 离线模式下删除操作在全部完成后一次性把新的调度配置写回_schedule.conf

5. 启停任务:enable / disable / enable_job / disable_job

# 启用/禁用某个任务 salt '*' schedule.enable_job job1 salt '*' schedule.disable_job job1 # 启用/禁用 minion 上全部任务(全局开关,对应 schedule 中的 enabled 标志) salt '*' schedule.enable salt '*' schedule.disable
  • enable_job/disable_job针对单个任务,操作后校验任务状态是否符合预期(salt/modules/schedule.py);
  • enable/disable操作的是全局enabled开关,disable后整个调度器暂停(salt/modules/schedule.py);
  • 均有test=True试运行与persist持久化参数。

6. 立即执行任务:schedule.run_job

# 立即触发一次 job1 salt '*' schedule.run_job job1 # 任务被禁用时强制触发 salt '*' schedule.run_job job1 force=True

实现要点(salt/modules/schedule.py):若任务存在且未禁用,则通过事件总线发射run_job指令让调度器立即执行一次;禁用状态下不带force=True会返回Job X is disabled.

7. 持久化与重载:schedule.save 与 schedule.reload

# 把当前调度任务(非 pillar 部分)保存到 _schedule.conf salt '*' schedule.save # 从 _schedule.conf 与 pillar 重新加载调度任务 salt '*' schedule.reload
  • save通过事件总线请求调度器把非 pillar 任务落盘(salt/modules/schedule.py);
  • reload会先触发pillar_refresh刷新 pillar 中的 schedule,再读取<config_dir>/minion.d/schedule.conf(注意:这里读取的是schedule.conf,与模块命令持久化使用的_schedule.conf是不同文件,前者是用户在 minion.d 下手写的配置),将其中内容重新加载到运行中的调度器(salt/modules/schedule.py)。

五、跨 minion 分发:schedule.move 与 schedule.copy

# 把 job1 迁移到 web1 minion(原 minion 上删除) salt '*' schedule.move job1 web1 # 把 job1 复制到 web1 minion(原任务保留) salt '*' schedule.copy job1 web1 # 支持复合目标 salt '*' schedule.copy job1 'web*'

实现要点(salt/modules/schedule.py):这两个函数读取任务定义后,把每个字段拼装为key=value形式的参数列表,通过__salt__"publish.publish"在目标 minion 上重新执行schedule.addmove在全部目标成功返回后才在原 minion 上delete;若有 minion 返回 False,则返回失败的 minion 列表并中止。返回结果中minions字段给出实际应答的 minion 列表。

六、时间控制:postpone_job / skip_job / show_next_fire_time / job_status

这三个功能自 Salt 2018.3.0 加入,用于对一次性/周期性任务做精确时间控制。

postpone_job:推迟任务

# 把原计划在 2026-09-24T03:00:00 触发的任务推迟到 03:30:00 salt '*' schedule.postpone_job job1 '2026-09-24T03:00:00' '2026-09-24T03:30:00' # 自定义时间格式 salt '*' schedule.postpone_job job1 '2026-09-24 03:00:00' '2026-09-24 03:30:00' time_fmt='%Y-%m-%d %H:%M:%S'

实现要点(salt/modules/schedule.py):current_timenew_time均需符合time_fmt(默认%Y-%m-%dT%H:%M:%S),源码用datetime.datetime.strptime严格校验格式,解析失败返回Date string could not be parsed.

skip_job:跳过任务

# 跳过 job1 在 2026-09-24T03:00:00 的这次触发 salt '*' schedule.skip_job job1 '2026-09-24T03:00:00'

实现要点(salt/modules/schedule.py):与postpone_job类似,在指定时间点跳过任务执行,同样支持time_fmt自定义格式。

show_next_fire_time:查看下次触发时间

salt '*' schedule.show_next_fire_time job1

实现要点(salt/modules/schedule.py):通过事件总线向调度器请求get_next_fire_time,返回next_fire_time字段,方便排查任务是否按预期排期。

job_status:查看任务运行信息

salt '*' schedule.job_status job1

实现要点(salt/modules/schedule.py):请求调度器返回指定任务最近一次运行的状态信息(含data字段),源码会把其中的datetime对象统一格式化为字符串再返回,便于直接展示。

is_enabled:查询启用状态

# 查询单个任务是否启用 salt '*' schedule.is_enabled name=job1 # 不带 name 时返回调度器全局 enabled 状态(自 2015.5.3 加入) salt '*' schedule.is_enabled

七、底层原理:事件总线驱动调度器

理解salt.modules.schedule的机制,关键在于它并不是直接操作调度数据结构,而是通过minion 事件总线与调度器通信:

  1. 模块函数(如add)调用__salt__"event.fire"在本地事件总线上发射manage_schedule事件,payload 中携带func(add/delete/modify/enable/disable/run_job/save_schedule/reload 等)与任务数据;
  2. minion 的调度器监听该事件并执行对应操作;
  3. 操作完成后,调度器在事件总线上回发对应的完成事件(如minion_schedule_add_completeminion_schedule_delete_completeminion_schedule_list_complete等);
  4. 模块函数用event_bus.get_event(tag=..., wait=30)等待完成事件并校验complete字段,据此组装result/comment/changes返回给调用方。

例如list的实现(salt/modules/schedule.py)先发{"func": "list", "where": where},再等待minion_schedule_list_complete事件取回完整调度表。若事件模块不可用(如某些极简环境),函数会捕获KeyError并返回"Event module not available"的提示而非崩溃。

真正的调度循环位于 salt/utils/schedule.py 的Schedule类(约 1979 行),它负责:按seconds/minutes/hours/dayswhencrononce等规则计算下次触发时间、在skip_during_range时段跳过、应用splay随机延迟、受maxrunning限制并发、并把执行结果按returner配置返回。模块中的SCHEDULE_CONF白名单与调度器的字段约定保持一致,确保经模块管理的任务能被调度器正确解析。

八、离线模式与持久化文件

模块中多数写操作(add/delete/modify/purge)支持offline=True,用于minion 未运行时直接编辑调度配置文件:

  • 配置文件路径由_get_schedule_config_file()计算(salt/modules/schedule.py):取__opts__["conf_dir"](无则取 minion 配置所在目录),拼上default_include的目录(默认minion.d),最终指向minion.d/_schedule.conf
  • 离线模式下跳过事件总线,直接以 YAML 形式读写该文件(写入内容形如{"schedule": {...}}),写入失败会记录错误日志但不会中断(捕获OSError);
  • 正常在线模式下,persist=True时调度器自身也会在完成事件后把非 pillar 任务写入同一文件。

因此查看任务时返回的saved字段正是以"该任务是否出现在_schedule.conf中"为依据(salt/modules/schedule.py)。minion 配置文档也明确提示:minion.d目录中以下划线开头的文件(典型如_schedule.conf)是 Salt 自行创建的内部文件,见 minion 配置文档。

九、测试与验证

仓库中为schedule模块提供了较完整的单元测试(tests/pytests/unit/modules/test_schedule.py),覆盖了:

  • test_addtest_deletetest_modifytest_purge:增删改与清空的参数解析与事件交互;
  • test_build_schedule_itemtest_build_schedule_item_invalid_whentest_build_schedule_item_invalid_jobs_argstest_build_schedule_item_jid_include:任务字段构建及非法when、非 list 的job_args等校验逻辑;
  • test_enable_jobtest_disable_jobtest_enabletest_disable:任务级与全局启停;
  • test_movetest_copy:跨 minion 分发的 publish 交互;
  • test_is_enabledtest_job_statustest_list及全局 enabled 相关用例。

此外,状态模块 salt/states/schedule.py 提供了声明式的schedule.present/schedule.absent状态,可在 state 文件(SLS)中描述任务期望状态,由状态系统自动调用本执行模块完成收敛,适合把定时任务纳入版本管理;相关单测见 tests/pytests/unit/states/test_schedule.py。

十、最佳实践小结

  1. 任务尽量来自 pillar 或 state:用schedule.present状态或 pillar 统一管理任务,避免在多台 minion 上手工schedule.add造成配置漂移;手工添加的任务需确认persist=True才能跨重启保留。
  2. 善用测试模式schedule.add/modify/delete/purge均支持test=True,先试运行确认变更内容再真正执行。
  3. 留意依赖when/range需要 python-dateutil,cron需要 croniter,缺依赖时任务会直接校验失败并给出明确报错。
  4. 区分全局与任务级启停schedule.disable会暂停整个调度器,schedule.disable_job只停单个任务;排障时先用schedule.list show_all=True观察enabledsaved字段。
  5. 跨机分发先小范围验证move会在目标全部成功后删除源任务,建议先在测试目标上copy验证,再执行move

【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址: https://gitcode.com/gh_mirrors/sa/salt

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 15:59:44

3个摄像头模块选型坑,面试必问性能优化全解

3个摄像头模块选型坑,面试必问性能优化全解 版本升级后 API 全变了,这是很多后端开发接手旧项目时的噩梦。特别是涉及硬件交互的模块,比如摄像头模块,厂商 SDK 一改,你的业务代码就得跟着重写。这种痛点在面试中也是高频考点,面试官喜欢问:“如何设计一个稳定的摄像头采集接口,以应对底层驱动或…

作者头像 李华
网站建设 2026/9/23 15:59:28

劳务班组长看这篇,一文搞懂当铺逻辑,3个代码示例搞定项目落地

劳务班组长看这篇,一文搞懂当铺逻辑,3个代码示例搞定项目落地 看了一堆教程还是不会写项目?别急,问题不在你笨,而在没人把业务逻辑翻译成代码。今天咱们不聊虚的,直接以 当铺 这个经典业务场景为例,把从需求到代码的全过程拆开揉碎。很多新手觉得当铺业务复杂,其实核心就三个字: 借、押、赎 。…

作者头像 李华
网站建设 2026/9/23 15:59:12

文化衫设计模板源码解析:3步搞定前端排版报错

文化衫设计模板源码解析:3步搞定前端排版报错 刚接手公司年会文化衫定制项目,打开 Figma 导出代码,页面直接崩了。控制台里飘着红彤彤的报错,一堆 TypeError: Cannot read properties of undefined (reading 'width')…

作者头像 李华
网站建设 2026/9/23 15:59:05

黄正备考避坑:3个底层逻辑助你拿下面试必问难题

黄正备考避坑:3个底层逻辑助你拿下面试必问难题 刚背完规范却还在现场瞎指挥?黄正考试里那些看似死记硬背的条款,其实全是工程现场的“生存法则”。很多兄弟觉得《公路工程》教材厚如砖头,读完就忘,一到面试必问的实操题就卡壳,根源就在于你只学了“语法”,没搭起“项目架构”。…

作者头像 李华
网站建设 2026/9/23 15:58:47

5个致命坑让日和坊技能变废铁?这份保姆级教程帮你避开

5个致命坑让日和坊技能变废铁?这份保姆级教程帮你避开 很多刚接触阴阳师PVE的萌新,手里攥着日和坊,觉得“这不就是个奶妈吗?加血就行”,结果在打高难副本时频频翻车。要么治疗量跟不上伤害,要么被控得动不了,要么能量枯竭导致全队暴毙。学会挂技能却不知怎么搭配阵容,这是最大的痛点。今天这篇保姆级教程,专门…

作者头像 李华