Files
iotlcm/docs/06-故障排查.md
T
MiFaZhan 503fe59fe7 docs: 部署文档
- README:项目概览、快速开始、目录结构、重要提示
- 01 环境准备:硬件/系统/Docker 版本、镜像仓库、端口、防火墙、可选外部服务
- 02 配置说明:.env 必填与可选项、10 份应用配置逐项说明、启动前自检
- 03 启动与验证:首次初始化流程、状态检查、6 步验证清单、首次登录必做项
- 04 可选服务:挡板 onelink-mock、飞书 bot、自带 nginx(含 HTTPS)、组合启动
- 05 升级与备份:版本固定、升级回滚、备份脚本与恢复、日志清理、监控指标
- 06 故障排查:启动/功能/性能/网络四类共 20 个问题
2026-10-28 13:54:39 +08:00

293 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 故障排查
## 通用排查步骤
```bash
# 1. 看容器状态
docker compose ps
# 2. 看某个服务的日志
docker compose logs --tail=100 <服务名>
# 3. 跟随日志
docker compose logs -f <服务名>
# 4. 进容器排查
docker compose exec <服务名> sh
```
## 启动类
### MySQL 一直不 healthy
**现象**:`iotlcm-mysql` 显示 `Up (healthrestarting)` 或一直 `starting`,其余服务不启动。
**排查**:
```bash
docker compose logs mysql | tail -50
```
| 日志关键词 | 原因 | 处理 |
| --- | --- | --- |
| `Access denied` | root 口令与旧数据卷中的不一致 | 改 `.env` 的 `MYSQL_ROOT_PASSWORD` 与卷内一致,或清空 `data/mysql` 重建 |
| `Can't create/write to file` | 目录权限问题 | `sudo chown -R 999:999 data/mysql` |
| `No space left on device` | 磁盘满 | 清理磁盘 |
| `mysqld: Can't open shared library` | 架构不匹配 | 确认镜像与宿主机架构一致 |
### schema 初始化失败
**现象**:MySQL 起来了,但表数量不对,或日志中有 SQL 报错。
**排查**:
```bash
docker compose logs mysql | grep -iE 'error|ERROR' | head -30
```
常见原因:
| 原因 | 处理 |
| --- | --- |
| `MYSQL_APP_PASSWORD` 未设置,`04-users.sh` 退出 | 在 `.env` 补上后清空 `data/mysql` 重建 |
| `MYSQL_ADMIN_PASSWORD` 未设置,`05-reset-passwords.sh` 退出 | 同上 |
| SQL 脚本本身语法问题 | 单独执行该脚本定位,见 [schema/README](../schema/README.md) |
::: warning 改脚本后必须清空数据卷
MySQL 只在 `data/mysql` 为空时执行 `schema/`。文件改了不会自动重跑:
```bash
docker compose down && sudo rm -rf ./data/mysql && docker compose up -d
```
:::
### 业务服务反复重启
**排查**:
```bash
docker compose logs --tail=80 admin-svr
```
| 报错 | 原因 | 处理 |
| --- | --- | --- |
| `possible config files "config" not found` | `config/` 目录不存在或挂载失败 | `cp -r config.example config` 并确认 `config/admin-svr/config.yaml` 存在 |
| `dial tcp mysql:3306: connect: connection refused` | MySQL 未就绪 | 等 MySQL healthy,或检查 `depends_on` 是否生效 |
| `Access denied for user 'u_iotlcm'` | 应用账号密码与数据库内不一致 | 确认 `.env` 的 `MYSQL_APP_PASSWORD` 与初始化时一致;改过则需重建库 |
| `panic: unsupported provider xxx` | 数据库通道模板与镜像版本不匹配 | 确认镜像版本与 schema 版本配套 |
## 功能类
### 登录后提示未授权 / 接口 401
**原因**:`JWT_SIGNING_KEY` 在服务间不一致。
**处理**:确认 `admin-svr`、`openapi`、`app-server` 三份配置里的 `jwt.signingKey` 都引用 `${JWT_SIGNING_KEY}`,且 `.env` 里有值。改完重启三个服务:
```bash
docker compose restart admin-svr openapi app-server
```
### 登录成功但页面空白 / 菜单不显示
**排查**:
1. 浏览器 F12 看 Network 有无 4xx/5xx
2. 检查菜单数据:
```bash
docker compose exec mysql sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "SELECT COUNT(*) FROM sys_menu WHERE status=0;"'
```
菜单为 0 说明种子数据未导入,需重建库。
3. 检查角色菜单关联:
```bash
docker compose exec mysql sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "SELECT COUNT(*) FROM sys_role_menu;"'
```
### 卡信息页通道列空白 / 筛选无选项
**原因**:Mock 通道未插入,或通道数据缺失。
**处理**:
```bash
docker compose exec mysql sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "SELECT channel_id, channel_name FROM t_sim_channels;"'
```
为空则手工执行种子脚本:
```bash
docker compose exec -T mysql sh -c \
'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm' < schema/08-seed-mock-channel.sql
```
### 批次执行一直失败
**排查顺序**:
1. **挡板是否启动**(无真实运营商时)
```bash
docker compose ps | grep onelink-mock
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/api.json
```
未启动则:`docker compose --profile with-mock up -d`
2. **worker-svr 是否连上 RabbitMQ**
```bash
docker compose logs worker-svr | grep -i 'rabbit\|amqp' | tail -20
```
3. **upstream-chan 是否能连到上游**
```bash
docker compose logs upstream-chan --tail=50
```
4. **运营商是否限流**
看 `worker-svr` 日志中是否有 `rate limit`、`429`、`12021` 等错误。检查 `batch.inbound.upstreamRateLimit` 配置的 QPS 是否超过上游配额。
### 历史流量查询无数据
**原因**:TDengine 未配置或未连接。
**处理**:
```bash
docker compose logs worker-svr | grep -i 'tdengine\|taos' | tail -20
```
确认 `.env` 中 `TDENGINE_HOST` 等变量已填,且 TDengine 的 WebSocket 端口(默认 6041)可达:
```bash
docker compose exec worker-svr sh -c 'nc -zv $TDENGINE_HOST $TDENGINE_PORT'
```
不用该功能时留空即可,不影响主流程。
### 短信发不出去
**排查**:
```bash
# 1. 短信服务健康
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11190/healthz
# 2. 服务日志
docker compose logs sms --tail=50
```
| 报错 | 原因 |
| --- | --- |
| `InvalidAccessKeyId` | `.env` 里 AK 未填或错误 |
| `isv.SMS_SIGNATURE_ILLEGAL` | `signName` 与服务商审核的签名不一致 |
| `isv.SMS_TEMPLATE_ILLEGAL` | 模板 ID 未在服务商侧审核通过 |
| `缺少 config.templateId` | 场景模板库未配置,调用方需自带 templateId |
### 图片上传失败 / 图片不显示
**原因**:`data/upload` 目录权限或挂载问题。
**处理**:
```bash
ls -la data/upload/
sudo chown -R 1000:1000 data/upload
docker compose restart admin-svr admin-ui
```
## 性能类
### CPU 被 worker-svr 打满
**原因**:定时任务开启了全量同步,大量卡片并发查询上游。
**处理**:
1. 降低并发:
```yaml
batch:
inbound:
upstreamConcurrency: 50 # 从 300 降低
upstreamBatchSize: 1
```
2. 降低限流 QPS,与上游配额匹配
3. 演示/试用环境建议直接停用定时任务:
```bash
docker compose exec mysql sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e "UPDATE sys_job SET status=1;"'
```
种子数据中定时任务默认已全部停用(`status=1`)。
### 磁盘持续增长
按占用从大到小排查:
```bash
du -sh data/* | sort -rh
```
| 目录 | 处理 |
| --- | --- |
| `data/logs` | 清理旧日志,见 [升级与备份](05-升级与备份.md) 的日志清理 |
| `data/upload` | 检查是否有异常大的上传文件 |
| `data/mysql` | 数据库增长正常,关注 binlog:`docker compose exec mysql du -sh /var/lib/mysql` |
| `data/redis` | AOF 文件,正常 |
## 网络类
### 容器间无法互访
```bash
# 查看网络
docker network inspect iotlcm-network
# 从容器内测试
docker compose exec admin-svr sh -c 'nc -zv mysql 3306'
```
服务名即主机名,配置里应使用 `mysql:3306`、`redis:6379` 这样的形式,不要写 `127.0.0.1`。
### 宿主机访问不了 127.0.0.1 绑定的服务
`admin-svr`、`admin-ui`、`openapi`、`app-server`、`app-web`、`docs` 默认只绑定 `127.0.0.1`,仅本机可访问。对外访问需:
1. 用 `--profile with-nginx` 启动反向代理,或
2. 在宿主机配置 nginx 转发到 `127.0.0.1:<端口>`
不建议直接把端口改成 `0.0.0.0` 暴露到公网。
## 获取帮助信息
排查时附上这些信息便于定位:
```bash
# 版本信息
docker compose version
docker version --format '{{.Server.Version}}'
# 服务状态
docker compose ps
# 相关服务日志
docker compose logs --tail=200 <服务名>
# 磁盘与内存
df -h
free -h
```