NFOS systemd 定时器全景(timers)¶
数据来源:主节点
systemctl show/systemctl list-timers只读采集(快照见.scratch/timers-data.json)。
文档版本:V1.0
文档性质:清单与排障类(只读采集,非规范)
采集范围:主节点 123.57.85.171(阿里云北京)单机;次节点 43.156.170.157 未采集
1. 总览¶
主节点上共有 17 个已安装并处于 active 的 NFOS 定时器(systemctl list-timers 'nfos-*' --all 报告
17 timers listed)。它们全部由 systemd 驱动(没有 crontab 参与),分布如下:
| 项目 | 值 |
|---|---|
| 主节点 | 123.57.85.171 |
| 已安装 timer 数 | 17(另有 1 个 nfos-tunnel.service 是常驻长进程,没有 timer) |
| 调度引擎 | systemd timer units(/etc/systemd/system/nfos-*.timer) |
| 触发方式 | 13 个走 OnCalendar(日历/挂钟),3 个走单调计时器(OnBootSec+OnUnitActiveSec)+ nfos-admin-password 为「OnCalendar + OnBootSec 混合」 |
| 运行身份 | 除 nfos-admin-password、nfos-cert-renew 采集到 User= 为空(按系统默认/单元自身设定)外,其余 timer 对应服务均为 root |
| 服务类型 | 对应 service 全部为 Type=oneshot:跑完即退出,相同周期内不会并发并存 |
| 日志落点 | 一半写 StandardOutput/StandardError=append: 到 /opt/Cy/services/logs/backend/nfos/*.log;nfos-admin-password、nfos-logrotate 未设置输出重定向 → 只进 journald |
快照时间口径:timers-data.json 的 generated_utc 字段记录为
2026-09-28T00:0Z (probe realtime: 2026-09-28 07:5x CST, primary node 123.57.85.171);
同一批采集中 systemctl list-timers 原始输出里最近一次触发为
Mon 2026-09-28 07:51:04 CST,NextElapseUSecRealtime 最早为 08:00:00 CST。
因此本页所有「已安装 / 未安装 / 最近触发」结论的统一口径为 2026-09-28 07:5x CST(CST = UTC+8),
超出该时刻的线上状态需重新采集,不要沿用本页。
读法提醒:本页的
OnCalendar与「运行的脚本与参数」两列是逐字从timers-data.json的on_calendar/exec_start抄录的,未做任何改写或省略; 「用途」列为便于阅读做了压缩,原文见快照purpose字段。 本页不记录任何口令、webhook URL、token 或私钥,涉及处一律写变量名或<redacted>。
2. 一览表(17 个 timer)¶
列含义:幂等键 = 脚本用于「同一件事只做一次 / 不重复通知」的状态文件;
备份位置 = 该 timer 确实产生备份产物的位置,不产生备份的写 —。
| Timer | OnCalendar | 运行的脚本与参数 | 用途 | 产物 / 日志路径 | 幂等键 | 备份位置 |
|---|---|---|---|---|---|---|
nfos-security.timer |
(单调:OnBootSec=120 / OnUnitActiveSec=60,Persistent=false) |
/bin/bash /opt/Cy/services/system.d/nfos/security-guard.sh |
每 60 秒安全防线巡检(特征库/规则库/模板库驱动):反弹 Shell、异常外联、暴露监听、持久化、SSH 爆破、完整性漂移;命中先过自动处置,再上报 nfarmsd(source=security)并升级到工单/飞书 |
日志 /opt/Cy/services/logs/backend/nfos/security-guard.log;状态 /opt/Cy/services/system.d/nfos/state/security-guard.state;证据 state/snap-*.txt、/opt/Cy/services/logs/backend/nfos/auto-respond.log |
state/security-guard.state(记录当前正在跟踪的 dedup_key 集合) |
— |
nfos-probe.timer |
(单调:OnBootSec=90 / OnUnitActiveSec=60,Persistent=false) |
/bin/bash /opt/Cy/services/system.d/nfos/probe-node.sh --quiet |
每 60 秒互监测探针(主/次节点同款):两节点各服务端口 TCP 拨测 + 应用层检查 + SSH 隧道状态,落成 Prometheus textfile | 日志 /opt/Cy/services/logs/backend/nfos/probe.log(当前 0 字节,输出全在 .prom 里);产物 /opt/Cy/services/system.d/nfos/monitoring/textfile/nfos_probe.prom |
(未见显式状态文件;每轮原子覆盖 textfile) | — |
nfos-ticket-notify.timer |
(单调:OnBootSec=120 / OnUnitActiveSec=300,Persistent=true) |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/monitoring/ticket-notify.py |
每 300 秒轮询本机 nfarmsd /tickets,与本地快照比对,新开单/状态转变 → 邮件 + 飞书(把无人消费的 ticket.created / ticket.updated 事件接上) |
日志 /opt/Cy/services/logs/backend/nfos/ticket-notify.log;状态 /opt/Cy/services/system.d/nfos/state/ticket-notify-state.json |
state/ticket-notify-state.json |
(仅 unit 文件备份:state/unit-backups/nfos-ticket-notify.{service,timer}.bak-20260926-164409) |
nfos-admin-password.timer |
*:0/10(另有 OnBootSec=3min,Persistent=true) |
/opt/Cy/services/system.d/nfos/admin-password-rotate.sh |
每 10 分钟检查超管口令是否等于当日派生值,未轮换则经 nfarmsd reset-password 接口轮换(当天已轮换则幂等退出) |
无输出重定向 → journald:journalctl -u nfos-admin-password.service;状态 /opt/Cy/services/system.d/nfos/state/admin-password-rotation.json |
state/admin-password-rotation.json(记录「今天已轮换」) |
(当日回滚记录:/opt/Cy/certs/backend/admin-password.txt,mode 600,内容 <redacted>) |
nfos-digest-hourly.timer |
*-*-* *:00:30 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/monitoring/digest.py --window 1h |
每小时汇总刚结束整点窗口的 critical 告警(攻击成功 / 抵御失败)→ 飞书安全群 + 安全邮箱 | 日志 /opt/Cy/services/logs/backend/nfos/digest.log;状态 /opt/Cy/services/system.d/nfos/state/digest.state.json |
state/digest.state.json(last_sent_bucket) |
— |
nfos-alert-sweep.timer |
*-*-* *:02,17,32,47:10 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/alert-sweep.py --apply |
每 15 分钟回收陈旧(孤儿)告警:只回收「recovered_at IS NULL + source=security + dedup_key LIKE 'nfos.sec:%' + 守卫已不再跟踪」的行,让活动告警数能降下来 |
日志 /opt/Cy/services/logs/backend/nfos/alert-sweep.log |
(无自有状态文件;判据 = state/security-guard.state + nfarmsd 库 recovered_at) |
— |
nfos-log-audit.timer |
*-*-* *:07:00 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/log-audit.py |
每小时系统日志异常审计(journalctl / messages / secure / kernel:OOM、I/O 与 FS 错误、磁盘写满、崩溃循环、core dump、认证失败激增、conntrack 满)→ 安全告警;告警看最近 15 分钟,报告按日归档 | 日志 /opt/Cy/services/logs/backend/nfos/log-audit.log;报告目录 /opt/Cy/services/logs/backend/nfos/log-audit/;状态 /opt/Cy/services/system.d/nfos/state/log-audit.state.json;产物 monitoring/textfile/nfos_log_audit.prom |
state/log-audit.state.json |
(仅 unit 文件备份:state/unit-backups/nfos-log-audit.timer.bak-20260926-170808) |
nfos-maintenance-revert.timer |
*-*-* 09:00:00 |
/bin/bash /opt/Cy/services/system.d/nfos/state/maintenance-revert.sh |
09:00 结束维护窗口,还原告警策略(恢复 observe_only,即把维护期置为「只记日志」的告警重新打开) |
日志 /opt/Cy/services/logs/backend/nfos/maintenance.log;快照凭据 state/unit-backups/maintenance-snapshot-path-20260928-074114.txt |
(未见显式状态文件;脚本对只读账号不可读,无法确认) | state/unit-backups/policy-before-maintenance-*.json、state/unit-backups/rule-library.yml.maintenance-* |
nfos-report-attack.timer |
*-*-* 07,09,11,13,15,17,19,21:00:20 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/monitoring/digest.py --report attack |
攻击成功通报:07:00–21:00 每 2 小时 → 飞书汇总群 + 安全邮箱 | 日志 /opt/Cy/services/logs/backend/nfos/report.log;状态 state/digest.state.json |
state/digest.state.json |
— |
nfos-report-tickets.timer |
*-*-* 08:00:00 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/monitoring/digest.py --report tickets |
每日 08:00 工单状态日报(已处理 / 未处理 / 未分配)→ 飞书汇总群 + 安全邮箱 | 日志 /opt/Cy/services/logs/backend/nfos/report.log;状态 state/digest.state.json |
state/digest.state.json(last_sent_bucket) |
— |
nfos-report-daily-alerts.timer |
*-*-* 10:00:00 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/monitoring/digest.py --report daily-alerts |
每日 10:00 前一日(00:00–23:59)告警汇总 → 飞书汇总群 + 安全邮箱 | 日志 /opt/Cy/services/logs/backend/nfos/report.log;状态 state/digest.state.json |
state/digest.state.json |
— |
nfos-report-libs.timer |
*-*-* 12:00:00 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/monitoring/digest.py --report libs |
每日 12:00 规则库 / 病毒特征库更新通报 → 飞书汇总群 + 安全邮箱 | 日志 /opt/Cy/services/logs/backend/nfos/report.log;状态 state/digest.state.json |
state/digest.state.json |
— |
nfos-digest-12h.timer |
*-*-* 00,12:00:30 |
/usr/bin/python3 /opt/Cy/services/system.d/nfos/monitoring/digest.py --window 12h |
每 12 小时汇总 high + warning 告警(攻击失败 / 抵御成功 / 结果未知)→ 飞书安全群 + 安全邮箱 | 日志 /opt/Cy/services/logs/backend/nfos/digest.log;状态 state/digest.state.json |
state/digest.state.json |
— |
nfos-logrotate.timer |
*-*-* 00:20:00 |
/usr/sbin/logrotate /etc/logrotate.conf(另有 ExecStartPost=/bin/true) |
每日 00:20 日志轮转,覆盖 /etc/logrotate.conf 与 /etc/logrotate.d/*,补齐主节点缺失的 /etc/cron.daily/logrotate(否则 logrotate.d/nfarms-nginx 永不执行) |
无输出重定向 → journald:journalctl -u nfos-logrotate.service;条件 ConditionPathExists=/etc/logrotate.conf;配置目录 /etc/logrotate.d/ |
(未见显式状态文件) | — |
nfos-backup.timer |
*-*-* 02:00:00 |
/bin/bash /opt/Cy/services/system.d/nfos/backup-daily.sh |
每日 02:00 备份日志 + 配置 + 数据库(nfarmsd SQLite 一致性快照、次节点 TimescaleDB pg_dump、NATS JetStream 归档),隧道/次节点不可达时降级为仅主节点并在 MANIFEST 注明 |
日志 /opt/Cy/services/logs/backend/nfos/backup.log;产物根 /home/ecs-user/temp/<YYYY-MM-DD>/;脚本自写日志 /home/ecs-user/temp/last-backup.log |
(无去重状态文件;产物按日期目录 /home/ecs-user/temp/<YYYY-MM-DD>/ 天然幂等) |
备份产物根 /home/ecs-user/temp/<YYYY-MM-DD>/(logs/、config/、db/、MANIFEST.md、CHECKSUMS.sha256;保留最近 14 个日期目录) |
nfos-cert-renew.timer |
*-*-* 03:20:00 |
/bin/bash /opt/Cy/services/system.d/nfos/cert-renew.sh renew |
每日检查 9 域 NFarms SAN 站点证书是否需要续期(acme.sh + Let's Encrypt HTTP-01 webroot),剩余 30 天内自动续期 | 日志 /opt/Cy/services/logs/backend/nfos/cert-renew.log |
(未见显式状态文件;「未变更则不部署」由脚本自身判定) | 脚本备份目录 /opt/Cy/certs/backup/nginx-<timestamp>/(保留最近 5 份) |
nfos-cert-sync.timer |
*-*-* 04:20:00 |
/bin/bash /opt/Cy/services/system.d/nfos/cert-sync.sh --quiet |
每日 04:20 证书审计:解析线上 nginx 配置、扫描 /opt/Cy/certs/nginx 下全部 PEM、登记 nfarmsd,并按规则产生/恢复安全告警(CertificateExpired / ExpiringCritical / ExpiringSoon / KeyPermissive / Missing) |
日志 /opt/Cy/services/logs/backend/nfos/cert-sync.log;状态 /opt/Cy/services/system.d/nfos/state/cert-sync.state |
state/cert-sync.state(记录「已上报的 dedup_key → 状态/时间」) |
— |
备份位置一列只有 5 处非空:nfos-ticket-notify、nfos-log-audit(仅 unit 文件备份)、
nfos-maintenance-revert(策略快照)、nfos-backup(备份产物根)、nfos-cert-renew(证书备份)。
其余 12 个 timer 不产生备份产物,写 —。
3. 安全防线¶
这几条是「秒/分钟级」的在线防线,也是唯一会在无人值守时自动处置的一层。 它们全部依赖
state/security-guard.state这条链,所以该状态文件被重建/迁移会连带影响nfos-alert-sweep。
nfos-security.timer¶
- 触发节奏:单调计时器 —— 开机后 120 秒首跑,之后每 60 秒一轮(
Persistent=false,不补跑)。 - 跑什么:
security-guard.sh逐条执行特征库检测(反弹 Shell / 异常外联 / 暴露监听 / 持久化 / SSH 爆破 / 完整性漂移),命中先过自动处置引擎(monitoring/auto-respond.py),再按rule-library.yml的dedup_key上报 nfarmsd(source=security),critical 自动转工单并推送飞书/邮件。 - 失败会怎样:单轮脚本非 0 退出 → 单元置
failed,security-guard.log留痕;unit 未启用Restart=,不会自动重试,靠 60 秒后的下一轮自愈。若脚本既未上报 nfarmsd 又未写日志,只能靠人工systemctl status发现——这正是第 8 节第 1 项缺口的风险点。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/security-guard.log(采集时 9.7 MB,最新写入 07:51)。 - 手工跑一次:
sudo systemctl start nfos-security.service
nfos-probe.timer¶
- 触发节奏:单调计时器 —— 开机后 90 秒首跑,之后每 60 秒一轮(
Persistent=false)。 - 跑什么:
probe-node.sh --quiet对主/次节点每个服务端口做 TCP 拨测 + 应用层拨测 + SSH 隧道状态检查,原子写入 node_exporter textfile。 - 失败会怎样:单元
failed;由于probe.log当前是 0 字节,失败信息主要落在单元状态里,journalctl -u nfos-probe.service才是第一现场。textfile 若未被刷新,Prometheus 侧体现为指标停更。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/probe.log(0 字节);产物/opt/Cy/services/system.d/nfos/monitoring/textfile/nfos_probe.prom。 - 手工跑一次:
sudo systemctl start nfos-probe.service
nfos-alert-sweep.timer¶
- 触发节奏:每小时
:02、:17、:32、:47各一次,另有 30 秒随机延迟。 - 跑什么:
alert-sweep.py --apply只读打开 nfarmsd 库,回收「守卫已不再跟踪」的nfos.sec:*孤儿告警;prometheus/...来源的告警绝不触碰。 - 失败会怎样:单元
failed;后果不是误报而是「活动告警数降不下来」,Prometheus 的「活动告警数超阈值」元告警可能长期无法恢复。下一轮 15 分钟后重来。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/alert-sweep.log。 - 手工跑一次:
sudo systemctl start nfos-alert-sweep.service(先加--dry-run之外的参数前请读脚本用法)
nfos-admin-password.timer¶
- 触发节奏:
OnCalendar=*:0/10(每小时:00、:10、…、:50)外加OnBootSec=3min开机补一次;Persistent=true。 - 跑什么:
admin-password-rotate.sh校验超管口令是否等于当日派生值,不等则经 nfarmsd/users/:id/reset-password接口轮换(走正规哈希与审计链,不直接改库)。 - 失败会怎样:脚本头部明确「失败即上报统一告警(warning),在 online 平台可见,不会静默失败」;由于每 10 分钟一轮,当天某次失败(例如 nfarmsd 正在重启)可在 10 分钟内自愈。
- 日志去哪:无输出重定向 → 只进 journald:
journalctl -u nfos-admin-password.service;状态文件/opt/Cy/services/system.d/nfos/state/admin-password-rotation.json。 - 手工跑一次:
sudo systemctl start nfos-admin-password.service;只读查看派生值与状态用--status子命令(派生规则与明文口令不在本页记录,见脚本头部)。 - 脱敏提醒:
/opt/Cy/certs/backend/admin-password.txt(mode 600)内容切勿粘贴到工单、文档或聊天。
4. 通知通报¶
这一层只「说话」不「动手」:7 个 timer 共用 2 个脚本(
monitoring/digest.py、monitoring/ticket-notify.py), 幂等键集中在state/digest.state.json与state/ticket-notify-state.json两个文件上。
nfos-ticket-notify.timer¶
- 触发节奏:单调计时器 —— 开机后 120 秒首跑,之后每 300 秒一轮;
Persistent=true(当轮没开机,开机补跑一次)。 - 跑什么:轮询
GET /tickets→ 与本地快照比对 → 新开单/状态转变发邮件 + 飞书(首轮只登记不发信,避免历史工单邮件风暴;发送成功才落盘)。 - 失败会怎样:发送失败保留旧状态、下一轮重试,不会静默丢通知;单元
failed+ticket-notify.log留痕。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/ticket-notify.log(采集时 2.4 MB)。 - 手工跑一次:
sudo systemctl start nfos-ticket-notify.service
nfos-digest-hourly.timer¶
- 触发节奏:每小时
:00:30;RandomizedDelaySec=0、AccuracySec=30s、Persistent=true。 - 跑什么:
digest.py --window 1h汇总刚结束的整点窗口 critical 告警(攻击成功 / 抵御失败)→ 飞书安全群 + 安全邮箱。 - 失败会怎样:窗口不会被重算(下轮只算新窗口),因此失败意味着该小时 critical 汇总缺失;
Persistent=true只能补跑一次错过的轮次。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/digest.log。 - 手工跑一次:
sudo systemctl start nfos-digest-hourly.service
nfos-digest-12h.timer¶
- 触发节奏:每天
00:00:30与12:00:30;RandomizedDelaySec=60、Persistent=true。 - 跑什么:
digest.py --window 12h汇总 high + warning(攻击失败 / 抵御成功 / 结果未知)→ 飞书安全群 + 安全邮箱。 - 失败会怎样:同 1h 档,错过的 12h 窗口不会重算;
digest.state.json的last_sent_bucket决定哪些窗口算「已发」。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/digest.log。 - 手工跑一次:
sudo systemctl start nfos-digest-12h.service
nfos-report-attack.timer¶
- 触发节奏:
07,09,11,13,15,17,19,21点的:00:20(每 2 小时);RandomizedDelaySec=60、Persistent=true。 - 跑什么:
digest.py --report attack—— 攻击成功通报 → 飞书汇总群 + 安全邮箱。 - 失败会怎样:单元
failed、report.log留痕;报告类通报失败不阻断业务,但当天窗口的报告会缺口。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/report.log。 - 手工跑一次:
sudo systemctl start nfos-report-attack.service
nfos-report-tickets.timer¶
- 触发节奏:每日
08:00:00(RandomizedDelaySec=120、Persistent=true)。采集时从未触发过(LastTriggerUSec为空),下次08:00:59。 - 跑什么:
digest.py --report tickets—— 工单状态日报(已处理 / 未处理 / 未分配)。 - 失败会怎样:单元
failed、report.log留痕;日报缺失不会自动补发(Persistent=true只在开机补跑,不补历史日)。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/report.log。 - 手工跑一次:
sudo systemctl start nfos-report-tickets.service
nfos-report-daily-alerts.timer¶
- 触发节奏:每日
10:00:00(RandomizedDelaySec=120、Persistent=true)。采集时同样从未触发。 - 跑什么:
digest.py --report daily-alerts—— 前一日 00:00–23:59 告警汇总。 - 失败会怎样:同日报类,失败只在
report.log与单元状态留痕。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/report.log。 - 手工跑一次:
sudo systemctl start nfos-report-daily-alerts.service
nfos-report-libs.timer¶
- 触发节奏:每日
12:00:00(RandomizedDelaySec=120、Persistent=true)。采集时同样从未触发。 - 跑什么:
digest.py --report libs—— 规则库 / 病毒特征库更新通报。 - 失败会怎样:同日报类;「库没更新」这类信息若漏报,等于失去一层对特征库停更的观测。
- 日志去哪:
/opt/Cy/services/logs/backend/nfos/report.log。 - 手工跑一次:
sudo systemctl start nfos-report-libs.service
5. 审计与轮转¶
nfos-log-audit.timer¶
- 触发节奏:每小时
:07:00(AccuracySec=30s、RandomizedDelaySec=60、Persistent=true)。注意:仓库里的OnCalendar与线上不同(详见第 9 节备注),线上现行为小时级。 - 跑什么:
log-audit.py读 journalctl / messages / secure / kernel,按特征库detect: log_anomaly条目计数并上报;告警只看最近 15 分钟,报告仍按整窗口统计并按日归档。 - 失败会怎样:单元
failed、log-audit.log留痕;设计上「每小时一轮 + 15 分钟近期窗口」意味着持续 ≥15 分钟的异常最迟 1 小时内告警,漏一轮就延长这段盲区。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/log-audit.log;报告/opt/Cy/services/logs/backend/nfos/log-audit/;指标产物monitoring/textfile/nfos_log_audit.prom。 - 手工跑一次:
sudo systemctl start nfos-log-audit.service;指定窗口可参考脚本用法(log-audit.py --since '3 days ago',直跑脚本前先确认参数)。
nfos-logrotate.timer¶
- 触发节奏:每日
00:20:00(RandomizedDelaySec=300、Persistent=true)。 - 跑什么:直接执行
/usr/sbin/logrotate /etc/logrotate.conf(另有ExecStartPost=/bin/true)——这是唯一不跑 NFOS 脚本的 timer,用于补齐主节点缺失的/etc/cron.daily/logrotate。 - 失败会怎样:单元
failed;直接后果是logrotate.d/nfarms-nginx等规则不执行,日志不轮转、磁盘缓慢增长。无输出重定向,只能从 journald 看。 - 日志去哪:
journalctl -u nfos-logrotate.service(无.log文件)。 - 手工跑一次:
sudo systemctl start nfos-logrotate.service;等价手跑sudo /usr/sbin/logrotate /etc/logrotate.conf。调试时务必先加-d(dry-run),否则会真的切分日志。
6. 备份与证书¶
本组是「可恢复性」的全部来源。第 8 节第 3 项指出:维护窗口还原脚本在仓库里没有副本。
nfos-backup.timer¶
- 触发节奏:每日
02:00:00(RandomizedDelaySec=300、Persistent=true;Persistent=true意味着关机错过也会开机补跑)。 - 跑什么:
backup-daily.sh打包日志(主节点/opt/Cy/services/logs+ 次节点经 SSH 尽力而为)、配置(敏感文件不复制,改为在config/REDACTED.txt登记跳过原因)、数据库(nfarmsd SQLite 一致性快照、次节点 TimescaleDBpg_dump -Fc、NATS JetStream 归档),生成MANIFEST.md与CHECKSUMS.sha256。 - 失败会怎样:任一产物
sha256sum -c不通过 → 脚本显式报错并非 0 退出(不静默通过);隧道/次节点不可达时降级为仅主节点并在 MANIFEST 注明,不算失败但备份面收窄。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/backup.log;脚本自写/home/ecs-user/temp/last-backup.log;产物/home/ecs-user/temp/<YYYY-MM-DD>/。 - 手工跑一次:
sudo systemctl start nfos-backup.service;也可直接sudo bash /opt/Cy/services/system.d/nfos/backup-daily.sh。 - 保留策略提醒:只保留最近 14 个严格
YYYY-MM-DD目录,且绝不删除今天及以后的目录;/home/ecs-user/temp下其他文件从不触碰。
nfos-cert-renew.timer¶
- 触发节奏:每日
03:20:00(RandomizedDelaySec=1800、Persistent=true)。线上该 timer 没有Unit=行也没有[Install] WantedBy,但仍处于enabled并按名字解析到nfos-cert-renew.service。 - 跑什么:
cert-renew.sh renew走 acme.sh + Let's Encrypt HTTP-01 webroot 检查 9 域 SAN 证书剩余天数,剩余 30 天内自动续期;未变更则不部署。 - 失败会怎样:单元
failed、cert-renew.log留痕;后果是证书到期前未被发现,最终表现为站点证书过期。脚本自带「部署前整体备份 + 校验 + 回滚」路径。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/cert-renew.log。 - 手工跑一次:
sudo systemctl start nfos-cert-renew.service;只读体检用bash /opt/Cy/services/system.d/nfos/cert-renew.sh check(零副作用)。 - 回滚:
bash /opt/Cy/services/system.d/nfos/cert-renew.sh rollback <备份目录>,备份目录在/opt/Cy/certs/backup/nginx-<时间戳>/。
nfos-cert-sync.timer¶
- 触发节奏:每日
04:20:00(RandomizedDelaySec=600、Persistent=true)。 - 跑什么:
cert-sync.sh --quiet解析 nginx 配置 → 扫描/opt/Cy/certs/nginx全部 PEM → 指纹比对后登记 nfarmsd(已存在则跳过)→ 按 5 条规则产生/恢复安全告警。 - 失败会怎样:单元
failed、cert-sync.log留痕;由于它同时是证书告警的恢复方,漏跑会让「已修复的证书问题」继续挂着告警(与nfos-alert-sweep的困扰同类)。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/cert-sync.log;状态/opt/Cy/services/system.d/nfos/state/cert-sync.state。 - 手工跑一次:
sudo systemctl start nfos-cert-sync.service;脚本支持--dry-run(零副作用),排障优先用它。
7. 维护窗口¶
nfos-maintenance-revert.timer¶
- 触发节奏:每日
09:00:00(AccuracySec=30s、Persistent=true)。这是每日固定开火的 timer,采集时LastTriggerUSec为空(当日尚未到点)。 - 跑什么:执行
/opt/Cy/services/system.d/nfos/state/maintenance-revert.sh—— 结束维护窗口,把告警策略还原到observe_only(即把关掉的告警重新打开)。它消费的是维护开始时留下的策略快照。 - 失败会怎样:后果偏严重——若还原没跑成,系统会停留在「维护期策略」上(告警被压制),而运维以为窗口已结束。日志在
maintenance.log,单元 failed 可在systemctl status看到。 - 日志去哪:
/opt/Cy/services/logs/backend/nfos/maintenance.log;策略快照state/unit-backups/policy-before-maintenance-*.json(例如.../policy-before-maintenance-20260928-074114.json)、state/unit-backups/rule-library.yml.maintenance-20260928-074039;另有state/unit-backups/maintenance-snapshot-path-20260928-074114.txt指向快照路径。 - 手工跑一次:
sudo systemctl start nfos-maintenance-revert.service。注意:脚本内容对只读账号不可读(见第 8 节第 3 项),执行前请自行确认当前策略与快照内容,不要盲跑。 - 配套事实:
deploy/nfos/下没有state/目录;.scratch/maintenance-window.sh、.scratch/maintenance-fix.sh只是操作者的一次性脚本,不是maintenance-revert.sh的副本。
8. ⚠️ 与实际不符的三项(必须正视,不做淡化处理)¶
8.1 nfos-alert-selftest.{service,timer} —— 仓库里有,线上未安装¶
- 事实:
systemctl is-enabled nfos-alert-selftest.timer→not-found;systemctl is-active nfos-alert-selftest.service→inactive; 且/opt/Cy/services/system.d/nfos/alert-selftest.sh在节点上不存在。 - 仓库 unit 描述的能力是:每日 08:00 做 告警链路端到端自检(校验 Prometheus / Alertmanager / 规则 / 桥接器 / nfarmsd 上报链路,注入合成告警,并做身份头伪造回归),
并原子刷新 node_exporter textfile 心跳
/opt/Cy/services/system.d/nfos/monitoring/textfile/nfos_alert_selftest.prom作为死人开关 (手册约定:心跳超 36 小时不刷新,NfosAlertSelftestStale规则应独立报出 critical)。 - 结论:仓库 unit 描述的「每日告警链路自检 / 死人开关」当前线上并不存在。 即:主节点此刻没有任何机制能发现「告警链路整体哑掉」这种事——告警不响本身不会告警。
- 处置方向(不在本页范围内):补部署
alert-selftest.sh+ 两个 unit,或明确接受该缺口并记录到风险清单。
8.2 nfos-monitor-tunnel.service —— 仓库里有,线上未安装¶
- 事实:
systemctl is-active nfos-monitor-tunnel.service→inactive;is-enabled→not-found; 在/etc/systemd/system、/lib/systemd/system、/usr/lib/systemd/system下按*monitor*搜索, 只命中cloudmonitor.service、lvm2-monitor.service(两份),搜不到任何*monitor-tunnel*fragment。 - 该 unit 的用途(仓库文本):次节点 → 主节点的监控反向隧道,在
10.89.0.1上转发nfarmsd api 9990 / metrics 9992 / node_exporter 9100, 供运行在nfos.net容器内的 Prometheus 采集(容器自身回环不可达宿主回环)。 - 结论:这条监控反向隧道在采集时刻未安装。与 8.1 叠加的效果是:不仅告警链路没有自检,采集侧本身也缺一条已被设计好的通路。
8.3 state/maintenance-revert.sh —— 仓库里没有副本,且线上只读账号读不到¶
- 事实:
ExecStart指向/opt/Cy/services/system.d/nfos/state/maintenance-revert.sh; 该文件 mode 为-rwxr-x--- root root,只读采集账号head返回Permission denied(内容从未被读出)。 - 仓库侧:
deploy/nfos/下没有state/目录(deploy/nfos/state不存在),快照script_repo_path为null。 - 结论:这是本页最硬的一条 —— 该脚本没有版本化副本,节点一旦损毁即不可恢复;
而它偏偏是「维护窗口结束自动还原
policy.observe_only」的唯一执行者(见第 7 节:还原失败会让系统静默停留在告警压制状态)。 - 处置方向(不在本页范围内):由有权账号把该脚本正文脱敏后纳入仓库版本管理,或至少做一次离线加密归档。
9. 仓库与线上差异对照¶
对照口径:仓库 deploy/nfos/systemd/(26 个文件,全平铺)↔ 主节点 /etc/systemd/system/(采集时刻)。
| 方向 | 数量 | 清单 |
|---|---|---|
线上有、仓库没有(units_live_only) |
12 个 unit(含 6 个 timer) | nfos-cert-renew.service、nfos-cert-renew.timer、nfos-cert-sync.service、nfos-cert-sync.timer、nfos-logrotate.service、nfos-logrotate.timer、nfos-maintenance-revert.service、nfos-maintenance-revert.timer、nfos-probe.service、nfos-probe.timer、nfos-security.service、nfos-security.timer |
仓库有、线上没有(units_repo_only) |
3 个 unit | nfos-alert-selftest.service、nfos-alert-selftest.timer、nfos-monitor-tunnel.service |
这意味着:6 条最关键的「分钟级防线 + 证书 + 轮转 + 维护窗口」timer(security / probe / cert-renew / cert-sync / logrotate / maintenance-revert)
在仓库里都没有 unit 副本——改坏了没有仓库侧参照,只能靠 state/unit-backups/ 的现场备份(见第 11 节)。
主节点 NRestarts 基线(采集时刻,用于判断「服务是否在被反复拉起」):
| 单元 | NRestarts |
ActiveState / SubState |
ExecMainStartTimestamp |
|---|---|---|---|
nfarmsd-api.service |
0 | active / running | Mon 2026-09-28 00:20:00 CST |
nfarmsd-gateway.service |
0 | active / running | Mon 2026-09-28 00:20:02 CST |
nfarmsd-worker.service |
0 | active / running | Mon 2026-09-28 00:20:04 CST |
nfarmsd-metrics.service |
0 | active / running | Mon 2026-09-28 00:20:06 CST |
四者同为 0 且启动时间同批(00:20:0x,与 nfos-logrotate.timer 当日 00:21 触发同窗口),说明采集时没有崩溃重启循环;
后续若该值增长,应优先查 nfos-logrotate 窗口与跨云隧道(nfos-tunnel.service)抖动。
两条易被忽略的差异备注:
nfos-log-audit.timer的OnCalendar线上为*-*-* *:07:00(小时级),仓库注释里记录它曾从「每天 03:20」改到小时级,现场留有 unit 备份state/unit-backups/nfos-log-audit.timer.bak-20260926-170808。nfos-ticket-notify.{service,timer}于 2026-09-26 现场改过,备份为state/unit-backups/nfos-ticket-notify.{service,timer}.bak-20260926-164409。nfos-tunnel.service线上enabled且在跑,但它没有 timer(Type=simple、Restart=always、StartLimitIntervalSec=0的常驻单元),所以不计入本页 17 个 timer。
10. 怎么手工触发 / 怎么排障¶
10.1 先看全局¶
# 17 个 timer 的下一跳、上一次触发、对应 service
systemctl list-timers 'nfos-*' --all --no-pager
# 单个 timer 的展开后调度(含单调计时器与日历两种形态)
systemctl show nfos-security.timer -p Id -p Unit -p TimersMonotonic -p TimersCalendar -p Persistent
systemctl show nfos-admin-password.timer -p Id -p TimersCalendar -p TimersMonotonic
10.2 手工触发一次¶
# 推荐:触发 service(oneshot 会同步跑完;timer 的 LastTrigger 不会因此更新)
sudo systemctl start nfos-security.service
sudo systemctl start nfos-log-audit.service
sudo systemctl start nfos-backup.service
# 也可触发 timer 本身(等价立刻到点)
sudo systemctl start nfos-cert-sync.timer
单次执行期间可用 systemctl status <unit> 观察 Active: activating (start),或直接看日志尾部。
10.3 看日志¶
# 只进 journald 的两个单元
journalctl -u nfos-admin-password.service -n 50 --no-pager
journalctl -u nfos-logrotate.service -n 50 --no-pager
# 其余单元的 unit 级日志(无论有没有 .log 文件都可用)
journalctl -u nfos-security.service -n 50 --no-pager
journalctl -u nfos-maintenance-revert.service -n 50 --no-pager
# 文件日志(真实路径,注意目录是 logs/backend/nfos/)
tail -n 50 /opt/Cy/services/logs/backend/nfos/security-guard.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/probe.log # 注意:当前 0 字节
tail -n 50 /opt/Cy/services/logs/backend/nfos/ticket-notify.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/alert-sweep.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/log-audit.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/digest.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/report.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/backup.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/cert-renew.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/cert-sync.log
tail -n 50 /opt/Cy/services/logs/backend/nfos/maintenance.log
10.4 看状态文件与产物¶
ls -l /opt/Cy/services/system.d/nfos/state/
cat /opt/Cy/services/system.d/nfos/state/digest.state.json
cat /opt/Cy/services/system.d/nfos/state/ticket-notify-state.json
cat /opt/Cy/services/system.d/nfos/state/cert-sync.state
cat /opt/Cy/services/system.d/nfos/state/log-audit.state.json
# 口令轮换状态(只读 JSON,切勿打印口令文件内容)
cat /opt/Cy/services/system.d/nfos/state/admin-password-rotation.json
# 指标产物
ls -l /opt/Cy/services/system.d/nfos/monitoring/textfile/
# 备份产物
ls -l /home/ecs-user/temp/ ; tail -n 30 /home/ecs-user/temp/last-backup.log
# 证书备份
ls -l /opt/Cy/certs/backup/
# unit 文件备份(回滚用)
ls -l /opt/Cy/services/system.d/nfos/state/unit-backups/
10.5 排障顺序(建议)¶
systemctl list-timers 'nfos-*' --all—— 先确认 timer 本身还在跳(NEXT有值、LAST在推进);systemctl status <svc>—— 看是否failed、退出码与Restart计数;journalctl -u <svc> -n 50 --no-pager—— 看单元级错误(含 systemd 自己的超时/权限报错);- 对应
.log文件 —— 看脚本自己的业务日志; - 状态文件与产物 —— 判断「跑了但没做事」还是「根本没跑」(幂等键没推进 = 脚本提前幂等退出或未执行)。
10.6 改完 unit 一定要 reload¶
单位文件(.service / .timer)任何改动后必须让 systemd 重新读取,否则改动不生效:
sudo systemctl daemon-reload
# 改了 .timer:重新启用一次并核对下一跳
sudo systemctl restart nfos-<name>.timer
systemctl list-timers 'nfos-<name>*' --all --no-pager
# 改了 .service:不需要 restart timer,但如需立即验证就跑一次
sudo systemctl start nfos-<name>.service
注意:daemon-reload 不会自动重启已在运行的 timer 的调度;改 OnCalendar 后请用 restart(或 daemon-reload + try-restart)确认 NEXT 已按新表达式变化。
11. 回滚方式(unit 文件改动)¶
11.1 铁律:备份绝不放在 /etc/systemd/system/¶
- 备份目录统一放
/opt/Cy/services/system.d/nfos/state/unit-backups/(现场既有实践,见该目录下nfos-log-audit.timer.bak-20260926-170808、nfos-ticket-notify.*.bak-20260926-164409)。 - 绝对不要把
.bak/~/.orig之类的副本留在/etc/systemd/system/里: 那个目录被完整性巡检盯着,会在/etc/systemd/system/下出现多余 fragment 时触发 PS-002 完整性告警, 把「一次正常的备份动作」变成一次安全事件。 - 命名约定(现场既有格式):
<unit 文件名>.bak-YYYYMMDD-HHMMSS。
11.2 改动前先备份¶
TS=$(date +%Y%m%d-%H%M%S)
BK=/opt/Cy/services/system.d/nfos/state/unit-backups
sudo install -d -m 755 "$BK"
sudo cp -a /etc/systemd/system/nfos-<name>.service "$BK/nfos-<name>.service.bak-$TS"
sudo cp -a /etc/systemd/system/nfos-<name>.timer "$BK/nfos-<name>.timer.bak-$TS"
ls -l "$BK" | grep 'nfos-<name>'
11.3 回滚命令模板¶
# 1) 选好要还原的备份(时间戳从 ls 里挑)
BK=/opt/Cy/services/system.d/nfos/state/unit-backups
ls -l "$BK" | grep 'nfos-<name>'
# 2) 还原(unit 文件属主/权限保持 root:root 0644)
sudo cp -a "$BK/nfos-<name>.service.bak-<YYYYMMDD-HHMMSS>" /etc/systemd/system/nfos-<name>.service
sudo cp -a "$BK/nfos-<name>.timer.bak-<YYYYMMDD-HHMMSS>" /etc/systemd/system/nfos-<name>.timer
sudo chown root:root /etc/systemd/system/nfos-<name>.{service,timer}
sudo chmod 644 /etc/systemd/system/nfos-<name>.{service,timer}
# 3) 让 systemd 重新读取 + 重启 timer/service
sudo systemctl daemon-reload
sudo systemctl restart nfos-<name>.timer
sudo systemctl restart nfos-<name>.service # 若是常驻单元或需立即验证
# 4) 核对:调度表达式与下一次触发是否回到预期
systemctl show nfos-<name>.timer -p Id -p TimersCalendar -p TimersMonotonic -p Persistent
systemctl list-timers 'nfos-<name>*' --all --no-pager
systemctl status nfos-<name>.service --no-pager
11.4 回滚后自检清单¶
systemctl list-timers 'nfos-<name>*' --all——NEXT有值、LAST会在一个周期内推进;journalctl -u nfos-<name>.service -n 50 --no-pager—— 无新的Permission denied/ 参数错误;- 对应
state/状态文件与日志文件在下一轮被写入(幂等键没推进 = 没真正跑起来); ls /etc/systemd/system/ | grep '\.bak'应为空——若不为空,说明有人把备份留在了原地,需立刻移走(PS-002 风险)。
12. 相关文档¶
- 脚本库总览 —— 本页所在目录的入口,NFOS 运维脚本的分类总览与索引。
- NFarms 备份机制规范(backup) —— 备份目标、阿里云盘上传、失败重试与恢复演练的规范口径;
nfos-backup.timer(每日 02:00)与其中的调度设计对应,冲突时以规范文档为准。