目标与现状
把原来跑在自动化助手自带定时器上的定时任务,沉淀为一个可整体迁移的定时任务容器:一容器多任务,目录即开关,任务高内聚,数据面可整体拷走。设计仓库只放设计与决策,实例目录不进版本管理、走同步工具备份。
现状:容器已拉起,五个任务在跑;其中两条旧链路已停用切容器侧,一条仍与旧链路并行对照,一条因依赖桌面进程与文件锁不容器化、已归档。
前置事实(实测)
| 事实 | 实测值 |
|---|---|
| 运行模式 | 单容器跑全部任务,非每任务一容器 |
| 宿主容器引擎 | 用户态 rootless 守护进程,数据根在用户主目录下 |
| 宿主余量 | 十余线程、数十 GB 内存、数据盘余数百 GB,文件系统为通用 Linux 文件系统 |
| 基础镜像与依赖 | 精简版官方 Python 镜像构建;调度器依赖与行情采集依赖均可从公共软件源直装 |
| 构建期网络 | 宿主守护进程注入的回环代理在默认构建网桥内不可达,构建需走宿主网络 |
| 通知链路 | 同一容器网络内的代理容器名可达;回环地址与网桥网关地址在容器内均不可达 |
实现过程
容器与目录结构
实例目录下三件套:容器编排文件、镜像定义、构建入口;加一个数据目录即全部状态。数据目录下分四块:框架层、启用任务、停用归档、运行结果日志,外加跨任务公共数据区。
实例/
编排文件 数据目录挂载为容器内数据根;设为国内时区;共享外部网络;CPU/内存上限;日志限流;行情直连所需的直连例外
镜像定义 官方精简 Python 基础镜像 + 时区数据 + 下载工具 + 本地化数据 + 调度器依赖 + 任务依赖
构建入口 校验框架层声明的版本号格式 → 透传当前环境代理 → 构建
数据目录/
框架层 版本声明、依赖清单、调度器、通知配置模板
启用任务 每个子目录一个任务:定时表达式文件 + 说明 + 代码目录
停用归档 移入即停用,不加载;含新任务模板
运行结果日志 每个任务每年一个文件,每次执行一行
公共数据区 跨任务共享数据,谁产出谁写、全员可读调度器
框架脚本随数据目录同步,改完重启容器即生效,不必重建镜像。启动时扫描一次启用目录下各任务的定时表达式文件;改规则或搬任务后重启即生效,不做周期重扫。表达式为标准 Linux 格式,命令以该任务目录为工作目录执行,脚本用相对路径即可。到点起独立线程跑,各规则独立计时不重复触发。
任务侧契约:在标准输出里打印 RESULT: 一句话 即进入结果行;多行用分隔符合并。取不到时失败取输出末行、成功记短横。结果行不截断,宁可长不断。
任务清单
| 任务 | 触发时刻 | 说明 |
|---|---|---|
| 仿真柜台采集 | 每日两个时刻(下午与晚间,晚间取当日结算单) | 采集结算单与交易快照;与旧链路错开数分钟并行对照,避免同分钟双打柜台前置 |
| 行情长表下载 | 每日收盘后 | 按快照口径定合约范围,下载分钟级行情长表;非交易日静默退出 |
| 站点地图更新 | 每日下午 | 生成站点地图并经开放接口写入目标笔记;写后回读自证,含对外下载端点逐字节比对 |
| 版本发布巡检 | 每日中午 | 巡检十余个上游项目的发布;本地按执行日期记账,渲染多日滚动表写入目标笔记 |
| 估值表更新 | 每日早晨(邮件凌晨到达之后) | 邮箱附件下载加增量解析;入口不读笔记本,直接调本地业务对象;数据与交易日历随数据面走 |
数据面
任务之间可以互相读数据:公共数据区是唯一共享面,每个数据面只有一个产出者,其余只读。任务目录只放代码加凭据加定时表达式加说明。迁移单位是整个数据目录。数据目录内不放任何软链接(挂载进容器后必断);方向只能是外部建软链接指进来,且指向公共数据区下不带任务名的路径,任务改名不断链。
日志与通知
只有两个去处:运行结果日志(每次执行一行:时间、任务名、成功失败、退出码、耗时、业务结果),同一行推送到指定群话题;容器控制台(任务完整输出逐行实时写这里,带任务名前缀,标准错误并入同一路),看最近各任务在干什么只看这里。手工触发跑在新进程里,输出只到该终端,结果行照常追加。
实测证据
| 项 | 结果 |
|---|---|
| 构建 | 一次通过,镜像内解释器版本与框架层声明一致,调度器与采集依赖均可导入 |
| 规则加载 | 启用任务全部列出,停用一条不出现;空启用目录明确提示无可用任务 |
| 手动触发 | 单任务只执行一次、只写一行;结果行含业务结果 |
| 失败路径 | 失败记失败与退出码;堆栈只在控制台,不进结果行 |
| 自动触发 | 分钟级表达式在分钟内真实执行并落行 |
| 停用 | 移入停用目录后不再调度(需重启生效一版) |
| 文件权限 | 结果日志按常规权限落盘(调度器启动时设掩码) |
| 实时输出 | 长任务输出逐行实时出现在控制台,多行输出间隔与真实执行间隔一致 |
| 通知 | 结果行原文推送到指定群话题,代理走同网络代理容器名,发送期临时摘掉直连例外、发完恢复 |
| 站点地图自证 | 写入接口成功且对外下载端点逐字节一致,含上千条地址 |
| 版本滚动表自证 | 写后回读表头与行数相符;记账只在版本号变动时追加 |
| 估值表全链路 | 无新增无追加时数秒内成功;新邮件到达后下载加解析行数增长;二次执行幂等 |
管理命令
| 用途 | 命令 |
|---|---|
| 构建(改版本或依赖后才需) | 构建入口脚本 |
| 启动 | compose up -d |
| 重启(改调度器、定时表达式、任务脚本后) | compose restart |
| 列出任务与规则 | compose exec 容器名 run |
| 手动跑一次某任务 | compose exec 容器名 run 任务名 |
| 手动跑并透传参数 | compose exec 容器名 run 任务名 -- 参数 |
| 手工冒烟不打扰群 | compose exec -T 容器名 run --no-notify 任务名 |
| 看运行时视图 | compose logs -f |
| 看某任务结果行 | tail 结果日志目录/任务名-年份.log |
| 停用或启用 | 在启用与停用目录之间整目录移动,改完重启 |
版本只有一个来源:框架层声明的版本号文件,编排目录下的环境文件是指向它的软链接,日常操作直接用编排命令即可。
踩坑记录
| 现象 | 成因 | 处置 |
|---|---|---|
| 构建期拉取或安装超时 | 注入的回环代理在默认构建网桥内不可达 | 构建走宿主网络 |
| 采集库加载直接崩溃,报本地化构造失败 | 精简镜像本地化数据不全,与语言环境变量无关,只补一种也不够 | 镜像内按宿主同一套生成所需本地化数据 |
| 巡检脚本找不到基线文件,写到容器用户主目录下 | 脚本内写死旧数据路径,覆盖了调用方传的环境变量 | 改为环境变量优先、缺省回旧布局;路径一律由调用方显式传 |
| 通知报网络不可达 | 为行情直连设的直连例外被标准库的代理绕行逻辑读走,通知也被直连 | 发送期间临时摘掉直连例外变量,发完恢复 |
| 通知地址在容器内连不上 | 回环地址与网桥网关地址在容器内都不指向宿主代理 | 用同一容器网络内代理容器的容器名加端口 |
| 写入接口返回成功但笔记正文变成固定字面量 | 请求的内容类型头用错 | 改用纯文本内容类型头,正文原样入库;写后必回读自证 |
| 任务配置里的变量进不了子进程 | 普通加载方式不导出变量 | 加载时开自动导出 |
| 同一任务手工跑一次写两行 | 按规则条数取目标,多表达式任务被跑多遍 | 同命令规则去重,命令不同才各跑一遍 |
| 邮箱登录报编码错误 | 配置文件解析器把行内注释读进密码值 | 注释单独成行 |
| 任务改名后外部引用全断 | 外部软链接指向含任务名的路径 | 外部软链接只指向公共数据区下不带任务名的路径 |
不做的事(用户裁定)
- 不补跑、不记上次执行、不防重叠、不加锁、不做任务级资源限制
- 框架不管数据滚动、上限、清理、归档,保留策略由任务脚本自己决定
- 不介入业务幂等,重叠与失败恢复由任务脚本自己的设计负责
- 容器内无推理与管理链路,笔记维护只做确定性更新:本地模板加本地数据足够成熟时可不读远端直接覆盖,但模板不成立时必须拒绝覆盖;凡需判断或改写结构的要么做成固定模板、要么另找能力入口
- 完整运行流水不另存文件:控制台滚动由编排文件的日志限流管,历史只看结果行
边界
本笔记只覆盖定时任务容器这一台实例的机制与判据,不覆盖各任务内部业务语义细节(见各任务目录内的说明)。版本解读中需人工判断的部分仍走人工。本次为只读整理与写笔记,未改容器内任何文件。