# 故障排查 ## 通用排查步骤 ```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 ```