Files
market_sync/docs/works/2026-07-03-04-systemd-drift-detection.md
gao cf45f98a8a feat(daily_check): systemd unit drift 自动检测
work #04 (2026-07-03):user 重申 sync 目标强调 timer/service/依赖/编码格式
全链路审计,发现 5 处 systemd unit drift:work #01 (4h timeout) 和 work #02
(morning.timer) 代码改了但没 cp 到 /etc/systemd/system/,等于没修。

根因:deployment 与 repo 各自演进无任何告警机制。

修复:在 daily_sync_check 加 _check_systemd_drift() 段,对比
bin/systemd/ 与 /etc/systemd/system/ 下所有 market-sync* unit:
- missing_in_etc (未部署) → status=not_deployed
- drifted (两边有但内容不一致, SHA256 比对) → status=drifted
- orphan_in_etc (/etc 有 repo 没维护, 捕获僵尸 unit) → status=orphan

drift 合入 alerts 让 webhook 也带;report schema_version 1→2。

立即验证:跑 daily_check 抓到 5 处 drift(4 未部署 + 1 内容不一致),
status=warning 让 webhook 自动发出——以后任何 deployment drift 都逃不掉。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 11:37:21 +08:00

3.3 KiB
Raw Permalink Blame History

2026-07-03 — work #04: daily_check 加 systemd unit drift 自动检测

发现

user 重申 sync 目标时强调"timer、system service、依赖、编码格式都要考虑",触发全链路审计:

Audit 结论
1. PG 编码 UTF8 / UTC / en_US.utf8
2. stock_code 格式(10 张表) 100% hermes (SH600000)0 漂移
3. dataset_registry 依赖链 12 dataset 全部 deps 解析正确,无自依赖
4. systemd unit 漂移 5 处 drift
5. 脚本可执行 + ExecStart 路径 全部 OK

Audit 4 是 sync 目标的最大阻断:之前 work #01 (4h timeout) 和 work #02 (morning.timer) 的代码没部署到 /etc/systemd/system/

repo units (14) / /etc units (10):
  ❌ 未部署 (4):
      - market-sync-daily-check.{service,timer}   (work #01)
      - market-sync-morning.{service,timer}       (work #02)
  ⚠️  内容不一致 (1):
      - market-sync.service                       (work #01: TimeoutStartSec 7200→14400)

如果今天有人手动 systemctl start market-sync.service,跑的是 /etc 里旧版2h timeout),仍会被 17:30 截断——work #01 形同虚设。

根因

deployment 与 repo 各自演进,无任何机制告警。market_sync_run.sh 注释甚至没说明要 cp 到哪里。这是流程缺口,不是代码 bug。

解决

bin/daily_sync_check.py 加 systemd drift 检测段:

  • missing_in_etcrepo 有 /etc 没装 → 标 not_deployed
  • drifted:两边都有但 SHA256 不一致 → 标 drifted(含前后 sha 前 12 位方便辨识)
  • orphan_in_etc/etc 有 repo 没维护 → 标 orphan(捕获僵尸 unit,比如之前删掉的 monitor

实现要点:

  • 用 SHA256 对比,避免误报时间戳/空白差异
  • drift 段独立于 dataset 状态,但合入 alerts 列表(让 webhook 也带上)
  • report schema_version 升 1 → 2(加 systemd_drift 字段)
  • overall 计算包含 drift 状态(任一 drift 出现 → overall=warning

验证

修复后立即跑:

counts: {'running': 1, 'missed': 8, 'not_expected': 1, 'ok': 2}
告警 (13):
  ...
  - [not_deployed] systemd/market-sync-daily-check.service ...
  - [not_deployed] systemd/market-sync-morning.service ...
  - [  drifted]    systemd/market-sync.service ...

systemd unit 漂移:
  repo units (14) / /etc units (10)
  ❌ 未部署 (4) / ⚠️ 内容不一致 (1)

5 处 drift 全部捕获,且 status=warning 让 webhook 自动发出。

关联

  • work #01: 2026-07-03-01-market-sync-timeout.md(修了代码但没部署,drift 检测让这事不再隐蔽)
  • work #02: 2026-07-03-02-morning-no-trigger.md(同上)
  • dual-scheduler-overlap:双调度器并存,本次 drift 检测在 timer / service 层加防护

后续(不在本 work 范围)

  • 写 deploy 助手脚本 bin/systemd_install.sh,一行 sudo bin/systemd_install.sh 把 repo 所有 unit cp 到 /etc + daemon-reload + enable + restart。降低手动部署出错概率。
  • 加 PG drift 检测:跑 SQLAlchemy Base.metadata 对比实际 PG information_schema.tables/columns,检测 schema 漂移(model 改了但没跑 migration)。
  • 加 stock_code 漂移检测:本次是 audit 一次性检查,应该也进 daily_check 自动段。