Files
hardware-dashboard/docs/DEPLOYMENT.zh-CN.md
T

310 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 192.168.1.37 监控与定时任务技术说明
版本:v1.3,2026 年 11 月 9 日。新增显卡数量设置和夜间节电计划,详见第 12 节。
## 1. 访问与登录
设备监控页面:**[http://192.168.1.37:8090](http://192.168.1.37:8090)**
定时任务入口在“问题与维护”下方,也可通过左侧“定时任务”菜单定位。手机底部有同名入口。
硬件监控仍可直接查看。定时任务能够实际执行服务器操作,因此使用独立的管理员账号登录:
- 账号:`admin`。
- 初始密码:见交付的 `定时任务管理员登录信息.txt`,本说明不包含密码。
- 登录最长有效 8 小时;修改密码后所有任务管理登录均失效。
- 此密码与 SSH root 密码相互独立,本次没有修改 SSH 密码。
页面只开放在原有局域网访问范围内。当前使用局域网 HTTP,适用于可信局域网;不应将这个具备 root 任务执行能力的入口直接映射到公网。将来如需远程公开访问,应先配置 HTTPS 和适当的访问控制。
## 2. 本次新增功能
“定时任务”支持:
- 新增、编辑、启用、停用和删除任务。
- 立即执行一次;这不会自动启用任务的定时计划。
- 停止本次执行;与停用后续调度分开处理。
- 查看下次计划时间、最近执行结果和是否正在执行。
- 查看单任务执行记录,以及包含已删除任务的全局执行记录。
- 查看开始/结束时间、耗时、退出码、标准输出和错误输出。
- 管理员登录、退出及修改密码。
执行方式有三种:
| 执行方式 | 如何填写 | 说明 |
|---|---|---|
| 直接输入命令 | 例如 `/usr/bin/nvidia-smi` | 使用 Bash 执行,支持管道和多行命令。 |
| 粘贴脚本内容 | 选择 Bash 或 Python 3,粘贴代码 | 可以导入本地 `.sh`、`.py` 或文本脚本到编辑框;保存后才会传给服务器。 |
| 服务器上的脚本文件 | 填写绝对路径和可选参数 | 可选择 Bash、Python 3 或按文件自身方式执行;最后一种要求文件具有执行权限。 |
执行身份固定为 **root**,因为重启服务器和维护证书通常需要管理员权限。它不是隔离的脚本沙箱:应只运行自己理解并信任的命令。
工作目录默认 `/root`,使用非交互执行环境,不继承某个 SSH 窗口的虚拟环境。PATH 包含常用系统目录和 `/snap/bin`;如需 Conda、venv 或自定义环境变量,应在脚本内明确设置。
## 3. 第一次添加任务
1. 刷新页面,打开“定时任务”,点击“登录管理”。
2. 使用交付文件中的 `admin` 与初始密码登录。
3. 点击“新增任务”,填写任务名称。
4. 选择执行方式,输入命令、脚本或服务器脚本路径。
5. 设置执行周期,点击“检查执行周期”核对下次时间。
6. 保持“保存后启用”不勾选,先保存为停用状态。
7. 如适合测试,点击“执行一次”,确认执行内容,查看执行日志。
8. 核对结果后,再启用计划。
保存任务本身不会立即执行脚本。新任务默认停用。退出登录、关闭网页或重启网页服务不会停止已经启用的系统计划。
### 常用模板
新增窗口提供:
- **显卡状态检查**:读取 `nvidia-smi`,结果保存在执行日志中。
- **服务器定时重启**:示例命令为 `systemctl reboot`,默认周期是每周日 03:00,默认停用。启用或立即执行前都会提示可能中断计算任务。
- **SSL 证书续签示例**:检查已有的 Certbot/acme.sh 工具并调用其续签入口。它不会安装工具、申请新域名或替用户配置证书。先确认现有续签配置,以及是否已经有其他自动续签任务;Web 服务的重新加载应按实际服务配置。
本次部署没有创建或启用实际的服务器重启任务和证书续签任务,也没有执行这两类操作。
## 4. 执行周期
所有日历时间按 **Asia/Shanghai,北京时间 UTC+8** 处理,页面显示服务器当前时间。
| 周期 | 规则 |
|---|---|
| 每天 | 指定时、分。 |
| 每周 | 可选择一个或多个星期及时间。 |
| 每月 | 选择 1 至 31 日和时间;当月不存在该日期时跳过。 |
| 每隔若干分钟/小时 | 启用后先等待指定间隔;每次执行结束后重新计时,避免长任务造成重叠。 |
| 指定日期,仅执行一次 | 选择未来的日期和时间,精确到分钟;至少晚于保存时刻 30 秒。 |
| 自定义 Cron | 标准五段:分、时、日、月、周。支持 `*`、逗号、范围、步长及常见英文月/星期名。 |
Cron 示例:
```text
0 3 * * * 每天 03:00
0 3 * * 0 每周日 03:00
0 3 1 * * 每月 1 日 03:00
*/30 * * * * 每小时的 00、30 分
```
不支持 `L`、`W`、`#`、`?` 等扩展语法,也不接受带秒的六段 Cron。日和星期同时指定时,按传统 Cron 的“任一满足”语义处理。
Cron 是日历触发,和“上次结束后再间隔 N 分钟”不同。例如长任务执行 10 分钟,再间隔 5 分钟,下一次大约在本次开始 15 分钟后。
### 开机、错过与长任务
- 日历任务设置 `Persistent=false`,服务器关机期间错过的执行时间不补跑,避免开机后补执行重启类任务。
- 同一个任务由固定 systemd 服务承载,并有文件锁,不会同时重叠执行两份。
- 一个任务执行超时后会被停止。默认最长 1800 秒,可在高级设置中改为 10 至 86400 秒。
- 关闭网页只会关闭查看界面,不会停止系统定时器。
- 本机历史上出现过时钟回拨。启用重启等任务前,应核对页面显示的服务器时间;定时调度依赖服务器时钟和系统校时状态。
部署验证时,服务器时区为 `Asia/Shanghai`,系统报告 `NTPSynchronized=yes`。
## 5. 看懂执行结果
| 显示 | 意义 |
|---|---|
| 尚未执行 | 没有记录到执行。 |
| 执行中 | 本次进程仍在运行。 |
| 执行成功 | 命令正常退出,退出码为 0。 |
| 执行失败 | 命令退出码非 0,或启动过程中发生错误。 |
| 超时已停止 | 达到设置的最长执行时间,已停止本次进程。 |
| 已停止 | 本次进程被停止,可能是手动操作或系统停止服务。 |
| 执行被中断 | 执行期间系统重启或进程中断,未取得完整结果。 |
成功只表示脚本返回了 0,不保证某个证书一定已更新、备份一定可恢复或某个业务结果一定正确;这些应结合脚本输出核对。
“停用”只取消将来的调度,不会强制中止已经开始的执行。“停止本次”会终止执行进程,脚本可能尚未完成修改,操作前会提示确认。
目前单任务窗口展示最近 40 次执行,全局窗口展示最近 80 次。服务器执行记录按 90 天清理;每次输出上限约 512 KiB,网页每次读取最后 128 KiB。输出与脚本快照还有合计 256 MiB 的空间限制,较旧文件可能提前清理,执行状态元数据仍保留至期限。
删除任务会停用计划并移除模块生成的定时器,保留已有执行记录。历史记录可在“全部执行记录”查看。
## 6. 参考与技术栈
界面参考 [宝塔 Linux 面板](https://www.bt.cn/) 计划任务的组织方式:任务列表、执行周期、Shell 脚本、启停、手动执行和执行日志。本次功能自行编写,沿用已有监控页面的深色风格,没有安装宝塔,也没有复制其面板源码。
原监控布局仍参考:
- [Zabbix](https://github.com/zabbix/zabbix):设备总览和告警分级。
- [Netdata](https://github.com/netdata/netdata):实时指标展示。
- [Grafana](https://github.com/grafana/grafana):历史曲线、图例和时间范围。
| 部分 | 使用技术 |
|---|---|
| 监控与任务前端 | HTML、CSS、原生 JavaScript、原生 `<dialog>` 对话框。 |
| 曲线 | 原生 SVG,保留原有 CPU、内存、GPU 温度、功耗和利用率历史。 |
| 网页后端 | Python 3.12 标准库,`ThreadingHTTPServer`。 |
| 数据库 | SQLite,WAL 模式;监控数据与任务数据分库存储。 |
| 系统调度 | systemd `.timer` 和 `.service`,不是浏览器内的倒计时。 |
| 网页与特权服务通信 | 本机 Unix socket,限制连接进程身份。 |
| 管理密码 | 随机盐、PBKDF2-HMAC-SHA256,240000 次迭代。 |
| 登录态 | 内存会话、HttpOnly/SameSite=Strict Cookie;写操作同时检查 Origin 与 CSRF。 |
| 执行控制 | 独立进程组、文件锁、超时、停止信号、限量输出、执行审计。 |
没有新增 npm、pip 或外部 CDN 依赖。服务器不需要 Node.js;开发阶段使用 Node.js 检查 JavaScript 语法。
## 7. 为什么要拆成两个服务
```mermaid
flowchart LR
A[浏览器:监控与任务界面] --> B[hardware-dashboard\n普通账号\nHTTP 8090]
B --> C[(原监控数据库)]
B -->|本机 Unix socket\n验证会话与 CSRF| D[hardware-taskd\nroot 任务管理服务]
D --> E[(独立任务数据库\nroot 专用目录)]
D --> F[systemd 定时器]
F --> G[task_exec\n执行指定任务]
G --> H[执行日志与结果]
H --> D
```
硬件网页继续使用原来的 `hardware-dashboard` 普通账号。新增 root 服务只通过本机 Unix socket 接收经验证的任务操作,不额外开放网络端口。
任务内容不插入 systemd 的 `ExecStart`。生成的服务只启动固定的执行器和经过验证的任务 ID;执行器从 root 专用数据库读取任务内容。这避免任务名称、换行或参数变成 systemd 单元配置。
上传或粘贴的脚本文本会统一换行为 Linux 格式。服务器脚本路径和参数以独立参数列表传递;命令内容模式则按管理员输入交给 Bash 执行。
root 服务不是恶意脚本隔离环境。管理员既然有权输入 root 命令,就应当视为具有服务器管理员权限。
## 8. 文件与数据库位置
以下均在 `192.168.1.37`:
| 路径 | 内容 |
|---|---|
| `/opt/hardware-dashboard/server.py` | 网页 HTTP 和监控 API;新增任务 API 代理。 |
| `/opt/hardware-dashboard/static/tasks.js` | 任务表单、登录、启停、执行和日志交互。 |
| `/opt/hardware-dashboard/task_core.py` | 校验、周期转换、任务数据库和 systemd 管理。 |
| `/opt/hardware-dashboard/taskd.py` | 本机任务服务、会话验证、审计和清理。 |
| `/opt/hardware-dashboard/task_exec.py` | 单次执行、输出、超时与退出结果。 |
| `/etc/systemd/system/hardware-taskd.service` | 新增任务管理常驻服务。 |
| `/etc/systemd/system/hwjob-任务ID.timer` | 本模块为已创建任务生成的定时器。 |
| `/etc/systemd/system/hwjob-任务ID.service` | 对应的执行服务。 |
| `/run/hardware-taskd/control.sock` | 普通网页与 root 服务之间的本机通信入口。 |
| `/etc/hardware-task-admin.json` | 管理密码的盐与散列,仅 root 可读。 |
| `/var/lib/hardware-taskd/tasks.sqlite3` | 任务定义、执行元数据和审计。 |
| `/var/lib/hardware-taskd/scripts/` | 脚本内容模式的执行快照。 |
| `/var/log/hardware-taskd/` | 每次执行的输出日志,仅经管理登录读取。 |
| `/var/lib/hardware-dashboard/monitor.sqlite3` | 原来的性能和硬件日志数据库,继续保留。 |
| `/root/hardware-task-admin-login.txt` | 初始登录信息的服务器副本,仅 root 可读。 |
原有 `hwlog` 命令、硬件采集器及性能历史继续运行。本模块只管理自己创建的 `hwjob-*` 单元,不接管现有 crontab、系统 timer 或其他面板任务。
## 9. 常用维护命令
```bash
# 查看网页服务与任务服务
systemctl status hardware-dashboard.service hardware-taskd.service
# 查看任务管理服务自身报错
journalctl -u hardware-taskd.service -n 50 --no-pager
# 查看本模块的全部系统定时器
systemctl list-timers --all 'hwjob-*.timer'
# 查看某个任务的系统层日志,任务 ID 可从页面或数据库查出
# 将下方 32 位 ID 替换成实际值,不要把说明文字直接输入命令
systemctl status hwjob-0123456789abcdef0123456789abcdef.service
# 重启网页服务:不会取消已启用的任务
systemctl restart hardware-dashboard.service
# 重启任务管理服务:已有网页登录会失效,系统定时器仍独立运行
systemctl restart hardware-taskd.service
```
忘记任务管理密码时,可通过 SSH 以 root 执行以下命令。新密码在交互输入,不放进命令行参数:
```bash
python3 /opt/hardware-dashboard/taskd.py password
systemctl restart hardware-taskd.service
```
只停止网页或任务管理进程,不等于停用计划。要停止后续执行,应先在页面将对应任务停用;需要立即中断时再使用“停止本次”。
## 10. 备份与回退
本次更新前的网页代码备份:`/opt/hardware-dashboard-backup.vNWXhLKD/`。
任务数据库与监控数据库分开。备份正在运行的 SQLite 应使用 SQLite 的备份接口,不能只复制主文件而忽略 WAL。备份任务数据库、管理密码散列和脚本/日志时,应继续保持 root 专用权限。
如需回退界面,先在页面停用所有不再需要的任务,再停止任务管理服务并恢复代码。只恢复旧网页代码不会自动删除或停用已经生成的系统定时器。
## 11. 已完成的验证
### 自动化测试
16 项本地和服务器测试通过,覆盖:
- 原监控数据、问题合并、时钟回拨及过期状态。
- 每日/每周/每月/间隔周期与 Cron 字段解析。
- Cron 日期和星期的 OR 语义、非法范围和步长。
- 任务名称与脚本内容不会成为 systemd 单元配置指令。
- 未登录和错误 CSRF 不会调用特权任务操作。
- 修改密码使所有旧会话失效。
- 日志读取量上限与路径编号验证。
### 服务器真实执行验证
使用明确命名的“部署验证”任务,确认了:
| 验证内容 | 实际结果 |
|---|---|
| 手动打印命令 | 成功,退出码 0。 |
| 指定日期自动触发 | 在计划时间由 systemd 自动执行,记录为 `scheduled`,退出码 0。 |
| Python 脚本内容 | 成功,输出 `PYTHON_TASK_OK`。 |
| 服务器已有文件执行 | 成功,输出 `EXISTING_FILE_OK`。 |
| 主动返回退出码 7 | 正确显示失败,保留错误输出。 |
| 10 秒超时 | 约 10 秒后停止,显示超时。 |
| 手动停止等待任务 | 正确停止并记录状态。 |
| 编辑、启用和停用 | 页面与系统定时器状态一致。 |
| 未登录、错误来源、错误 CSRF | 分别被 HTTP 401/403 拒绝。 |
这些测试仅打印信息或短暂等待,没有执行重启、关机、证书续签或修改现有业务配置。测试计划在交付前移除,历史中保留有“部署验证”字样的执行记录。
## 12. 显卡数量与夜间节电(2026 年 11 月 9 日)
### 显卡数量
在“设备总览”的“计算显卡”卡片中,选择“1 张显卡”或“2 张显卡”,再点击“保存”。使用已有任务管理员账号登录后,设置保存到 `/etc/hardware-watch.json`,刷新网页、重启服务和重启服务器后仍有效。多个浏览器共用服务器上的设置。
保存会更新 `expected_gpu_count` 并清空 `expected_gpu_uuids`。监控随后按 NVIDIA 显卡数量检查,不再要求原来的 P100 UUID;可更换为 V100 等受当前驱动支持的 NVIDIA 显卡。温度、供电、驱动异常、PCIe 通信错误继续检测。选择 2 张而实际仅检测到 1 张时,仍会报缺卡。
页面通过 `/api/tasks/gpu-count` 调用已有 root 任务代理,沿用管理员会话、CSRF 和来源检查。普通网页进程仍无权写入 `/etc`。配置变更记录到任务管理审计表,同时触发一次硬件采集;页面每 10 秒刷新。
配置变更不会删除历史告警。旧缺卡和旧 UUID 告警随采集恢复保留在历史中,不应再作为当前故障显示。
### 每天 00:00 关机、06:00 开机
页面“定时任务”中使用名称“夜间节电:00:00 关机,06:00 开机”。执行周期为北京时间每天 `00:00`,命令为:
```bash
/usr/bin/python3 /opt/hardware-dashboard/scripts/night-power-cycle.py
```
脚本先用 `rtcwake --utc --mode no` 设置当天北京时间 06:00 的 RTC 唤醒,并回读 `/sys/class/rtc/rtc0/wakealarm` 核对时间。确认成功后调用 `systemctl --no-block poweroff` 正常关机。服务器的硬件时钟采用 UTC;不要求存在 `/etc/adjtime`。
脚本仅允许在北京时间 00:00 至 00:04 执行关机,避免白天点击“执行一次”时意外关机。若缺少 RTC 接口、设置闹钟失败或回读时间不符,本轮任务报错并保持开机。每天的任务均重新设置闹钟。系统定时器使用 `Persistent=false`,开机后不会补执行错过的关机任务。
仅检查环境和下次唤醒时间:
```bash
/usr/bin/python3 /opt/hardware-dashboard/scripts/night-power-cycle.py --check
```
这个检查不设置闹钟,也不执行关机。真正开机由主板完成,需要 BIOS 支持并启用关机状态 RTC 唤醒,且电源持续接通。00:00 正在运行的计算任务会中断;关机期间无法访问监控网页。RTC 闹钟设置成功不能证明主板一定能从关机状态唤醒,完整效果应在首次夜间周期后确认。
需要全天运行时,在页面停用这项计划。停用会取消后续关机调度,已设置的 RTC 闹钟仍可能保留。新增任务的“夜间节电”模板默认停用;如已有启用的同名任务,应编辑已有任务,避免重复添加。
本次在 192.168.1.37 已创建并启用一项夜间节电任务。首次关机计划为北京时间 `2026-11-10 00:00`,RTC 闹钟已设置并回读确认为 `2026-11-10 06:00`。未在部署时执行实际关机。原代码、原显卡配置和任务数据库的备份位于 `/root/hardware-dashboard-backups/20261109-183339/`。
### 新增文件与验证
- `scripts/night-power-cycle.py`:RTC 设置、时间回读、关机及只读检查。
- `tests/test_power_cycle.py`:验证时区、跨日、白天禁止关机、闹钟失败或不匹配时不关机。
- `tests/test_tasks.py`:增加显卡数量输入校验、登录与 CSRF、其他配置保留及关机任务识别检查。
技术栈仍为 Python 标准库、HTML、CSS、原生 JavaScript、SQLite 和 systemd;RTC 唤醒使用系统已有的 util-linux `rtcwake`。部署时需同步新增的 `scripts/` 目录,无需安装前端构建工具或第三方 Python 包。
本次本地和服务器端均通过 22 项自动化测试,前端脚本语法检查通过。实际网页已验证单卡显示 `1/1`、无缺卡告警、仅显示一张显卡卡片;390 像素手机视口无横向溢出。登录后保存 1/2 张显卡及夜间模板填写流程使用浏览器模拟接口验证,没有执行真实关机。RTC 闹钟已在真实设备上设置并回读,完整关机唤醒周期尚待首次计划执行后验证。