提交 5cdbc480 authored 作者: 陈泽健's avatar 陈泽健

docs(prd): 更新服务监测、服务自检、远程容器更新需求文档计划执行文档

- 删除了远程容器升级需求文档
- 完全重写了服务监测需求文档,增加了详细的日志暴涨检测、ERROR日志聚合、
  资源监测等功能说明
- 扩展了服务自检需求文档,添加了Windows和Linux双版本的详细对比说明
- 更新了功能需求、技术规范和验收标准等内容
- 统一了文档格式和术语说明,提高了文档的可读性和完整性
上级 65a4b306
......@@ -19,7 +19,7 @@
.NOTES
作者: 自动化运维团队
版本: 1.0.0
版本: 1.0.3
============================================================
依赖说明(零安装,开箱即用):
......
......@@ -20,7 +20,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ✅ 确保相对路径(./linux_x86_adb/adb 等)以脚本目录为基准
cd "$SCRIPT_DIR"
SCRIPT_VERSION="1.0.0"
SCRIPT_VERSION="1.0.3"
SSH_TIMEOUT=30 # 占位:与 ps1 一致,这里不使用
LOG_DIR="$SCRIPT_DIR/logs"
......
# 服务检测需求说明文档
## 📋 概述
本脚本主要用于在自动化运行期间的服务器监测,用于检测在自动化运行期间的服务器时间的服务信息。
### 背景
目前针对系统服务进行自检,需要区分两种平台环境:
- **新统一平台**:使用 `/data/` 目录结构
- **传统平台**:使用 `/var/www/` 目录结构
## 🎯 功能实现总览
### 服务监测 (`AutomatedServiceMonitoring.sh`)
脚本路径:E:\GithubData\自动化\ubains-module-test\AuxiliaryTool\ScriptTool\自动化服务监测\AutomatedServiceMonitoring.sh
#### 检测需求
##### 1、平台识别(✅ 已实现):
新统一平台:基于 /data/services 目录存在性检测
传统平台:当 /data/services 目录不存在时自动识别为传统平台
##### 2、系统识别(✅ 已实现):
自动检测目标服务器的系统类型(检测容器分为三种:ujava、upython、upython_voice,如果有ujava则有会议预定系统、python对应运维集控系统、upython_voice对应转录系统)
##### 3、服务监测(未完成):
3.1、日志审计(✅ 已实现):
- 根据平台类型持续审计日志信息在某个时间段是否存在暴涨的情况:
每次采样获取日志总行数 total_lines 和采样时间 now_ts。
与上次采样 last_total、last_ts 比较,得到 elapsed 和 delta_lines。
只有当 elapsed ≥ window_seconds(默认 300 秒)时进行判定:
若 delta_lines ≥ min_lines_threshold(默认 1000 行),判定为暴涨。
或 delta_lines/elapsed ≥ rate_threshold_per_sec(默认 5 行/秒),判定为暴涨。
记录暴涨时间段为 [上次采样时间 ~ 本次采样时间],分别用服务器 date -d @epoch 转成人类可读时间。
- 通过监测ubains-ERROR.log记录最近1小时内的ERROR日志信息的时间段,补充对于对内与对外日志的同时监测。
新平台:
预定系统:
1、2.0对内后端日志:
路径:/data/services/api/java-meeting/java-meeting2.0
日志打印:tail -f logs/ubains-INFO-AND-ERROR.log
2、3.0对内后端日志:
路径:/data/services/api/java-meeting/java-meeting3.0
日志打印:tail -f logs/ubains-INFO-AND-ERROR.log
传统平台:
预定系统:
1、2.0对内后端日志:
路径:/var/www/java/api-java-meeting2.0
日志打印:tail -f logs/ubains-INFO-AND-ERROR.log
3.2、内存资源消耗(✅ 已实现):
根据平台类型持续监测服务器内存占用情况,记录峰值与峰值时的时间点,以及记录平均值。
3.3、mysql连接数(✅ 已实现):
根据平台类型持续监测mysql连接数在某个时间段是否存在暴涨,或是异常一直没断开的连接数。
先检查mysql容器名称,然后通过进入mysql容器内部进行查询,mysql账号为root,密码为dNrprU&2S
3.4、emqx连接数(未实现):
根据平台类型持续监测EMQX连接数量峰值、平均值,以及是否存在暴涨情况,或是判断一直没断开的异常连接。
3.5、容器信息检测(✅ 已实现):
针对当前服务器上存在的运行和未运行的容器进行查询检测,分别记录运行的容器信息和未运行的容器信息,需要列出容器信息。
3.6、硬盘空间检测(待实现):
针对当前服务器上存在的硬盘空间使用情况进行检测,分别记录硬盘使用率峰值和平均值,以及是否存在硬盘使用率异常过高的情况。详细列出硬盘的分布情况。
##### 4、监测日志审计(✅ 已实现):
需要丰富日志体系,日志需要用中文打印
##### 5、监测报告输出(✅ 已实现):
1、将本次监测的所有检测项信息输出成markdown格式报告,内容排版需要优美,markdown格式报告发送于钉钉。
2、将本次监测的所有检测项信息输出成word格式报告,内容排版优美,word格式报告发送于邮件。
##### 6、对接邮件发送(✅ 已实现):
需要能实现将当前的监测后输出的md文件报告,以邮件的形式发送给czj@huazhaochina.com
##### 7、对接钉钉发送:
需要能实现将当前的监测后输出的报告,调用钉钉发送给群消息。
参考代码:E:\GithubData\自动化\ubains-module-test\新统一平台\Base\base.py中的dingding_send_message函数来实现
## 规范获取
代码规范:Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md
问题总结:Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md
方法总结:Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md
文档规范:Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md
测试规范:Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md
\ No newline at end of file
# _PRD_服务监测需求文档
> 版本:V1.0
> 更新日期:2026-01-28
> 适用范围:自动化服务监测脚本
> 实现脚本:`AuxiliaryTool\ScriptTool\自动化服务监测\AutomatedServiceMonitoring.sh`
---
## 1. 背景与目标
### 1.1 背景
服务器运行的核心服务(ujava/upython/upython_voice)会产生大量日志,同时需要持续监控内存、MySQL连接、硬盘空间等资源使用情况。为降低运维成本,需要实现自动化服务监测工具,定期生成监测报告并发送通知。
### 1.2 目标
实现一套自动化服务监测脚本,具备:
- 自动识别平台类型(新统一平台/传统平台)
- 自动识别系统类型(meeting/ops/transcription)
- 日志暴涨检测与告警
- ERROR日志聚合分析(按时间段)
- 资源监测(内存/MySQL连接/硬盘空间)
- 容器信息采集
- Markdown报告生成与邮件通知
---
## 2. 总体范围
### 2.1 纳入监测对象
#### 2.1.1 平台类型
1. **新统一平台**
- 检测标志:存在 `/data/services` 目录
2. **传统平台**
- 检测标志:不存在 `/data/services` 目录
#### 2.1.2 系统容器类型
| 容器名 | 说明 | 系统标识 |
|--------|------|----------|
| ujava* | Java 服务容器(会议预定) | meeting |
| upython* | 运维集控系统 | ops |
| upython_voice* | 语音转写系统 | transcription |
#### 2.1.3 日志路径映射
**新统一平台:**
| 系统 | 日志路径 |
|------|----------|
| meeting-2.0 | /data/services/api/java-meeting/java-meeting2.0/logs/ubains-INFO-AND-ERROR.log |
| meeting-3.0 | /data/services/api/java-meeting/java-meeting3.0/logs/ubains-INFO-AND-ERROR.log |
**传统平台:**
| 系统 | 日志路径 |
|------|----------|
| meeting-2.0 | /var/www/java/api-java-meeting2.0/logs/ubains-INFO-AND-ERROR.log |
### 2.2 不在本期范围(可扩展)
- 实时监控告警(钉钉/邮件/短信)
- 服务性能指标监控(响应时间、吞吐量)
- 自动扩缩容策略
- 多服务器批量监测
---
## 3. 术语说明
| 术语 | 说明 |
|------|------|
| 平台类型 | `new`(新统一平台)或 `legacy`(传统平台) |
| 系统类型 | 容器名称对应的服务类型:meeting/ops/transcription |
| 时间戳 | 格式 `YYYY-MM-DD HH:MM:SS``YYYYMMDD_HHmmss` |
| 日志暴涨窗口 | 监测日志打印速率的时间窗口(默认300秒) |
| ERROR聚合间隔 | 相邻ERROR日志时间间隔≤60秒归为同一时间段 |
| 钉钉签名 | HMAC-SHA256签名算法(加签模式) |
---
## 4. 功能需求
### 4.1 依赖检查
#### 4.1.1 必需依赖
- `bash`:Shell执行环境(4.0+)
- `docker`:容器操作
- `mailx`/`mail`:邮件发送
- `mysql`/`mysqladmin`:MySQL客户端
- `df`:磁盘空间检查
- `awk`/`sed`/`grep`:文本处理
#### 4.1.2 可选依赖
- `curl`:钉钉通知(已注释)
- `openssl`:钉钉签名计算
#### 4.1.3 包管理器检测
自动检测系统包管理器(yum/apt)用于安装mailx和sendmail
---
### 4.2 平台与系统识别
#### 4.2.1 平台类型检测
```bash
[ -d /data/services ] && echo 'NEW_PLATFORM' || echo 'OLD_PLATFORM'
```
| 类型 | 判定条件 | 基路径 |
|------|----------|--------|
| new | 存在 /data/services 目录 | /data/services |
| legacy | 不存在 /data/services 目录 | /var/www |
#### 4.2.2 系统类型检测
通过Docker容器名称匹配:
| 容器匹配 | 系统标识 | 说明 |
|---------|---------|------|
| `ujava` | meeting | 会议预定系统 |
| `upython`(非voice) | ops | 运维集控系统 |
| `upython_voice` | transcription | 语音转写系统 |
---
### 4.3 日志暴涨审计
#### 4.3.1 监测参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
| 窗口时间 | 300秒 | 统计时间窗口 |
| 行数阈值 | 1000行 | 最小新增行数阈值 |
| 速率阈值 | 5行/秒 | 打印速率阈值 |
#### 4.3.2 监测流程
1. **初始化阶段**:首次执行记录总行数和时间戳
2. **累积阶段**:等待窗口期内收集数据
3. **判定阶段**:窗口期满后计算速率
4. **告警触发**:满足以下任一条件即触发
- 新增行数 ≥ 1000
- 速率 ≥ 5行/秒
#### 4.3.3 状态值
| 状态 | 说明 |
|------|------|
| INIT | 初始化窗口,首次记录基线数据 |
| COLLECTING | 数据采集中,窗口未满 |
| BURST | 检测到日志暴涨 |
| OK | 未检测到异常 |
| NO_FILE | 日志文件不存在 |
| UNKNOWN | 无法获取总行数 |
#### 4.3.4 判定逻辑
```bash
# 计算增量行数
delta_lines = total_lines - last_total
# 计算时间跨度(秒)
elapsed = now_ts - last_ts
# 计算速率(行/秒)
rate = delta_lines / elapsed
# 判定条件
if (delta_lines >= 1000) || (rate >= 5) then
status = "BURST"
else
status = "OK"
fi
```
---
### 4.4 ERROR日志监测
#### 4.4.1 监测参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
| ERROR_TAIL_LINES | 5000 | 扫描最后N行 |
| ERROR_GROUP_GAP_SECONDS | 60 | 相邻错误≤60s归为同段 |
| ERROR_LOOKBACK_SECONDS | 3600 | 最近1小时窗口 |
| ERROR_PRINT_MAX_LINES_PER_RANGE | 10 | 每段最多打印行数 |
| ERROR_MAX_RANGES_IN_REPORT | 5 | 最多展示时间段数 |
#### 4.4.2 监测流程
1. **扫描日志**:读取最后N行日志
2. **提取时间戳**:支持 `YYYY-MM-DD``YYYY/MM/DD` 格式
3. **时间过滤**:仅保留最近1小时内的记录
4. **时间排序**:按时间戳升序排序
5. **聚合分组**:相邻错误≤60秒归为同一时间段
6. **样例保存**:每个时间段保存最多N行样例
7. **输出报告**:只输出最近N个时间段
#### 4.4.3 时间戳提取
**支持格式:**
- `YYYY-MM-DD HH:MM:SS`
- `YYYY/MM/DD HH:MM:SS`
- `YYYY-MM-DDTHH:MM:SS`
**提取正则:**
```bash
grep -Eo '20[0-9]{2}[-/][0-9]{2}[-/][0-9]{2}[ T][0-9]{2}:[0-9]{2}:[0-9]{2}'
```
#### 4.4.4 聚合逻辑
```
1. 读取日志行并提取时间戳转换为epoch
2. 过滤最近1小时内的记录
3. 按时间升序排序
4. 遍历记录,相邻时间间隔≤60秒 → 同时间段
5. 每个时间段记录:start|end|count|sample_file
6. 只输出最近N个时间段
7. 每个时间段最多打印M行样例
```
#### 4.4.5 日志分类
| 分类 | 说明 |
|------|------|
| 对内日志 | meeting后端服务日志(ubains-ERROR.log) |
| 对外日志 | meeting对外服务日志(与对内同目录或nginx日志) |
---
### 4.5 资源监测
#### 4.5.1 内存监测
**监测方式:**
```bash
cat /proc/meminfo | awk '/^MemTotal:/ {print $2}' # 总内存(KB)
cat /proc/meminfo | awk '/^MemAvailable:/ {print $2}' # 可用内存(KB)
```
**统计指标:**
- 当前使用量(MB)
- 平均使用量(MB)
- 峰值使用量(MB)
- 峰值发生时间
#### 4.5.2 MySQL连接数监测
**连接数获取方式(多级回退):**
1. **优先级1**:容器内mysql命令
```bash
docker exec mysql_container mysql -u{user} -p{password} -e "SHOW STATUS LIKE 'Threads_connected';"
```
2. **优先级2**:容器内mysqladmin
```bash
docker exec mysql_container mysqladmin status | grep 'Threads:'
```
3. **优先级3**:本机mysqladmin
```bash
mysqladmin status | grep 'Threads:'
```
4. **优先级4**:本机mysql SQL查询
```bash
mysql -u{user} -p{password} -e "SHOW STATUS LIKE 'Threads_connected';"
```
**MySQL连接暴涨监测:**
| 参数 | 默认值 | 说明 |
|------|--------|------|
| 窗口时间 | 300秒 | 统计时间窗口 |
| 连接增量阈值 | 200 | 最小新增连接数阈值 |
| 速率阈值 | 1/秒 | 连接速率阈值 |
**判定条件:**
- 增量连接数 ≥ 200 或
- 速率 ≥ 1/秒
#### 4.5.3 硬盘空间监测
**监测方式:**
```bash
df -P / | awk 'NR==2 {gsub(/%/,"",$5); print $5}' # 根分区/使用率
```
**统计指标:**
- 当前使用率(%)
- 平均使用率(%)
- 峰值使用率(%)
- 峰值发生时间
**告警阈值:**
- 90%:触发WARN级别告警
---
### 4.6 容器信息采集
#### 4.6.1 运行中容器
**采集命令:**
```bash
docker ps --format '{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}'
```
#### 4.6.2 未运行容器
**采集命令:**
```bash
docker ps -a --filter 'status=exited' --format '{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}'
# 补充其它非Up状态容器
docker ps -a --format '{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}' | grep -v 'Up '
```
#### 4.6.3 表格化输出
| 容器ID | 名称 | 镜像 | 状态 | 创建时间 |
|--------|------|------|------|----------|
---
### 4.7 报告生成
#### 4.7.1 报告格式
Markdown格式
#### 4.7.2 报告路径
```
./monitor_reports/monitor_report_{主机名}_{时间戳}.md
```
#### 4.7.3 报告内容结构
```
# 自动化服务监测报告
- 生成时间、主机名、平台类型、系统识别
## 一、日志审计概览(仅暴涨情况)
### 日志:meeting-2.0
### 日志:meeting-3.0
## 二、ERROR日志监测(最近1小时,按时间段聚合)
### 2.1 对内日志(meeting 后端)
### 2.2 对外日志
## 三、内存资源消耗
- 当前/平均/峰值
## 四、MySQL连接数监测
- 当前/平均/峰值
- 暴涨状态
## 五、硬盘空间检测
- 当前/平均/峰值使用率
## 六、容器信息检测
### 6.1 运行中的容器
### 6.2 未运行的容器
```
---
### 4.8 邮件发送
#### 4.8.1 邮件依赖安装
**自动安装流程:**
1. 检测包管理器(yum/apt)
2. 安装mailx/mailutils
3. 生成/etc/mail.rc配置文件
#### 4.8.2 SMTP配置
**配置文件:** `/etc/mail.rc`
**配置内容:**
```bash
set from="发件人邮箱"
set smtp="smtps://smtp.exmail.qq.com:465"
set smtp-auth=login
set smtp-auth-user="发件人邮箱"
set smtp-auth-password="密码"
set ssl-verify=ignore
```
#### 4.8.3 邮件标题
**格式:** `【{前缀}】{服务器IP} - {时间戳}`
**示例:** `【内部服务器监测】192.168.5.48 - 2026-01-28 10:00:00`
#### 4.8.4 邮件发送
**发送命令:**
```bash
mailx -s "{标题}" {收件人} < {报告文件}
```
---
### 4.9 钉钉通知(已注释)
#### 4.9.1 钉钉机器人配置
| 配置项 | 说明 |
|--------|------|
| DINGDING_ACCESS_TOKEN | 机器人access_token |
| DINGDING_SECRET | 加签密钥 |
| DINGDING_AT_MOBILES | @手机号列表(逗号分隔) |
| DINGDING_AT_ALL | 是否@所有人(true/false) |
#### 4.9.2 Webhook URL构造
**签名计算流程:**
1. 构造签名字符串:`timestamp + "\n" + secret`
2. HMAC-SHA256计算并Base64编码
3. URL编码(RFC3986)
4. 拼接Webhook URL
**URL编码规则:**
1. 字母/数字/`.-_/~` 保留
2. 空格 → `+`
3. 其它字符(包括+/=/)都转成 `%XX`
#### 4.9.3 消息发送
**消息类型:** text
**消息体格式:**
```json
{
"msgtype": "text",
"text": {
"content": "消息内容"
},
"at": {
"atMobiles": ["手机号1", "手机号2"],
"isAtAll": false
}
}
```
---
## 5. 主流程
```
┌─────────────────────────────────────────────────────────────┐
│ 自动化服务监测流程(单次执行) │
└─────────────────────────────────────────────────────────────┘
1. [启动] 脚本初始化
2. [环境准备] 安装mailx、配置SMTP、安装sendmail
3. [平台识别] 检测/data/services目录
│ ├─ 存在 → 新统一平台 (/data/services)
│ └─ 不存在 → 传统平台 (/var/www)
4. [系统识别] 识别Docker容器(ujava/upython/upython_voice)
5. [日志审计] 日志暴涨检测(300秒窗口、1000行/5行秒)
6. [资源监测]
│ ├─ 内存监测(/proc/meminfo)
│ ├─ MySQL连接数+暴涨监测
│ ├─ 硬盘空间监测(根分区/)
│ └─ 容器信息采集
7. [ERROR日志] 最近1小时ERROR聚合分析(≤60s分组)
8. [报告生成] Markdown格式报告
9. [邮件发送] 发送报告到指定邮箱
10. [钉钉通知] 发送摘要到钉钉群(已注释)
11. [结束] 本次监测完成
```
---
## 6. 配置与可变项
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| MYSQL_USER | root | MySQL用户名 |
| MYSQL_PASSWORD | - | MySQL密码(需配置) |
| LOG_FILE | ./AutomatedServiceMonitoring.sh.log | 日志文件路径 |
| REPORT_DIR | ./monitor_reports | 报告目录 |
| HOST_NAME | $(hostname) | 主机名(可通过环境变量覆盖) |
| MAIL_TO | - | 收件人邮箱(逗号分隔,需配置) |
| MAIL_SUBJECT_PREFIX | 【内部服务器监测】 | 邮件标题前缀(可覆盖) |
| MAIL_SMTP_HOST | smtp.exmail.qq.com | SMTP服务器 |
| MAIL_SMTP_PORT | 465 | SMTP端口 |
| MAIL_SMTP_USER | - | SMTP用户名(需配置) |
| MAIL_SMTP_PASS | - | SMTP密码(需配置) |
| DINGDING_ACCESS_TOKEN | - | 钉钉机器人token(已注释) |
| DINGDING_SECRET | - | 钉钉加签密钥(已注释) |
| ERROR_TAIL_LINES | 5000 | ERROR日志扫描行数 |
| ERROR_GROUP_GAP_SECONDS | 60 | ERROR聚合间隔 |
| ERROR_LOOKBACK_SECONDS | 3600 | ERROR时间窗口 |
| ERROR_PRINT_MAX_LINES_PER_RANGE | 10 | 每段样例行数 |
| ERROR_MAX_RANGES_IN_REPORT | 5 | 输出时间段数 |
---
## 7. 异常处理与容错要求
### 7.1 通用异常处理
1. **命令执行失败**:记录到日志,不中断主流程
2. **邮件发送失败**:记录ERROR但报告已落盘
3. **Docker不可用**:跳过容器检测,记录INFO
4. **文件不存在**:记录NO_FILE状态,继续其他监测
5. **解析失败**:记录ERROR,使用默认值或N/A
### 7.2 MySQL连接多级回退
- 容器内mysql命令 → 容器内mysqladmin → 本机mysqladmin → 本机mysql SQL
- 任一方式成功即返回结果
### 7.3 日志时间戳解析容错
- 支持 `-``/` 两种日期分隔符
- 支持 `T` 和空格两种日期时间分隔符
- 解析失败时跳过该行
---
## 8. 交付物
### 8.1 脚本文件
1. **主脚本**: `AuxiliaryTool\ScriptTool\自动化服务监测\AutomatedServiceMonitoring.sh`
### 8.2 输出文件
1. **日志文件**: `./AutomatedServiceMonitoring.sh.log`
2. **监测报告**: `./monitor_reports/monitor_report_{主机名}_{时间戳}.md`
### 8.3 外部依赖
- **必需**:docker, mailx/mail, mysql/mysqladmin
- **可选**:curl, openssl(钉钉通知用)
---
## 9. 验收标准
### 9.1 基础功能
1. 脚本可正常执行,无语法错误
2. 日志正确输出到LOG_FILE
3. 平台类型自动检测正确
4. 系统容器识别正确(ujava/upython/upython_voice)
### 9.2 日志审计功能
1. 日志目标路径正确解析
2. 日志暴涨监测正常(300秒窗口、1000行/5行秒)
3. 状态正确记录(INIT/COLLECTING/BURST/OK/NO_FILE/UNKNOWN)
4. 暴涨时触发WARN级别日志
### 9.3 ERROR日志监测
1. 时间戳提取正确(支持多种格式)
2. 最近1小时窗口过滤正确
3. 时间段聚合正确(≤60秒间隔)
4. 最多展示5个时间段
5. 每段最多打印10行样例
### 9.4 资源监测
1. 内存监测正确(当前/平均/峰值)
2. MySQL连接数获取正确(多级回退)
3. MySQL连接暴涨监测正常
4. 硬盘使用率监测正确
5. 90%阈值告警触发
### 9.5 容器检测
1. 运行中容器列表正确
2. 未运行容器列表正确
3. Docker不可用时正确跳过
4. 表格格式输出正确
### 9.6 报告生成
1. Markdown报告正确生成
2. 报告文件命名正确(主机名+时间戳)
3. 报告章节完整(6个章节)
4. 表格格式正确
### 9.7 邮件发送
1. 主机名自动获取正确
2. 邮件标题包含IP+时间戳
3. mailx/mail发送成功
4. SMTP配置自动生成
5. 发送失败时记录ERROR但不终止
### 9.8 钉钉通知(可选)
1. Webhook URL构造正确
2. 签名计算正确
3. text消息发送成功
4. @人功能正常
---
## 10. 需求规范
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
# 计划执行_自动化服务监测脚本开发
> 版本:V1.0
> 创建日期:2026-01-28
> 基于文档:`_PRD_服务监测需求文档.md`
> 交付物:`AutomatedServiceMonitoring.sh`
---
## 一、任务概述
开发一套自动化服务监测脚本,实现对服务器资源的单次执行监测,包括日志审计、内存/MySQL/硬盘资源监测、容器状态检测、ERROR日志聚合分析,并生成Markdown报告发送邮件通知。
---
## 二、开发阶段划分
### 阶段一:基础框架搭建(优先级:高)
| 序号 | 任务 | 描述 | 预计产出 |
|------|------|------|----------|
| 1.1 | 创建脚本文件 | 创建 `AutomatedServiceMonitoring.sh` | 空壳脚本 |
| 1.2 | 实现日志函数 | `log()` 函数,统一日志格式 | 日志工具 |
| 1.3 | 定义全局配置 | 主机名、日志文件、报告目录等配置变量 | 配置段 |
| 1.4 | 邮件通知配置 | 收件人、SMTP配置、钉钉配置 | 通知配置 |
| 1.5 | 依赖检测函数 | `detect_os_and_pkg_mgr()` | 包管理器识别 |
**日志格式规范:**
```bash
[YYYY-MM-DD HH:MM:SS] [LEVEL] 消息内容
```
**全局配置:**
```bash
MYSQL_USER="root"
MYSQL_PASSWORD="密码"
LOG_FILE="./AutomatedServiceMonitoring.sh.log"
REPORT_DIR="./monitor_reports"
HOST_NAME="${HOST_NAME_OVERRIDE:-$(hostname)}"
MAIL_TO="收件人邮箱"
MAIL_SUBJECT_PREFIX="${MAIL_SUBJECT_PREFIX:-自动化服务监测报告}" # 默认值,可通过环境变量覆盖
```
---
### 阶段二:平台与系统识别(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 2.1 | 平台类型检测 | `detect_platform()` | 检测/data/services目录 |
| 2.2 | 系统容器识别 | `detect_systems()` | 识别ujava/upython/upython_voice |
| 2.3 | 基路径设置 | 根据平台设置BASE_PATH | 新平台/data/services,旧平台/var/www |
**平台检测逻辑:**
```bash
if [ -d "/data/services" ]; then
PLATFORM_TYPE="new"
BASE_PATH="/data/services"
else
PLATFORM_TYPE="legacy"
BASE_PATH="/var/www"
fi
```
**系统容器识别:**
```bash
# 通过容器名前缀识别系统类型
# ujava → meeting(会议预定)
# upython → ops(运维管理)
# upython_voice → transcription(语音转写)
```
---
### 阶段三:依赖安装与邮件配置(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 3.1 | mailx自动安装 | `ensure_mailx_installed()` | yum/apt-get自动安装 |
| 3.2 | SMTP配置生成 | `ensure_mailx_smtp_config()` | 生成/etc/mail.rc |
| 3.3 | sendmail安装 | `ensure_sendmail_installed()` | postfix安装 |
| 3.4 | mailx兼容性检测 | `mailx_supports_a_header()` | 检测-a参数支持 |
**依赖安装逻辑:**
```bash
# 检测包管理器(yum/apt)
if command -v yum >/dev/null 2>&1; then
yum install -y mailx
elif command -v apt-get >/dev/null 2>&1; then
apt-get install -y mailutils
fi
```
**SMTP配置(/etc/mail.rc):**
```bash
set from="发件人邮箱"
set smtp="smtps://smtp.exmail.qq.com:465"
set smtp-auth=login
set smtp-auth-user="发件人邮箱"
set smtp-auth-password="密码"
set ssl-verify=ignore
```
---
### 阶段四:钉钉通知功能(优先级:中)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 4.1 | Webhook URL构造 | `build_dingtalk_url()` | access_token+timestamp+sign |
| 4.2 | URL编码实现 | 手动实现RFC3986编码 | 字母/数字/.-_/~保留,空格→+ |
| 4.3 | 消息发送 | `send_dingtalk_markdown()` | text类型消息 |
| 4.4 | @人支持 | at_mobiles+at_all | 支持@指定人 |
**钉钉签名计算:**
```bash
# 1. 构造签名字符串
string_to_sign="${timestamp}"$'\n'"${secret}"
# 2. HMAC-SHA256计算
sign_raw="$(printf '%s' "$string_to_sign" | openssl dgst -sha256 -hmac "$secret" -binary | openssl base64)"
# 3. URL编码(自定义实现)
sign_enc="$(url_encode "$sign_raw")"
# 4. 拼接Webhook URL
url="https://oapi.dingtalk.com/robot/send?access_token=${access_token}&timestamp=${timestamp}&sign=${sign_enc}"
```
---
### 阶段五:日志暴涨审计(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 5.1 | 日志目标解析 | `resolve_log_targets()` | 根据平台+系统确定日志路径 |
| 5.2 | 日志键构造 | `make_log_key()` | sys_name\|log_path格式 |
| 5.3 | 暴涨监测 | `monitor_log_burst_once()` | 300秒窗口检测 |
**日志暴涨监测逻辑:**
```bash
# 监测参数
window_seconds=300 # 时间窗口:5分钟
min_lines_threshold=1000 # 最小行数阈值
rate_threshold_per_sec=5 # 速率阈值:5行/秒
# 监测步骤
1. 初始化:首次执行记录总行数和时间戳
2. 累积:等待窗口期内收集数据
3. 判定:窗口期满后计算速率
4. 告警:满足以下任一条件即触发
- 新增行数 >= 1000
- 速率 >= 5行/秒
```
**日志路径映射:**
| 平台 | 系统 | 日志路径 |
| --- | --- | --- |
| 新平台 | meeting-2.0 | /data/services/api/java-meeting/java-meeting2.0/logs/ubains-INFO-AND-ERROR.log |
| 新平台 | meeting-3.0 | /data/services/api/java-meeting/java-meeting3.0/logs/ubains-INFO-AND-ERROR.log |
| 旧平台 | meeting-2.0 | /var/www/java/api-java-meeting2.0/logs/ubains-INFO-AND-ERROR.log |
---
### 阶段六:ERROR日志监测(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 6.1 | 时间戳提取 | `extract_epoch_from_log_line()` | 支持YYYY-MM-DD/YYYY/MM/DD格式 |
| 6.2 | 日志扫描 | `scan_ubains_error_log_to_md()` | 最近N行+1小时窗口 |
| 6.3 | 时间段聚合 | 相邻错误≤60s归为同段 | 按时间戳分组 |
| 6.4 | 报告写入 | `write_error_log_section_to_report()` | Markdown格式输出 |
**ERROR日志聚合参数:**
```bash
ERROR_TAIL_LINES=5000 # 扫描最后5000行
ERROR_GROUP_GAP_SECONDS=60 # 相邻错误≤60s归为同段
ERROR_LOOKBACK_SECONDS=3600 # 最近1小时窗口
ERROR_PRINT_MAX_LINES_PER_RANGE=10 # 每段最多打印10行
ERROR_MAX_RANGES_IN_REPORT=5 # 最多展示5个时间段
```
**聚合逻辑:**
```
1. 扫描日志文件最后N行
2. 提取时间戳并转换为epoch
3. 过滤最近1小时内的记录
4. 按时间排序
5. 相邻错误时间间隔≤60s → 同时间段
6. 每个时间段记录:start|end|count|sample_file
7. 只输出最近N个时间段
8. 每段最多打印M行样例
```
---
### 阶段七:资源监测(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 7.1 | 内存监测 | `monitor_mem_once()` | /proc/meminfo解析 |
| 7.2 | MySQL监测 | `monitor_mysql_once()` | 连接数+连接暴涨 |
| 7.3 | 硬盘监测 | `monitor_disk_once()` | 根分区使用率 |
**内存监测逻辑:**
```bash
# 从/proc/meminfo获取
total_kb=$(cat /proc/meminfo | awk '/^MemTotal:/ {print $2}')
avail_kb=$(cat /proc/meminfo | awk '/^MemAvailable:/ {print $2}')
# 计算使用量(MB)
used_mb=$(( (total_kb - avail_kb) / 1024 ))
# 统计
MEM_SAMPLES+=1
MEM_SUM_USED_MB=$((MEM_SUM_USED_MB + used_mb))
MEM_PEAK_USED_MB=max(MEM_PEAK_USED_MB, used_mb)
```
**MySQL连接暴涨监测:**
```bash
# 获取连接数
conn=$(docker exec mysql_container mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -e "SHOW STATUS LIKE 'Threads_connected';" | tail -n1 | cut -f2)
# 暴涨检测(与日志暴涨类似)
window_seconds=300
min_burst_conn=200
rate_threshold_per_sec=1
# 判定条件:增量>=200 或 速率>=1/秒
```
**硬盘空间监测:**
```bash
# 取根分区/使用率
used_pct=$(df -P / | awk 'NR==2 { gsub(/%/,"",$5); print $5 }')
# 阈值:90%
if (( used_pct >= 90 )); then
log WARN "[硬盘监测] 根分区(/)使用率偏高:当前=${used_pct}%"
fi
```
---
### 阶段八:容器信息检测(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 8.1 | 运行中容器采集 | `collect_container_info()` | docker ps格式化输出 |
| 8.2 | 未运行容器采集 | Exited+非Up状态 | docker ps -a过滤 |
| 8.3 | 表格化输出 | Markdown表格格式 | ID\|名称\|镜像\|状态\|创建时间 |
**容器信息采集:**
```bash
# 运行中容器
docker ps --format '{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}'
# 未运行容器(Exited/非Up)
docker ps -a --filter 'status=exited' --format '{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}'
```
---
### 阶段九:报告生成(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 9.1 | 报告目录创建 | `mkdir -p "$REPORT_DIR"` | 自动创建 |
| 9.2 | 报告文件命名 | `monitor_report_${HOST_NAME}_${file_ts}.md` | 包含主机名和时间戳 |
| 9.3 | Markdown报告写入 | `write_md_report()` | 分章节写入 |
| 9.4 | HTML转换 | `md_to_html_simple()` | awk+sed转换 |
**报告结构:**
```
# 自动化服务监测报告
- 生成时间、主机名、平台类型、系统识别
## 一、日志审计概览
### 日志:meeting-2.0
- 日志路径
- 日志暴涨状态
- 日志暴涨详情
## 二、ERROR日志监测(最近1小时)
### 2.1 对内日志(meeting后端)
### 2.2 对外日志
## 三、内存资源消耗
- 当前/平均/峰值
## 四、MySQL连接数监测
- 当前/平均/峰值
- 暴涨状态
## 五、硬盘空间检测
- 当前/平均/峰值使用率
## 六、容器信息检测
### 6.1 运行中的容器
### 6.2 未运行的容器
```
---
### 阶段十:邮件发送(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 10.1 | 主IP获取 | `get_primary_ip()` | 路由出口优先 |
| 10.2 | 邮件标题构造 | 包含IP+时间戳 | 【内部】192.168.x.x - YYYY-MM-DD HH:MM:SS |
| 10.3 | 纯文本邮件发送 | `send_report_mail()` | mailx/mailx发送 |
| 10.4 | HTML转换支持 | `md_to_html_simple()` | 可选HTML邮件 |
**主IP获取逻辑:**
```bash
# 方法1:通过路由默认出口
ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++){if($i=="src"){print $(i+1); exit}}}')
# 方法2:回退到hostname -I
if [[ -z "$ip" ]]; then
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
fi
```
**邮件发送逻辑:**
```bash
# 使用mailx(优先)或mail(回退)
if command -v mailx >/dev/null 2>&1; then
mailx -s "$subject" $to < "$report_md"
else
mail -s "$subject" $to < "$report_md"
fi
```
---
### 阶段十一:主流程集成(优先级:高)
| 序号 | 任务 | 描述 | 执行顺序 |
|------|------|------|----------|
| 11.1 | 主函数框架 | `main_run_once()` | - |
| 11.2 | 环境准备 | 依赖安装+SMTP配置 | 步骤0 |
| 11.3 | 平台系统识别 | detect_platform+detect_systems | 步骤1-2 |
| 11.4 | 资源监测执行 | 内存+MySQL+硬盘+容器 | 步骤3-6 |
| 11.5 | 报告生成 | write_md_report() | 步骤7 |
| 11.6 | 邮件发送 | send_report_mail() | 步骤8 |
**主流程伪代码:**
```bash
main_run_once() {
log "[启动] 自动化服务监测脚本(单次执行模式)"
# 步骤0: 环境准备
ensure_mailx_installed
ensure_mailx_smtp_config
ensure_sendmail_installed
# 步骤1-2: 平台与系统识别
detect_platform
detect_systems
# 步骤3: 日志暴涨审计
resolve_log_targets
for each log_target; do
monitor_log_burst_once
done
# 步骤4-6: 资源监测
monitor_mem_once
monitor_mysql_once
collect_container_info
monitor_disk_once
# 步骤7: 报告生成
report_file=$(write_md_report)
# 步骤8: 邮件发送
send_report_mail "$report_file"
# 钉钉通知(已注释,不执行)
# DD_TEXT="$dd_text" send_dingtalk_markdown
log "[结束] 本次监测已完成"
}
```
---
### 阶段十二:异常处理与容错(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 12.1 | MySQL回退机制 | 容器→本机命令→SQL查询 | 多层级回退 |
| 12.2 | 硬盘检测容错 | df不可用时记录ERROR | 不影响主流程 |
| 12.3 | 日志文件缺失 | 文件不存在时记录NO_FILE | 不终止监测 |
| 12.4 | 邮件发送失败 | 记录ERROR但不终止 | 报告已落盘 |
| 12.5 | Docker不可用 | 记录INFO跳过容器检测 | 继续其他监测 |
**MySQL连接回退机制:**
```bash
# 优先级1: 容器内mysql命令
conn=$(docker exec mysql_container mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -e "SHOW STATUS LIKE 'Threads_connected';")
# 优先级2: 容器内mysqladmin
conn=$(docker exec mysql_container mysqladmin status | grep -oE 'Threads:[[:space:]]*[0-9]+')
# 优先级3: 本机mysqladmin
conn=$(mysqladmin status | grep -oE 'Threads:[[:space:]]*[0-9]+')
# 优先级4: 本机mysql SQL查询
conn=$(mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -e "SHOW STATUS LIKE 'Threads_connected';")
```
---
### 阶段十三:测试与验收(优先级:高)
| 序号 | 测试场景 | 验收标准 | 测试方法 |
|------|----------|----------|----------|
| 13.1 | 脚本执行 | 无语法错误,日志正常输出 | 直接执行脚本 |
| 13.2 | 平台识别 | 正确识别新/旧平台 | 在两类服务器上执行 |
| 13.3 | 日志暴涨 | 满足条件时触发WARN | 模拟日志暴涨 |
| 13.4 | 内存监测 | 正确计算当前/平均/峰值 | 对比free命令 |
| 13.5 | MySQL监测 | 正确获取连接数 | docker exec/mysqladmin对比 |
| 13.6 | 硬盘监测 | 正确获取根分区使用率 | df命令对比 |
| 13.7 | 容器检测 | 正确列出运行/未运行容器 | docker ps对比 |
| 13.8 | ERROR日志聚合 | 正确聚合时间段 | 构造测试日志 |
| 13.9 | 邮件发送 | 收到Markdown格式邮件 | 检查邮箱 |
| 13.10 | 钉钉通知 | Webhook调用成功 | 检查钉钉群 |
---
## 三、文件结构
```
AutomatedServiceMonitoring.sh (1575行)
├── 全局配置段
│ ├── 数据库配置(MYSQL_USER/PASSWORD)
│ ├── 日志配置(LOG_FILE)
│ ├── 报告目录(REPORT_DIR,WORD_REPORT_DIR已定义但未使用)
│ ├── 主机名(HOST_NAME)
│ ├── 邮件通知配置(MAIL_TO/MAIL_SUBJECT_PREFIX/MAIL_SMTP_**)
│ └── 钉钉配置(DINGDING_ACCESS_TOKEN/DINGDING_SECRET/**)
├── 日志函数
│ └── log() # 统一日志输出(控制台+文件)
├── 依赖安装函数
│ ├── detect_os_and_pkg_mgr() # 检测包管理器
│ ├── ensure_mailx_installed() # 安装mailx/mailutils
│ ├── ensure_mailx_smtp_config() # 生成/etc/mail.rc
│ ├── mailx_supports_a_header() # 检测mailx -a参数支持
│ ├── ensure_sendmail_installed() # 安装postfix/sendmail
├── 钉钉通知函数
│ ├── build_dingtalk_url() # 构造Webhook URL(含签名)
│ └── send_dingtalk_markdown() # 发送text消息
├── 平台与系统识别
│ ├── detect_platform() # 检测新/旧平台
│ └── detect_systems() # 识别ujava/upython/upython_voice
├── 日志暴涨审计
│ ├── resolve_log_targets() # 解析日志目标路径
│ ├── make_log_key() # 构造日志键
│ └── monitor_log_burst_once() # 单次暴涨监测
├── ERROR日志监测
│ ├── extract_epoch_from_log_line() # 提取时间戳epoch
│ ├── get_meeting_service_dirs() # 获取meeting服务目录
│ ├── get_meeting_internal_error_logs() # 对内日志列表
│ ├── get_meeting_external_error_logs() # 对外日志列表
│ ├── scan_ubains_error_log_to_md() # 扫描并聚合ERROR
│ └── write_error_log_section_to_report() # 写入报告
├── 资源监测函数
│ ├── monitor_mem_once() # 内存监测
│ ├── monitor_mysql_once() # MySQL连接数+暴涨监测
│ ├── find_mysql_container() # 查找MySQL容器
│ ├── get_mysql_threads_connected_via_container() # 容器内获取连接数
│ ├── monitor_disk_once() # 硬盘空间监测
│ ├── get_root_disk_used_pct() # 获取根分区使用率
│ └── get_disk_used_pct_max() # 获取最大使用率
├── 容器信息检测
│ └── collect_container_info() # 采集运行中/未运行容器
├── 工具函数
│ ├── get_primary_ip() # 获取本机主IP
│ └── md_to_html_simple() # Markdown转HTML
├── 邮件发送
│ └── send_report_mail() # 发送邮件报告
└── 主流程
└── main_run_once() # 主函数
```
---
## 四、依赖与规范
### 4.1 依赖文档
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
### 4.2 外部依赖
**必需依赖:**
- `bash`:Shell执行环境
- `docker`:容器操作(容器监测、MySQL容器查找)
- `mysql`/`mysqladmin`:MySQL客户端(本机MySQL连接)
- `df`:磁盘空间检查
- `awk`/`sed`/`grep`:文本处理
**可选依赖:**
- `mailx`/`mail`:邮件发送
- `curl`:钉钉通知(已注释)
- `openssl`:钉钉签名计算
**平台兼容:**
- 支持新统一平台(/data/services)
- 支持传统平台(/var/www)
---
## 五、验收清单
### 5.1 基础功能
- [ ] 脚本可正常执行,无语法错误
- [ ] 日志正确输出到LOG_FILE
- [ ] 平台类型自动检测正确
- [ ] 系统容器识别正确(ujava/upython/upython_voice)
### 5.2 日志审计功能
- [ ] 日志目标路径正确解析
- [ ] 日志暴涨监测正常(300秒窗口、1000行/5行秒)
- [ ] 状态正确记录(INIT/COLLECTING/BURST/OK/NO_FILE/UNKNOWN)
- [ ] 暴涨时触发WARN级别日志
### 5.3 ERROR日志监测
- [ ] 时间戳提取正确
- [ ] 最近1小时窗口过滤正确
- [ ] 时间段聚合正确(≤60秒间隔)
- [ ] 最多展示5个时间段
- [ ] 每段最多打印10行样例
### 5.4 资源监测
- [ ] 内存监测正确(当前/平均/峰值)
- [ ] MySQL连接数获取正确(容器→本机多级回退)
- [ ] MySQL连接暴涨监测正常
- [ ] 硬盘使用率监测正确
- [ ] 90%阈值告警触发
### 5.5 容器检测
- [ ] 运行中容器列表正确
- [ ] 未运行容器列表正确
- [ ] Docker不可用时正确跳过
- [ ] 表格格式输出正确
### 5.6 报告生成
- [ ] Markdown报告正确生成
- [ ] 报告文件命名正确(主机名+时间戳)
- [ ] 报告章节完整(6个章节)
- [ ] 表格格式正确
### 5.7 邮件发送
- [ ] 主IP自动获取正确
- [ ] 邮件标题包含IP+时间戳
- [ ] mailx/mail发送成功
- [ ] SMTP配置自动生成
- [ ] 发送失败时记录ERROR但不终止
### 5.8 钉钉通知(已注释)
- [ ] Webhook URL构造正确
- [ ] 签名计算正确
- [ ] text消息发送成功
- [ ] @人功能正常
- [ ] 注:主流程中钉钉发送代码已注释(main_run_once函数第1524-1569行),如需启用请取消注释
---
## 六、部署说明
### 6.1 文件放置
```
自动化服务监测/
└── AutomatedServiceMonitoring.sh
```
### 6.2 权限设置
```bash
chmod +x AutomatedServiceMonitoring.sh
```
### 6.3 配置修改
**必须修改的配置:**
```bash
# 数据库密码
MYSQL_PASSWORD="your_mysql_password"
# 邮件收件人
MAIL_TO="recipient1@example.com,recipient2@example.com"
# SMTP配置
MAIL_SMTP_USER="your_email@example.com"
MAIL_SMTP_PASS="your_email_password"
```
**可选修改的配置:**
```bash
# 主机名覆盖(默认使用hostname)
export HOST_NAME_OVERRIDE="custom-hostname"
# 邮件标题前缀
export MAIL_SUBJECT_PREFIX="【自定义】"
```
### 6.4 执行方式
**单次执行:**
```bash
./AutomatedServiceMonitoring.sh
```
**后台执行:**
```bash
nohup ./AutomatedServiceMonitoring.sh >/dev/null 2>&1 &
```
**定时执行(crontab):**
```bash
# 每小时执行一次
0 * * * * /path/to/AutomatedServiceMonitoring.sh
# 每30分钟执行一次
*/30 * * * * /path/to/AutomatedServiceMonitoring.sh
```
### 6.5 报告查看
- Markdown报告:`./monitor_reports/monitor_report_<hostname>_<timestamp>.md`
- 脚本日志:`./AutomatedServiceMonitoring.sh.log`
---
## 七、关键实现要点
### 7.1 MySQL连接多级回退
**需求描述:**
优先从容器内获取连接数,失败则依次回退到本机命令。
**实现代码:**
```bash
get_mysql_threads_connected_via_container() {
local container="$1"
local auth="-u${MYSQL_USER} -p${MYSQL_PASSWORD}"
# 方法1: 容器内mysql命令
local out
out="$(docker exec -i "${container}" mysql -ss ${auth} -e "SHOW STATUS LIKE 'Threads_connected';" 2>/dev/null | tail -n1 | cut -f2)"
if [[ "$out" =~ ^[0-9]+$ ]]; then
echo "$out"
return 0
fi
# 方法2: 容器内mysqladmin
out="$(docker exec -i "${container}" mysqladmin ${auth} status 2>/dev/null)"
local val
val="$(echo "$out" | grep -oE 'Threads:[[:space:]]*[0-9]+' | awk '{print $2}')"
if [[ "$val" =~ ^[0-9]+$ ]]; then
echo "$val"
return 0
fi
return 1
}
```
---
### 7.2 时间戳提取兼容多种格式
**需求描述:**
支持 YYYY-MM-DD HH:MM:SS 和 YYYY/MM/DD HH:MM:SS 格式。
**实现代码:**
```bash
extract_epoch_from_log_line() {
local line="$1"
local dt
# 提取日期时间(兼容-和/分隔符)
dt="$(echo "$line" | grep -Eo '20[0-9]{2}[-/][0-9]{2}[-/][0-9]{2}[ T][0-9]{2}:[0-9]{2}:[0-9]{2}' | head -n 1)"
[[ -z "$dt" ]] && return 1
# 统一成date命令格式:YYYY-MM-DD HH:MM:SS
dt="${dt//\//-}" # 替换/为-
dt="${dt/T/ }" # 替换T为空格
date -d "$dt" +%s 2>/dev/null
}
```
---
### 7.3 钉钉签名URL编码
**需求描述:**
严格对齐Python的urllib.parse.quote_plus,实现RFC3986 URL编码。
**编码规则:**
1. 字母/数字/.-/_/~ 保留
2. 空格 → +
3. 其它字符(包括+/=/)都转成 %XX
**实现代码:**
```bash
sign_enc=""
local i ch hex
for ((i=0; i<${#sign_raw}; i++)); do
ch="${sign_raw:$i:1}"
case "$ch" in
[a-zA-Z0-9.~_-])
sign_enc+="$ch"
;;
' ')
sign_enc="+"
;;
*)
printf -v hex '%%%02X' "'$ch"
sign_enc+="$hex"
;;
esac
done
```
---
### 7.4 ERROR日志时间段聚合
**需求描述:**
将最近1小时内的ERROR日志按时间间隔≤60秒聚合为时间段。
**聚合逻辑:**
```bash
# 初始化
range_start=0
range_end=0
last_ts=0
printed=0
while IFS='|' read -r ts line; do
if (( range_start == 0 )); then
# 第一个有效记录
range_start=$ts
range_end=$ts
last_ts=$ts
printed=0
continue
fi
local gap=$(( ts - last_ts ))
if (( gap <= ERROR_GROUP_GAP_SECONDS )); then
# 同一时间段
range_end=$ts
last_ts=$ts
# 记录样例(最多N行)
if (( printed < ERROR_PRINT_MAX_LINES_PER_RANGE )); then
echo "$line" >> "$range_lines_tmp"
printed=$((printed + 1))
fi
else
# 新时间段:flush旧时间段
# 保存元数据:start|end|count|sample_file
# 继续处理下一行
fi
done
```
---
### 7.5 硬盘空间稳定采集方案
**需求描述:**
为保证稳定性,只采集根分区/的使用率,避免复杂解析导致失败。
**实现代码:**
```bash
get_root_disk_used_pct() {
if ! command -v df >/dev/null 2>&1; then
return 1
fi
# df -P 输出稳定,Use%在第5列
df -P / 2>/dev/null | awk 'NR==2 { gsub(/%/,"",$5); print $5 }'
}
```
---
## 八、进度跟踪
| 阶段 | 状态 | 完成时间 | 备注 |
|------|------|----------|------|
| 阶段一:基础框架搭建 | ✅ 已完成 | 2026-01-28 | 日志函数、全局配置、依赖检测 |
| 阶段二:平台与系统识别 | ✅ 已完成 | 2026-01-28 | 平台检测、系统容器识别 |
| 阶段三:依赖安装与邮件配置 | ✅ 已完成 | 2026-01-28 | mailx自动安装、SMTP配置生成 |
| 阶段四:钉钉通知功能 | ✅ 已完成(已注释) | 2026-01-28 | Webhook构造、签名计算、消息发送;主流程中已注释不调用 |
| 阶段五:日志暴涨审计 | ✅ 已完成 | 2026-01-28 | 300秒窗口、速率阈值检测 |
| 阶段六:ERROR日志监测 | ✅ 已完成 | 2026-01-28 | 1小时窗口、时间段聚合 |
| 阶段七:资源监测 | ✅ 已完成 | 2026-01-28 | 内存/MySQL/硬盘监测 |
| 阶段八:容器信息检测 | ✅ 已完成 | 2026-01-28 | 运行中/未运行容器采集 |
| 阶段九:报告生成 | ✅ 已完成 | 2026-01-28 | Markdown报告+HTML转换 |
| 阶段段十:邮件发送 | ✅ 已完成 | 2026-01-28 | 主IP获取、邮件发送 |
| 阶段十一:主流程集成 | ✅ 已完成 | 2026-01-28 | 单次执行主流程 |
| 阶段十二:异常处理与容错 | ✅ 已完成 | 2026-01-28 | MySQL回退、容错处理 |
| 阶段十三:测试与验收 | ⬜ 待执行 | - | 功能测试、邮件发送测试 |
**脚本规模统计:**
| 脚本 | 行数 | 主要功能 |
|------|------|----------|
| AutomatedServiceMonitoring.sh | 1575行 | 自动化服务监测脚本(单次执行版) |
| **合计** | **1575行** | 完整实现 |
---
## 九、重要注意事项
### 9.1 与monitor_inner_api_services.sh的区别
| 特性 | AutomatedServiceMonitoring.sh | monitor_inner_api_services.sh |
| --- | --- | --- |
| 主要用途 | 资源监测+报告生成 | 服务监测+自愈重启 |
| 执行模式 | 单次执行 | 定时执行(crontab) |
| 监测对象 | 日志暴涨+ERROR日志+资源+容器 | 平台API+容器内服务+端口 |
| 输出方式 | Markdown报告+邮件 | 日志文件 |
| 平台支持 | 新平台+传统平台 | 仅预定系统 |
### 9.2 开发顺序建议
1. **先开发基础框架**:日志函数、全局配置
2. **再开发监测功能**:日志暴涨、ERROR聚合、资源监测
3. **最后开发通知功能**:邮件发送、钉钉通知
### 9.3 关键技术点
1. **日志暴涨检测**:使用300秒滑动窗口,行数+速率双重阈值
2. **ERROR日志聚合**:时间戳提取+时间间隔聚合
3. **MySQL多级回退**:容器→本机命令→SQL查询
4. **钉钉签名计算**:HMAC-SHA256+Base64+自定义URL编码
5. **Markdown转HTML**:awk+sed轻量转换器
6. **平台自动识别**:检测/data/services目录
7. **资源统计**:使用全局变量累计计算平均/峰值
### 9.4 测试策略
1. **单元测试**:每个监测函数独立测试
2. **集成测试**:完整流程联调测试
3. **平台测试**:新平台和旧平台分别测试
4. **邮件测试**:验证邮件发送和格式
5. **ERROR日志测试**:构造测试日志验证聚合逻辑
### 9.5 钉钉通知说明
钉钉通知功能已完整实现(`build_dingtalk_url()``send_dingtalk_markdown()`),但在 `main_run_once()` 主流程中已注释(第1524-1569行)。如需启用钉钉通知,请取消相关代码的注释。
---
*本文档基于 AutomatedServiceMonitoring.sh 脚本分析生成,如有疑问请参考源脚本。*
# 服务自检需求说明文档
## 📋 概述
本脚本主要用于服务自检,用于检测服务器上的服务是否正常启动,并返回结果。
### 背景
目前针对系统服务进行自检,需要区分两种平台环境:
- **新统一平台**:使用 `/data/` 目录结构
- **传统平台**:使用 `/var/www/` 目录结构
## 🎯 功能实现总览
### 服务自检 (`check_server_health.ps1`和`check_server_health.sh`)
powershell脚本路径:AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.ps1
shell脚本路径:AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.sh
#### 检测需求
##### 1、SSH连接(✅ 已实现):
功能描述:
负责与目标服务器建立 SSH 连接,支持多种连接方式,作为所有后续检测和修复操作的基础。(sh脚本不需要做连接!!!)
具体要求:
1)支持两种接入方式:
- 预设服务器列表:从本地配置中读取服务器信息(IP / 端口 / 用户名 / 密码),供用户选择;
- 手动输入模式:由用户在脚本运行时手动输入服务器 IP、端口、用户名、密码。
2)连接前进行基本参数校验(IP 格式、端口范围等),避免明显的输入错误。
3)连接失败时,需要在日志中记录详细原因(认证失败 / 超时 / 网络不可达等),并给出友好提示。
##### 2、平台识别(✅ 已实现):
功能描述:
在成功连接目标服务器后,自动识别其所属平台类型,用于后续按平台选择不同的检测路径和目录结构。
识别规则:
- 新统一平台:
- 特征:存在 `/data/services` 目录;
- 目录结构主要以 `/data/` 为根路径。
- 传统平台:
- 特征:不存在 `/data/services` 目录;
- 目录结构主要以 `/var/www/` 为根路径。
具体要求:
1)识别结果需要在日志中明确打印(例如:“平台类型:新统一平台”或“平台类型:传统平台”)。
2)将平台类型保存到全局变量或上下文中,供后续检测函数统一使用(如文件路径、容器路径等)。
3)若无法明确识别(目录缺失或权限不足),需给出清晰的错误提示,并中止依赖平台判断的后续检测。
##### 3、系统识别(✅ 已实现):
功能描述:
在完成平台识别后,对目标服务器上的业务系统类型进行识别,
主要通过容器信息判断当前服务器是否部署了会议预定系统、运维集控系统、转录系统等。
识别规则(基于容器):
- ujava 容器存在:
- 判断宿主机的目录是否存在:/var/www/java/unifiedPlatform目录,如果存在则归类为统一平台系统,否则为会议预定系统;
- upython 容器存在:
- 说明:部署了运维集控系统(Python CMDB / 运维相关服务);
- upython_voice 容器存在:
- 说明:部署了转录系统(语音转写 / 语音相关服务)。
具体要求:
1)通过 `docker ps` / `docker ps -a` 等命令获取容器列表,根据容器名称包含 `ujava`、`upython`、`upython_voice` 进行判断。
2)在日志中打印当前服务器安装的系统类型组合,例如:
- “检测到系统类型:会议预定系统 + 运维集控系统”;
- “检测到系统类型:仅部署会议预定系统”。
3)识别结果用于后续有条件的检测步骤:
- 若无 ujava 容器,则跳过会议预定系统相关检测;
- 若无 upython 容器,则跳过运维集控系统相关检测;
- 若无 upython_voice 容器,则跳过转录系统相关检测。
##### 4、服务进程检测(✅ 已实现):
根据平台类型不同需要分别在不同的位置进行检测,具体如下:
###### 新统一平台(✅ 已实现):
ujava后端服务分为容器内和容器外
需进入ujava2容器内检查,共有以下五个基础服务进程:
["auth"]="ubains-auth.jar"
["gateway"]="ubains-gateway.jar"
["system"]="ubains-modules-system.jar"
["meeting2.0"]="java-meeting2.0/ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
["meeting3.0"]="java-meeting3.0/ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
["mqtt"]="ubains-meeting-mqtt-1.0-SNAPSHOT.jar"
["quartz"]="ubains-meeting-quartz-1.0-SNAPSHOT.jar"
["message"]="ubains-meeting-message-scheduling-1.0-SNAPSHOT.jar"
在宿主机上路径:
/data/services/api/java-meeting/java-meeting-extapi目录下的ubains-meeting-api-1.0-SNAPSHOT.jar进程
异常修复:
如果是宿主机上ubains-meeting-api-1.0-SNAPSHOT.jar进程没有启动的话,也需要进行远程修复:
对外服务修复函数名称:fix_external_service_disconnect
功能描述:上传当前目录下的issue_handler.sh脚本,将脚本上传到目标服务器,并且通过传入的修复函数,调用issue_handler对应的函数来修复。修复完成后需进行复检。
upython后端服务需进入upython容器内检查,共有以下4个端口:
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 14/memcached
tcp 0 0 127.0.0.1:36917 0.0.0.0:* LISTEN 38/uwsgi
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 38/uwsgi
tcp6 0 0 :::11211 :::* LISTEN 14/memcached
upython_voice后端服务需进入upython_voice容器内检查,共有以下:
tcp 0 0 127.0.0.1:39573 0.0.0.0:* LISTEN 114/uwsgi
tcp 0 0 0.0.0.0:1883 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 114/uwsgi
tcp 0 0 0.0.0.0:9001 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 79/memcached
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 47/nginx: master pr
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 47/nginx: master pr
tcp6 0 0 :::1883 :::* LISTEN -
tcp6 0 0 :::11211 :::* LISTEN 79/memcached
tcp6 0 0 :::80 :::* LISTEN 47/nginx: master pr
###### 传统平台(✅ 已实现):
ujava后端服务分为容器内和容器外
需进入ujava2容器内检查,共有以下两个基础服务进程:
root 8 1 0 15:26 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
ubains-meeting-inner-api-1.0-SNAPSHOT.jar
在宿主机路径:/var/www/java/external-meeting-api的进程:
ubains-meeting-api-1.0-SNAPSHOT.jar
异常修复:
如果是宿主机上ubains-meeting-api-1.0-SNAPSHOT.jar进程没有启动的话,也需要进行远程修复:
对外服务修复函数名称:fix_external_service_disconnect
功能描述:上传当前目录下的issue_handler.sh脚本,将脚本上传到目标服务器,并且通过传入的修复函数,调用issue_handler对应的函数来修复。修复完成后需进行复检。
upython后端服务在容器内:
tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN 101/nginx: master p
tcp 0 0 127.0.0.1:37817 0.0.0.0:* LISTEN 124/uwsgi
tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN 101/nginx: master p
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 124/uwsgi
tcp 0 0 0.0.0.0:8002 0.0.0.0:* LISTEN 14/httpd
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 105/memcached
tcp6 0 0 :::11211 :::* LISTEN 105/memcached
##### 5、DNS解析问题(✅ 已实现):
功能描述:
检测目标服务器当前的 DNS 配置是否正常,能否进行域名解析。
当解析异常时,参考 NTP 远程修复的方式,自动执行修复脚本(无需区分新统一平台或传统平台)。
具体要求:
1)通过常用命令(如 nslookup / dig / ping 域名等)验证 DNS 是否可用;
2)当检测到解析失败或严重异常时:
- 在日志中详细记录异常表现(如“无法解析域名”、“超时”、“无可用 DNS 服务器”等);
- 调用 Upload_the_repair_script,远程执行 issue_handler.sh 中的 DNS 修复函数;
3)修复完成后,重新进行一次 DNS 解析测试,并将复检结果记录到日志和自检报告中:
- 成功:标记为“DNS 解析修复成功”;
- 失败:标记为“DNS 解析仍异常,需要人工排查”。
##### 6、服务器资源分析(✅ 已实现):
功能描述:
综合检测目标服务器的资源与基础环境状态,包括:
- 磁盘空间使用情况
- 内存使用情况
- CPU 使用情况
- 防火墙开放端口情况
- 服务器硬件架构与操作系统版本信息
防火墙修复:
1)修复函数名称:Upload_the_repair_script
- 该函数仅负责上传并调用 issue_handler.sh 中的具体防火墙修复函数(如 fix_port_access)。
2)检测与判定规则:
- 若目标服务器未开启防火墙服务:
- 判定为异常,记录到日志和报告中;
- 自动调用防火墙修复脚本尝试开启并配置必要端口。
- 若防火墙已开启,根据系统角色进行端口校验:
- 会议预定系统:
- 必须开放端口:22、443、1883、8306
- 运维集控系统:
- 必须开放端口:22、8443、1883、8306
- 任一必需端口未开放时,判定为异常,需要执行修复脚本。
3)修复执行与结果记录:
- 由 Upload_the_repair_script 远程执行 issue_handler.sh 中的防火墙修复函数,自动添加缺失端口规则;
- 修复完成后不强制要求再次自动复检,但需在日志中明确打印修复执行结果,
包括:尝试执行的命令、成功/失败状态及可能的错误原因,便于后续人工确认。
##### 7、服务日志导出(✅ 已实现):
将目标服务器上的服务日志采集,需判断传统平台还是新统一平台
传统平台:
如果有ujava容器:
1、将/var/www/java/api-java-meeting2.0/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出来命名为:对内后端_ubains-INFO-AND-ERROR.log
2、将/var/www/java/external-meeting-api/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出来命名为:对外后端_ubains-INFO-AND-ERROR.log
3、将nginx日志导出:
- 步骤1:通过docker cp将/usr/local/nginx/logs路径下的error.log日志文件拷贝到宿主机上
- 步骤2:再将宿主机上的这份error.log日志文件命名改为nginx_error.log导出到本地
如果有upython容器:
1、将/var/www/html/log目录下的error.log、uinfo.log和uwsgi.log日志文件导出来命名都增加前缀:运维集控_error.log、运维集控_uinfo.log和运维集控_uwsgi.log
2、将nginx日志导出:
- 步骤1:通过docker cp将/usr/local/nginx/logs路径下的error.log日志文件拷贝到宿主机上
- 步骤2:再将宿主机上的这份error.log日志文件命名改为nginx_error.log导出到本地
新统一平台:
如果有ujava容器:
1、将/data/services/api/auth/auth-sso-auth目录下的log.out日志文件导出来命名为auth_log.out
2、将/data/services/api/auth/auth-sso-gatway目录下的log.out日志文件导出来命名为gatway_log.out
3、将/data/services/api/auth/auth-sso-system目录下的log.out日志文件导出命名为system_log.out
4、将/data/services/api/java-meeting/java-meeting2.0/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为对内2.0_ubains-INFO-AND-ERROR.log
5、将/data/services/api/java-meeting/java-meeting3.0/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为对内3.0_ubains-INFO-AND-ERROR.log
6、将/data/services/api/java-meeting/java-meeting-extapi/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为对外服务_ubains-INFO-AND-ERROR.log
7、将/data/services/api/java-meeting/java-message-scheduling/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为信息调度_ubains-INFO-AND-ERROR.log
8、将/data/services/api/java-meeting/java-mqtt/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为MQTT_ubains-INFO-AND-ERROR.log
9、将/data/services/api/java-meeting/java-quartz/logs目录下的ubains-INFO-AND-ERROR.log日志文件导出命名为定时任务_ubains-INFO-AND-ERROR.log
##### 8、配置文件IP检测(✅ 已实现)
检测目标服务器上的配置文件中的IP地址配置是否正确为目标服务器IP,需判断传统平台还是新统一平台
传统平台:
如果有ujava容器:
1、判断系统为会议预定系统,还是统一平台系统
- 预定系统:
- 检测/var/www/java/api-java-meeting2.0/config目录下的yml、properties、js格式的配置文件中的IP地址
- 检测/var/www/java/external-meeting-api/config目录下的yml、properties、js格式的配置文件中的IP地址
- 检测/var/www/java/ubains-web-2.0/static目录下的config.json配置文件的IP地址
- 检测/var/www/java/ubains-web-admin/static目录下的config.json配置文件的IP地址
- 检测/var/www/java/ubains-web-h5/static/h5目录下的config.js或config.json配置文件的IP地址
- 检测/var/www/java/nginx-conf.d目录下的conf配置文件中的IP地址
- 统一平台系统:
- 检测/var/www/java/api-java-meeting2.0/config目录下的yml、properties、js格式的配置文件中的IP地址
- 检测/var/www/java/external-meeting-api/config目录下的yml、properties、js格式的配置文件中的IP地址
- 检测/var/www/java/unifiedPlatform/ubains-web-2.0/static目录下的config.json配置文件的IP地址
- 检测/var/www/java/unifiedPlatform/ubains-web-admin/static目录下的config.json配置文件的IP地址
- 检测/var/www/java/unifiedPlatform/nginx-conf.d目录下的conf配置文件中的IP地址
如果有upython容器:
1、检测/var/www/html目录下的conf配置文件的IP地址
2、检测/var/www/html/nginx-conf目录下的conf配置文件的IP地址
3、检测/var/www/html/web-vue-rms/static文件夹下的config.json配置文件的IP地址
4、检测/var/www/html/web-vue-h5/static/h5目录下的config.js或config.json配置文件的IP地址
新统一平台:
如果有ujava容器:
1、检测/data/services/api/auth/auth-sso-auth/config目录下的yml、properties、js、json、conf格式文件中IP地址
2、检测/data/services/api/auth/auth-sso-gatway/config目录下的yml、properties、js、json、conf格式文件中IP地址
3、检测/data/services/api/auth/auth-sso-system/config目录下的yml、properties、js、json、conf格式文件中IP地址
4、检测/data/services/api/java-meeting/java-meeting2.0/config目录下的yml、properties、js、json、conf格式文件中IP地址
5、检测/data/services/api/java-meeting/java-meeting3.0/config目录下的yml、properties、js、json、conf格式文件中IP地址
6、检测/data/services/api/java-meeting/java-meeting-extapi/config目录下的yml、properties、js、json、conf格式文件中IP地址
7、检测/data/services/api/java-meeting/java-message-scheduling/config目录下的yml、properties、js、json、conf格式文件中IP地址
8、检测/data/services/api/java-meeting/java-mqtt/config目录下的yml、properties、js、json、conf格式文件中IP地址
9、检测/data/services/api/java-meeting/java-quartz/config目录下的yml、properties、js、json、conf格式文件中IP地址
如果有upython容器:
1、/data/services/api/python-cmdb目录下的yml、properties、js、json、conf格式文件中IP地址
前端配置文件检测:
1、检测/data/services/web/pc/pc-vue2-ai目录下的js、json、conf格式文件中IP地址
2、检测/data/services/web/pc/pc-vue2-backstage目录下的js、json、conf格式文件中IP地址
3、检测/data/services/web/pc/pc-vue2-editor目录下的js、json、conf格式文件中IP地址
4、检测/data/services/web/pc/pc-vue2-main目录下的js、json、conf格式文件中IP地址
5、检测/data/services/web/pc/pc-vue2-meetingControl目录下的js、json、conf格式文件中IP地址
6、检测/data/services/web/pc/pc-vue2-meetngV2目录下的js、json、conf格式文件中IP地址
7、检测/data/services/web/pc/pc-vue2-meetngV3目录下的js、json、conf格式文件中IP地址
8、检测/data/services/web/pc/pc-vue2-moniter目录下的js、json、conf格式文件中IP地址
9、检测/data/services/web/pc/pc-vue2-platform目录下的js、json、conf格式文件中IP地址
10、检测/data/services/web/pc/pc-vue2-voice目录下的js、json、conf格式文件中IP地址
中间件配置文件检测:
1、检测/data/middleware/nginx/config目录下的conf格式文件中的IP地址
注意:如果遇到有172.17.0.1、127.0.0.1的IP地址以及和目标服务器IP地址一致的需判断为正确!检测日志需要打印出配置文件的路径,不要进行修改IP的操作!将单个配置文件内的IP地址检测收集结果后再标注打印,并增加配置项地址。日志需要更简洁明了!合法地址可以不打印出来。
##### 9、服务器NTP服务检测(✅ 已实现):
功能描述:
检测目标服务器上的时间同步服务是否正常,当前主要包括 ntp 和 chronyd 两种实现。
需要确认:
1)时间同步服务是否已安装并处于运行状态;
2)目标服务器时间与北京时间是否保持一致(误差在可接受范围内)。
具体要求:
1)优先检测 chronyd / ntpd 等服务状态,判断是否处于 active / running。
2)通过 date、timedatectl 或 ntpstat 等命令获取当前系统时间和同步状态。
3)将目标服务器时间与北京时间进行对比:
- 若时间差在约定范围内(例如 ±3 分钟),视为正常;
- 若时间差超出范围,视为异常,在日志和自检报告中进行标记。
4)检测过程中只做状态和时间差的检查,不对 ntp/chronyd 配置文件做任何修改操作。
注意:
- 此检测步骤需要在“服务日志导出”之前执行,避免因时间不准导致日志分析困难。
- 本脚本仅负责检测,不自动修改 NTP 相关配置,必要的修复由人工或其他修复脚本完成。
##### 10、文件权限检测(✅ 已实现):
功能描述:
检测目标服务器上与服务运行密切相关的关键文件权限,包括:
- 数据库用户权限
- nginx 运行用户及相关文件权限
- 各服务启动脚本(run.sh / start.sh)权限
- Redis / Emqx 等中间件配置文件权限
- 系统 rc.local 启动项权限
根据平台类型(传统平台 / 新统一平台)分别检查不同路径下的文件权限。
具体检测范围:
传统平台:
如果有 ujava 容器:
1)根据类型检测对应脚本路径
- 会议预定系统:
- 检测 /var/www/java/api-java-meeting2.0 目录下 run.sh 启动脚本权限
- 检测 /var/www/java/external-meeting-api 目录下 run.sh 启动脚本权限
- 检测 /var/www/java 目录下 start.sh 启动脚本权限
- 统一平台系统:
- 检测 /var/www/java/api-java-meeting2.0 目录下 run.sh 启动脚本权限
- 检测 /var/www/java/external-meeting-api 目录下 run.sh 启动脚本权限
- 检测 /var/www/java/unifiedPlatform 目录下 start.sh 启动脚本权限
如果有 upython 容器:
1)检测 /var/www/html 目录下 start.sh 启动脚本权限
如果有 cardtable 容器:
1)检测 /var/www/wifi-local 目录下 config.ini、startDB.sh、wifi 的文件权限
如果有 paperless 容器:
1)检测 /var/www/paperless 目录下 run.sh 和 start.sh 文件权限
公共部分:
1)检测 /var/www/redis 目录下 redis-*.conf 文件权限
2)检测 /var/www/emqx 或 /var/www/emqx/config 目录下 *.conf 文件权限
新统一平台:
如果有 ujava 容器:
1)检测 /data/services/api/auth/auth-sso-auth 目录下 run.sh 文件权限
2)检测 /data/services/api/auth/auth-sso-gatway 目录下 run.sh 文件权限
3)检测 /data/services/api/auth/auth-sso-system 目录下 run.sh 文件权限
4)检测 /data/services/api/java-meeting/java-meeting2.0 目录下 run.sh 文件权限
5)检测 /data/services/api/java-meeting/java-meeting3.0 目录下 run.sh 文件权限
6)检测 /data/services/api/java-meeting/java-meeting-extapi 目录下 run.sh 文件权限
7)检测 /data/services/api/java-meeting/java-message-scheduling 目录下 run.sh 文件权限
8)检测 /data/services/api/java-meeting/java-mqtt 目录下 run.sh 文件权限
9)检测 /data/services/api/java-meeting/java-quartz 目录下 run.sh 文件权限
10)检测 /data/services/api 目录下 start.sh 文件权限
11)检测 /data/services/scripts 目录下所有 *.sh 脚本文件权限
如果有 upython 容器:
1)检测 /data/services/api/python-cmdb 目录下所有 *.sh 脚本文件权限
如果有 upython_voice 容器:
1)检测 /data/services/api/python-voice 目录下所有 *.sh 脚本文件权限
如果有 paperless 容器:
1)检测 /data/third_party/paperless 目录下 run.sh 和 start.sh 脚本文件权限
如果有 cardtable 容器:
1)检测 /data/third_party/wifi-local 目录下 config.ini、startDB.sh、wifi 的文件权限
公共部分(两种平台通用):
1)检测 /etc/rc.d/rc.local 文件权限
2)数据库用户权限:
- 进入 umysql 容器内检查;
- 数据库账号:root,密码:dNrprU&2S;
- 重点关注 ubains 和 devops 数据库对应用户及权限配置是否符合预期。
日志与执行顺序要求:
1)此检测函数需要在“服务日志导出”函数之前执行,确保日志中能完整记录权限情况。
2)main 主函数和日志记录函数中都需要补充对本检测函数的调用,并将各关键文件的权限信息打印到日志中。
3)仅做检测与记录,不在该步骤自动修改任何文件权限;具体权限调整由人工或其他修复脚本处理。
##### 11、现场数据备份(✅ 已实现):
函数名称:DataBakup
先判断目标服务器是新统一平台还是传统平台,再备份对应平台的服务包与配置文件等数据,最后在目标服务器上压缩成tar.gz格式文件导出到电脑上。
传统平台:
如果有ujava容器:
1、根据系统类型判断
- 会议预定系统:
- 将/var/www/java目录复制到/home/bakup目录下
- 统一平台系统:
- 将/var/www/java/unifiedPlatform目录复制到/home/bakup目录下
如果有upython容器:
1、将/var/www/html目录复制到/home/bakup目录下
如果有cardtable容器:
1、将/var/www/wifi-local目录复制到/home/bakup目录下
如果有paperless容器:
1、将/var/www/paperless目录复制到/home/bakup目录下
共有:
1、将/var/www/emqx和/var/www/redis目录复制到/home/bakup目录下
2、数据库备份,账号为root,密码为dNrprU&2S,数据库容器是umysql容器,数据库是ubains和devops,数据库备份完成后也复制到/home/bakup目录下
最后将/home/bakup目录压缩成tar.gz格式文件并导出,文件命名补充时间戳,导出完成后清理/home目录下的这个备份文件。
##### 12、容器信息收集(✅ 已实现):
函数名称:Test-ContainerInformation
需求描述:
查询当前服务器上所有容器信息(包含未运行与运行中的信息),可以通过docker inspect来获取MAC地址、端口映射信息、启动文件位置。
信息打印排版:先打印运行中的容器信息,再打印未运行的容器信息。在容器信息打印之间增加分隔线。
容器异常:
Redis容器:
1、日志获取:
检测
新统一平台:
路径:/data/middleware/redis/data/redis.log
传统平台:
路径:/var/www/redis/data/redis.log
将上述redis.log文件从目标服务器导出到本地自检结果目录中(例如logs/redis/子目录),并在自检日志中记录导出结果。
2、异常判定:
如果 uredis 容器未运行,并且没有其他名字中包含“redis”的运行中容器,则判定为 Redis 容器异常。
3、远端修复操作:
调用上传修复脚本函数 Upload_the_repair_script,将当前目录下的issue_handler.sh脚本上传到目标服务器,并执行其中的 redis_container_exception 修复函数。
调用示例(逻辑示意):
./issue_handler.sh --action redis_container_exception --non-interactive --yes
4、修复完成后的复检:
修复脚本执行完成后,再次检查 uredis 容器状态:
- 如果 uredis 已处于运行状态,记录“Redis容器复检成功”到自检日志和md报告中;
- 如果 uredis 仍未运行,则记录“Redis容器复检失败,需要人工排查”到自检日志和md报告中。
emqx容器:
1、日志获取:
检测
新统一平台:
路径:/data/middleware/emqx/log/emqx.log.1
传统平台:
路径:/var/www/emqx/log/emqx.log.1
将上述log文件从目标服务器导出到本地自检结果目录中(例如logs/emqx/子目录),并在自检日志中记录导出结果。
2、异常判定:
如果 uemqx 容器未运行,并且没有其他名字中包含“emqx”的运行中容器,则判定为 Emqx 容器异常。
3、远端修复操作:
调用上传修复脚本函数 Upload_the_repair_script,将当前目录下的issue_handler.sh脚本上传到目标服务器,并执行其中的 emqx_container_exception 修复函数。
调用示例(逻辑示意):
./issue_handler.sh --action emqx_container_exception --non-interactive --yes
4、修复完成后的复检:
修复脚本执行完成后,再次检查 uemqx 容器状态:
- 如果 uemqx 已处于运行状态,记录“Emqx容器复检成功”到自检日志和md报告中;
- 如果 uemqx 仍未运行,则记录“Emqx容器复检失败,需要人工排查”到自检日志和md报告中。
##### 13、定时任务查询(待实现):
函数名称:Test-ScheduledTask
需求描述:通过crontab -l检查目标服务器上的定时任务.
新统一平台:
- */3 * * * * /data/services/scripts/ujava2-startup.sh
- 0 13 * * * bash /usr/local/docker/UbainsmysqlBakUp.sh
传统平台:
公有:
- 0 13 * * * bash /usr/local/docker/UbainsmysqlBakUp.sh
ujava存在:
-
##### 14、上传修复脚本(已实现NTP和防火墙修复):
函数名称:Upload_the_repair_script
功能描述:
该函数仅负责:
1)将当前目录下的 issue_handler.sh 脚本上传到目标服务器指定目录;
2)在远端赋予执行权限并进行必要的换行符处理(如 dos2unix);
3)根据传入的修复动作名称(action),在远端调用 issue_handler.sh 中对应的修复函数。
使用方式:
- 由各检测函数在发现异常后调用 Upload_the_repair_script,并通过参数指定需要执行的修复动作。
- Upload_the_repair_script 内部会在远端执行类似如下命令:
./issue_handler.sh --action <修复函数名> [--platform <平台类型>] [--non-interactive --yes]
典型调用示例:
1)NTP 服务异常修复:
检测到目标服务器 NTP 服务配置或状态异常时:
调用 issue_handler.sh 中的 fix_ntp_config 函数:
./issue_handler.sh --action fix_ntp_config
2)防火墙端口异常修复:
检测到目标服务器防火墙未开启或必要端口未开放(如 22/443/1883/8443 等)时:
调用 issue_handler.sh 中的 fix_port_access 函数(通常以非交互方式执行):
./issue_handler.sh --action fix_port_access --non-interactive --yes
3)对外后端服务未启动修复:
检测到对外后端服务进程(ubains-meeting-api-1.0-SNAPSHOT.jar)未启动时:
调用 issue_handler.sh 中的 fix_external_service_disconnect 函数:
./issue_handler.sh --action fix_external_service_disconnect --non-interactive --yes
4)Redis 容器启动异常修复:
在容器信息收集中检测到 Redis 容器异常(uredis 未运行,且无其他 redis 容器)时:
调用 issue_handler.sh 中的 redis_container_exception 函数(以非交互方式执行,自动清理 data 并重启):
./issue_handler.sh --action redis_container_exception --non-interactive --yes
5)Emqx 容器启动异常修复(预留):
在容器信息收集中检测到 Emqx 容器异常(uemqx 未运行,且无其他 emqx 容器)时:
预期调用 issue_handler.sh 中的 emqx_container_exception 函数(同样支持非交互方式):
./issue_handler.sh --action emqx_container_exception --non-interactive --yes
说明:
- Upload_the_repair_script 不直接实现具体修复逻辑,只负责:
“上传脚本 → 远端准备 → 拼接命令行 → 触发执行”
- 具体修复行为(如修改配置、重启服务、清理数据目录等)均在 issue_handler.sh 中的各修复函数内部实现。
- 对于高风险操作(如 Redis data 目录清理),自检场景采用非交互模式(--non-interactive --yes),
手工运维场景则可直接在服务器上交互式执行 issue_handler.sh,由运维人员确认后再进行删除或修改操作。
##### 15、安卓设备的自检(✅ 已实现 check_server_health.ps1):
功能描述:
针对连接到服务器的安卓设备,执行一系列自检操作以确保设备状态正常。
主要检测项包括:
- 设备连接状态
- 日志文件收集
具体要求:
1)设备连接状态检测:
- 设备IP由手动输入,端口默认为5555.连接指令为:adb connect <device_ip>:<port>
- 使用 adb 命令连接设备,并检查连接状态。
- 若设备未连接或连接异常,记录错误信息并终止后续检测。
2)日志文件收集:
- 通过指令导出日志文件目录,并使用 adb 命令导出日志文件。
- adb pull /sdcard/Android/data/com.ubains.local.gviewer/files/ 当前脚本所在目录下的 logs/android/ 子目录。
- adb pull /sdcard/Android/data/com.ubains.local.gviewer/cache/ 当前脚本所在目录下的 logs/android/ 子目录。如果没有cache目录就记录打印后跳过这个目录导出操作。
3)断开连接:
- 收集完成后需要自动断开连接,adb disconnect <device_ip>:<port>
报告输出:
- 将安卓设备自检结果整合到整体自检报告中,明确标识各检测项的状态。
- 对于异常项,提供简要说明和建议处理措施。
##### 15、安卓设备的自检(✅ 已实现 check_server_health.sh):
功能描述:
针对连接到服务器的安卓设备,执行一系列自检操作以确保设备状态正常。
主要检测项包括:
- 设备连接状态
- 日志文件收集
具体要求:
1)服务器架构判断:
- 根据服务器架构判断为X86还是ARM架构;
- 如果为X86架构,则在resolve_adb函数中将目录设置为linux_x86_adb,并且x86架构的执行文件为adb文件
- 如果为ARM架构,则在resolve_adb函数中将目录设置为linux_arm_adb,并且arm架构的可执行文件为run-adb.sh文件
2)设备连接状态检测:
- 设备IP由手动输入,端口默认为5555.连接指令为:adb connect <device_ip>:<port>
- 使用 adb 命令连接设备,并检查连接状态。
- 若设备未连接或连接异常,记录错误信息并终止后续检测。
3)日志文件收集:
- 通过指令导出日志文件目录,并使用 adb 命令导出日志文件。
- adb pull /sdcard/Android/data/com.ubains.local.gviewer/files/ 当前脚本所在目录下的 logs/android/ 子目录。
- adb pull /sdcard/Android/data/com.ubains.local.gviewer/cache/ 当前脚本所在目录下的 logs/android/ 子目录。如果没有cache目录就记录打印后跳过这个目录导出操作。
4)断开连接:
- 收集完成后需要自动断开连接,adb disconnect <device_ip>:<port>
报告输出:
- 将安卓设备自检结果整合到整体自检报告中,明确标识各检测项的状态。
- 对于异常项,提供简要说明和建议处理措施。
##### 16、服务自检报告输出(✅ 已实现):
功能描述:
将本次服务自检过程中所有检测项的执行步骤、检测结果、异常说明及修复情况统一输出为报告文件,
既包含完整日志记录,也生成便于阅读的 Markdown 自检报告。
具体要求:
1)完整日志输出:
- 将脚本执行过程中的关键信息(开始/结束时间、目标服务器信息、各检测项结果等)
持久化到日志文件(.log),便于后续排查。
- 日志内容应包含每个检测函数的开始/结束标记、检测结论和异常详情。
2)Markdown 自检报告输出:
- 生成一份结构化的自检报告(.md),按检测顺序分章节展示各检测项的结果,
包括:检测项名称、检测说明、检测结果(成功/失败/警告)、简要结论。
- 每条检测结果前需增加明显的图标标识状态:
- ✅ 表示检测通过 / 状态正常
- ⚠️ 表示存在风险 / 需关注
- ❌ 表示检测失败 / 状态异常
- 对于异常项,需要在报告中简要说明异常原因,必要时附上建议处理方向。
3)报告内容范围:
- 至少包含以下模块的检测结果:
- SSH 连接与平台识别
- 系统识别与服务进程检测
- DNS / NTP / 防火墙 / 资源使用情况
- 配置文件 IP 检测、文件权限检测
- 服务日志导出、现场数据备份
- 容器信息收集及容器异常(Redis / Emqx 等)处理结果
4)输出形式与存放位置:
- 日志文件和 Markdown 报告文件建议按服务器 IP + 时间戳命名,方便区分多次自检结果。
- 报告文件统一存放在脚本所在目录下的指定输出目录中(例如:logs/ 或 reports/ 子目录)。
5)可读性要求:
- 报告结构清晰,分级标题明确(如:一级为大模块,二级为具体检测项)。
- 对终端用户(非开发人员)也能直观理解当前服务器健康状况以及需要关注的问题。
# _PRD_服务自检需求文档-新.md
> 版本:V1.1
> 更新日期:2026-01-28
> 适用范围:服务自检脚本(Windows 远程版本 + Linux 本机版本)
> 实现脚本:
> - Windows 版:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.ps1`
> - Linux 版:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.sh`
---
## 1. 背景与目标
### 1.1 背景
会议预定系统和统一平台部署在 Linux 服务器上,核心服务运行在 Docker 容器中(ujava/upython/upython_voice)。为降低运维成本,需要实现服务健康检查工具,并自动修复常见问题。
### 1.2 目标
实现两套功能一致的服务自检脚本,具备:
- 自动识别平台类型(新统一平台/传统平台)
- 检测容器内服务进程和端口监听状态
- DNS/NTP/防火墙等系统级检测与自动修复
- 生成详细的健康检查报告(Markdown 格式)
### 1.3 双版本说明
| 版本 | 文件名 | 运行环境 | 版本号 | 说明 |
|------|--------|----------|--------|------|
| Windows 版 | check_server_health.ps1 | Windows 10+ | 1.0.3 | 通过 SSH 远程连接 Linux 服务器进行检测 |
| Linux 版 | check_server_health.sh | Linux 服务器 | 1.0.3 | 直接在服务器本机运行,无需 SSH 连接 |
**重要说明:**
- 两版本核心功能保持一致,后续功能优化需**同步更新**两个脚本
- 差异部分见下节"版本差异说明"
---
## 1.4 版本差异说明
### 1.4.1 运行环境差异
| 差异项 | Windows 版 (.ps1) | Linux 版 (.sh) |
|--------|-------------------|----------------|
| 运行位置 | Windows 端 | Linux 服务器本机 |
| 服务器连接 | 需要远程 SSH 连接 | 无需连接,直接执行本地命令 |
| 服务器选择 | 支持预设列表/手动输入 | 自动检测本机 IP |
### 1.4.2 依赖检查差异
| 差异项 | Windows 版 (.ps1) | Linux 版 (.sh) |
|--------|-------------------|----------------|
| 运行环境要求 | PowerShell 5.1+ | Bash 4.0+ |
| SSH 工具要求 | plink.exe / pscp.exe 或 sshpass | 无需 SSH 工具 |
| plink 查找优先级 | 脚本同目录 > 远程容器更新目录 > 系统 PATH | - |
| adb 工具路径 | 系统 PATH 中的 adb | 脚本同目录 `linux_x86_adb/adb` |
### 1.4.3 命令执行方式差异
| 差异项 | Windows 版 (.ps1) | Linux 版 (.sh) |
|--------|-------------------|----------------|
| 命令执行 | 通过 SSH 远程执行 | 直接执行本地命令 |
| 日志输出 | 自定义 Write-Log 函数 | exec + tee 实现实时打印和日志 |
| 日志输出目标 | 控制台 + 日志文件 | 控制台 + 日志文件(同步) |
### 1.4.4 修复脚本调用差异
| 差异项 | Windows 版 (.ps1) | Linux 版 (.sh) |
|--------|-------------------|----------------|
| 调用方式 | 通过 SSH 上传并执行远程脚本 | 直接调用同目录的 `issue_handler.sh` |
| 函数名 | `Upload_the_repair_script` | `run_issue_handler` |
| 参数格式 | `--action {action} --platform {platform}` | `--action {action} --platform {platform} --non-interactive --yes` |
### 1.4.5 日志导出差异
| 差异项 | Windows 版 (.ps1) | Linux 版 (.sh) |
|--------|-------------------|----------------|
| 导出方式 | pscp.exe 远程下载 | cp 本地复制 |
| 目标目录 | Windows 端 `output/{IP}/logs/` | 服务器端 `output/{IP}/logs/` |
### 1.4.6 安卓设备自检差异
| 差异项 | Windows 版 (.ps1) | Linux 版 (.sh) |
|--------|-------------------|----------------|
| adb 工具 | 使用 Windows 端 adb | 使用脚本同目录 `linux_x86_adb/adb` |
| 连接方式 | 从 Windows 端连接 | 从服务器端连接 |
| 日志保存 | Windows 端目录 | 服务器端目录 |
### 1.4.7 数据备份差异
| 差异项 | Windows 版 (.ps1) | Linux 版 (.sh) |
|--------|-------------------|----------------|
| 备份位置 | 远程服务器 | 本机 |
| 下载方式 | 通过 SSH 下载到本地 | 本地复制,无需下载 |
### 1.4.8 新增功能(Linux 版独有)
| 功能 | 说明 |
|------|------|
| 定时任务查询 | 检测系统定时任务(crontab)配置 |
---
## 2. 总体范围
### 2.1 纳入检测对象
#### 2.1.1 平台类型
1. **新统一平台**
- 检测标志:存在 `/data/services` 目录
2. **传统平台**
- 检测标志:不存在 `/data/services` 目录
#### 2.1.2 容器类型
| 容器名 | 说明 | 检测方式 |
|--------|------|----------|
| ujava* | Java 服务容器 | docker ps 匹配 `^ujava\d*$` |
| upython* | 运维集控系统 | docker ps 匹配 `^upython\d*$`(非 voice) |
| upython_voice* | 转录系统 | docker ps 匹配 `^upython_voice\d*$` |
#### 2.1.3 新统一平台 - ujava 容器内服务
| 服务key | jar文件名 | 说明 |
|---------|-----------|------|
| auth | ubains-auth.jar | 认证服务 |
| gateway | ubains-gateway.jar | 网关服务 |
| system | ubains-modules-system.jar | 系统服务 |
| meeting2.0 | ubains-meeting-inner-api-1.0-SNAPSHOT.jar | 对内2.0服务 |
| meeting3.0 | ubains-meeting-inner-api-1.0-SNAPSHOT.jar | 对内3.0服务 |
| mqtt | ubains-meeting-mqtt-1.0-SNAPSHOT.jar | MQTT服务 |
| quartz | ubains-meeting-quartz-1.0-SNAPSHOT.jar | 定时任务服务 |
| message | ubains-meeting-message-scheduling-1.0-SNAPSHOT.jar | 信息调度服务 |
#### 2.1.4 新统一平台 - ujava 宿主机服务
| 服务key | jar文件名 | 路径 |
|---------|-----------|------|
| extapi | ubains-meeting-api-1.0-SNAPSHOT.jar | /data/services/api/java-meeting/java-meeting-extapi/ |
#### 2.1.5 传统平台 - ujava 容器内服务
| 服务key | 检测模式 | 说明 |
|---------|----------|------|
| nginx | nginx: master process | Nginx 主进程 |
| meeting | ubains-meeting-inner-api-1.0-SNAPSHOT.jar | 对内后端服务 |
#### 2.1.6 传统平台 - ujava 宿主机服务
| 服务key | jar文件名 | 路径 |
|---------|-----------|------|
| extapi | ubains-meeting-api-1.0-SNAPSHOT.jar | /var/www/java/external-meeting-api/ |
#### 2.1.7 容器端口检测
**upython 容器(运维集控系统):**
| 端口 | 进程名 | 描述 |
|------|--------|------|
| 11211 | memcached | Memcached 缓存服务 |
| 8000 | uwsgi | uWSGI 应用服务 |
**upython_voice 容器(转录系统):**
| 端口 | 进程名 | 描述 |
|------|--------|------|
| 1883 | mosquitto | MQTT Broker 服务 |
| 8000 | uwsgi | uWSGI 应用服务 |
| 9001 | mosquitto | MQTT WebSocket 服务 |
| 11211 | memcached | Memcached 缓存服务 |
| 8080 | nginx | Nginx 代理服务 (8080) |
| 80 | nginx | Nginx Web 服务 (80) |
**传统平台 upython 容器:**
| 端口 | 进程名 | 描述 |
|------|--------|------|
| 8081 | nginx | Nginx 代理服务 (8081) |
| 8443 | nginx | Nginx HTTPS 服务 (8443) |
| 8000 | uwsgi | uWSGI 应用服务 |
| 8002 | httpd | Apache HTTPD 服务 |
| 11211 | memcached | Memcached 缓冲服务 |
### 2.2 不在本期范围(可扩展)
- 实时监控告警(钉钉/邮件/短信)
- 服务性能指标监控(响应时间、吞吐量)
- 自动扩缩容策略
- 多服务器批量检测
---
## 3. 术语说明
| 术语 | 说明 |
|------|------|
| 服务 key | 脚本内部用于标识服务的字符串,如 `auth`、`gateway` |
| jar标识 | 用于 ps 命令匹配进程的 jar 文件名 |
| 平台类型 | `new`(新统一平台)或 `old`(传统平台) |
| 系统类型 | 容器名称,如 `ujava2`、`upython`、`upython_voice` |
| 时间戳 | 格式 `yyyy-MM-dd HH:mm:ss` 或 `yyyyMMdd_HHmmss` |
| 双版本同步 | Windows 版和 Linux 版功能保持一致,优化时需同步更新 |
---
## 4. 功能需求
### 4.1 依赖检查
#### 4.1.1 Windows 版依赖
**检查项:**
- PowerShell 版本 >= 5.1
- SSH 密码认证工具:plink.exe 或 sshpass
**plink.exe 查找优先级:**
1. 脚本同目录下
2. 上级目录的 `远程容器更新\plink.exe`
3. 系统 PATH 中
**下载地址:**
- plink.exe: https://the.earth.li/~sgtatham/putty/latest/w64/plink.exe
**处理策略:**
- 未检测到依赖工具时,提示用户下载并放置到指定位置
- 输出详细的错误信息和下载链接
#### 4.1.2 Linux 版依赖
**检查项:**
- Bash 4.0+
- Docker 命令
- 常用系统命令:docker, ps, grep, awk, sed, netstat/ss, free, df, 等
**处理策略:**
- 脚本通过 `command_exists` 函数检查命令可用性
- 缺少命令时跳过相关检测或使用替代方案
---
### 4.2 服务器连接
#### 4.2.1 Windows 版 - 预设服务器列表
| 编号 | IP地址 | 用户名 | 密码 | 描述 |
|------|--------|--------|------|------|
| 1 | 192.168.5.48 | root | Ubains@123 | 标准版预定运维服务器 |
| 2 | 192.168.5.67 | root | Ubains@123 | 阿曼项目预定服务器 |
| 3 | 192.168.5.47 | root | Ubains@1234 | 标准版预定运维测试发布服务器 |
| 4 | 192.168.5.44 | root | Ubains@123 | 新统一平台测试服务器 |
#### 4.2.2 Windows 版 - 手动输入模式
- 支持 IP 地址、SSH 端口(默认 22)、用户名(默认 root)、密码输入
#### 4.2.3 Windows 版 - 连接测试
- 执行 `echo CONNECTION_OK` 验证 SSH 连接
- 处理首次连接主机密钥确认问题
#### 4.2.4 Linux 版 - 自动检测
- 自动检测本机 IP 地址
- 无需用户选择服务器
---
### 4.3 平台自动检测
#### 4.3.1 检测逻辑
```bash
[ -d /data/services ] && echo 'NEW_PLATFORM' || echo 'OLD_PLATFORM'
```
#### 4.3.2 平台类型
| 类型 | 判定条件 | 说明 |
|------|----------|------|
| new | 存在 /data/services 目录 | 新统一平台 |
| old | 不存在 /data/services 目录 | 传统平台 |
**版本差异:**
- Windows 版:通过 SSH 远程执行检测命令
- Linux 版:直接执行本地检测命令
---
### 4.4 系统类型检测
#### 4.4.1 容器检测命令
```bash
docker ps --format '{{.Names}}'
```
#### 4.4.2 容器匹配规则
| 容器类型 | 匹配模式 | 变量存储 |
|----------|----------|----------|
| ujava | `^ujava\d*$` | UjavaContainer |
| upython | `^upython\d*$`(非 voice) | UpythonContainer |
| upython_voice | `^upython_voice\d*$` | UpythonVoiceContainer |
#### 4.4.3 ujava 系统细分(传统平台)
```bash
[ -d /var/www/java/unifiedPlatform ] && echo 'UNIFIED' || echo 'MEETING'
```
- `unified`: 统一平台系统(会议预定系统变体)
- `meeting`: 会议预定系统
**版本差异:**
- Windows 版:通过 `Invoke-SSHCommand` 执行
- Linux 版:直接执行本地命令
---
### 4.5 服务检测
#### 4.5.1 ujava 容器内服务检测
**检测命令(容器内):**
```bash
docker exec {container} ps aux | grep -v grep | grep '{jar文件名}' | wc -l
```
**特殊处理(meeting2.0/meeting3.0):**
- meeting2.0: 需同时匹配路径中包含 `java-meeting2.0`
- meeting3.0: 需同时匹配路径中包含 `java-meeting3.0`
**判定逻辑:**
- 进程数 > 0: 运行中
- 进程数 = 0: 未运行
**回退机制:**
- 容器内检测失败时,自动尝试在宿主机检测
**版本差异:**
- Windows 版:通过 SSH 执行 `docker exec` 命令
- Linux 版:直接执行 `docker exec` 命令
#### 4.5.2 ujava 宿主机服务检测
**检测命令(宿主机):**
```bash
ps aux | grep -v grep | grep '{jar文件名}' | wc -l
```
**新统一平台路径:**
- extapi: /data/services/api/java-meeting/java-meeting-extapi/
**传统平台路径:**
- extapi: /var/www/java/external-meeting-api/
**版本差异:**
- Windows 版:通过 SSH 执行命令
- Linux 版:直接执行本地命令
#### 4.5.3 传统平台 ujava 容器内服务检测
**检测命令:**
```bash
docker exec {container} ps aux | grep -v grep | grep '{pattern}' | wc -l
```
#### 4.5.4 容器端口检测
**检测命令:**
```bash
docker exec {container} netstat -tlnp | grep ':{端口}'
# 或
docker exec {container} ss -tlnp | grep ':{端口}'
```
**判定逻辑:**
- 端口在监听: 正常
- 端口未监听: 异常
**版本差异:**
- Windows 版:通过 SSH 执行命令,支持 netstat/ss/lsof
- Linux 版:直接执行本地命令,支持 netstat/ss
---
### 4.6 DNS 解析检测
#### 4.6.1 DNS 配置检查
**检查文件:** `/etc/resolv.conf`
**检查命令:**
```bash
cat /etc/resolv.conf | grep -E '^nameserver' | head -n 3
```
**输出信息:**
- DNS 服务器列表
#### 4.6.2 DNS 解析测试
**测试域名:**
- www.baidu.com
- www.qq.com
- www.aliyun.com
**测试命令:**
```bash
nslookup {domain}
# 或
host {domain}
```
#### 4.6.3 网络连通性测试(Windows 版)
**测试命令:**
```bash
ping -c 2 -W 2 {domain}
```
#### 4.6.4 自动修复
**触发条件:**
- DNS 配置异常(未找到 nameserver)
- DNS 解析失败(所有域名解析失败)
**修复方式:**
- Windows 版:调用远程修复脚本 `fix_dns_config`
- Linux 版:直接调用同目录 `issue_handler.sh --action fix_dns_config`
- 修复后复检 DNS 解析功能
**版本差异:**
| 差异项 | Windows 版 | Linux 版 |
|--------|-----------|----------|
| 修复调用 | `Upload_the_repair_script` | `run_issue_handler` |
| 执行位置 | 远程服务器 | 本机 |
| 参数 | `--action fix_dns_config --platform auto` | `--action fix_dns_config --platform auto --non-interactive --yes` |
---
### 4.7 服务器资源分析
#### 4.7.1 操作系统信息
**检测命令:**
```bash
cat /etc/os-release | grep -E '^(NAME|VERSION)='
```
#### 4.7.2 系统架构
**检测命令:**
```bash
uname -m && uname -r
```
#### 4.7.3 CPU 使用情况
**检测命令:**
```bash
top -bn1 | grep 'Cpu(s)' | awk '{print $2+$4}'
nproc
```
**状态判定:**
- < 70%: 正常
- 70-90%: 警告
- > 90%: 危险
#### 4.7.4 内存使用情况
**检测命令:**
```bash
free -m | awk -F"[[:space:]]+" '$1 == "Mem:" { ... }'
```
**状态判定:**
- < 70%: 正常
- 70-90%: 警告
- > 90%: 危险
#### 4.7.5 磁盘使用情况
**检测命令:**
```bash
df -h | grep -E '^/dev/'
```
**状态判定:**
- < 70%: 正常
- 70-90%: 警告
- > 90%: 危险
#### 4.7.6 防火墙状态
**检测命令:**
```bash
systemctl is-active firewalld
# 或
service iptables status
```
**开放端口查询:**
```bash
firewall-cmd --list-ports
firewall-cmd --list-services
# 或
iptables -L INPUT -n
```
#### 4.7.7 防火墙自动修复
**触发条件:**
- 防火墙未启用或状态异常
**修复方式:**
- Windows 版:调用远程修复脚本 `fix_port_access`
- Linux 版:⚠️ **暂未实现自动修复功能**,仅检测状态
**修复后复检:**
- Windows 版:修复后复检防火墙状态
- Linux 版:不适用
#### 4.7.8 系统负载
**检测命令:**
```bash
uptime | awk -F'load average:' '{print $2}'
```
**版本差异:**
- Windows 版:通过 SSH 执行所有命令
- Linux 版:直接执行本地命令
---
### 4.8 NTP 服务检测
#### 4.8.1 服务状态检测
**检测命令:**
```bash
systemctl is-active chronyd
```
#### 4.8.2 时区配置检查
**检测命令:**
```bash
timedatectl | grep 'Time zone'
```
#### 4.8.3 时间偏差检测
**检测命令:**
```bash
chronyc tracking | grep 'Last offset'
```
#### 4.8.4 自动修复
**触发条件:**
- chronyd 服务未运行
- 时区配置异常
- 时间偏差超过阈值
**修复方式:**
- Windows 版:调用远程修复脚本 `fix_ntp_config --platform auto --ntp-auto`
- Linux 版:调用本地修复脚本 `fix_ntp_config --ntp-auto`
- 修复后复检时间同步状态
**版本差异:**
| 差异项 | Windows 版 | Linux 版 |
|--------|-----------|----------|
| 修复调用 | `Upload_the_repair_script` | `run_issue_handler` |
| 执行位置 | 远程服务器 | 本机 |
| 额外参数 | `--platform auto --ntp-auto` | `--ntp-auto` |
---
### 4.9 文件权限检测
#### 4.9.1 检测文件
根据平台类型和系统类型,检测关键脚本和配置文件权限:
- run.sh 启动脚本
- 配置文件
- 日志文件
#### 4.9.2 权限要求
- 脚本文件: 可执行 (755 或 775)
- 配置文件: 可读 (644 或 664)
- 日志文件: 可写 (644 或 664)
**版本差异:**
- Windows 版:通过 SSH 执行检测
- Linux 版:直接执行本地检测
---
### 4.10 容器信息收集
#### 4.10.1 容器列表
**检测命令:**
```bash
docker ps -a --format '{{.ID}} {{.Names}} {{.Status}}'
```
#### 4.10.2 容器详细信息
| 信息项 | 命令 |
|--------|------|
| 镜像 | `docker inspect -f '{{.Config.Image}}' {name}` |
| 状态 | `docker inspect -f '{{.State.Status}}' {name}` |
| 健康检查 | `docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{end}}' {name}` |
| 重启策略 | `docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' {name}` |
| 重启次数 | `docker inspect -f '{{.RestartCount}}' {name}` |
| IP地址 | `docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$v.IPAddress}}{{end}}' {name}` |
| 端口 | `docker port {name}` |
| 网络 | `docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}:{{$v.IPAddress}}{{end}}' {name}` |
| 挂载 | `docker inspect -f '{{range .Mounts}}{{.Source}}:{{.Destination}}({{.Mode}});{{end}}' {name}` |
| 大小 | `docker ps -a --size --filter "name=^/{name}$" --format "{{.Size}}"` |
**版本差异:**
- Windows 版:通过 SSH 执行命令
- Linux 版:直接执行本地命令
---
### 4.11 Redis/Emqx 容器异常处理
#### 4.11.1 Redis 容器检测
**检测目标:** uredis 容器
**异常判定:**
- uredis 未运行
- 且无其他 redis 容器运行中
**日志路径:**
- 新统一平台: `/data/middleware/redis/data/redis.log`
- 传统平台: `/var/www/redis/data/redis.log`
**自动修复:**
- Windows 版:调用远程修复脚本 `redis_container_exception`
- Linux 版:调用 `issue_handler.sh --action redis_container_exception`
- 修复后复检容器状态
#### 4.11.2 Emqx 容器检测
**检测目标:** uemqx 容器
**异常判定:**
- uemqx 未运行
- 且无其他 emqx 容器运行中
**日志路径:**
- 新统一平台: `/data/middleware/emqx/log/emqx.log.1`
- 传统平台: `/var/www/emqx/log/emqx.log.1`
**说明:** Emqx 容器异常暂不自动修复,仅记录状态
**版本差异:**
- Windows 版:日志导出到 Windows 端,修复通过远程脚本
- Linux 版:日志保留在服务器端,修复通过本地脚本
---
### 4.12 对外服务异常处理
#### 4.12.1 检测目标
ubains-meeting-api-1.0-SNAPSHOT.jar 进程
#### 4.12.2 异常判定
- 宿主机未检测到 extapi 进程
#### 4.12.3 自动修复
- Windows 版:调用远程修复脚本 `fix_external_service_disconnect`
- Linux 版:调用 `issue_handler.sh --action fix_external_service_disconnect`
- 修复后复检进程状态
---
### 4.13 配置文件 IP 检测
#### 4.13.1 新统一平台
**检测路径:**
- `/data/services/api/auth/`
- `/data/services/api/java-meeting/`
- `/data/middleware/`
**检测方式:**
- grep 搜索 IP 地址模式
- 输出匹配的文件和行号
#### 4.13.2 传统平台
**检测路径(会议预定系统):**
- `/var/www/java/api-java-meeting2.0/`
- `/var/www/html/`
**检测路径(统一平台系统):**
- `/var/www/java/unifiedPlatform/`
**检测方式:**
- grep 搜索 IP 地址模式
- 输出匹配的文件和行号
**版本差异:**
- Windows 版:通过 SSH 执行 grep 命令
- Linux 版:直接执行本地 grep 命令
---
### 4.14 安卓设备自检
#### 4.14.1 Windows 版
**adb 工具检测:**
```powershell
adb version
```
**设备连接测试:**
```powershell
adb devices
```
**日志拉取:**
1. 用户输入设备 IP
2. 执行 adb connect
3. 拉取日志文件到 Windows 端本地
4. 执行 adb disconnect
#### 4.14.2 Linux 版
**adb 工具路径:**
- 优先使用脚本同目录 `linux_x86_adb/adb`
- 备选系统 PATH 中的 adb
**设备连接测试:**
```bash
./linux_x86_adb/adb devices
```
**日志拉取:**
1. 用户输入设备 IP
2. 执行 adb connect
3. 拉取日志文件到服务器端本地
4. 执行 adb disconnect
---
### 4.15 定时任务查询(Linux 版独有)
#### 4.15.1 检测内容
- root 用户的 crontab 定时任务
- 系统级定时任务(/etc/cron.d/)
#### 4.15.2 检测命令
```bash
crontab -l 2>/dev/null
ls -la /etc/cron.d/ 2>/dev/null
```
#### 4.15.3 输出内容
- 定时任务列表
- 任务执行时间
- 任务执行命令
---
### 4.16 服务日志导出
#### 4.16.1 新统一平台日志
| 日志名 | 远程路径 |
|--------|----------|
| auth_log.out | /data/services/api/auth/auth-sso-auth/log.out |
| gatway_log.out | /data/services/api/auth/auth-sso-gatway/log.out |
| system_log.out | /data/services/api/auth/auth-sso-system/log.out |
| 对内2.0_ubains-INFO-AND-ERROR.log | /data/services/api/java-meeting/java-meeting2.0/logs/ubains-INFO-AND-ERROR.log |
| 对内3.0_ubains-INFO-AND-ERROR.log | /data/services/api/java-meeting/java-meeting3.0/logs/ubains-INFO-AND-ERROR.log |
| 对外服务_ubains-INFO-AND-ERROR.log | /data/services/api/java-meeting/java-meeting-extapi/logs/ubains-INFO-AND-ERROR.log |
| 信息调度_ubains-INFO-AND-ERROR.log | /data/services/api/java-meeting/java-message-scheduling/logs/ubains-INFO-AND-ERROR.log |
| MQTT_ubains-INFO-AND-ERROR.log | /data/services/api/java-meeting/java-mqtt/logs/ubains-INFO-AND-ERROR.log |
| 定时任务_ubains-INFO-AND-ERROR.log | /data/services/api/java-meeting/java-quartz/logs/ubains-INFO-AND-ERROR.log |
#### 4.16.2 传统平台 ujava 日志
| 日志名 | 远程路径 |
|--------|----------|
| 对内后端_ubains-INFO-AND-ERROR.log | /var/www/java/api-java-meeting2.0/logs/ubains-INFO-AND-ERROR.log |
| 对外后端_ubains-INFO-AND-ERROR.log | /var/www/java/external-meeting-api/logs/ubains-INFO-AND-ERROR.log |
#### 4.16.3 传统平台 upython 日志
| 日志名 | 远程路径 |
|--------|----------|
| 运维集控_error.log | /var/www/html/log/error.log |
| 运维集控_uinfo.log | /var/www/html/log/uinfo.log |
| 运维集控_uwsgi.log | /var/www/html/log/uwsgi.log |
#### 4.16.4 导出方式
**Windows 版:**
- 使用 pscp.exe 远程下载
- 目标目录:Windows 端 `output/{IP}/logs/`
**Linux 版:**
- 使用 cp 本地复制
- 目标目录:服务器端 `output/{IP}/logs/`
---
### 4.17 数据备份(可选)
#### 4.17.1 备份内容
- 数据库备份(mysqldump)
- 配置文件备份
- 日志文件备份
#### 4.17.2 备份流程
**Windows 版:**
1. 通过 SSH 在远程服务器执行备份
2. 打包备份文件
3. 通过 SSH 下载到 Windows 端本地
**Linux 版:**
1. 在本机执行备份
2. 打包备份文件
3. 保留在服务器端
---
### 4.18 报告生成
#### 4.18.1 报告格式
Markdown 格式
#### 4.18.2 报告路径
**Windows 版:**
`{脚本目录}\Reports\health_report_{服务器IP}_{时间戳}.md`
**Linux 版:**
`{脚本目录}/Reports/health_report_{服务器IP}_{时间戳}.md`
#### 4.18.3 报告内容
| 章节 | 内容 |
|------|------|
| 标题 | 服务器地址、描述、平台类型、检测时间 |
| 系统类型识别 | 检测到的容器类型 |
| ujava 容器内服务状态 | 各服务运行状态 |
| ujava 宿主机服务状态 | extapi 服务状态 |
| upython 容器服务状态 | 端口监听状态 |
| upython_voice 容器服务状态 | 端口监听状态 |
| DNS 解析检测结果 | 配置、解析、连通性状态 |
| 服务器资源分析 | OS、架构、CPU、内存、磁盘、防火墙、负载 |
| NTP 服务检测 | 服务状态、时区、时间偏差 |
| 文件权限检测 | 权限检查结果 |
| 容器信息 | 容器列表和详细信息 |
| 安卓设备自检 | adb 连接和日志拉取结果 |
| 定时任务 | crontab 定时任务列表(Linux 版) |
| 检测总结 | 总服务数、正常数、异常数、结论 |
| 服务日志导出 | 导出状态和文件列表 |
**版本差异:**
- Linux 版报告额外包含定时任务信息
---
## 5. 主流程
### 5.1 Windows 版主流程
```
1. 依赖检查(plink.exe/pscp.exe)
2. 选择服务器(预设列表/手动输入)
3. 测试 SSH 连接
4. 检测平台类型(new/old)
5. 检测系统类型(容器)
6. 服务检测
├── 新统一平台: ujava容器内服务 + ujava宿主机服务 + upython + upython_voice
└── 传统平台: ujava容器内服务 + ujava宿主机服务 + upython
7. DNS 解析检测(含自动修复)
8. 服务器资源分析(含防火墙自动修复)
9. 容器信息收集(含 Redis/Emqx 异常处理)
10. 配置文件 IP 检测
11. NTP 服务检测(含自动修复)
12. 文件权限检测
13. 可选: 数据备份(远程执行+下载)
14. 可选: 服务日志导出(远程下载)
15. 安卓设备自检
16. 生成健康检查报告
```
### 5.2 Linux 版主流程
```
1. 初始化日志(exec + tee)
2. 检测平台类型(new/old)
3. 检测系统类型(容器)
4. 服务检测
├── 新统一平台: ujava容器内服务 + ujava宿主机服务 + upython + upython_voice
└── 传统平台: ujava容器内服务 + ujava宿主机服务 + upython
5. DNS 解析检测(含自动修复)
6. 服务器资源分析(含防火墙自动修复)
7. 配置文件 IP 检测
8. NTP 服务检测(含自动修复)
9. 文件权限检测
10. 可选: 数据备份(本地执行)
11. 可选: 服务日志导出(本地复制)
12. 容器信息收集(含 Redis/Emqx 异常处理)
13. 安卓设备自检
14. 定时任务查询
15. 生成健康检查报告
```
---
## 6. 配置与可变项
| 配置项 | Windows 版 | Linux 版 | 说明 |
|--------|-----------|----------|------|
| 脚本版本 | 1.0.3 | 1.0.3 | 用于日志与报告 |
| SSH 超时 | 30秒 | 不适用 | Linux 版无需 SSH |
| 日志目录 | {脚本目录}\logs\ | {脚本目录}/logs/ | 日志文件存储位置 |
| 报告目录 | {脚本目录}\Reports\ | {脚本目录}/Reports/ | Markdown 报告存储位置 |
| 输出目录 | {脚本目录}\output\ | {脚本目录}/output/ | 日志导出存储位置 |
| DNS 测试域名 | www.baidu.com<br>www.qq.com<br>www.aliyun.com | 同左 | DNS 解析测试 |
| 新统一平台检测目录 | /data/services | 同左 | 平台类型判定 |
| 传统平台统一平台检测目录 | /var/www/java/unifiedPlatform | 同左 | ujava 系统细分判定 |
| Redis 日志路径(新平台) | /data/middleware/redis/data/redis.log | 同左 | |
| Redis 日志路径(传统) | /var/www/redis/data/redis.log | 同左 | |
| Emqx 日志路径(新平台) | /data/middleware/emqx/log/emqx.log.1 | 同左 | |
| Emqx 日志路径(传统) | /var/www/emqx/log/emqx.log.1 | 同左 | |
| adb 工具路径 | 系统 PATH 中的 adb | linux_x86_adb/adb | 安卓设备自检 |
| 修复脚本调用 | Upload_the_repair_script | run_issue_handler | |
---
## 7. 异常处理与容错要求
### 7.1 通用异常处理(两版共同)
1. **命令执行失败**: 记录到日志,继续后续检测
2. **自动修复失败**: 记录失败原因,不中断主流程
3. **容器不存在**: 跳过相关检测,输出警告信息
4. **进程检测回退**: 容器内检测失败时自动尝试宿主机检测
5. **端口检测兼容**: 支持 netstat/ss/lsof 多种命令
### 7.2 Windows 版特有异常处理
1. **SSH 连接失败**: 输出详细错误信息,提示检查 IP、端口、密码、网络连通性
2. **依赖工具缺失**: 明确提示下载链接和放置位置
3. **日志文件写入失败**: 不影响主流程,仅记录错误
4. **主机密钥确认**: 自动处理首次连接的主机密钥确认问题
### 7.3 Linux 版特有异常处理
1. **权限不足**: 检测 root 权限,提示使用 sudo
2. **命令不存在**: 使用 `command_exists` 检查,提供替代方案
3. **日志同步**: exec + tee 确保日志实时同步
---
## 8. 交付物
### 8.1 Windows 版交付物
1. **主脚本**: `AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.ps1`
2. **日志文件**: `{脚本目录}\logs\health_check_{时间戳}.log`
3. **健康报告**: `{脚本目录}\Reports\health_report_{IP}_{时间戳}.md`
4. **导出日志**: `{脚本目录}\output\{IP}\logs\`
### 8.2 Linux 版交付物
1. **主脚本**: `AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.sh`
2. **修复脚本**: `AuxiliaryTool\ScriptTool\ServiceSelfInspection\issue_handler.sh`
3. **日志文件**: `{脚本目录}/logs/health_check_{时间戳}.log`
4. **健康报告**: `{脚本目录}/Reports/health_report_{IP}_{时间戳}.md`
5. **导出日志**: `{脚本目录}/output/{IP}/logs/`
### 8.3 版本同步要求
> **重要:后续功能优化时,必须同时更新两个版本脚本,保持功能一致。**
- 新增功能需要在两个版本中实现
- 修复 Bug 需要在两个版本中同步修复
- 配置变更需要在两个版本中同步更新
- 测试时需要分别测试两个版本
---
## 9. 验收标准
### 9.1 Windows 版验收标准
1. 脚本可在 Windows 10+ 上正常运行
2. 可成功连接预设服务器列表中的服务器
3. 可正确识别新统一平台和传统平台
4. 可检测到容器内服务进程运行状态
5. 可检测到容器端口监听状态
6. DNS/NTP/防火墙异常时可自动修复
7. 可生成完整的 Markdown 格式健康检查报告
8. 所有检测步骤有日志记录,不静默失败
### 9.2 Linux 版验收标准
1. 脚本可在 Linux 服务器上正常运行(需要 root 权限)
2. 可正确识别新统一平台和传统平台
3. 可检测到容器内服务进程运行状态
4. 可检测到容器端口监听状态
5. DNS/NTP/防火墙异常时可自动修复
6. 可生成完整的 Markdown 格式健康检查报告
7. 可查询定时任务配置
8. 所有检测步骤有日志记录,不静默失败
### 9.3 双版本一致性验收
1. 两个版本功能检测结果一致
2. 两个版本报告格式一致
3. 两个版本修复逻辑一致
4. 版本号同步更新
---
## 需求规范
代码规范:Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md
问题总结:Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md
方法总结:Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md
文档规范:Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md
测试规范:Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md
\ No newline at end of file
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
# _PRD_服务自检需求文档_计划执行.md
> 版本:V1.0
> 创建日期:2026-01-28
> 关联需求:`_PRD_服务自检需求文档-新.md`
> 适用范围:服务自检脚本(Windows 版 + Linux 版)开发维护
---
## 1. 文档说明
### 1.1 目的
本文档基于服务自检需求文档,制定详细的开发和维护计划,确保 Windows 版和 Linux 版脚本功能同步、质量可控。
### 1.2 范围
- Windows 版脚本:`check_server_health.ps1` (v1.0.3)
- Linux 版脚本:`check_server_health.sh` (v1.0.3)
- 功能优化同步执行
- Bug 修复同步执行
### 1.3 版本同步原则
> **核心原则:双版本同步开发,功能一致实现。**
| 操作类型 | Windows 版 | Linux 版 | 同步要求 |
|----------|-----------|----------|----------|
| 新增功能 | 必须实现 | 必须实现 | 同时开发 |
| 功能优化 | 必须同步 | 必须同步 | 同时发布 |
| Bug 修复 | 必须同步 | 必须同步 | 同时修复 |
| 配置变更 | 必须同步 | 必须同步 | 保持一致 |
---
## 2. 当前状态评估
### 2.1 版本信息
| 版本 | 当前版本 | 状态 | 说明 |
|------|----------|------|------|
| Windows 版 | 1.0.3 | ✅ 稳定 | 功能完整,已投入使用 |
| Linux 版 | 1.0.3 | ✅ 稳定 | 功能完整,已投入使用 |
### 2.2 功能对比矩阵
| 功能模块 | Windows 版 | Linux 版 | 同步状态 |
|----------|-----------|----------|----------|
| 依赖检查 | ✅ | ✅ | ✅ 一致 |
| 服务器连接 | ✅ | N/A | - (架构差异) |
| 平台检测 | ✅ | ✅ | ✅ 一致 |
| 系统类型检测 | ✅ | ✅ | ✅ 一致 |
| ujava 服务检测 | ✅ | ✅ | ✅ 一致 |
| 宿主机服务检测 | ✅ | ✅ | ✅ 一致 |
| 容器端口检测 | ✅ | ✅ | ✅ 一致 |
| DNS 检测与修复 | ✅ | ✅ | ✅ 一致 |
| 资源分析 | ✅ | ✅ | ✅ 一致 |
| 防火墙检测与修复 | ✅ | ⚠️ | ⚠️ 部分一致(Linux 版仅检测,无修复) |
| NTP 检测与修复 | ✅ | ✅ | ✅ 一致 |
| 文件权限检测 | ✅ | ✅ | ✅ 一致 |
| 容器信息收集 | ✅ | ✅ | ✅ 一致 |
| Redis 异常处理 | ✅ | ✅ | ✅ 一致 |
| Emqx 异常处理 | ✅ | ✅ | ✅ 一致 |
| 对外服务修复 | ✅ | ✅ | ✅ 一致 |
| 配置文件 IP 检测 | ✅ | ✅ | ✅ 一致 |
| 安卓设备自检 | ✅ | ✅ | ⚠️ 差异 (adb 路径) |
| 日志导出 | ✅ | ✅ | ⚠️ 差异 (导出方式) |
| 数据备份 | ✅ | ✅ | ⚠️ 差异 (下载方式) |
| 定时任务查询 | ❌ | ✅ | ⚠️ Linux 独有 |
**图例:**
- ✅ 已实现
- ❌ 未实现
- ⚠️ 有差异
- - 不适用
### 2.3 待办事项
| 优先级 | 任务 | Windows 版 | Linux 版 |
|--------|------|-----------|----------|
| 🟢 低 | 版本号已统一为 1.0.3 | 1.0.3 | 1.0.3 |
| 🟢 低 | 差异说明文档化 | 已完成 | 已完成 |
---
## 3. 开发计划
### 3.1 版本规划
#### v1.1.0 - 功能增强(规划中)
**目标:** 基于当前 v1.0.3 版本,规划和实现功能增强
| 任务ID | 任务描述 | Windows 版 | Linux 版 | 负责人 | 状态 |
|--------|----------|-----------|----------|--------|------|
| T-1101 | 定时任务查询功能移植到 Windows 版 | 待开发 | 已实现 | - | 🔲 待开始 |
| T-1102 | 增加更多中间件检测 | 待开发 | 待开发 | - | 🔲 待开始 |
| T-1103 | 支持配置文件外部化 | 待开发 | 待开发 | - | 🔲 待开始 |
#### v1.2.0 - 更多功能增强(规划中)
| 任务ID | 任务描述 | 说明 | 状态 |
|--------|----------|------|------|
| T-1201 | 增加邮件/钉钉告警 | 检测到异常时发送告警 | 🔲 计划中 |
| T-1202 | 支持配置文件外部化 | 服务列表、端口等可配置 | 🔲 计划中 |
| T-1203 | Web 界面支持 | 提供 Web 界面查看检测结果 | 🔲 计划中 |
---
### 3.2 任务分解
#### 阶段一:功能增强(v1.1.0)
**预计工作量:** 按具体功能确定
| 步骤 | 任务 | 工作内容 | 产出物 |
|------|------|----------|--------|
| 1.1 | 定时任务查询移植 | 将 Linux 版定时任务查询移植到 Windows 版 | 新增代码 |
| 1.2 | 功能对比验证 | 验证两版本功能一致性 | 测试报告 |
| 1.3 | 文档更新 | 更新需求文档和计划文档 | 更新的文档 |
#### 阶段二:质量优化(v1.1.1)
**预计工作量:** 3-5 天
| 步骤 | 任务 | 工作内容 | 产出物 |
|------|------|----------|--------|
| 2.1 | 代码规范检查 | 按代码规范检查并修复 | 规范化代码 |
| 2.2 | 错误处理增强 | 完善异常处理和容错逻辑 | 增强的错误处理 |
| 2.3 | 日志输出优化 | 统一日志格式和输出级别 | 统一的日志格式 |
| 2.4 | 性能优化 | 减少不必要的命令执行 | 优化后的脚本 |
| 2.5 | 文档完善 | 补充代码注释和使用说明 | 完善的文档 |
#### 阶段三:功能增强(v1.2.0+)
**预计工作量:** 按具体功能确定
| 步骤 | 任务 | 工作内容 | 产出物 |
|------|------|----------|--------|
| 3.1 | 需求评审 | 评审新功能需求和优先级 | 需求评审报告 |
| 3.2 | 设计评审 | 评审技术方案和实现方式 | 设计文档 |
| 3.3 | 双版本开发 | 同时开发 Windows 版和 Linux 版 | 新功能代码 |
| 3.4 | 联调测试 | 两个版本功能一致性测试 | 测试报告 |
| 3.5 | 发布上线 | 版本发布和部署 | 发布说明 |
---
## 4. 开发流程规范
### 4.1 新功能开发流程
```mermaid
graph TD
A[需求提出] --> B[需求评审]
B --> C[技术方案设计]
C --> D{是否影响两个版本?}
D -->|是| E[双版本同步开发]
D -->|否| F[单版本开发+文档说明]
E --> G[代码审查]
F --> G
G --> H[功能测试]
H --> I[双版本一致性验证]
I --> J[版本发布]
```
### 4.2 Bug 修复流程
```mermaid
graph TD
A[Bug 报告] --> B[Bun 确认]
B --> C{影响哪个版本?}
C -->|Windows 版| D[修复 Windows 版]
C -->|Linux 版| E[修复 Linux 版]
C -->|两版本| F[双版本同步修复]
D --> G[验证修复]
E --> G
F --> G
G --> H[版本发布]
```
### 4.3 版本发布流程
| 阶段 | 操作 | Windows 版 | Linux 版 |
|------|------|-----------|----------|
| 1 | 版本号更新 | 修改 $SCRIPT_VERSION | 修改 SCRIPT_VERSION |
| 2 | 变更日志 | 编写 CHANGELOG | 编写 CHANGELOG |
| 3 | 代码审查 | 人工审查 | 人工审查 |
| 4 | 功能测试 | 执行测试用例 | 执行测试用例 |
| 5 | 一致性验证 | 对比两版本结果 | 对比两版本结果 |
| 6 | 文档更新 | 更新需求文档 | 更新需求文档 |
| 7 | 发布 | 打包发布 | 打包发布 |
---
## 5. 测试计划
### 5.1 测试环境
| 环境类型 | Windows 版测试环境 | Linux 版测试环境 |
|----------|-------------------|------------------|
| 开发环境 | Windows 10/11 + PowerShell 5.1+ | CentOS 7/8 或 Ubuntu 20.04+ |
| 测试服务器 | 标准版预定运维服务器 | 同左 |
| 平台覆盖 | 新统一平台 + 传统平台 | 新统一平台 + 传统平台 |
### 5.2 测试用例
| 用例ID | 测试项 | Windows 版 | Linux 版 | 预期结果 |
|--------|--------|-----------|----------|----------|
| TC-001 | 依赖检查 | ✅ | ✅ | 正确检测依赖工具 |
| TC-002 | 平台识别 | ✅ | ✅ | 正确识别新统一平台/传统平台 |
| TC-003 | 容器检测 | ✅ | ✅ | 正确检测 ujava/upython/upython_voice |
| TC-004 | 服务进程检测 | ✅ | ✅ | 正确检测容器内/宿主机服务 |
| TC-005 | 端口监听检测 | ✅ | ✅ | 正确检测端口监听状态 |
| TC-006 | DNS 检测与修复 | ✅ | ✅ | 检测异常并自动修复 |
| TC-007 | NTP 检测与修复 | ✅ | ✅ | 检测异常并自动修复 |
| TC-008 | 防火墙检测与修复 | ✅ | ⚠️ | Windows 版检测并自动修复,Linux 版仅检测 |
| TC-009 | 资源分析 | ✅ | ✅ | 正确获取 CPU/内存/磁盘信息 |
| TC-010 | Redis 异常处理 | ✅ | ✅ | 检测异常并自动修复 |
| TC-011 | 报告生成 | ✅ | ✅ | 生成完整 Markdown 报告 |
| TC-012 | 日志导出 | ✅ | ✅ | 成功导出日志文件 |
| TC-013 | 定时任务查询 | ❌ | ✅ | Linux 版显示定时任务 |
### 5.3 一致性验证
**验证方法:** 在同一服务器上,分别运行 Windows 版和 Linux 版,对比检测结果。
| 对比项 | 验证方法 | 一致性标准 |
|--------|----------|------------|
| 服务状态 | 对比服务运行状态列表 | 完全一致 |
| 端口监听 | 对比端口监听状态 | 完全一致 |
| 资源数据 | 对比 CPU/内存/磁盘使用率 | 误差 < 5% |
| 报告内容 | 对比报告章节和格式 | 结构一致,内容语义一致 |
---
## 6. 变更管理
### 6.1 变更类型
| 变更类型 | 版本号变化 | 示例 |
|----------|-----------|------|
| Bug 修复 | Patch 版本 (x.x.z+1) | 1.0.3 → 1.0.4 |
| 功能新增 | Minor 版本 (x.y+1.0) | 1.0.4 → 1.1.0 |
| 破坏性变更 | Major 版本 (x+1.0.0) | 1.1.0 → 2.0.0 |
### 6.2 变更流程
1. **变更申请**
- 填写变更申请表
- 说明变更原因和影响范围
2. **影响评估**
- 评估对 Windows 版和 Linux 版的影响
- 确定是否需要双版本同步
3. **变更审批**
- 技术负责人审批
- 确认变更方案
4. **变更实施**
- 按开发流程实施变更
- 确保双版本同步
5. **变更验证**
- 执行测试用例
- 验证一致性
6. **变更发布**
- 更新版本号
- 编写发布说明
### 6.3 变更记录
| 版本 | 日期 | 变更类型 | 变更内容 | 影响范围 |
|------|------|----------|----------|----------|
| 1.0.3 | 2026-01-28 | Patch | 双版本版本号统一,功能完善 | 两版本 |
| 1.0.3 | 2026-01-XX | Patch | Windows 版功能完善 | Windows 版 |
| 1.0.0 | 2026-01-XX | Major | 初始版本 | 两版本 |
---
## 7. 风险管理
### 7.1 技术风险
| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|----------|
| 双版本功能不一致 | 高 | 中 | 严格的版本同步流程,一致性验证 |
| SSH 连接不稳定 | 中 | 中 | 增加重试机制,详细错误提示 |
| 修复脚本调用失败 | 高 | 低 | 充分测试修复脚本,添加异常处理 |
| 平台识别错误 | 高 | 低 | 多条件验证,增加日志输出 |
### 7.2 流程风险
| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|----------|
| 版本发布不同步 | 高 | 中 | 建立版本检查清单,发布前确认 |
| 测试覆盖不足 | 高 | 中 | 完善测试用例,自动化测试 |
| 文档更新滞后 | 中 | 高 | 文档与代码同步更新 |
---
## 8. 质量保证
### 8.1 代码质量标准
| 指标 | 标准 | 检查方式 |
|------|------|----------|
| 代码规范 | 符合项目代码规范 | 人工审查 |
| 注释覆盖率 | 核心函数必须有注释 | 人工审查 |
| 错误处理 | 所有可能的异常都有处理 | 代码审查+测试 |
| 日志完整性 | 关键步骤都有日志输出 | 测试验证 |
### 8.2 测试质量标准
| 指标 | 标准 | 检查方式 |
|------|------|----------|
| 测试用例覆盖率 | 核心功能 100% | 测试用例清单 |
| 用例执行通过率 | 100% | 测试报告 |
| 双版本一致性 | 核心检测结果一致 | 一致性验证报告 |
### 8.3 文档质量标准
| 文档 | 更新频率 | 责任人 |
|------|----------|--------|
| 需求文档 | 每次版本更新 | 技术负责人 |
| 计划文档 | 每季度或重大变更 | 项目经理 |
| 代码注释 | 随代码更新 | 开发人员 |
| 用户手册 | 每次版本更新 | 技术负责人 |
---
## 9. 沟通协作
### 9.1 角色职责
| 角色 | 职责 | Windows 版 | Linux 版 |
|------|------|-----------|----------|
| 技术负责人 | 架构设计、技术决策 | 负责 | 负责 |
| Windows 版开发者 | PowerShell 脚本开发 | 主要 | 辅助 |
| Linux 版开发者 | Bash 脚本开发 | 辅助 | 主要 |
| 测试工程师 | 功能测试、一致性验证 | 测试 | 测试 |
| 运维工程师 | 部署、监控、问题反馈 | 使用 | 使用 |
### 9.2 沟通机制
| 会议类型 | 频率 | 参与人员 | 议题 |
|----------|------|----------|------|
| 需求评审会 | 按需 | 全体 | 新功能需求讨论 |
| 技术方案评审会 | 按需 | 技术人员 | 实现方案讨论 |
| 版本发布会 | 每次发布 | 全体 | 版本内容、注意事项 |
| 双周例会 | 双周 | 全体 | 进展同步、问题讨论 |
### 9.3 协作工具
| 工具类型 | 推荐工具 | 用途 |
|----------|----------|------|
| 代码管理 | Git | 版本控制、分支管理 |
| 问题跟踪 | GitHub Issues / Jira | Bug 跟踪、需求管理 |
| 文档协作 | Markdown + Git | 文档版本控制 |
| 即时沟通 | 企业微信 / 钉钉 | 日常沟通 |
---
## 10. 里程碑
| 里程碑 | 目标 | 预计时间 | 状态 |
|--------|------|----------|------|
| M1 | 需求文档完成 | 2026-01-28 | ✅ 已完成 |
| M2 | 计划文档完成 | 2026-01-28 | ✅ 已完成 |
| M3 | 双版本 v1.0.3 统一 | 2026-01-28 | ✅ 已完成 |
| M4 | v1.1.0 功能增强完成 | 待定 | 🔲 计划中 |
| M5 | v1.2.0 更多功能增强完成 | 待定 | 🔲 计划中 |
---
## 11. 附录
### 11.1 文件路径
**脚本文件:**
- Windows 版:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.ps1`
- Linux 版:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.sh`
**文档文件:**
- 需求文档:`Docs\PRD\服务自检\_PRD_服务自检需求文档-新.md`
- 计划文档:`Docs\PRD\服务自检\_PRD_服务自检需求文档_计划执行.md`
**输出文件:**
- Windows 版日志:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\logs\health_check_{时间戳}.log`
- Linux 版日志:`AuxiliaryTool\ScriptTool\ServiceSelfInspection/logs/health_check_{时间戳}.log`
- Windows 版报告:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\Reports\health_report_{IP}_{时间戳}.md`
- Linux 版报告:`AuxiliaryTool\ScriptTool\ServiceSelfInspection/Reports/health_report_{IP}_{时间戳}.md`
### 11.2 相关文档
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
### 11.3 快速参考
**Windows 版运行:**
```powershell
cd C:\PycharmData\ubains-module-test\AuxiliaryTool\ScriptTool\ServiceSelfInspection
.\check_server_health.ps1
```
**Linux 版运行:**
```bash
cd /path/to/AuxiliaryTool/ScriptTool/ServiceSelfInspection
chmod +x check_server_health.sh
./check_server_health.sh
```
### 11.4 联系方式
如有问题或建议,请联系:
- 技术负责人:[待填写]
- 项目经理:[待填写]
---
**文档版本历史:**
| 版本 | 日期 | 变更内容 | 作者 |
|------|------|----------|------|
| V1.1 | 2026-01-28 | 更新双版本版本号为 1.0.3 | Claude |
| V1.0 | 2026-01-28 | 初始版本 | Claude |
# _PRD_根据PRD文档生成测试用例.md
> 版本:V1.1
> 更新日期:2026-01-28
> 适用范围:自动化测试用例生成工具
> 实现脚本:`AuxiliaryTool\用例生成\create_test_xlsx.py`
---
## 1. 背景与目标
### 1.1 背景
测试用例编写是测试工作的基础,但手工从 PRD 文档中提取需求并编写测试用例耗时耗力。现有项目 PRD 文档采用 Markdown 格式编写,具备结构化的编号和描述,适合自动化解析。
### 1.2 目标
实现一套自动化测试用例生成工具,具备:
- 自动从 PRD(Markdown)文档中抽取需求条目
- 基于规则引擎生成测试用例记录(JSON + Excel)
- 支持交互式选择多个 PRD 文档
- 生成的用例符合现有测试用例模板格式
---
## 2. 总体范围
### 2.1 纳入范围
1. **PRD 文档解析**
- 支持 Markdown 格式的 PRD 文档
- 自动识别编号结构(如 `1.1、2.3.1`
- 兼容 Markdown 标题语法(`# 标题`
2. **测试用例生成**
- 自动生成测试用例 JSON 文件
- 写入 Excel 测试用例模板
3. **交互式操作**
- 列出可选 PRD 文档
- 支持多选(如 `1,2,5``1 2 5`
- 自定义 Sheet 名称和输出文件名
### 2.2 不在本期范围
- PRD 文档的语法校验
- 测试用例的智能去重
- 复杂的测试数据生成
- 用例间的依赖关系处理
---
## 3. 术语说明
- **需求条目**:PRD 文档中带有编号的功能描述项
- **用例编号前缀**:测试用例 ID 的前缀(默认 `TC`
- **功能模块**:测试用例所属的模块名称
- **Sheet 复用**:多 PRD 选中时,用例写入同一个 Sheet(追加模式)
---
## 4. 功能需求
### 4.1 PRD 目录定位
#### 4.1.1 目录兼容
脚本需兼容以下两种 PRD 目录结构:
- `Docs/开发PRD`(PRD 文档中约定的路径)
- `Docs/PRD`(仓库中实际常见的路径)
#### 4.1.2 自动检测逻辑
- 按优先级检测目录是否存在
- 若均不存在,使用第一个路径并在后续报错提示
#### 4.1.3 相关路径
| 配置项 | 路径 |
| --- | --- |
| PRD 目录 | `Docs/开发PRD``Docs/PRD` |
| 模板文件 | `AuxiliaryTool/用例生成/用例文件/兰州中石化项目测试用例20251203.xlsx` |
| JSON 输出 | `AuxiliaryTool/用例生成/config/` |
| Excel 输出 | `AuxiliaryTool/用例生成/用例文件/` |
---
### 4.2 需求条目抽取规则
#### 4.2.1 编号识别
支持以下两种格式:
1. **点分编号**:正则 `^\s*(\d+(?:\.\d+)*)\s*[、\.\-]\s*(.+?)\s*[::]?\s*$`
- 示例:`1.1 用户登录``2.3.1-异常处理`
2. **Markdown 标题**:正则 `^\s*(#{1,6})\s+(.+?)\s*$`
- 示例:`## 用户登录``### 异常处理`
#### 4.2.2 内容抽取
- 标题后续行作为详情内容(直到下一个标题)
- **Markdown 标题嵌套处理**:如果当前已有标题,遇到新的 Markdown 标题时,该标题行会被作为详情内容追加,而非创建新条目
- 自动过滤:
- 代码块(```...```
- HTML 注释(<!--...-->
- 无效标题(说明、目录、背景、概述)
- 标题过滤条件:标题长度必须 ≥ 2 个字符
#### 4.2.3 清洗规则
- 移除列表符号(`-``*``+`
- 合并多余空格
- 保留文本核心语义
---
### 4.3 测试用例分类与优先级
#### 4.3.1 功能类别判定
| 类别 | 关键词 |
| --- | --- |
| 异常场景 | 异常、失败、错误、告警、报警、超时、断开、暴涨 |
| 安全/鉴权 | 安全、权限、鉴权、加密、脱敏 |
| 运维可观测 | 报告、输出、word、markdown、邮件、钉钉、通知 |
| 功能测试 | 其他(默认) |
#### 4.3.2 用例等级判定
| 等级 | 关键词 |
| --- | --- |
| 高 | 必须、报警、告警、峰值、暴涨、发送、对接 |
| 中 | 待实现、可以、建议、优化 |
| 中 | 其他(默认) |
#### 4.3.3 用例字段生成规则
| 字段 | 生成规则 |
| --- | --- |
| 序号 | 递增(多 PRD 时全局递增,跨 PRD 累加) |
| 用例编号 | `{前缀}-{序号:03d}`,如 `TC-001``TC-002` |
| 用例名称 | 需求标题 |
| 功能描述 | 需求标题 |
| 功能编号 | 抽取的编号(可能为空,适用于 Markdown 标题) |
| 预置条件 | 固定:`1. 环境已部署; 2. 具备执行权限; 3. 必要依赖/配置已准备` |
| 操作步骤 | 从详情中提取含"检查、监测、记录、输出、发送、查询、进入、生成、判定、调用、执行"的句子;最多提取 6 条,去重后以分号分隔;**在 Excel 中分号会转换为换行显示**;若无匹配内容,使用默认值:`1. 按 PRD 描述执行该功能/流程; 2. 采集相关日志/输出; 3. 记录实际结果` |
| 预期结果 | 从详情中提取含"需要、应、必须、输出、记录、发送、判定、成功、失败"的句子;最多提取 6 条,去重后以分号分隔;若无匹配内容,使用默认值:`{标题} 按 PRD 约定产出正确结果(请补充具体断言点)` |
| JSON | **Excel 中强制清空(仅 JSON 文件保留)** |
| 测试结果 | 空(待人工填写) |
| 测试结论 | 空(待人工填写) |
| 日志截屏 | 空(待人工填写) |
| 备注 | 空(待人工填写) |
---
### 4.4 交互式选择
#### 4.4.1 PRD 列表展示
- 列出 PRD 目录下所有 `.md` 文件
- 按文件名排序显示
- 格式:`序号. 文件名`
#### 4.4.2 多选输入
- 支持格式:`1,2,5``1 2 5`
- 支持逗号或空格分隔
- 自动去重
- 输入验证:序号必须在范围内
#### 4.4.3 自定义名称
1. **Sheet 名称**
- 单 PRD 默认:`{PRD文件名}_用例`
- 多 PRD 默认:`多PRD_用例`
- 用户可覆盖输入
- **名称安全处理**
- 自动过滤 Excel 不允许的字符:`[ ] : * ? / \` 替换为 `_`
- 限制最大长度:**31 字符**(Excel Sheet 名称限制)
2. **输出文件名**
- 单 PRD 默认:`{PRD文件名}`
- 多 PRD 默认:`MultiPRD`
- 自动添加时间戳:`{名称}_{YYYYMMDD_HHMMSS}.xlsx`
---
### 4.5 Excel 输出
#### 4.5.1 表头顺序
序号、功能模块、功能类别、用例编号、功能描述、用例等级、功能编号、用例名称、预置条件、操作步骤、JSON、预期结果、测试结果、测试结论、日志截屏、备注
#### 4.5.2 Sheet 处理
- **表头查找**:在模板的前 30 行中,查找包含至少 2 个关键字(`序号`、`用例名称`、`操作步骤`、`预期结果`)的行作为表头行;若未找到,默认使用第 3 行
- 若 Sheet 不存在:创建新 Sheet,复制模板表头行和样式(字体、填充、边框、对齐、数字格式)
- 若 Sheet 已存在:追加数据(多 PRD 场景),从当前最大行下一行开始写入
- 冻结首行:`B2`
#### 4.5.3 样式设置
- **自动换行**:预置条件、操作步骤列
- **对齐方式**:顶部对齐
- **列宽**:根据内容自适应,最大 60
#### 4.5.4 JSON 输出
- 每个 PRD 单独生成 JSON 文件
- 文件名:`{PRD文件名}_用例.json`
- 存放目录:`AuxiliaryTool/用例生成/config/`
---
## 5. 配置与可变项
### 5.1 命令行参数
脚本支持通过命令行参数覆盖默认配置:
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `--template` | `兰州中石化项目测试用例20251203.xlsx` | 模板 Excel 文件路径 |
| `--module` | `通用模块` | 功能模块字段值(多 PRD 时统一使用该值) |
| `--prefix` | `TC` | 用例编号前缀(多 PRD 时统一使用该值) |
| `--overwrite` | `false` | 是否覆盖保存到模板文件(默认另存为新文件) |
### 5.2 可变配置项
| 配置项 | 默认值 | 说明 |
| --- | --- | --- |
| PRD 目录 | `Docs/开发PRD` 或 `Docs/PRD` | 自动检测,优先使用存在的目录 |
| 模板文件 | `兰州中石化项目测试用例20251203.xlsx` | 位于 `AuxiliaryTool/用例生成/用例文件/` |
| JSON 输出目录 | `config/` | 位于 `AuxiliaryTool/用例生成/` |
| Excel 输出目录 | `用例文件/` | 位于 `AuxiliaryTool/用例生成/` |
---
## 6. 异常处理与容错要求
1. **PRD 目录为空或不存在**:明确报错提示路径
2. **模板文件不存在**:提示文件路径并退出
3. **无效输入**:提示重试,不退出
4. **无可用需求条目**:记录日志,跳过该 PRD
5. **写入失败**:明确提示错误原因
---
## 7. 交付物
1. Python 脚本:
- `AuxiliaryTool/用例生成/create_test_xlsx.py`
2. 配置输出:
- `AuxiliaryTool/用例生成/config/{PRD名}_用例.json`
3. 用例输出:
- `AuxiliaryTool/用例生成/用例文件/{名称}_{时间戳}.xlsx`
---
## 8. 验收标准
1. 能正确解析 PRD 文档中的编号结构
2. 生成的用例分类和优先级符合规则
3. Excel 输出格式与模板一致
4. 支持多 PRD 合并到同一 Sheet
5. 所有操作有明确日志输出
6. 异常情况有友好提示
---
## 9. 使用示例
### 9.1 基本使用(交互式)
```bash
# 执行脚本
python AuxiliaryTool/用例生成/create_test_xlsx.py
# 交互流程
PRD目录:C:\PycharmData\ubains-module-test\Docs\PRD
1. _PRD_预定系统后端服务监测需求文档.md
2. _PRD_其他需求文档.md
支持多选:输入多个序号(如 1,2,5 或 1 2 5)。
请输入PRD序号(可多选):1
请输入Sheet名称(回车使用默认:_PRD_预定系统后端服务监测需求文档_用例):
请输入生成的测试用例文件名称(回车使用默认:_PRD_预定系统后端服务监测需求文档):
# 输出
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_预定系统后端服务监测需求文档_用例.json(15条)
已生成Excel:AuxiliaryTool/用例生成/用例文件/_PRD_预定系统后端服务监测需求文档_20260128_150000.xlsx
Sheet:_PRD_预定系统后端服务监测需求文档_用例
总用例条数:15
```
### 9.2 使用命令行参数
```bash
# 指定功能模块和用例前缀
python AuxiliaryTool/用例生成/create_test_xlsx.py --module "会议系统" --prefix "MT"
# 使用自定义模板文件
python AuxiliaryTool/用例生成/create_test_xlsx.py --template "自定义模板.xlsx"
# 覆盖模式(直接覆盖模板文件,不另存)
python AuxiliaryTool/用例生成/create_test_xlsx.py --overwrite
# 组合使用
python AuxiliaryTool/用例生成/create_test_xlsx.py --module "会议系统" --prefix "MT" --template "自定义模板.xlsx"
```
### 9.3 多 PRD 合并示例
```bash
# 选择多个 PRD
请输入PRD序号(可多选):1 2 3
请输入Sheet名称(回车使用默认:多PRD_用例):
请输入生成的测试用例文件名称(回车使用默认:MultiPRD):
# 输出:三个 PRD 的用例合并在同一个 Sheet 中,序号全局递增
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_预定系统后端服务监测需求文档_用例.json(15条)
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_其他需求文档_用例.json(8条)
已生成 JSON:AuxiliaryTool/用例生成/config/_PRD_第三个文档_用例.json(12条)
已生成Excel:AuxiliaryTool/用例生成/用例文件/MultiPRD_20260128_150000.xlsx
Sheet:多PRD_用例
总用例条数:35
```
---
## 需求规范
代码规范:Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md
问题总结:Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md
方法总结:Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md
文档规范:Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md
测试规范:Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md
# _PRD_根据PRD文档生成测试用例_计划执行.md
> 版本:V1.0
> 创建日期:2026-01-28
> 关联需求:`_PRD_根据PRD文档生成测试用例.md`
> 目标:基于现有脚本实现,输出完整的开发计划与技术规范
---
## 1. 开发概述
### 1.1 脚本基本信息
| 项目 | 内容 |
|------|------|
| 脚本路径 | `AuxiliaryTool/用例生成/create_test_xlsx.py` |
| 编程语言 | Python 3.x |
| 核心依赖 | `openpyxl`(Excel 操作) |
| 入口函数 | `main()` |
### 1.2 功能模块划分
```
create_test_xlsx.py
├── 1) 路径与常量定义
├── 2) PRD 解析与用例生成
│ ├── RequirementItem 数据类
│ ├── normalize_md() - Markdown 预处理
│ ├── extract_requirement_items() - 需求条目抽取
│ ├── classify_category() - 功能类别判定
│ ├── decide_priority() - 用例等级判定
│ ├── build_steps() - 操作步骤生成
│ ├── build_expected() - 预期结果生成
│ └── prd_to_cases() - PRD 转用例
├── 3) Excel 写入
│ ├── find_header_row() - 表头查找
│ ├── safe_sheet_name() - Sheet 名称安全处理
│ └── write_cases_to_sheet() - 写入 Sheet
└── 4) 交互式操作
├── pick_prds_interactively() - PRD 选择
├── ask_sheet_name_interactively() - Sheet 名称输入
└── ask_output_name_interactively() - 输出文件名输入
```
---
## 2. 开发任务清单
### 2.1 阶段一:基础框架(优先级:高)
#### 任务 1.1:路径与常量定义
**目标**:定义所有路径常量和全局配置
**实现要点**
```python
# 路径计算
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
REPO_ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
# PRD 目录候选(兼容两种路径)
PRD_DIR_CANDIDATES = [
os.path.join(REPO_ROOT, "Docs", "开发PRD"),
os.path.join(REPO_ROOT, "Docs", "PRD"),
]
def resolve_prd_dir() -> str:
"""按优先级检测 PRD 目录,返回存在的路径"""
for d in PRD_DIR_CANDIDATES:
if os.path.isdir(d):
return d
return PRD_DIR_CANDIDATES[0] # 默认第一个
```
**表头顺序常量**
```python
headers_order = [
"序号", "功能模块", "功能类别", "用例编号", "功能描述", "用例等级",
"功能编号", "用例名称", "预置条件", "操作步骤", "JSON", "预期结果",
"测试结果", "测试结论", "日志截屏", "备注",
]
```
**验收标准**
- 路径计算正确,跨平台兼容
- `resolve_prd_dir()` 能正确检测存在的目录
---
#### 任务 1.2:命令行参数解析
**目标**:支持命令行参数覆盖默认配置
**实现要点**
```python
import argparse
parser = argparse.ArgumentParser(description="根据PRD生成用例JSON,并写入测试用例Excel")
parser.add_argument("--template", default=TEMPLATE_PATH_DEFAULT, help="模板Excel路径")
parser.add_argument("--module", default="通用模块", help="功能模块字段值")
parser.add_argument("--prefix", default="TC", help="用例编号前缀")
parser.add_argument("--overwrite", action="store_true", help="是否覆盖保存")
args = parser.parse_args()
```
**验收标准**
- `--help` 能正确显示帮助信息
- 各参数能正确覆盖默认值
---
### 2.2 阶段二:PRD 解析(优先级:高)
#### 任务 2.1:数据类定义
**目标**:定义需求条目数据结构
**实现要点**
```python
from dataclasses import dataclass
from typing import List
@dataclass
class RequirementItem:
code: str # 编号(如 "1.1")
title: str # 标题
detail_lines: List[str] # 详情内容行列表
```
---
#### 任务 2.2:正则表达式定义
**目标**:定义编号识别的正则表达式
**实现要点**
```python
# 点分编号:1.1、2.3.1 等
RE_SECTION = re.compile(r"^\s*(?P<code>\d+(?:\.\d+)*)\s*[、\.\-]\s*(?P<title>.+?)\s*[::]?\s*$")
# Markdown 标题:# ## ### 等
RE_MD_HEADING = re.compile(r"^\s*(#{1,6})\s+(?P<title>.+?)\s*$")
```
**测试用例**
| 输入 | 匹配结果 | code | title |
|------|----------|------|-------|
| `1.1 用户登录` | ✓ | `1.1` | `用户登录` |
| `2.3.1-异常处理` | ✓ | `2.3.1` | `异常处理` |
| `## 功能描述` | ✓ | - | `功能描述` |
| `### 接口定义` | ✓ | - | `接口定义` |
---
#### 任务 2.3:Markdown 预处理
**目标**:过滤代码块和 HTML 注释
**实现要点**
```python
def normalize_md(text: str) -> str:
# 统一换行符
text = text.replace("\r\n", "\n").replace("\r", "\n")
# 移除代码块
text = re.sub(r"```.*?```", "", text, flags=re.S)
# 移除 HTML 注释
text = re.sub(r"<!--.*?-->", "", text, flags=re.S)
return text
```
**验收标准**
- 代码块被正确移除
- HTML 注释被正确移除
- 换行符统一为 `\n`
---
#### 任务 2.4:需求条目抽取
**目标**:从 Markdown 文本中抽取需求条目
**核心逻辑**
```python
def extract_requirement_items(md_text: str) -> List[RequirementItem]:
lines = md_text.split("\n")
items: List[RequirementItem] = []
current_code = ""
current_title = ""
current_detail: List[str] = []
def flush():
nonlocal current_code, current_title, current_detail
if current_title:
detail = [clean_line(x) for x in current_detail if clean_line(x)]
items.append(RequirementItem(
code=current_code,
title=clean_line(current_title),
detail_lines=detail
))
current_code = ""
current_title = ""
current_detail = []
for raw in lines:
line = raw.rstrip("\n")
# 匹配点分编号
m = RE_SECTION.match(line)
if m:
flush()
current_code = m.group("code").strip()
current_title = m.group("title").strip()
continue
# 匹配 Markdown 标题
mh = RE_MD_HEADING.match(line)
if mh:
if current_title:
# 已有标题,嵌套标题作为详情
current_detail.append(line)
else:
# 新标题
flush()
current_code = ""
current_title = mh.group("title").strip()
continue
# 详情行
if current_title:
current_detail.append(line)
flush()
# 过滤:标题长度 >= 2
items = [it for it in items if it.title and len(it.title) >= 2]
return items
```
**关键点**
1. 遇到点分编号时,`flush()` 当前条目,开始新条目
2. 遇到 Markdown 标题时:
- 如果已有标题 → 作为详情追加(嵌套处理)
- 如果无标题 → `flush()` 并创建新条目
3. 最后必须 `flush()` 一次
4. 过滤标题长度 < 2 的条目
---
#### 任务 2.5:行清洗
**目标**:清洗单行文本
**实现要点**
```python
def clean_line(line: str) -> str:
line = line.strip()
# 移除列表符号
line = re.sub(r"^[\-\*\+]\s+", "", line)
# 合并多余空格
line = re.sub(r"\s+", " ", line)
return line
```
---
### 2.3 阶段三:用例生成规则(优先级:高)
#### 任务 3.1:功能类别判定
**目标**:根据关键词判定功能类别
**实现要点**
```python
def classify_category(title: str, detail_lines: List[str]) -> str:
text = (title + " " + " ".join(detail_lines)).lower()
if any(k in text for k in ["异常", "失败", "错误", "告警", "报警", "超时", "断开", "暴涨"]):
return "异常场景"
if any(k in text for k in ["安全", "权限", "鉴权", "加密", "脱敏"]):
return "安全/鉴权"
if any(k in text for k in ["报告", "输出", "word", "markdown", "邮件", "钉钉", "通知"]):
return "运维可观测"
return "功能测试"
```
**判定顺序**:异常场景 → 安全/鉴权 → 运维可观测 → 功能测试(默认)
---
#### 任务 3.2:用例等级判定
**目标**:根据关键词判定用例等级
**实现要点**
```python
def decide_priority(title: str, detail_lines: List[str]) -> str:
text = title + " " + " ".join(detail_lines)
if any(k in text for k in ["必须", "报警", "告警", "峰值", "暴涨", "发送", "对接"]):
return "高"
if any(k in text for k in ["待实现", "可以", "建议", "优化"]):
return "中"
return "中" # 默认
```
**判定顺序**:高 → 中(待实现) → 中(默认)
---
#### 任务 3.3:操作步骤生成
**目标**:从详情中提取操作步骤
**实现要点**
```python
def build_steps(detail_lines: List[str]) -> str:
keywords = ["检查", "监测", "记录", "输出", "发送", "查询", "进入", "生成", "判定", "调用", "执行"]
candidates: List[str] = []
for ln in detail_lines:
s = clean_line(ln)
if not s:
continue
if any(k in s for k in keywords):
candidates.append(s)
# 去重
uniq: List[str] = []
for c in candidates:
if c not in uniq:
uniq.append(c)
if not uniq:
return "1. 按 PRD 描述执行该功能/流程; 2. 采集相关日志/输出; 3. 记录实际结果"
uniq = uniq[:6] # 最多 6 条
return "; ".join([f"{i+1}. {x}" for i, x in enumerate(uniq)])
```
**关键点**
- 关键词列表必须完整
- 去重逻辑保证不重复
- 最多 6 条
- 默认值必须准确
---
#### 任务 3.4:预期结果生成
**目标**:从详情中提取预期结果
**实现要点**
```python
def build_expected(title: str, detail_lines: List[str]) -> str:
keywords = ["需要", "应", "必须", "输出", "记录", "发送", "判定", "成功", "失败"]
candidates: List[str] = []
for ln in detail_lines:
s = clean_line(ln)
if not s:
continue
if any(k in s for k in keywords):
candidates.append(s)
# 去重
uniq: List[str] = []
for c in candidates:
if c not in uniq:
uniq.append(c)
if not uniq:
return f"{title} 按 PRD 约定产出正确结果(请补充具体断言点)"
uniq = uniq[:6] # 最多 6 条
return "; ".join(uniq)
```
---
#### 任务 3.5:用例编号生成
**目标**:生成格式化的用例编号
**实现要点**
```python
def make_case_id(prefix: str, idx: int) -> str:
return f"{prefix}-{idx:03d}" # TC-001, TC-002, ...
```
---
#### 任务 3.6:用例记录生成
**目标**:将需求条目转换为用例记录
**实现要点**
```python
def to_case_record(idx: int, module: str, prefix: str, req: RequirementItem) -> Dict[str, str]:
record: Dict[str, str] = {
"序号": idx,
"功能模块": module,
"功能类别": classify_category(req.title, req.detail_lines),
"用例编号": make_case_id(prefix, idx),
"功能描述": req.title,
"用例等级": decide_priority(req.title, req.detail_lines),
"功能编号": req.code or "",
"用例名称": req.title,
"预置条件": "1. 环境已部署; 2. 具备执行权限; 3. 必要依赖/配置已准备",
"操作步骤": build_steps(req.detail_lines),
"JSON": "",
"预期结果": build_expected(req.title, req.detail_lines),
"测试结果": "",
"测试结论": "",
"日志截屏": "",
"备注": "",
}
# 确保所有字段存在
for k in headers_order:
record.setdefault(k, "")
return record
```
---
#### 任务 3.7:PRD 转用例(主函数)
**目标**:读取 PRD 文件并转换为用例列表
**实现要点**
```python
def prd_to_cases(prd_path: str, module: str, prefix: str, start_idx: int = 1) -> List[Dict[str, str]]:
with open(prd_path, "r", encoding="utf-8") as f:
md = normalize_md(f.read())
items = extract_requirement_items(md)
cases: List[Dict[str, str]] = []
idx = start_idx
for it in items:
# 过滤无效标题
if any(k in it.title for k in ["说明", "目录", "背景", "概述"]):
continue
cases.append(to_case_record(idx=idx, module=module, prefix=prefix, req=it))
idx += 1
return cases
```
**关键点**
- `start_idx` 支持多 PRD 全局递增
- 过滤"说明、目录、背景、概述"等无效标题
---
### 2.4 阶段四:Excel 写入(优先级:高)
#### 任务 4.1:表头查找
**目标**:在模板中查找表头行
**实现要点**
```python
def find_header_row(template_sheet) -> int:
must_keys = {"序号", "用例名称", "操作步骤", "预期结果"}
max_scan = min(30, template_sheet.max_row or 30)
for r in range(1, max_scan + 1):
values = []
for c in range(1, min(40, template_sheet.max_column or 40) + 1):
v = template_sheet.cell(row=r, column=c).value
if v is None:
continue
values.append(str(v).strip())
# 至少匹配 2 个关键字
if len(must_keys.intersection(values)) >= 2:
return r
return 3 # 默认第 3 行
```
**关键点**
- 扫描前 30 行
- 匹配至少 2 个关键字
- 默认返回 3
---
#### 任务 4.2:Sheet 名称安全处理
**目标**:确保 Sheet 名称符合 Excel 规范
**实现要点**
```python
def safe_sheet_name(name: str) -> str:
# Excel 不允许的字符: [ ] : * ? / \
name = re.sub(r"[\[\]\:\*\?\/\\]", "_", name)
return name[:31] # 最大 31 字符
```
**Excel Sheet 名称限制**
- 最大长度:31 字符
- 不允许字符:`[ ] : * ? / \`
---
#### 任务 4.3:写入 Sheet
**目标**:将用例写入 Excel Sheet
**实现要点**:
```python
from copy import copy
from openpyxl.styles import Alignment
def write_cases_to_sheet(wb, template_sheet, sheet_name: str, cases: List[Dict[str, str]]):
# Sheet 存在检查
if sheet_name in wb.sheetnames:
ws = wb[sheet_name]
start_row = (ws.max_row or 1) + 1 # 追加模式
else:
ws = wb.create_sheet(sheet_name)
start_row = 2
# 复制表头
header_row_index = find_header_row(template_sheet)
for col_idx, cell in enumerate(template_sheet[header_row_index], start=1):
new_cell = ws.cell(row=1, column=col_idx, value=cell.value)
if cell.has_style:
new_cell.font = copy(cell.font)
new_cell.fill = copy(cell.fill)
new_cell.border = copy(cell.border)
new_cell.alignment = copy(cell.alignment)
new_cell.number_format = cell.number_format
ws.freeze_panes = "B2" # 冻结首行
# 写入数据
row = start_row
for case in cases:
for col_idx, header in enumerate(headers_order, start=1):
val = case.get(header, "")
# 分号转换行(预置条件、操作步骤)
if header in ("预置条件", "操作步骤") and isinstance(val, str):
val = val.replace("; ", "\n")
# JSON 强制清空
if header == "JSON":
val = ""
ws.cell(row=row, column=col_idx, value=val)
row += 1
# 自动换行(预置条件、操作步骤)
col_idx_pre = headers_order.index("预置条件") + 1
col_idx_steps = headers_order.index("操作步骤") + 1
for r in range(1, row):
ws.cell(row=r, column=col_idx_pre).alignment = Alignment(wrap_text=True, vertical="top")
ws.cell(row=r, column=col_idx_steps).alignment = Alignment(wrap_text=True, vertical="top")
# 列宽自适应
for col in ws.columns:
max_len = 0
col_letter = col[0].column_letter
for c in col:
v = c.value
if v is None:
continue
l = len(str(v))
if l > max_len:
max_len = l
ws.column_dimensions[col_letter].width = min(max_len + 2, 60)
return ws
```
**关键点**:
1. Sheet 复用:存在则追加,不存在则创建
2. 表头样式复制:font、fill、border、alignment、number_format
3. 分号转换行:预置条件、操作步骤
4. JSON 强制清空
5. 自动换行:预置条件、操作步骤列
6. 列宽最大 60
---
### 2.5 阶段五:交互式操作(优先级:中)
#### 任务 5.1:列出 PRD 文件
**目标**:列出 PRD 目录下的所有 .md 文件
**实现要点**:
```python
def list_prd_files(prd_dir: str) -> List[str]:
if not os.path.isdir(prd_dir):
return []
fs = [f for f in os.listdir(prd_dir) if f.lower().endswith(".md")]
fs.sort()
return fs
```
---
#### 任务 5.2:解析多选输入
**目标**:解析用户输入的序号
**实现要点**:
```python
def parse_multi_input(s: str) -> List[str]:
s = (s or "").strip()
if not s:
return []
parts = re.split(r"[,\s]+", s) # 逗号或空格分隔
return [p for p in (x.strip() for x in parts) if p]
```
**支持格式**:`1,2,5` 或 `1 2 5` 或 `1, 2, 5`
---
#### 任务 5.3:交互式选择 PRD
**目标**:引导用户选择 PRD 文件
**实现要点**:
```python
def pick_prds_interactively(prd_dir: str) -> List[str]:
files = list_prd_files(prd_dir)
if not files:
raise RuntimeError(f"PRD目录为空或不存在:{prd_dir}")
print(f"PRD目录:{prd_dir}")
for i, f in enumerate(files, start=1):
print(f"{i:>2}. {f}")
print("支持多选:输入多个序号(如 1,2,5 或 1 2 5)。")
while True:
s = input("请输入PRD序号(可多选):").strip()
parts = parse_multi_input(s)
if not parts or not all(p.isdigit() for p in parts):
print("输入无效,请输入序号(可多选)。")
continue
idxs: List[int] = []
ok = True
for p in parts:
n = int(p)
if not (1 <= n <= len(files)):
ok = False
break
idxs.append(n)
if not ok:
print("序号超出范围,请重试。")
continue
# 去重并构建路径
seen = set()
paths: List[str] = []
for n in idxs:
if n in seen:
continue
seen.add(n)
paths.append(os.path.join(prd_dir, files[n - 1]))
return paths
```
**关键点**:
1. 空目录检测
2. 输入验证(数字、范围)
3. 自动去重
---
#### 任务 5.4:输入 Sheet 名称
**目标**:获取 Sheet 名称并安全处理
**实现要点**:
```python
def ask_sheet_name_interactively(default_name: str) -> str:
s = input(f"请输入Sheet名称(回车使用默认:{default_name}):").strip()
return safe_sheet_name(s or default_name)
```
---
#### 任务 5.5:输入输出文件名
**目标**:获取输出文件名
**实现要点**:
```python
def ask_output_name_interactively(default_name: str) -> str:
s = input(f"请输入生成的测试用例文件名称(回车使用默认:{default_name}):").strip()
return s or default_name
```
---
### 2.6 阶段六:主流程集成(优先级:高)
#### 任务 6.1:主函数
**目标**:整合所有模块,实现完整流程
**实现要点**:
```python
from datetime import datetime
def main():
# 1. 解析命令行参数
parser = argparse.ArgumentParser(description="根据PRD生成用例JSON,并写入测试用例Excel")
parser.add_argument("--template", default=TEMPLATE_PATH_DEFAULT, help="模板Excel路径")
parser.add_argument("--module", default="通用模块", help="功能模块字段值")
parser.add_argument("--prefix", default="TC", help="用例编号前缀")
parser.add_argument("--overwrite", action="store_true", help="是否覆盖保存")
args = parser.parse_args()
# 2. 定位 PRD 目录
prd_dir = resolve_prd_dir()
# 3. 检查模板文件
if not os.path.exists(args.template):
print("找不到模板文件:", args.template)
return
# 4. 交互式选择 PRD
prd_paths = pick_prds_interactively(prd_dir)
# 5. 确定 Sheet 名称
if len(prd_paths) == 1:
default_sheet = f"{os.path.splitext(os.path.basename(prd_paths[0]))[0]}_用例"
else:
default_sheet = "多PRD_用例"
sheet_name = ask_sheet_name_interactively(default_sheet)
# 6. 确定输出文件名
if len(prd_paths) == 1:
default_out_base = os.path.splitext(os.path.basename(prd_paths[0]))[0]
else:
default_out_base = "MultiPRD"
out_base = ask_output_name_interactively(default_out_base)
# 7. 打开模板
wb = load_workbook(args.template)
template_sheet = wb.worksheets[0]
# 8. 多 PRD 合并到同一个 sheet(序号全局递增)
os.makedirs(OUTPUT_JSON_DIR, exist_ok=True)
total_cases = 0
next_idx = 1
for prd_path in prd_paths:
prd_base = os.path.splitext(os.path.basename(prd_path))[0]
# 生成用例
cases = prd_to_cases(prd_path=prd_path, module=args.module, prefix=args.prefix, start_idx=next_idx)
if not cases:
print(f"未从PRD抽取到可生成用例的条目:{prd_path}")
continue
# 输出 JSON
json_path = os.path.join(OUTPUT_JSON_DIR, f"{prd_base}_用例.json")
with open(json_path, "w", encoding="utf-8") as f:
json.dump(cases, f, ensure_ascii=False, indent=4)
print(f"已生成 JSON:{json_path}({len(cases)}条)")
# 写入 Sheet
write_cases_to_sheet(wb, template_sheet, sheet_name, cases)
total_cases += len(cases)
next_idx += len(cases)
# 9. 保存 Excel
if args.overwrite:
out_xlsx = args.template
else:
os.makedirs(OUTPUT_XLSX_DIR, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
out_xlsx = os.path.join(OUTPUT_XLSX_DIR, f"{out_base}_{ts}.xlsx")
wb.save(out_xlsx)
print("已生成Excel:", out_xlsx)
print("Sheet:", sheet_name)
print("总用例条数:", total_cases)
if __name__ == "__main__":
main()
```
---
## 3. 测试验证
### 3.1 单元测试
| 模块 | 测试项 | 预期结果 |
|------|--------|----------|
| `resolve_prd_dir()` | 目录存在检测 | 返回存在的目录路径 |
| `normalize_md()` | 代码块过滤 | ````...```` 被移除 |
| `normalize_md()` | HTML 注释过滤 | `<!--...-->` 被移除 |
| `extract_requirement_items()` | 点分编号解析 | 正确提取 code 和 title |
| `extract_requirement_items()` | Markdown 标题解析 | 正确提取 title |
| `extract_requirement_items()` | 嵌套标题处理 | 嵌套标题作为详情 |
| `classify_category()` | 异常场景 | 包含"异常"返回"异常场景" |
| `classify_category()` | 安全/鉴权 | 包含"安全"返回"安全/鉴权" |
| `decide_priority()` | 高等级 | 包含"必须"返回"高" |
| `build_steps()` | 无匹配内容 | 返回默认值 |
| `build_steps()` | 有匹配内容 | 返回分号分隔的步骤 |
| `build_expected()` | 无匹配内容 | 返回默认值 |
| `safe_sheet_name()` | 非法字符过滤 | `[ ] : * ? / \` 替换为 `_` |
| `safe_sheet_name()` | 长度限制 | 超过 31 字符截断 |
| `find_header_row()` | 表头查找 | 返回正确行号 |
### 3.2 集成测试
| 场景 | 操作 | 预期结果 |
|------|------|----------|
| 单 PRD 生成 | 选择 1 个 PRD | 生成 JSON 和 Excel |
| 多 PRD 合并 | 选择多个 PRD | 合并在同一 Sheet,序号递增 |
| Sheet 复用 | 重复生成到同一 Sheet | 数据追加而非覆盖 |
| 覆盖模式 | 使用 `--overwrite` | 覆盖模板文件 |
| 命令行参数 | 使用 `--module``--prefix` | 参数生效 |
### 3.3 边界测试
| 场景 | 操作 | 预期结果 |
|------|------|----------|
| 空 PRD 目录 | 无 .md 文件 | 报错提示 |
| 模板文件不存在 | 指定不存在的模板 | 报错提示 |
| 无效输入 | 输入非数字序号 | 提示重试 |
| 序号超范围 | 输入超出范围的序号 | 提示重试 |
| 无可用条目 | PRD 无有效需求 | 跳过该 PRD |
---
## 4. 依赖关系
### 4.1 模块依赖图
```
main()
├── argparse (命令行解析)
├── resolve_prd_dir() (目录定位)
├── pick_prds_interactively()
│ └── list_prd_files()
│ └── parse_multi_input()
├── ask_sheet_name_interactively()
│ └── safe_sheet_name()
├── ask_output_name_interactively()
├── load_workbook() (openpyxl)
├── prd_to_cases()
│ ├── normalize_md()
│ ├── extract_requirement_items()
│ │ ├── clean_line()
│ │ └── RequirementItem
│ └── to_case_record()
│ ├── classify_category()
│ ├── decide_priority()
│ ├── build_steps()
│ ├── build_expected()
│ └── make_case_id()
├── write_cases_to_sheet()
│ ├── find_header_row()
│ └── Alignment() (openpyxl)
└── wb.save() (openpyxl)
```
### 4.2 开发顺序建议
1. **第一批**:基础框架
- 路径与常量定义
- 命令行参数解析
- 数据类定义
2. **第二批**:PRD 解析
- 正则表达式定义
- Markdown 预处理
- 需求条目抽取
- 行清洗
3. **第三批**:用例生成
- 功能类别判定
- 用例等级判定
- 操作步骤生成
- 预期结果生成
- 用例编号生成
- 用例记录生成
- PRD 转用例主函数
4. **第四批**:Excel 写入
- 表头查找
- Sheet 名称安全处理
- 写入 Sheet
5. **第五批**:交互式操作
- 列出 PRD 文件
- 解析多选输入
- 交互式选择 PRD
- 输入处理函数
6. **第六批**:主流程集成
- 主函数
---
## 5. 关键常量与配置
### 5.1 正则表达式
| 变量 | 表达式 | 用途 |
|------|--------|------|
| `RE_SECTION` | `^\s*(\d+(?:\.\d+)*)\s*[、\.\-]\s*(.+?)\s*[::]?\s*$` | 匹配点分编号 |
| `RE_MD_HEADING` | `^\s*(#{1,6})\s+(.+?)\s*$` | 匹配 Markdown 标题 |
### 5.2 关键词列表
| 用途 | 关键词 |
|------|--------|
| 异常场景 | `["异常", "失败", "错误", "告警", "报警", "超时", "断开", "暴涨"]` |
| 安全/鉴权 | `["安全", "权限", "鉴权", "加密", "脱敏"]` |
| 运维可观测 | `["报告", "输出", "word", "markdown", "邮件", "钉钉", "通知"]` |
| 高等级 | `["必须", "报警", "告警", "峰值", "暴涨", "发送", "对接"]` |
| 中等级 | `["待实现", "可以", "建议", "优化"]` |
| 操作步骤 | `["检查", "监测", "记录", "输出", "发送", "查询", "进入", "生成", "判定", "调用", "执行"]` |
| 预期结果 | `["需要", "应", "必须", "输出", "记录", "发送", "判定", "成功", "失败"]` |
| 过滤标题 | `["说明", "目录", "背景", "概述"]` |
### 5.3 默认值
| 配置项 | 默认值 |
|--------|--------|
| 模板文件 | `兰州中石化项目测试用例20251203.xlsx` |
| 功能模块 | `通用模块` |
| 用例编号前缀 | `TC` |
| 预置条件 | `1. 环境已部署; 2. 具备执行权限; 3. 必要依赖/配置已准备` |
| 操作步骤默认值 | `1. 按 PRD 描述执行该功能/流程; 2. 采集相关日志/输出; 3. 记录实际结果` |
| 预期结果默认值 | `{标题} 按 PRD 约定产出正确结果(请补充具体断言点)` |
| Sheet 单 PRD 默认 | `{PRD文件名}_用例` |
| Sheet 多 PRD 默认 | `多PRD_用例` |
| 输出文件单 PRD 默认 | `{PRD文件名}` |
| 输出文件多 PRD 默认 | `MultiPRD` |
### 5.4 限制值
| 配置项 | 限制值 |
|--------|--------|
| Sheet 名称最大长度 | 31 字符 |
| 表头扫描最大行数 | 30 行 |
| 表头扫描最大列数 | 40 列 |
| 操作步骤最大条数 | 6 条 |
| 预期结果最大条数 | 6 条 |
| 列宽最大值 | 60 |
| 标题最小长度 | 2 字符 |
---
## 6. 验收清单
### 6.1 功能验收
- [ ] 能正确解析点分编号格式(如 `1.1``2.3.1`
- [ ] 能正确解析 Markdown 标题格式(如 `## 标题`
- [ ] 能处理 Markdown 标题嵌套场景
- [ ] 功能类别判定符合规则
- [ ] 用例等级判定符合规则
- [ ] 操作步骤生成符合规则(分号分隔、最多 6 条)
- [ ] 预期结果生成符合规则(分号分隔、最多 6 条)
- [ ] 支持单 PRD 生成
- [ ] 支持多 PRD 合并(序号全局递增)
- [ ] Sheet 复用功能正常(追加模式)
- [ ] JSON 文件正确生成
- [ ] Excel 文件正确生成
- [ ] 分号在 Excel 中转换为换行
- [ ] JSON 列在 Excel 中强制清空
- [ ] Sheet 名称非法字符被过滤
- [ ] Sheet 名称长度限制为 31 字符
- [ ] 命令行参数生效
- [ ] 覆盖模式正常工作
### 6.2 异常验收
- [ ] PRD 目录为空时正确报错
- [ ] 模板文件不存在时正确报错
- [ ] 无效输入时提示重试
- [ ] 序号超范围时提示重试
- [ ] 无可用需求条目时跳过该 PRD
- [ ] 写入失败时有明确提示
---
## 7. 参考资料
### 7.1 需求文档
- `_PRD_根据PRD文档生成测试用例.md`
### 7.2 现有实现
- `AuxiliaryTool/用例生成/create_test_xlsx.py`
### 7.3 依赖库
- `openpyxl`:Excel 文件操作
- Python 标准库:`os`, `re`, `json`, `argparse`, `datetime`, `dataclasses`, `typing`, `copy`
### 7.4 需求规范
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
# 根据PRD需求文档生成用例
## 1. 概述
需求文档生成用例,根据PRD需求文档快速生成测试用例,提升测试效率,确保测试覆盖率。
## 2. 需求文档读取
PRD需求文档统一路径为:Docs/开发PRD目录
执行脚本时通过交互型输入需要生成测试用例的PRD文档编号,并且如果是多选时则生成再同一个sheet表中,sheet表的命名为输入的测试用例的名称。
## 3. 需求文档解析
读取开发的PRD文档,解析出各个功能模块及其对应的测试点,重点关注逻辑层面的测试需求。输出测试用例的初步结构。
## 4. 测试用例生成
根据解析出的测试点,再参考测试用例模板,对测试点进行测试用例的生成,生成对应的测试用例,涵盖功能测试、边界测试、异常测试等。生成的用例存放在:AuxiliaryTool/用例生成/用例文件。
生成的测试用例名称通过交互型输入名称后追加时间来命名。
## 规范获取
代码规范:Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md
问题总结:Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md
方法总结:Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md
文档规范:Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md
测试规范:Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md
\ No newline at end of file
# 容器升级需求说明文档
## 📋 概述
本系统由三个核心 Shell 脚本组成,用于实现容器化服务的远程部署与升级管理:
1. **`remote_update.sh`**:远程升级控制脚本,负责从主服务器向目标服务器传输镜像和部署脚本,并触发远端部署流程,路径:E:\GithubData\自动化\ubains-module-test\辅助工具\脚本工具\远程容器更新\remote_update.sh
2. **`container_update.sh`**:容器部署执行脚本,支持交互式和命令行两种模式,可单独执行进行本地容器部署,路径:E:\GithubData\自动化\ubains-module-test\辅助工具\脚本工具\远程容器更新\container_update.sh
3. **`upload_to_nas.sh`**:镜像打包上传脚本,负责将容器镜像打包并上传至公司 NAS 网盘,路径:E:\GithubData\自动化\ubains-module-test\辅助工具\脚本工具\远程容器更新\upload_to_nas.sh
### 背景
目前系统支持多种容器服务(Java、Redis、EMQX、Python、Nacos、Nginx)的部署与升级,需要区分两种平台环境:
- **新统一平台**:使用 `/data/` 目录结构
- **传统平台**:使用 `/var/www/` 目录结构
**现有功能**
- ✅ 远程升级指定服务器的容器版本 `[已实现]`
1. java容器远程更新(传统平台和新统一平台已验证通过)
2. uemqx容器(传统平台和新统一平台已验证通过)
3. uredis容器(传统平台和新统一平台已验证通过)
4. upython容器()
5. nacos容器(新统一平台已验证通过)
6. nginx容器(新统一平台已验证通过)
7. mysql容器(未验证)
- ✅ 自动校验目标服务器架构(仅支持 x86)`[已实现]`
- ✅ 自动递增容器编号避免命名冲突 `[已实现]`
- ✅ 支持多种容器类型的差异化部署 `[已实现]`
- ✅ 支持预设服务器和手动输入服务器信息(IP/端口/用户名/密码)`[已实现]`
- ✅ EMQX 文件同步(配置、数据、日志目录)`[已实现]`
- ✅ Nginx 文件同步(配置、HTML、证书目录)`[已实现]`
- ✅ 自动校验目标服务器是新统一平台目录还是传统平台目录,通过宿主机上的目录来判断,存在/data/services目录的是新统一平台,不存在的则是传统平台 `[已实现]`
- ✅ 自动校验目标服务器上的容器是否已更新 `[已实现]`
- ✅ 远程更新完成后需要将目标服务器上的镜像包清理 `[已实现]`
- ⏸️ Python 文件同步 `[功能保留,暂不启用]`
- ✅ 将主服务器上的容器镜像及部署脚本打包上传至网盘 `[已实现]`
- ✅ container_update.sh 支持单独执行(交互式模式)`[已实现]`
**待实现功能**
- ❌ 最终将sh脚本改为电脑命令行工具上可执行的脚本格式 `[待最后开发]`
---
## 🎯 功能实现总览
> 最后更新时间:2025-12-07
### 远程升级功能 (`remote_update.sh`)
| 功能模块 | 描述 | 状态 |
|----------|------|------|
| 服务器选择 | 支持预设服务器列表和手动输入(IP/端口/用户名/密码) | ✅ 已实现 |
| 自定义端口 | 支持自定义 SSH 端口(默认 22) | ✅ 已实现 |
| 架构校验 | 校验目标服务器是否为 x86 架构 | ✅ 已实现 |
| 镜像传输 | 自动传输镜像文件和部署脚本 | ✅ 已实现 |
| 容器停止 | 自动停止远端旧容器 | ✅ 已实现 |
| 平台识别 | 自动检测目标服务器平台类型(检测 /data/services 目录) | ✅ 已实现 |
| 版本校验 | 自动校验远端容器镜像版本是否已更新 | ✅ 已实现 |
| EMQX 同步 | 同步 EMQX 配置、数据、日志目录 | ✅ 已实现 |
| Python 同步 | 同步 Python 代码和配置 | ⏸️ 暂停 |
| Nginx 同步 | 同步 Nginx 配置、HTML、证书 | ✅ 已实现 |
| 容器编号 | 自动递增容器编号 | ✅ 已实现 |
| 远端执行 | 调用远端部署脚本 | ✅ 已实现 |
| 镜像清理 | 部署完成后自动清理远端镜像包和部署脚本 | ✅ 已实现 |
### 容器部署功能 (`container_update.sh`)
| 容器类型 | 新平台 | 传统平台 | 备注 |
|----------|--------|----------|------|
| Java (ujava) | ✅ | ✅ | 完整端口映射和目录挂载 |
| Redis (uredis) | ✅ | ✅ | 支持配置迁移和端口释放 |
| EMQX (uemqx) | ✅ | ✅ | 多端口映射,完整目录挂载 |
| Python (upython) | ✅ | ✅ | 容器部署正常,暂不同步文件 |
| Nacos (unacos) | ✅ | ❌ | 仅支持新平台,单机模式 |
| Nginx (unginx) | ✅ | ❌ | 仅支持新平台,完整目录挂载 |
### 交互式部署功能 (`container_update.sh` 交互式模式)
| 步骤 | 功能描述 | 状态 |
|------|----------|------|
| 步骤1 | 询问更新哪个容器 | ✅ 已实现 |
| 步骤2 | 检查压缩包中是否存在所需镜像和配置文件 | ✅ 已实现 |
| 步骤3 | 判断当前服务器是传统平台还是新统一平台 | ✅ 已实现 |
| 步骤4 | 检查并停止正在运行的同类型容器(不删除) | ✅ 已实现 |
| 步骤5 | 备份原有配置文件 | ❌ 待开发 |
| 步骤6 | 更新配置文件并替换IP地址为当前服务器IP | ❌ 待开发 |
| 步骤7 | 执行容器部署操作 | ✅ 已实现 |
| 步骤8 | 验证容器部署是否成功 | ✅ 已实现 |
| 步骤9 | 记录操作日志到当前目录(日志审计) | ✅ 已实现 |
### 镜像上传功能 (`upload_to_nas.sh`)
| 功能模块 | 描述 | 状态 |
|----------|------|------|
| 目录打包 | 将指定目录压缩为 tar.gz 格式 | ✅ 已实现 |
| NAS 挂载 | 自动挂载公司 SMB 网盘 | ✅ 已实现 |
| 进度显示 | 打包和上传过程显示进度条 | ✅ 已实现 |
| 密码加密 | 网盘密码使用 base64 加密存储 | ✅ 已实现 |
### 新增脚本remote_update_win.ps1
> 参考脚本:E:\GithubData\自动化\ubains-module-test\辅助工具\脚本工具\远程容器更新\remote_update.sh
> 新增脚本:E:\GithubData\自动化\ubains-module-test\辅助工具\脚本工具\远程容器更新\remote_update_win.ps1
> 脚本要求:最好能够直接运行,不用安装依赖
| 功能模块 | 描述 | 状态 |
|----------|------|------|
| 服务器选择 | 支持预设服务器列表和手动输入(IP/端口/用户名/密码) | ✅ 已实现 |
| 自定义端口 | 支持自定义 SSH 端口(默认 22) | ✅ 已实现 |
| 架构校验 | 校验目标服务器是否为 x86 架构 | ✅ 已实现 |
| 镜像传输 | 自动传输镜像文件和部署脚本 | ✅ 已实现|
| 容器停止 | 自动停止远端旧容器 | ✅ 已实现 |
| 平台识别 | 自动检测目标服务器平台类型(检测 /data/services 目录) | ✅ 已实现 |
| 版本校验 | 自动校验远端容器镜像版本是否已更新 | ✅ 已实现 |
| EMQX 同步 | 同步 EMQX 配置、数据、日志目录 | ❌ 待开发 |
| Python 同步 | 同步 Python 代码和配置 | ⏸️ 暂停 |
| Nginx 同步 | 同步 Nginx 配置、HTML、证书 | ❌ 待开发 |
| 容器编号 | 自动递增容器编号 | ✅ 已实现 |
| 远端执行 | 调用远端部署脚本 | ✅ 已实现 |
| 日志审计 | 每一步骤的日志都需要记录到log文件中 | ✅ 已实现 |
### 待开发功能
| 功能 | 描述 | 状态 |
|------|------|------|
| 批量部署 | 一次升级多台服务器 | ❌ 待开发 |
| 版本管理 | 记录部署版本和时间 | ❌ 待开发 |
---
## 规范获取
代码规范:Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md
问题总结:Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md
方法总结:Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md
文档规范:Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md
测试规范:Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md
\ No newline at end of file
# _PRD_远程容器升级需求文档
> 版本:V1.0
> 更新日期:2026-01-28
> 适用范围:远程容器升级自动化(Linux服务器容器更新)
> 实现脚本:
> - `远程容器更新/remote_update.sh` (Linux版本)
> - `远程容器更新/remote_container_update_win.ps1` (Windows版本)
> - `远程容器更新/container_update.sh` (目标服务器部署脚本)
---
## 1. 背景与目标
### 1.1 背景
当前容器版本更新需要运维人员手动登录目标服务器,执行一系列操作:上传镜像、停止旧容器、加载新镜像、启动新容器等。这种方式效率低下、容易出错,且无法快速响应多服务器的批量更新需求。
### 1.2 目标
实现一套远程容器自动化更新工具,具备:
- 支持从Linux/Windows本地主机远程更新Linux服务器上的Docker容器
- 支持多种容器类型(Java、EMQX、Redis、Python、Nacos、Nginx)
- 自动识别目标服务器平台类型(新统一平台/传统平台)
- 自动校验远端容器版本,避免重复更新
- 自动同步容器相关配置文件和数据目录
- 完整的日志记录与审计功能
---
## 2. 总体范围
### 2.1 纳入更新对象
1. **支持的容器类型**
| 容器前缀 | 容器描述 | 镜像文件 |
| --- | --- | --- |
| ujava | Java 服务容器 | java1.8.0_472.tar.gz |
| uemqx | EMQX 消息队列容器 | uemqx5.8.4.tar.gz |
| uredis | Redis 缓存容器 | redis8.2.2.tar.gz |
| upython | Python 服务容器 | python_v15.tar.gz |
| unacos | Nacos 注册中心容器 | nacos-server-v2.5.2.tar.gz |
| unginx | Nginx 反向代理容器 | nginx-1.29.3.tar.gz |
**注意**`remote_update.sh` 中 Nginx 镜像文件路径为 `/data/temp/nginx-1.29.3.tar.gz`(绝对路径),与其他容器(相对路径)不同。
2. **预设服务器列表**
- **Linux版本** (remote_update.sh):3台预设服务器
- **Windows版本** (remote_container_update_win.ps1):4台预设服务器
| 编号 | 服务器描述 | IP地址 | 脚本支持 |
| --- | --- | --- | --- |
| 1 | 标准版预定运维服务器 | 192.168.5.48 | Linux + Windows |
| 2 | 阿曼项目预定服务器 | 192.168.5.67 | Linux + Windows |
| 3 | 标准版预定运维测试发布服务器 | 192.168.5.47 | Linux + Windows |
| 4 | 新统一平台测试服务器 | 192.168.5.44 | 仅 Windows |
3. **平台类型**
- **新统一平台**:使用 `/data/` 目录结构
- **传统平台**:使用 `/var/www/` 目录结构
### 2.2 不在本期范围(可扩展)
- 多服务器并发更新(当前为串行更新)
- 容器版本回滚功能
- 更新前自动备份容器数据
- 统一告警通道(钉钉/邮件/短信)
- Web界面操作
---
## 3. 术语说明
| 术语 | 说明 |
| --- | --- |
| 容器前缀 | 容器名称的前缀,如 `ujava``uemqx`,用于标识容器类型 |
| 平台类型 | 目标服务器的部署架构,分为新统一平台和传统平台 |
| 镜像版本校验 | 通过检查远端Docker镜像列表,判断是否已安装目标版本 |
| 容器编号递增 | 新容器名称自动在旧容器编号基础上+1,如 `ujava2``ujava3` |
| sshpass | Linux下的SSH自动密码认证工具 |
| plink/pscp | Windows下的PuTTY工具,用于SSH连接和SCP文件传输 |
---
## 4. 功能需求
### 4.1 日志与审计
#### 4.1.1 日志文件
- **remote_update.sh (Linux版本)**: 控制台输出,无固定日志文件
- **remote_container_update_win.ps1 (Windows版本)**: `脚本目录/logs/remote_update_YYYYMMDD_HHMMSS.log`
- **container_update.sh (部署脚本)**: `脚本目录/container_update_YYYYMMDD_HHMMSS.log`(交互式模式时启用)
#### 4.1.2 日志格式
每行必须包含:
- 时间戳:`[YYYY-MM-DD HH:MM:SS]`
- 级别:`[INFO]` `[WARN]` `[ERROR]` `[AUDIT]`
- 日志内容
示例:
- `[2026-01-28 10:00:00] [INFO] ✅ 远端架构 x86_64 校验通过`
- `[2026-01-28 10:05:00] [AUDIT] Action=DEPLOY_SUCCESS | Server=192.168.5.48 | Container=ujava3 | Details=部署成功`
---
### 4.2 服务器连接与校验
#### 4.2.1 服务器选择
支持两种方式:
1. **预设服务器列表**:从4台预设服务器中选择
2. **手动输入**:手动输入IP地址、SSH端口、用户名、密码
#### 4.2.2 SSH连接测试
- 执行命令:`echo CONNECTION_OK`
- 判定标准:退出码为0 且 输出包含 `CONNECTION_OK`
- 失败处理:提示检查IP、端口、密码、网络连通性
#### 4.2.3 架构校验
- 执行命令:`uname -m`
- 允许的架构:`x86_64``amd64``i386``i686`
- 不满足时终止操作
---
### 4.3 容器选择与镜像检查
#### 4.3.1 容器类型选择
交互式选择容器类型(1-6):
```
[1] ujava - Java 服务容器
[2] uemqx - EMQX 消息队列容器
[3] uredis - Redis 缓存容器
[4] upython - Python 服务容器
[5] unacos - Nacos 注册中心容器
[6] unginx - Nginx 反向代理容器
```
#### 4.3.2 镜像文件检查
- 检查镜像文件是否存在于脚本目录
- 输出镜像文件大小
- 缺失时提示并终止
#### 4.3.3 部署脚本检查
- 检查 `container_update.sh` 是否存在
- 缺失时提示并终止
---
### 4.4 平台类型检测
#### 4.4.1 自动检测
- 检测命令:`[ -d /data/services ] && echo 'NEW_PLATFORM' || echo 'OLD_PLATFORM'`
- 检测到 `/data/services` 目录 → 新统一平台
- 未检测到 → 传统平台
#### 4.4.2 手动确认(自动检测失败时)
- 交互式询问:`目标服务器是否为新统一平台? (y/n)`
#### 4.4.3 平台限制检查
- `unacos``unginx` 仅支持新统一平台
- 在传统平台部署时终止操作
---
### 4.5 远端镜像版本校验
#### 4.5.1 校验逻辑
- 获取远端镜像列表:`docker images --format '{{.Repository}}:{{.Tag}}'`
- 检查是否已存在目标镜像(按容器类型匹配)
- 目标镜像映射:
| 容器 | 目标镜像 |
| --- | --- |
| ujava | 139.9.60.86:5000/ujava:v6 |
| uemqx | 139.9.60.86:5000/uemqx:v2 |
| uredis | 139.9.60.86:5000/redis:v3 |
| upython (新平台) | 139.9.60.86:5000/upython:v15 |
| upython (旧平台) | 139.9.60.86:5000/upython:v14 |
| unacos | nacos-server:v2.5.2 |
| unginx | nginx:1.29.3 |
#### 4.5.2 处理策略
- 若检测到远端已存在目标镜像:
- 检查是否有容器在使用该镜像
- 交互式询问是否继续更新
- 用户选择跳过 → 终止操作
---
### 4.6 文件传输
#### 4.6.1 传输内容
1. 镜像文件(tar.gz格式)
2. 部署脚本 `container_update.sh`
#### 4.6.2 传输方式
- **Linux版本**
- 镜像文件/脚本传输:使用 `scp`
- 目录同步(EMQX/Nginx):优先使用 `rsync`,回退到 `scp`
- **Windows版本**:使用 `pscp`(PuTTY工具)
#### 4.6.3 目标目录
- 默认:`/home/containerUpdate`
- 可通过参数自定义
#### 4.6.4 本地目录配置(用于同步)
不同脚本的本地目录配置有差异:
| 脚本 | EMQX本地目录 | Nginx本地目录 |
| --- | --- | --- |
| remote_update.sh | `/data/middleware/emqx` (绝对路径) | `/data/middleware/nginx` (绝对路径) |
| remote_container_update_win.ps1 | `脚本目录/emqx` (相对路径) | `脚本目录/nginx` (相对路径) |
**说明**
- Linux版本的本地目录是固定的绝对路径,需要在执行前确认文件存在
- Windows版本的本地目录是相对路径,相对于脚本所在目录
---
### 4.7 目录同步(按容器类型)
#### 4.7.1 EMQX容器同步
- **本地目录**`脚本目录/emqx`
- **远端目录**
- 新平台:`/data/middleware/emqx`
- 旧平台:`/var/www/emqx`
- **同步前备份**:自动备份远端目录为 `目录名_backup_时间戳`
- **权限设置**:同步后设置 `config/*.conf` 为可执行
#### 4.7.2 Nginx容器同步
- **本地目录**`脚本目录/nginx`
- **远端目录**`/data/middleware/nginx`(仅新平台)
- **同步前备份**:自动备份远端目录
#### 4.7.3 Python容器同步
- 当前版本:暂不执行文件同步,仅部署容器
- remote_update.sh中代码已实现但未启用(已注释)
---
### 4.8 远端容器更新
#### 4.8.1 停止旧容器
- 查找命令:`docker ps --format '{{.Names}}' | grep -E '^容器前缀([0-9]+)?$' | sort -V | tail -n1`
- 停止命令:`docker stop 容器名`
#### 4.8.2 确定新容器名称
- 提取旧容器编号(如 `ujava2``2`
- 新编号 = 旧编号 + 1
- 若无旧容器,新编号 = 1
#### 4.8.3 调用远端部署脚本
```bash
cd /home/containerUpdate
sed -i 's/\r$//' container_update.sh
chmod +x container_update.sh
./container_update.sh 新容器名 镜像文件路径 [--new-platform]
```
#### 4.8.4 部署脚本功能(container_update.sh)
**运行模式**
- **交互式模式**(无参数):`./container_update.sh`
- 引导选择容器类型
- 自动检测镜像文件(支持多路径搜索)
- 自动检测平台类型
- 显示部署信息摘要
- 确认后执行部署
- **命令行模式**(带参数):`./container_update.sh <容器名> <镜像文件> [--new-platform]`
- 适用于远程调用或自动化脚本
- 容器名:如 ujava3
- 镜像文件:tar包路径(绝对或相对路径)
- --new-platform:目标为新统一平台时添加
**部署逻辑**
- 检查镜像是否存在,不存在则加载
- 停止并移除同名旧容器
- 根据平台类型启动新容器
- 配置端口映射和目录挂载
- 等待容器启动并验证状态
**镜像文件搜索路径**(交互式模式):
1. `脚本目录/镜像文件`
2. `当前工作目录/镜像文件`
3. `/data/temp/镜像文件`
4. `/home/containerUpdate/镜像文件`
5. 当前目录递归搜索(深度2级)
---
### 4.9 清理临时文件
部署完成后清理:
1. 远端镜像包
2. 远端部署脚本 `container_update.sh`
3. 空目录 `/home/containerUpdate`(如果为空)
---
## 5. 主流程
```
┌─────────────────────────────────────────────────────────────┐
│ 远程容器更新流程 │
└─────────────────────────────────────────────────────────────┘
1. [启动] 选择脚本类型(Linux版本 或 Windows版本)
2. [选择] 选择容器类型(ujava/uemqx/uredis/upython/unacos/unginx)
3. [检查] 本地镜像文件是否存在
│ └─ 不存在 → 终止
4. [选择] 选择目标服务器(预设列表 或 手动输入)
5. [校验] SSH连接测试
│ └─ 失败 → 提示检查网络/密码 → 终止
6. [校验] 目标服务器架构(x86)
│ └─ 非x86 → 终止
7. [检测] 平台类型(自动检测 /data/services)
│ └─ 失败 → 手动确认
8. [校验] 平台限制(unacos/unginx仅支持新平台)
│ └─ 不满足 → 终止
9. [校验] 远端镜像版本
│ └─ 已存在 → 询问是否继续 → 否 → 终止
10. [传输] 镜像文件 + 部署脚本 → 远端目录
11. [停止] 远端旧容器
12. [同步] 相关目录(EMQX/Nginx,按容器类型)
│ └─ 同步前备份远端目录
13. [部署] 调用远端部署脚本
│ ├─ 加载镜像
│ ├─ 启动新容器(编号递增)
│ └─ 验证容器状态
14. [清理] 远端临时文件
15. [完成] 输出更新摘要
```
---
## 6. 配置与可变项
| 配置项 | 默认/当前值 | 说明 |
| --- | --- | --- |
| 预设服务器列表 | 4台服务器 | 可在脚本中修改 `ServerList`/`SERVER_IP` |
| SSH端口 | 22 | 手动输入模式可自定义 |
| 远端目录 | /home/containerUpdate | 可通过参数修改 |
| SSH超时 | 30秒 | 可在脚本中修改 `SSH_TIMEOUT` |
| 容器与镜像映射 | 见4.3.1节 | 可在脚本中修改 `ContainerImage` |
### 容器目录映射
| 容器类型 | 平台 | 远端目录 |
| --- | --- | --- |
| ujava | 新平台 | /data/services/api, /data/services/web |
| ujava | 旧平台 | /var/www/java |
| uemqx | 新平台 | /data/middleware/emqx |
| uemqx | 旧平台 | /var/www/emqx |
| uredis | 新平台 | /data/middleware/redis |
| uredis | 旧平台 | /var/www/redis |
| upython | 新平台 | /data/services/api/python-cmdb |
| upython | 旧平台 | /var/www/html |
| unacos | - | /data/middleware/nacos |
| unginx | - | /data/middleware/nginx |
---
## 7. 异常处理与容错要求
1. **SSH连接失败**
- 输出详细错误信息
- 提示检查IP、端口、密码、网络连通性
- 终止操作
2. **架构不匹配**
- 输出远端架构信息
- 提示仅支持x86架构
- 终止操作
3. **镜像文件缺失**
- 提示镜像文件路径
- 终止操作
4. **平台限制不满足**
- 提示容器仅支持的平台类型
- 终止操作
5. **文件传输失败**
- 输出传输错误信息
- 终止操作
6. **远端部署失败**
- 输出远端脚本执行结果
- 保留远端临时文件用于排查
- 终止操作
7. **目录同步失败**
- EMQX/Nginx:回退到使用远端已有配置
- 不影响主流程继续
---
## 8. 交付物
### 8.1 脚本文件
1. **remote_update.sh** - Linux版本远程更新脚本(在本地主机执行)
2. **remote_container_update_win.ps1** - Windows版本远程更新脚本(在本地主机执行)
3. **container_update.sh** - 目标服务器部署脚本(传输到目标服务器后执行)
### 8.2 脚本用途说明
- **remote_update.sh / remote_container_update_win.ps1**:从本地主机远程更新目标服务器上的容器
- 选择目标服务器
- 选择要更新的容器类型
- 传输镜像文件和部署脚本到目标服务器
- 调用目标服务器上的部署脚本
- 支持版本查询(`--version` / `-v`
- **container_update.sh**:在目标服务器上实际执行容器部署
- 支持交互式模式(无参数)和命令行模式(带参数)
- 交互式模式:引导选择容器类型、检测平台类型、自动查找镜像文件
- 命令行模式:`./container_update.sh <容器名> <镜像文件> [--new-platform]`
### 8.3 依赖工具
- **Linux版本**`sshpass`(自动密码认证)
- **Windows版本**`plink.exe``pscp.exe`(PuTTY工具)
- **目标服务器**:Docker
### 8.4 使用说明
1. 将镜像文件和部署脚本放在同一目录
2. 根据操作系统选择对应版本的脚本执行
3. 按交互式提示完成操作
---
## 9. 验收标准
1. **服务器连接**
- 能成功连接预设服务器列表中的所有服务器
- 能通过手动输入连接任意服务器
2. **容器更新**
- 能成功更新所有6种容器类型
- 新容器编号正确递增
- 旧容器正确停止
3. **平台兼容**
- 正确识别新统一平台和传统平台
- 按平台类型使用正确的目录结构
4. **版本校验**
- 检测到远端已安装目标镜像时提示用户
- 用户可选择跳过或继续
5. **目录同步**
- EMQX/Nginx目录正确同步
- 同步前正确备份远端目录
6. **日志审计**
- 关键操作有日志记录
- Windows版本生成审计日志文件
7. **异常处理**
- 各类异常情况有明确提示
- 不会静默失败
---
## 10. 需求规范
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
# 计划执行_远程容器升级脚本开发
> 版本:V1.0
> 创建日期:2026-01-28
> 基于文档:`_PRD_远程容器升级需求文档.md`
> 交付物:
> - `remote_update.sh` (Linux版本)
> - `remote_container_update_win.ps1` (Windows版本)
> - `container_update.sh` (目标服务器部署脚本)
---
## 一、任务概述
开发一套远程容器自动化更新工具,支持从Linux/Windows本地主机远程更新Linux服务器上的Docker容器,涵盖Java、EMQX、Redis、Python、Nacos、Nginx六种容器类型,支持新统一平台和传统平台两种部署架构。
---
## 二、开发阶段划分
### 阶段一:container_update.sh 基础框架(优先级:高)
| 序号 | 任务 | 描述 | 预计产出 |
|------|------|------|----------|
| 1.1 | 创建脚本文件 | 创建 `container_update.sh` | 空壳脚本 |
| 1.2 | 实现日志函数 | `log()``log_to_file()` 函数 | 日志工具 |
| 1.3 | 定义容器配置 | 容器与镜像映射、容器描述 | 配置数组 |
| 1.4 | usage帮助函数 | usage() 函数 | 帮助文档 |
| 1.5 | 工具函数 | `release_port_if_busy()` 端口释放函数 | 端口管理 |
**日志格式规范:**
```bash
[YYYY-MM-DD HH:MM:SS] [INFO] 消息内容
[YYYY-MM-DD HH:MM:SS] [WARN] 警告内容
[YYYY-MM-DD HH:MM:SS] [ERROR] 错误内容
```
---
### 阶段二:container_update.sh 交互式模式(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 2.1 | 容器选择函数 | `select_container()` | 6种容器选择 |
| 2.2 | 文件检查函数 | `check_required_files()` | 多路径搜索镜像文件 |
| 2.3 | 平台检测函数 | `detect_platform()` | 检测 /data/services |
| 2.4 | 容器检查函数 | `check_and_stop_running_container()` | 查找并停止旧容器 |
| 2.5 | 交互式主函数 | `interactive_mode()` | 4步流程编排 |
**镜像文件搜索路径(按优先级):**
```bash
1. 脚本目录/镜像文件
2. 当前工作目录/镜像文件
3. /data/temp/镜像文件
4. /home/containerUpdate/镜像文件
5. 当前目录递归搜索(深度2级)
```
---
### 阶段三:container_update.sh 命令行模式(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 3.1 | 参数解析 | main() 函数参数处理 | 容器名、镜像包、平台标识 |
| 3.2 | 路径处理 | 支持绝对路径和相对路径 | 自动转换为绝对路径 |
| 3.3 | 容器前缀提取 | 正则提取容器前缀 | 匹配 ujava3 → ujava |
**命令行模式用法:**
```bash
./container_update.sh <容器名> <镜像包> [--new-platform]
./container_update.sh ujava3 /data/temp/java1.8.0_472.tar.gz --new-platform
```
---
### 阶段四:container_update.sh 部署函数(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 4.1 | Java容器部署 | `java_newplatform_x86()``java_oldplatform_x86()` | 端口映射、目录挂载 |
| 4.2 | Redis容器部署 | `redis_newplatform_x86()``redis_oldplatform_x86()` | 配置迁移、端口释放 |
| 4.3 | EMQX容器部署 | `emqx_newplatform_x86()``emqx_oldplatform_x86()` | 端口释放 |
| 4.4 | Python容器部署 | `python_newplatform_x86()``python_oldplatform_x86()` | 目录检查、启动脚本 |
| 4.5 | Nacos容器部署 | `nacos_newplatform_x86()` | 仅新平台、端口释放 |
| 4.6 | Nginx容器部署 | `nginx_newplatform_x86()` | 仅新平台、用户创建、权限设置 |
**容器目录挂载(新旧平台对比):**
| 容器类型 | 新平台目录 | 旧平台目录 |
| --- | --- | --- |
| ujava | /data/services/api, /data/services/web | /var/www/java |
| uredis | /data/middleware/redis/config, /data/middleware/redis/data | /var/www/redis/redis-8.2.2.conf, /var/www/redis/data |
| uemqx | /data/middleware/emqx/{config,data,log} | /var/www/emqx/{config,data,log} |
| upython | /data/services/api/python-cmdb | /var/www/html |
| unacos | /data/middleware/nacos → /home/nacos | - |
| unginx | /data/middleware/nginx/{config,data/html,log} | - |
---
### 阶段五:remote_update.sh 基础框架(优先级:高)
| 序号 | 任务 | 描述 | 预计产出 |
|------|------|------|----------|
| 5.1 | 创建脚本文件 | 创建 `remote_update.sh` | 空壳脚本 |
| 5.2 | 实现日志函数 | `log()` 函数 | 日志工具 |
| 5.3 | 版本参数支持 | `--version` / `-v` 参数 | 版本显示 |
| 5.4 | 定义服务器配置 | 3台预设服务器 | 关联数组配置 |
| 5.5 | 定义容器配置 | 容器与镜像映射、Docker镜像版本 | 关联数组配置 |
| 5.6 | SSH工具函数 | `ssh_exec()``scp_exec()` | sshpass封装 |
**服务器配置:**
```bash
# 3台预设服务器
SERVER_IP["1"]="192.168.5.48"
SERVER_USER["1"]="root"
SERVER_PASS["1"]="Ubains@123"
SERVER_DESC["1"]="标准版预定运维服务器"
# ... 服务器2、3 ...
```
---
### 阶段六:remote_update.sh 核心功能(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 6.1 | 容器选择 | 交互式选择6种容器 | 镜像文件映射 |
| 6.2 | 服务器选择 | 预设列表 + 手动输入 | 支持自定义SSH端口 |
| 6.3 | SSH连接测试 | `echo CONNECTION_OK` 验证 | 10秒超时 |
| 6.4 | 架构校验 | `uname -m` 检查 | x86架构验证 |
| 6.5 | 平台类型检测 | 检测 /data/services | 自动识别 + 手动确认 |
| 6.6 | 镜像版本校验 | `check_remote_image_version()` | 避免重复更新 |
| 6.7 | 文件传输 | scp/rsync 传输镜像和脚本 | 30秒超时 |
| 6.8 | 停止旧容器 | docker stop | 模糊匹配容器名 |
| 6.9 | 目录同步 | EMQX/Nginx/Python目录同步 | rsync优先,scp回退 |
**镜像版本映射:**
```bash
# Docker镜像版本(用于版本校验)
CONTAINER_DOCKER_IMAGE["ujava"]="139.9.60.86:5000/ujava:v6"
CONTAINER_DOCKER_IMAGE["uemqx"]="139.9.60.86:5000/uemqx:v2"
CONTAINER_DOCKER_IMAGE["uredis"]="139.9.60.86:5000/redis:v3"
CONTAINER_DOCKER_IMAGE["upython_new"]="139.9.60.86:5000/upython:v15"
CONTAINER_DOCKER_IMAGE["upython_old"]="139.9.60.86:5000/upython:v14"
CONTAINER_DOCKER_IMAGE["unacos"]="nacos-server:v2.5.2"
CONTAINER_DOCKER_IMAGE["unginx"]="nginx:1.29.3"
```
**本地目录配置(注意:remote_update.sh 使用绝对路径):**
```bash
LOCAL_EMQX_DIR="/data/middleware/emqx"
LOCAL_PYTHON_DIR="/data/services/api/python-cmdb"
LOCAL_NGINX_DIR="/data/middleware/nginx"
```
---
### 阶段七:remote_update.sh 目录同步(优先级:中)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 7.1 | EMQX目录同步 | `sync_emqx_assets()` | 备份 + rsync/scp + 权限设置 |
| 7.2 | Nginx目录同步 | `sync_nginx_assets()` | 备份 + rsync/scp |
| 7.3 | Python目录同步 | `sync_python_assets()` | 预留功能,暂不启用 |
**目录同步逻辑(EMQX为例):**
```bash
# 1. 创建远端目录
ssh_exec "mkdir -p '$remote_dir'"
# 2. 备份远端目录
backup_cmd="备份远端目录为 目录名_backup_时间戳"
# 3. 传输文件(优先rsync,回退scp)
if command -v rsync; then
rsync -az --delete -e ssh ...
else
scp -r ...
fi
# 4. 设置配置文件权限
ssh_exec "cd '$remote_dir/config' && chmod +x *.conf"
```
---
### 阶段八:remote_update.sh 远端部署(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 8.1 | 容器编号递增 | 从旧容器编号自动递增 | ujava2 → ujava3 |
| 8.2 | 远端脚本调用 | ssh执行部署脚本 | sed去\r + chmod +x |
| 8.3 | 清理临时文件 | 清理远端镜像包和脚本 | 保留或删除空目录 |
**容器编号递增逻辑:**
```bash
# 1. 查找旧容器
CURRENT_CONTAINER=$(docker ps --format '{{.Names}}' | grep -E '^容器前缀([0-9]+)?$' | sort -V | tail -n1)
# 2. 提取编号
if [[ -n "$CURRENT_CONTAINER" ]]; then
LAST_NUM="${CURRENT_CONTAINER##*[!0-9]}"
NEXT_NUM=$((LAST_NUM + 1))
else
NEXT_NUM=1
fi
# 3. 新容器名
NEW_CONTAINER="${CONTAINER_PREFIX}${NEXT_NUM}"
```
---
### 阶段九:remote_container_update_win.ps1 基础框架(优先级:高)
| 序号 | 任务 | 描述 | 预计产出 |
|------|------|------|----------|
| 9.1 | 创建脚本文件 | 创建 `remote_container_update_win.ps1` | PowerShell脚本 |
| 9.2 | 实现日志函数 | `Write-Log()``Write-AuditLog()` | 日志+审计 |
| 9.3 | UTF-8编码设置 | 解决中文乱码 | 输入输出编码设置 |
| 9.4 | 服务器配置 | 4台预设服务器 | Hashtable配置 |
**日志+审计格式:**
```powershell
# 普通日志
[$timestamp] [INFO] 消息内容
# 审计日志
[$timestamp] [AUDIT] Action=DEPLOY_SUCCESS | Server=192.168.5.48 | Container=ujava3 | Details=部署成功
```
**UTF-8编码设置(重要):**
```powershell
# 解决中文乱码问题
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
$env:PYTHONIOENCODING = "utf-8"
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
```
---
### 阶段十:remote_container_update_win.ps1 依赖检测(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 10.1 | plink/pscp检测 | `Test-Dependencies()` | 本地优先、系统回退 |
| 10.2 | sshpass回退 | Git Bash/WSL/Cygwin环境 | 第三方工具支持 |
| 10.3 | 主机密钥处理 | 首次连接自动接受 | echo y \| 管道处理 |
**PuTTY工具查找顺序(重要):**
```
1. 优先:脚本同目录下的 plink.exe/pscp.exe(离线可用)
2. 回退1:系统 PATH 中的 plink/pscp(已安装PuTTY)
3. 回退2:sshpass(Git Bash/WSL/Cygwin环境)
```
---
### 阶段十一:remote_container_update_win.ps1 核心功能(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 11.1 | SSH命令执行 | `Invoke-SSHCommand()` | plink优先 + sshpass回退 |
| 11.2 | SCP文件传输 | `Send-SCPFile()` | pscp优先 + sshpass回退 |
| 11.3 | SCP目录传输 | `Send-SCPDirectory()` | 递归传输 |
| 11.4 | 容器选择 | `Select-Container()` | 6种容器选择 |
| 11.5 | 服务器选择 | `Select-Server()` | 4台预设 + 手动输入 |
| 11.6 | 平台类型检测 | `Get-PlatformType()` | 自动检测 + 手动确认 |
| 11.7 | 镜像版本校验 | `Test-RemoteImageVersion()` | 容器镜像版本检查 |
| 11.8 | 目录同步 | `Sync-EmqxAssets()``Sync-NginxAssets()` | 备份 + 传输 |
| 11.9 | 远端部署执行 | 带TTY的plink执行 | 实时输出显示 |
**本地目录配置(注意:Windows版本使用相对路径):**
```powershell
# 相对于脚本所在目录
$LOCAL_EMQX_DIR = "emqx"
$LOCAL_NGINX_DIR = "nginx"
```
---
### 阶段十二:异常处理与容错(优先级:高)
| 序号 | 任务 | 描述 | 实现要点 |
|------|------|------|----------|
| 12.1 | SSH连接失败 | 输出详细错误信息 | 检查点清单(IP/端口/密码/网络) |
| 12.2 | 架构不匹配 | 输出远端架构并终止 | 仅支持x86架构 |
| 12.3 | 镜像文件缺失 | 提示路径并终止 | 多路径搜索后仍缺失 |
| 12.4 | 平台限制不满足 | unacos/unginx仅支持新平台 | 终止操作 |
| 12.5 | 文件传输失败 | 输出错误并终止 | scp/rsync失败处理 |
| 12.6 | 远端部署失败 | 保留临时文件用于排查 | 退出码检查 |
| 12.7 | 目录同步失败 | EMQX/Nginx回退到远端配置 | 不影响主流程 |
**退出码定义(建议):**
```bash
# remote_update.sh / remote_container_update_win.ps1
0 # 成功
1 # 通用错误
2 # 镜像文件不存在
3 # 部署脚本不存在
4 # SSH连接失败
5 # 架构不匹配
6 # 用户输入无效
7 # 服务器配置无效
8 # sshpass未安装(Linux版本)
9 # 容器编号无效
10 # 容器未配置镜像映射
11-19 # 各类操作失败(文件传输、目录同步等)
20 # 平台限制不满足
```
---
### 阶段十三:测试与验收(优先级:高)
| 序号 | 测试场景 | 验收标准 | 测试方法 |
|------|----------|----------|----------|
| 13.1 | 服务器连接 | 能成功连接所有预设服务器 | 执行脚本选择各服务器 |
| 13.2 | 容器更新(6种) | 能成功更新所有容器类型 | 逐一测试各容器 |
| 13.3 | 平台兼容 | 新旧平台正确识别和部署 | 选择新旧平台服务器 |
| 13.4 | 版本校验 | 检测到已存在镜像时提示用户 | 先部署一次,再部署 |
| 13.5 | 目录同步 | EMQX/Nginx目录正确同步 | 检查远端目录 |
| 13.6 | 日志审计 | 关键操作有日志记录 | 检查日志文件 |
| 13.7 | 异常处理 | 各异常情况有明确提示 | 模拟各类异常 |
---
## 三、文件结构
### 3.1 remote_update.sh 文件结构
```bash
remote_update.sh
├── 版本与脚本头部
│ ├── SCRIPT_VERSION="1.0.0"
│ └── shebang + set选项
├── 配置变量段
│ ├── REMOTE_ARCH_ALLOW_REGEX # 架构白名单
│ ├── SERVER_IP/USER/PASS/DESC # 3台预设服务器
│ ├── CONTAINER_OPTIONS # 6种容器类型
│ ├── CONTAINER_IMAGE # 容器与镜像文件映射
│ ├── CONTAINER_DOCKER_IMAGE # Docker镜像版本映射
│ ├── LOCAL_EMQX_DIR # 本地EMQX目录(绝对路径)
│ ├── LOCAL_PYTHON_DIR # 本地Python目录(绝对路径)
│ ├── LOCAL_NGINX_DIR # 本地Nginx目录(绝对路径)
│ └── DEFAULT_REMOTE_DIR # 默认远端目录
├── 工具函数段
│ ├── log() # 日志输出
│ ├── ssh_exec() # SSH命令执行(sshpass封装)
│ ├── scp_exec() # SCP文件传输(sshpass封装)
│ ├── sync_emqx_assets() # EMQX目录同步
│ ├── sync_python_assets() # Python目录同步(预留)
│ ├── sync_nginx_assets() # Nginx目录同步
│ └── check_remote_image_version() # 远端镜像版本校验
└── 主流程段
├── 服务器选择
├── 容器选择
├── SSH连接测试
├── 架构校验
├── 平台类型检测
├── 镜像版本校验
├── 文件传输
├── 停止旧容器
├── 目录同步
├── 远端部署脚本调用
└── 清理临时文件
```
### 3.2 remote_container_update_win.ps1 文件结构
```powershell
remote_container_update_win.ps1
├── 版本与脚本头部
│ ├── $SCRIPT_VERSION = "1.2.1"
│ ├── #Requires -Version 5.1
│ └── 参数定义(RemoteDir)
├── UTF-8编码设置
│ ├── [Console]::OutputEncoding
│ ├── [Console]::InputEncoding
│ ├── $env:PYTHONIOENCODING
│ └── $PSDefaultParameterValues
├── 全局配置
│ ├── $REMOTE_ARCH_ALLOW_REGEX
│ ├── $SSH_TIMEOUT
│ ├── $ServerList(4台预设服务器)
│ ├── $ContainerOptions(6种容器)
│ ├── $ContainerDescMap(容器描述)
│ ├── $ContainerImage(镜像文件)
│ ├── $ContainerDockerImage(Docker镜像版本)
│ ├── $LOCAL_EMQX_DIR(相对路径)
│ ├── $LOCAL_NGINX_DIR(相对路径)
│ └── $DEPLOY_SCRIPT
├── 日志与审计
│ ├── Write-Log() # 普通日志(控制台+文件)
│ ├── Write-AuditLog() # 审计日志(仅文件)
│ └── $LOG_FILE # 日志文件路径
├── 依赖检测
│ └── Test-Dependencies() # plink/pscp/sshpass检测
├── SSH与SCP函数
│ ├── Invoke-SSHCommand() # SSH命令执行(plink优先)
│ ├── Send-SCPFile() # SCP文件传输(pscp优先)
│ └── Send-SCPDirectory() # SCP目录传输(递归)
├── 选择器函数
│ ├── Select-Container() # 容器选择
│ └── Select-Server() # 服务器选择
├── 校验与检测函数
│ ├── Test-SSHConnection() # SSH连接测试
│ ├── Test-RemoteArchitecture() # 架构校验
│ ├── Get-PlatformType() # 平台类型检测
│ └── Test-RemoteImageVersion() # 镜像版本校验
├── 同步函数
│ ├── Sync-EmqxAssets() # EMQX目录同步
│ └── Sync-NginxAssets() # Nginx目录同步
├── 操作函数
│ ├── Stop-RemoteContainer() # 停止远端旧容器
│ ├── Get-NewContainerName() # 容器编号递增
│ └── Clear-RemoteFiles() # 清理远端临时文件
└── 主流程段
└── Main() # 主函数
```
### 3.3 container_update.sh 文件结构
```bash
container_update.sh
├── 版本与脚本头部
│ ├── shebang + set选项
│ ├── LOG_FILE(日志文件路径)
│ └── INTERACTIVE_LOG_ENABLED(日志开关)
├── 日志函数
│ ├── log() # 控制台+文件输出
│ └── log_to_file() # 仅文件输出
├── 配置变量
│ ├── CONTAINER_IMAGE_MAP # 容器与镜像文件映射
│ └── CONTAINER_DESC_MAP # 容器描述信息
├── 工具函数
│ └── release_port_if_busy() # 端口释放函数
├── 交互式模式
│ ├── select_container() # 步骤1:选择容器
│ ├── check_required_files() # 步骤2:检查文件
│ ├── detect_platform() # 步骤3:检测平台
│ ├── check_and_stop_running_container() # 步骤4:停止旧容器
│ └── interactive_mode() # 交互式主函数
├── 命令行模式
│ ├── main() # 命令行主函数
│ ├── 参数解析
│ ├── 容器前缀提取
│ └── 部署函数路由
└── 部署函数(10个函数)
├── java_newplatform_x86() # Java新平台
├── java_oldplatform_x86() # Java旧平台
├── redis_oldplatform_x86() # Redis旧平台
├── redis_newplatform_x86() # Redis新平台
├── emqx_oldplatform_x86() # EMQX旧平台
├── emqx_newplatform_x86() # EMQX新平台
├── python_newplatform_x86() # Python新平台
├── python_oldplatform_x86() # Python旧平台
├── nacos_newplatform_x86() # Nacos(仅新平台)
└── nginx_newplatform_x86() # Nginx(仅新平台)
```
---
## 四、依赖与规范
### 4.1 依赖文档
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
### 4.2 外部依赖
#### remote_update.sh(Linux版本)
- `sshpass`:SSH自动密码认证
- `rsync`(可选):高效文件传输
- `scp`:文件传输(rsync回退)
- `ssh`:远程命令执行
- `docker`:目标服务器容器操作
#### remote_container_update_win.ps1(Windows版本)
- `plink.exe`(PuTTY):SSH连接(优先)
- `pscp.exe`(PuTTY):文件传输(优先)
- `sshpass`(可选):Git Bash/WSL/Cygwin环境回退
- PowerShell 5.1+
#### container_update.sh(目标服务器)
- `docker`:容器操作
- `bash`:Shell环境
---
## 五、验收清单
### 5.1 remote_update.sh(Linux版本)
- [ ] 脚本可正常执行,无语法错误
- [ ] 能选择3台预设服务器或手动输入服务器信息
- [ ] 能选择6种容器类型
- [ ] SSH连接测试功能正常
- [ ] 架构校验功能正常(仅x86通过)
- [ ] 平台类型自动检测功能正常(检测 /data/services)
- [ ] 远端镜像版本校验功能正常(已存在镜像时提示)
- [ ] 镜像文件和部署脚本传输功能正常
- [ ] 停止远端旧容器功能正常
- [ ] EMQX/Nginx目录同步功能正常(备份+传输+权限)
- [ ] 容器编号自动递增功能正常
- [ ] 远端部署脚本调用成功
- [ ] 清理远端临时文件功能正常
- [ ] 本地目录配置正确(绝对路径)
### 5.2 remote_container_update_win.ps1(Windows版本)
- [ ] 脚本可正常执行,无语法错误
- [ ] 能选择4台预设服务器或手动输入服务器信息
- [ ] UTF-8编码设置生效(无中文乱码)
- [ ] plink/pscp工具检测功能正常(本地优先+系统回退)
- [ ] SSH命令执行功能正常
- [ ] SCP文件/目录传输功能正常
- [ ] 日志文件生成在 logs/ 目录下
- [ ] 审计日志记录关键操作(SCRIPT_START/DEPLOY_START等)
- [ ] 能选择6种容器类型
- [ ] 架构校验功能正常
- [ ] 平台类型自动检测功能正常
- [ ] 远端镜像版本校验功能正常
- [ ] EMQX/Nginx目录同步功能正常
- [ ] 容器编号自动递增功能正常
- [ ] 远端部署脚本调用成功(带TTY显示实时输出)
- [ ] 清理远端临时文件功能正常
- [ ] 本地目录配置正确(相对路径)
### 5.3 container_update.sh(目标服务器部署脚本)
- [ ] 脚本可正常执行,无语法错误
- [ ] `--version` / `-v` 参数正常显示版本号
- [ ] 交互式模式4步流程正常(选择容器→检查文件→检测平台→停止容器)
- [ ] 命令行模式参数解析正常(容器名+镜像包+平台标识)
- [ ] 能更新所有6种容器类型
- [ ] 新平台和旧平台部署逻辑正确
- [ ] 镜像文件多路径搜索功能正常
- [ ] 容器编号自动递增功能正常
- [ ] 旧容器停止并移除功能正常
- [ ] 镜像加载功能正常(不存在则加载)
- [ ] 端口释放功能正常(Redis/EMQX/Nacos/Nginx)
- [ ] 配置文件迁移功能正常(Redis旧平台)
- [ ] nginx用户和组创建功能正常(Nginx)
- [ ] 容器启动并验证状态功能正常
- [ ] 交互式模式日志文件生成正常
- [ ] unacos/unginx平台限制检查正常
### 5.4 联合测试
- [ ] 从Linux主机能成功更新所有预设服务器
- [ ] 从Windows主机能成功更新所有预设服务器
- [ ] 手动输入服务器信息功能正常
- [ ] 新平台服务器部署功能正常
- [ ] 旧平台服务器部署功能正常
- [ ] unacos/unginx仅在平台限制检查时正确终止
- [ ] EMQX/Nginx目录同步功能正常
- [ ] 容器编号递增功能正常(ujava2→ujava3)
- [ ] 远端镜像已存在时版本校验功能正常
- [ ] Python容器更新时只部署容器不同步文件
---
## 六、部署说明
### 6.1 文件放置
```
远程容器更新/
├── remote_update.sh # Linux版本远程更新脚本
├── remote_container_update_win.ps1 # Windows版本远程更新脚本
├── container_update.sh # 目标服务器部署脚本
├── plink.exe # Windows依赖(可选)
├── pscp.exe # Windows依赖(可选)
├── emqx/ # Windows本地EMQX目录(可选)
├── nginx/ # Windows本地Nginx目录(可选)
├── java1.8.0_472.tar.gz # Java镜像文件
├── uemqx5.8.4.tar.gz # EMQX镜像文件
├── redis8.2.2.tar.gz # Redis镜像文件
├── python_v15.tar.gz # Python镜像文件
├── nacos-server-v2.5.2.tar.gz # Nacos镜像文件
└── nginx-1.29.3.tar.gz # Nginx镜像文件
```
### 6.2 Linux版本部署
```bash
# 1. 将脚本设置为可执行
chmod +x remote_update.sh
# 2. 安装sshpass(如未安装)
sudo apt-get install sshpass
# 或
sudo yum install sshpass
# 3. 将镜像文件放在脚本同目录
# 4. 执行脚本
./remote_update.sh
# 5. 或指定远端目录
./remote_update.sh /data/temp
```
### 6.3 Windows版本部署
```powershell
# 1. 下载 plink.exe 和 pscp.exe
# 下载地址:https://www.chiark.greenend.org/~sgtatham/putty/latest.html
# 或将文件放在脚本同目录
# 2. 打开PowerShell,设置执行策略
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# 3. 执行脚本
.\remote_container_update_win.ps1
# 4. 或指定远端目录
.\remote_container_update_win.ps1 -RemoteDir "/data/temp"
```
### 6.4 目标服务器部署(container_update.sh)
该脚本会自动传输到目标服务器并执行,无需手动部署。
如需单独使用:
```bash
# 交互式模式
./container_update.sh
# 命令行模式
./container_update.sh ujava3 /data/temp/java1.8.0_472.tar.gz --new-platform
```
---
## 七、关键实现要点
### 7.1 容器编号递增逻辑
**需求描述:**
- 查找匹配 `容器前缀([0-9]+)?$` 的最新容器
- 提取容器编号并自动+1
- 若无旧容器,新编号为1
**实现代码:**
```bash
# 查找旧容器
CURRENT_CONTAINER=$(docker ps --format '{{.Names}}' | grep -E '^${CONTAINER_PREFIX}([0-9]+)?$' | sort -V | tail -n1)
# 确定新容器编号
if [[ -n "$CURRENT_CONTAINER" ]]; then
LAST_NUM="${CURRENT_CONTAINER##*[!0-9]}"
if [[ -z "$LAST_NUM" ]]; then
LAST_NUM=0
fi
NEXT_NUM=$((LAST_NUM + 1))
else
NEXT_NUM=1
fi
NEW_CONTAINER="${CONTAINER_PREFIX}${NEXT_NUM}"
```
**测试用例:**
- 无旧容器:ujava1 → ujava2
- 有旧容器:ujava2 → ujava3
- 多版本容器:ujava3 → ujava4
---
### 7.2 平台类型自动检测逻辑
**需求描述:**
- 检测 `/data/services` 目录是否存在
- 存在→新统一平台
- 不存在→传统平台
- 检测失败时切换为手动确认
**实现代码:**
```bash
# 自动检测
PLATFORM_CHECK_OUTPUT="$(ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "[ -d /data/services ] && echo 'NEW_PLATFORM' || echo 'OLD_PLATFORM'" 2>/dev/null || echo "CHECK_FAILED")"
PLATFORM_CHECK_RESULT="$(echo "${PLATFORM_CHECK_OUTPUT}" | tr -d '\r' | awk 'NF {last=$0} END {print last}')"
if [[ "${PLATFORM_CHECK_RESULT}" == "NEW_PLATFORM" ]]; then
PLATFORM_FLAG="--new-platform"
PLATFORM_TYPE="new"
log INFO "✅ 检测到 /data/services 目录存在,自动识别为新统一平台"
elif [[ "${PLATFORM_CHECK_RESULT}" == "OLD_PLATFORM" ]]; then
PLATFORM_FLAG=""
PLATFORM_TYPE="old"
log INFO "✅ 未检测到 /data/services 目录,自动识别为传统平台"
else
log WARN "⚠️ 自动检测平台类型失败,切换为手动确认模式"
read -rp "目标服务器是否为新统一平台? (y/n): " PLATFORM_INPUT
# ... 手动确认逻辑
fi
```
---
### 7.3 远端镜像版本校验逻辑
**需求描述:**
- 检查远端是否已存在目标Docker镜像
- 若存在,检查是否有容器在使用
- 交互式询问是否继续更新
**实现代码:**
```bash
check_remote_image_version() {
local container_prefix="$1"
local platform_type="$2"
local expected_image
# 根据容器类型和平台类型获取期望的镜像名称
if [[ "${container_prefix}" == "upython" ]]; then
if [[ "${platform_type}" == "new" ]]; then
expected_image="${CONTAINER_DOCKER_IMAGE["upython_new"]}"
else
expected_image="${CONTAINER_DOCKER_IMAGE["upython_old"]}"
fi
else
expected_image="${CONTAINER_DOCKER_IMAGE["${container_prefix}"]}"
fi
# 获取远端镜像列表
local remote_images
remote_images="$(ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "docker images --format '{{.Repository}}:{{.Tag}}'" 2>/dev/null || true)"
# 检查镜像是否存在
if echo "${remote_images}" | grep -qw "${expected_image}"; then
log WARN "⚠️ 检测到远端服务器已存在目标镜像: ${expected_image}"
log WARN "⚠️ 目标服务器可能已完成更新"
# 检查是否有容器在使用
local running_container
running_container="$(ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "docker ps --format '{{.Names}}' | grep -E '^${container_prefix}([0-9]+)?$' | head -n1" 2>/dev/null || true)"
if [[ -n "${running_container}" ]]; then
# 获取容器使用的镜像
local container_image
container_image="$(ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "docker inspect --format '{{.Config.Image}}' '${running_container}'" 2>/dev/null || true)"
if [[ "${container_image}" == "${expected_image}" ]]; then
log WARN "⚠️ 检测到容器 ${running_container} 正在使用目标镜像 ${expected_image}"
log WARN "⚠️ 目标服务器该容器已是最新版本,无需更新"
read -rp "是否仍要继续更新? (y/n): " CONTINUE_INPUT
# ... 用户选择逻辑
fi
fi
# ... 交互式询问
else
log INFO "✅ 远端服务器尚未安装目标镜像 ${expected_image},继续更新流程"
return 0
fi
}
```
---
### 7.4 EMQX目录同步完整流程
**需求描述:**
- 本地EMQX目录同步到远端
- 同步前自动备份远端目录
- 使用rsync优先,scp回退
- 同步后设置配置文件权限
**实现代码:**
```bash
sync_emqx_assets() {
local remote_dir="$1"
local platform_label="$2"
# 检查本地目录是否存在
if [[ ! -d "${LOCAL_EMQX_DIR}" ]]; then
log ERROR "本地 EMQX 目录不存在: ${LOCAL_EMQX_DIR}"
exit 11
fi
log INFO "准备同步 EMQX 目录 (${LOCAL_EMQX_DIR} -> ${REMOTE_HOST}:${remote_dir})"
ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "mkdir -p '${remote_dir}'"
# 备份远端目录
local backup_cmd
backup_cmd=$(cat <<'EOF'
set -e
TARGET_DIR="$1"
if [[ -d "${TARGET_DIR}" && -n "$(ls -A "${TARGET_DIR}")" ]]; then
ts=$(date +%Y%m%d_%H%M%S)
backup="${TARGET_DIR}_backup_${ts}"
cp -r "${TARGET_DIR}" "${backup}"
echo "${backup}"
fi
EOF
)
local backup_path
backup_path="$(ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "bash -s '${remote_dir}'" <<<"${backup_cmd}" || true)"
if [[ -n "${backup_path}" ]]; then
log INFO "远端目录已备份到 ${backup_path}"
else
log INFO "远端目录为空或不存在,跳过备份"
fi
# 传输文件(优先rsync,回退scp)
if command -v rsync >/dev/null 2>&1; then
log INFO "使用 rsync 同步 EMQX 目录"
if ! sshpass -p "${REMOTE_PASS}" rsync -az --delete -e "ssh -p ${SSH_PORT} -o StrictHostKeyChecking=no -o ConnectTimeout=${SSH_TIMEOUT}" "${LOCAL_EMQX_DIR}/" "${REMOTE_USER}@${REMOTE_HOST}:${remote_dir}/"; then
log ERROR "rsync 同步失败"
exit 12
fi
else
log WARN "未检测到 rsync,改用 scp 拷贝 EMQX 目录"
if ! scp_exec -r "${LOCAL_EMQX_DIR}/." "${REMOTE_USER}@${REMOTE_HOST}:${remote_dir}/"; then
log ERROR "scp 拷贝失败"
exit 13
fi
fi
# 设置配置文件权限
ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "if [[ -d '${remote_dir}/config' ]]; then cd '${remote_dir}/config' && ls *.conf >/dev/null 2>&1 && chmod +x *.conf || true; else echo 'WARN: config 目录不存在'; fi"
log INFO "远端 EMQX 配置权限处理完成 (${platform_label} 平台)"
}
```
---
### 7.5 Nginx镜像文件特殊处理
**需求描述:**
- Nginx镜像文件使用绝对路径 `/data/temp/nginx-1.29.3.tar.gz`
- 与其他容器(相对路径)不同
**实现代码:**
```bash
# 容器与镜像映射配置
CONTAINER_IMAGE["unginx"]="/data/temp/nginx-1.29.3.tar.gz" # 绝对路径
# nginx 使用绝对路径,需要特殊处理
if [[ "${IMAGE_FILE}" = /* ]]; then
# 绝对路径:先复制到临时位置,再传输
TEMP_IMAGE="/tmp/$(basename "${IMAGE_FILE}")"
cp "${IMAGE_FILE}" "${TEMP_IMAGE}"
scp_exec "${TEMP_IMAGE}" "${DEPLOY_SCRIPT}" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/"
rm -f "${TEMP_IMAGE}"
# 在远端将镜像文件移动到正确位置
ssh_exec "${REMOTE_USER}@${REMOTE_HOST}" "mkdir -p /data/temp && mv '${REMOTE_DIR}/$(basename "${IMAGE_FILE}")' /data/temp/$(basename "${IMAGE_FILE}")"
else
# 相对路径:直接传输
scp_exec "${IMAGE_FILE}" "${DEPLOY_SCRIPT}" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/"
fi
```
---
## 八、进度跟踪
| 阶段 | 状态 | 完成时间 | 备注 |
|------|------|----------|------|
| 阶段一:container_update.sh基础框架 | ✅ 已完成 | 2026-01-28 | 1492行,日志函数、配置变量、版本参数 |
| 阶段二:container_update.sh交互式模式 | ✅ 已完成 | 2026-01-28 | 4步交互流程、多路径镜像搜索 |
| 阶段三:container_update.sh命令行模式 | ✅ 已完成 | 2026-01-28 | 参数解析、路径处理、容器前缀提取 |
| 阶段四:container_update.sh部署函数 | ✅ 已完成 | 2026-01-28 | 10个部署函数(Java×2/Redis×2/EMQX×2/Python×2/Nacos×1/Nginx×1) |
| 阶段五:remote_update.sh基础框架 | ✅ 已完成 | 2026-01-28 | 653行,3台预设服务器、sshpass封装 |
| 阶段六:remote_update.sh核心功能 | ✅ 已完成 | 2026-01-28 | SSH连接、架构校验、平台检测、镜像版本校验、文件传输 |
| 阶段七:remote_update.sh目录同步 | ✅ 已完成 | 2026-01-28 | EMQX/Nginx/Python同步(rsync优先,scp回退) |
| 阶段八:remote_update.sh远端部署 | ✅ 已完成 | 2026-01-28 | 容器递增、远端脚本调用、临时文件清理 |
| 阶段九:remote_container_update_win.ps1基础框架 | ✅ 已完成 | 2026-01-28 | 1387行,4台预设服务器、UTF-8编码设置、日志审计 |
| 阶段十:remote_container_update_win.ps1依赖检测 | ✅ 已完成 | 2026-01-28 | plink/pscp本地优先+系统回退、sshpass回退、主机密钥自动接受 |
| 阶段十一:remote_container_update_win.ps1核心功能 | ✅ 已完成 | 2026-01-28 | SSH/SCP/目录传输、容器/服务器选择器、平台检测、镜像校验、目录同步 |
| 阶段十二:异常处理与容错 | ✅ 已完成 | 2026-01-28 | SSH连接失败、架构不匹配、镜像缺失、平台限制、文件传输失败等 |
| 阶段十三:测试与验收 | ⬜ 待执行 | - | 功能测试、联合测试 |
**脚本规模统计:**
| 脚本 | 行数 | 主要功能 |
|------|------|----------|
| container_update.sh | 1492行 | 目标服务器部署脚本,支持交互式和命令行模式 |
| remote_update.sh | 653行 | Linux版本远程更新脚本,3台预设服务器 |
| remote_container_update_win.ps1 | 1387行 | Windows版本远程更新脚本,4台预设服务器 |
| **合计** | **3532行** | 三份脚本完整实现 |
---
## 九、重要注意事项
### 9.1 脚本间差异总结
| 特性 | remote_update.sh | remote_container_update_win.ps1 | container_update.sh |
| --- | --- | --- | --- |
| 预设服务器数量 | 3台 | 4台 | - |
| 本地EMQX目录 | `/data/middleware/emqx`(绝对路径) | `脚本目录/emqx`(相对路径) | - |
| 本地Nginx目录 | `/data/middleware/nginx`(绝对路径) | `脚本目录/nginx`(相对路径) | - |
| 日志文件 | 无固定日志文件 | `脚本目录/logs/remote_update_*.log` | `脚本目录/container_update_*.log`(交互式) |
| 文件传输方式 | rsync优先,scp回退 | pscp(PuTTY工具) | - |
| SSH认证 | sshpass | plink优先,sshpass回退 | - |
| 主机密钥处理 | 自动接受 | 自动接受(echo y \|) | - |
| UTF-8编码 | 默认UTF-8 | 需设置UTF-8编码 | - |
| Nginx镜像路径 | `/data/temp/nginx-1.29.3.tar.gz`(绝对路径) | `nginx-1.29.3.tar.gz`(相对路径) | - |
### 9.2 开发顺序建议
1. **先开发 container_update.sh**
- 这是基础,其他两个脚本都会调用它
- 可以独立测试部署逻辑
2. **再开发 remote_update.sh**
- Linux版本,相对简单
- 依赖sshpass
3. **最后开发 remote_container_update_win.ps1**
- Windows版本,需要处理编码问题
- 需要处理PuTTY工具兼容性
### 9.3 关键技术点
1. **容器编号递增**:使用 `sort -V` 排序后取最新容器
2. **平台类型检测**:通过检测 `/data/services` 目录
3. **镜像版本校验**:避免重复更新
4. **目录同步备份**:防止配置丢失
5. **UTF-8编码处理**:Windows版本需特殊处理
6. **主机密钥自动接受**:首次连接时的处理
7. **绝对路径vs相对路径**:Nginx镜像文件的特殊处理
### 9.4 测试策略
1. **单元测试**:每个脚本独立功能测试
2. **集成测试**:三个脚本联调测试
3. **平台测试**:新平台和旧平台分别测试
4. **容器测试**:6种容器类型分别测试
5. **异常测试**:模拟各种异常场景
---
*本文档基于 PRD 自动生成,如有疑问请参考原需求文档。*
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论