简介:Control-M(简称CTM)作业定义指南,面向初次接触CTM的运维与调度人员,也适合有经验的管理员作为参考,覆盖从概念到上手的完整路径。内容先说明CONTROL-M/EM与SERVER如何协同工作,再系统讲解作业生命周期、作业组与作业流组织方式、基于时间或前置作业完成的依赖设置、SMART TABLE与SUB TABLE定义、一次性/周期/事件触发调度、监控告警和高级工作流设计,并配有多个定义实例和最佳实践,能帮助读者理解作业运行方式,掌握作业配置中的关键考虑因素。压缩包内含1个doc文档,整体约3.05MB;目录按概述、CONTROL-M概览、作业概览、作业定义工具、表与作业定义、实例等模块组织,章节明确,便于按需查阅和反复研读。已有74529人浏览学习,是CTM作业配置学习与日常排错时值得收藏的参考资料。若需快速定位作业定义难点、核对调度配置,可将其作为常用工具书。
1. 把 Control-M 作业配置讲透:一份让新手少走弯路的实战手册
第一次接触 Control-M(简称 CTM)的人,多半是被企业里那些动不动几百个批处理作业的调度环境逼着上手的。你可能会先翻官方文档,然后被 EM、Server、Agent、AJF、SMART Table 这一串名词砸晕——文档把概念讲得很严谨,但没人告诉你第一步该在哪点鼠标。这份《Control-M 作业配置手册》刚好补上了这个空档。它不是官方文档的翻译,而是从实际项目里沉淀出来的操作指南。我读完的感受是:它把“定义一张表、定义一个作业、把作业放进当天调度环境”这条主链路讲得很完整,对刚接手 CTM 运维或第一次做作业定义的人来说,照着点就能跑通;对已经写过作业的老手,里面的实例部分也能当参考手册用。本文会按这份手册的主线,把这套企业级批处理调度系统从头拆到尾。
2. CTM 架构拆解:EM、Server、Agent 和 AJF 是如何协作的
2.1 三个核心组件各自的职责
Control-M 不是单机软件,它是一个典型的三层架构。手册里对 EM、Server、Agent 的分工讲得很清楚,我用一句话概括:EM 是管界面和配置的,Server 是算调度计划的,Agent 是真正去干活儿的。
- Control-M/EM(Enterprise Manager):集中管理入口。你定义作业、监控作业、管理整个 CTM 环境,都是通过 EM 完成的。一个 EM 可以挂多个 Server,相当于一个控制台管多个执行引擎。
- Control-M/Server:调度核心。Server 里有一个排程引擎,它负责读取作业定义、计算调度时间、触发作业执行、维护作业状态。你可以理解为一个“强化版 crond”,但它能处理的依赖关系和条件逻辑比 crond 复杂得多。
- Control-M/Agent:安装在目标业务机上的执行器。Server 发出指令后,由 Agent 在目标机上拉起具体的命令或脚本。文档里特别提到 Agent 需要超级用户权限,这一点在实际部署时经常被忽略,权限不够的话作业会直接失败。
2.2 作业定义库和 AJF:一个容易绕晕的概念
手册第二章花了不小篇幅讲 AJF(Active Jobs File),这是理解 CTM 调度机制的关键,也是很多新手最容易绕晕的地方。我换个方式解释:
CTM 里存在两套“作业存放地”。第一套是定义库,存在数据库里,里面是静态的作业定义,包括运行什么命令、在哪个机器上跑、什么时间跑、依赖什么条件。第二套是 AJF,它是一个活动作业文件,里面是“今天”实际要跑的作业清单。作业从定义库进到 AJF 这个过程,叫做Order或Force。
这个设计的意义在于:你可以把几百个作业定义都存在库里,但只有被 Order 进 AJF 的作业才会在当天被 Server 真正调度。每天零点前后,Server 执行一次 New Day 处理,把前一天的作业清出去,把今天按周期该跑的作业放进 AJF,然后同步到 EM 的数据库里。如果当天作业运行状态有变化,或者有人工干预操作,也会实时更新 AJF。
提示:Order 和 Force 的区别在于,Order 是按作业定义里的调度日历正常放行,Force 是忽略调度日历强制执行。排查作业为什么没跑时,先看它有没有被 Order 进 AJF。
2.3 一张图看懂 Write、Upload、Load、Download 的关系
手册里有张图我没法原样搬过来,但它描述了 Write、Upload、Load、Download 四种操作的关系。实际使用中你只需要记住:写作业定义(Write)→ 上传到 Server(Upload)→ Server 载入调度环境(Load)→ 下发到 Agent 执行(Download)。在 Desktop 里保存作业定义时,默认会走完上传和载入流程,但命令行工具可以分别执行这些步骤,用于排查问题。
我把这四步简化成一张表方便对照:
| 操作 | 作用对象 | 实际效果 |
|---|---|---|
| Write | 数据库 | 保存作业定义的草稿或正式版本 |
| Upload | Server | 将作业定义从 EM 传到 Server |
| Load | AJF | 将作业载入活动作业环境,等待调度 |
| Download | Agent | Server 将执行指令下发到目标机 Agent |
3. 作业定义实操:从登录 Desktop 到建出第一个 Job
3.1 登录 EM/Desktop 的完整步骤
作业定义的主战场是 Control-M/Desktop。手册里强调了一个容易被忽略的点:Desktop 不是独立软件,它通过 EM 连接到 Server,所以登录时要填的不只是用户名密码,还要指定连接哪个 Server。我补一个常见的登录坑:如果你在客户端机器上启动 Desktop,但网络不通或 EM 服务没起,程序会卡在登录界面不动,这时候先检查网络端口通不通,不要反复重试。
登录后你会看到 Desktop 的主界面,左边是服务器树,右边是作业定义区域。第一次进来可能有点懵,但别急着点,先建立一张表。
3.2 定义 SMART Table:排程的入口
SMART Table(Scheduling Table)是 CTM 里作业的“容器”,你可以理解为一张表代表一套批处理流程。新建 SMART Table 后,要依次填写几个关键 Tab:
General Tab:
- CONTROL-M:选择执行这张表的 Server
- Table:表名,通常和业务或应用名对应
- Application:应用名,用于逻辑分组
- Group:组名,建议与表名保持一致
- Author:定义者用户名
Scheduling Tab:
- RBC Name:运行日历名称。这里有个实际项目中常犯的错——把 RBC Name 和表名设为一致没错,但如果多个表共用同一个 RBC,修改了这一个日历会影响所有引用它的表。所以新建表时一般会同步新建一个专属 RBC。
- Days:每周哪几天执行
- Months:每年哪几个月执行
Execution Tab:
- Submit:开始执行时间。如果是凌晨批处理,一般填一个相对早的时间,比如
00:05。手册里提到>表示无限延长,这个符号在 CTM 里代表“继续等待所有条件满足”。
Condition Tab(IN/OUT Condition):
这是 CTM 最核心的调度逻辑。IN Condition 是这张表开始执行前必须满足的前置条件,OUT Condition 是表执行完后产生的后置条件。比如你有一张表 A 需要在表 B 之后运行,那 B 的 OUT Condition 就叫B_DONE,A 的 IN Condition 就填B_DONE。这就像给作业发信号——上一棒跑完,下一棒接棒。
PostProc Tab:
表执行完之后的处理动作。最常见的用法是:表执行成功后,把它的 IN Condition 删掉(释放信号),同时生成 OUT Condition(通知下游)。这样第二天重跑时不会因为前一天残留的条件状态而误判。
3.3 定义 Sub Table:SMART Table 里的分组单元
Sub Table 是挂在 SMART Table 下的子表,作用是把一批相关的作业划到一个分组里,便于管理。新建 Sub Table 的方式是在 SMART Table 上右键选择 Add Sub-table。它也有自己的 General、Scheduling、Execution、Condition 这些 Tab,但有几个点要注意:
- Sub Table 的 Application 和 Group 要跟所属 SMART Table 保持一致,否则作业归属会乱
- Scheduling Tab 一般选默认 All 即可,它继承父表的调度日历
- Execution 里的 Submit 可以为空,这样 Sub Table 的执行时机完全由作业级条件和依赖控制
我的习惯是:在 SMART Table 层只配通用的运行日历和条件,Sub Table 层尽量少配条件,把具体的 IN/OUT Condition 留给 Job 级去设置。这样修改依赖关系时不用一层层去翻。
3.4 定义 Job:命令类型作业的关键参数
Job 是真正干活的单元。手册介绍了四种 Job 类型:Command、Job、Detached、Dummy。最常用的是 Command,它在目标机上执行一条命令或一个脚本,本质就是 crontab 的升级版。定义 Command Job 时,除了常规的服务器、应用名、组名之外,最关键的几个参数是:
- Command:要执行的命令行。如果是执行脚本,建议写绝对路径,并慎重对待环境变量——CTM 的 Agent 进程环境变量往往和手动登录终端时不一样,这是脚本在被 CTM 拉起时报错的第一大原因。
- Run On:目标机器或机器组。可以指定具体主机名,也可以指定 Agent 组(Pool),让 Server 在组内选一台空闲机执行。
- Start Condition:作业开始的条件。可以填 IN Condition(依赖其他作业的信号),也可以填时间窗口,比如
15:00之前必须启动。 - Execution Frequency:执行频率。比如每天跑、工作日跑、每月某天跑。这里要小心时区问题,New Day 是按 Server 所在时区算的。
- Post Processing:作业执行完后的动作,比如成功/失败后生成条件、是否重试。这个跟 SMART Table 的 PostProc 类似,但作用范围是作业级。
下面模拟一份 Command Job 的配置代码(按 Desktop 界面字段整理):
Job Name: APP_DB_BACKUP Job Type: Command [General] CONTROL-M Server: PROD_SERVER_01 Application: PAYMENT_CORE Group: NIGHT_BATCH Owner: sched_user Priority: 200 [Execution] Command: /home/pay/scripts/db_backup.sh --full --db=payment Run On: srv-db-01 Submit Time: 01:00 Days: MON,TUE,WED,THU,FRI,SUN Months: ALL [Conditions] IN Condition: ETL_DONE (PAYMENT_CORE) OUT Condition: BACKUP_DONE (PAYMENT_CORE) [Post Processing] Success: DELETE CONDITION ETL_DONE; ADD CONDITION BACKUP_DONE Failure: ADD CONDITION BACKUP_FAILED Retry: 2 times, interval 5 minutes这段配置的逻辑是:每个工作日及周日的凌晨 1 点,等ETL_DONE信号到位后,在数据库服务器上执行备份脚本。成功了就把ETL_DONE删掉、生成BACKUP_DONE信号通知下游;失败了重试两次,每次隔 5 分钟,同时生成一个BACKUP_FAILED告警信号。
参数说明:
- Priority(200):作业在队列中的优先级,数字越大越先执行。如果多个作业同时准备就绪,Server 按优先级排序调度。这个参数在实际生产里很有用,比如凌晨数据库备份和报表计算撞在同一时间,你肯定希望备份先跑完再跑报表。
- Run On 指定
srv-db-01:如果这里填的是主机名,Agent 必须在该主机上安装并正常注册。如果 Agent 没起来,作业会一直挂在 Queued 状态,而不是直接失败——这是排查时容易误判的点。 - IN Condition 里的
(PAYMENT_CORE):括号里是 Application 名。CTM 的条件名不是全局唯一的,同一名字在不同 Application 下互不干扰。这一点比 crontab 先进,但也意味着你管理条件时要严格规范命名,否则很难排查“为什么信号到了作业还是没触发”——多半是这个条件名不是在同一个 Application 下。
4. 实例拆解:六个典型作业定义场景的配置逻辑
4.1 例一到例三:从单作业到多作业依赖
手册第六章给了六个完整实例,这是整份手册含金量最高的部分。虽然原文的界面截图和参数我没法逐字还原,但其配置逻辑可以完整讲清楚。
例一是最基础的场景:定义一个每日自动执行的作业。它覆盖了新建 SMART Table、设置调度日历(每天的02:00)、新建 Command Job 执行一个脚本。这里值得注意的知识点是:SMART Table 的执行时间Submit不等于作业的执行时间,它只是表开始“检查”的时间起点。如果表里有作业依赖前置条件,表会在Submit时间之后持续等待,直到所有条件满足。
例二引入了依赖关系。场景是:作业 B 必须等作业 A 的脚本执行成功后才能启动。实现方式是:作业 A 定义 OUT ConditionJOB_A_DONE,作业 B 定义 IN ConditionJOB_A_DONE,这样就形成了一条简单的作业链。
例三的典型场景是并发控制:两个作业互斥,比如一个写入临时表,一个读取该表做计算,不能同时跑。解法不是去抢资源锁,而是共用同一个 IN/OUT Condition 机制——作业 C 执行前必须拿到LOCK_DB_TEMP信号,作业 D 也一样。谁先拿到谁先跑,后到者等待。
4.2 例四和例五:周期表和日历的高级用法
例四讲的是多日历场景。假设有一个报表系统,工作日(周一到周五)跑日批,每月最后一个工作日跑月批。实现方案有两种:
- 定义两个 SMART Table,一个挂“工作日日历”,一个挂“自定义月结日历”
- 定义一张 SMART Table,在 Scheduling Tab 里用两个 RBC 规则来区分
实际项目里我推荐第一种,原因很直接:分开定义排查问题简单。你监控界面里看到一个作业状态异常,直接锁定它所在的表去排查,不用去猜当前生效的是哪个日历规则。
例五的关键词是量化条件和到期日(Until)。在这个例子里,作业 E 在每天03:00执行,但它有一个强制截止时间06:00,超过这个时间即使前置条件满足也不会再启动。这个参数在数据批处理里特别实用——凌晨计算如果拖到资金系统对外营业还没跑完,结果数据会把生产数据覆盖掉,宁可今天不跑也不能晚跑。
4.3 例六:作业失败自动重试的最佳实践
例六是定义 Dummy Job 并配合 On Failure 动作实现失败重试。Dummy Job 不执行实际命令,只作为逻辑节点存在,用于控制流管理。它的典型用途是:作为一组并发作业的汇合点。比如三个作业并行走,全部完成后才触发下游作业,最简单的方式就是定义三个作业的 OUT Condition 指向同一个 Dummy Job 的 IN Condition。
重试配置方面,我把几个关键参数总结一下:
| 参数 | 说明 | 实际建议 |
|---|---|---|
| Max Retries | 最大重试次数 | 依据脚本类型,稳定脚本建议 1-2 次,外部接口调用建议 3 次 |
| Retry Interval | 重试间隔(分钟) | 数据库类作业至少 5 分钟以上,避免锁没释放就重试 |
| Retry Condition | 仅当某些退出码才重试 | 只对可重试错误重试,比如脚本退出码 8,不要什么错误都重试,否则会掩盖真正的 bug |
提示:重试不是万能的。如果作业是依赖外部系统(比如对账平台)的结果,重试间隔应该拉长到对方系统的处理周期,否则前一次请求还没落库,后一次重试又发过去,反而把数据搞重了。
这六个例子拼在一起就是一条完整的批处理链路:定义单作业 → 串起依赖 → 控制并发 → 挂日历 → 设截止时间 → 补重试策略。这是 CTM 实际交付项目里最常见的六块拼图。
5. 避坑指南:作业定义阶段最容易踩的五个坑
5.1 作业一直停留在 Queued 状态,不执行也不报错
现象:作业状态是 Queued/Running,但 Agent 侧完全没有进程拉起。
原因:可能有两个,一是 Agent 服务没启动或网络不通,Server 无法下发指令;二是作业的 IN Condition 没满足,它虽然在 AJF 里,但调度条件被卡住了。
解决:先看 Agent 是否在线(Desktop 左侧服务器树里有 Agent 状态灯),再看条件依赖。我遇到最多的是第二种——上游作业失败后没人清理条件,下游作业卡了一整晚。处理方式是在 PostProc 里加超时删除条件的逻辑,或者让值班运维在 New Day 前检查条件列表。
5.2 脚本在终端手动执行正常,被 CTM 拉起后报错
现象:同样的脚本,登录服务器手动跑一遍成功,CTM 调度执行时却报command not found或环境变量为空。
原因:Agent 启动的进程环境是精简环境,没有加载用户登录时的.bash_profile。
解决:脚本开头显式声明环境,比如#!/bin/sh后加. /home/user/.profile,或者将需要的PATH硬编码在脚本里。还有一个思路是在作业定义里设置环境变量,CTM 的 Job 属性中可以直接配 Environment Variables,比在脚本里改动更优雅。
5.3 New Day 后作业没有进入当天的调度计划
现象:早上看 Monitor 界面,今天某些作业没有出现在列表里。
原因:作业定义里FORCE/ORDER标志没有设置正确,或者作业未关联到有效的 RBC(运行日历)。
解决:确认作业的 Scheduling 里 Days/Months 是否覆盖今天,然后检查 SMART Table 是否被手动设置成了 Do Not Order。我第一次遇到这种情况时以为是作业丢了,后来发现是表被人改了状态。建议在 New Day 后养成检查 Order List 的习惯。
5.4 条件名冲突导致作业被错误触发
现象:一个新定义的作业只要前置条件满足就会启动,但条件其实是别人留下的旧信号。
原因:CTM 条件名在 Application 范围内生效,不同 Application 可以用同名条件。如果两个作业在不同 Application 下复用了同一个条件名,就会发生串扰。
解决:条件命名规范建议带上应用前缀,比如PAYMENT_DAILY_DONE、REPORT_MONTH_DONE,避免裸名。已有体系里如果已经乱了,就要靠 PostProc 在成功和失败的路径上都清理条件,确保每次执行后信号是干净的。
5.5 Run On 指定的主机不在 Server 的 Agent 池中
现象:作业状态变 Red,在输出日志里看到Agent not reachable。
原因:Run On 填的机器名和 Agent 注册时的主机名不一致,或者目标机 Agent 没加入当前 Server。
解决:在 CCM(组件管理工具)里确认目标主机 Agent 的注册状态和 Agent Name,作业里填的 Run On 值要和 Agent 注册名完全一致,包括大小写。另一个坑是目标机装了 Agent 但配到了另一个 Server 上,这种情况再怎么重装 Agent 也没用。
6. 二次开发思路:把 Excel 作业清单批量导入 CTM 定义的方法
这份手册主要讲图形化操作,但实际生产环境里,第一次批量上线几百个作业时,一个窗口一个窗口点效率太低了。这一章讲一种可行的批量导入方案——基于文档里的命令行工具思路,适合需要做作业批量迁移的读者。
实现的思路很直接:Excel 保存作业定义清单(作业名、应用名、组名、命令、调度日期、条件、目标主机),再写一个 Python 脚本读 Excel 后调用 CTM 命令行工具完成导入。命令行工具的核心是ctm系列命令,常见做法是先导出模板再按格式填充,或者使用 Rest API 接口方式。
一个简化版的批量导入脚本流程(概念示意):
import subprocess import openpyxl wb = openpyxl.load_workbook("jobs.xlsx") sheet = wb.active for row in sheet.iter_rows(min_row=2, values_only=True): job_name, app_name, group_name, command, run_on, days, condition = row # 生成完整命令行 cmd = ( f"ctm create job --server PROD_SERVER_01 " f"--name {job_name} --app {app_name} --group {group_name} " f"--command \"{command}\" --run-on {run_on} " f"--days {days} --condition {condition}" ) result = subprocess.run(cmd, shell=True, capture_output=True, text=True) print(f"{job_name}: {result.returncode}") if result.returncode != 0: print(result.stderr)参数说明:
--days直接传MON,WED,FRI这样的简写,可读性好--condition里要包含 Application 前缀,比如PAYMENT_ETL_DONE(PAYMENT_CORE),避免条件串扰- 跑了第一条成功了,务必在 Desktop 里打开确认参数无误,再跑全量。全量跑完后再抽查几份作业的运行日历和依赖关系
导入只是第一步。验证批处理定义是否正确,我习惯的做法是新建一张临时测试表,把要验证的作业挂上去,用 Force 的方式强制 Order 到 AJF 里,然后盯着执行日志看状态流转。确认无误后,再改正式表的日历,让作业在下一个调度日起自动跑。
这份手册的例子从最简单的单作业一直铺到条件依赖和重试策略,基本覆盖了各种常见的批处理配置场景。唯一可惜的是用文字描述界面操作有天然的信息损耗——截图的直观感没法完全还原,但表格、参数和实例串起来的知识链路,自己跟着做一遍也足够打通主流程了。希望这份手记能帮你在处理企业级批处理作业调度时少走一些弯路。
本文还有配套的精品资源,点击获取