chore: 并入 iotlcm-docs 历史到 docs/(原提交 947e22e)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: 其他部署方式
|
||||
description: 二进制部署、自建镜像、分离部署与离线部署
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 其他部署方式
|
||||
|
||||
[启动与验证](install) 描述的是 **Docker Compose 单机部署**,也是推荐方式。本组文档覆盖其他场景。
|
||||
|
||||
## 方式对比
|
||||
|
||||
| 方式 | 适用场景 | 复杂度 | 参考 |
|
||||
| --- | --- | --- | --- |
|
||||
| **单机 Compose**(推荐) | 试用、中小规模生产 | 低 | [启动与验证](install) |
|
||||
| **二进制部署** | 不能装 Docker,或需纳入 systemd 体系 | 中 | [二进制部署](binary) |
|
||||
| **自建镜像** | 需改代码、加依赖、适配私有基础镜像 | 中 | [自建镜像](build-image) |
|
||||
| **分离部署** | 数据库走云托管,或中间件已有独立集群 | 中 | [分离部署](split) |
|
||||
| **离线部署** | 服务器无法访问镜像仓库 | 中 | [离线部署](offline) |
|
||||
|
||||
## 服务依赖关系
|
||||
|
||||
选择部署方式前需了解各服务依赖哪些组件:
|
||||
|
||||
| 服务 | 依赖 |
|
||||
| --- | --- |
|
||||
| admin-svr | MySQL、Redis、RabbitMQ |
|
||||
| openapi | MySQL、Redis、RabbitMQ |
|
||||
| worker-svr | MySQL、Redis、RabbitMQ |
|
||||
| app-server | MySQL、Redis |
|
||||
| upstream-chan | MySQL、Redis、Consul |
|
||||
| upstream-proxy | MySQL、Redis、Consul |
|
||||
| admin-ui | admin-svr(反向代理) |
|
||||
| nginx | admin-ui、openapi、app-web、docs |
|
||||
| sms | 无 |
|
||||
| docs | 无 |
|
||||
| onelink-mock | 无 |
|
||||
|
||||
## 通用前提
|
||||
|
||||
无论哪种方式,这三项不变:
|
||||
|
||||
1. **配置文件必须能被服务读到**——容器部署时挂载到 `/app/config`,二进制部署时放在工作目录的 `config/`
|
||||
2. **`JWT_SIGNING_KEY` 在 admin-svr、openapi、app-server 三处必须一致**
|
||||
3. **数据库需先完成初始化**——`schema/` 脚本按顺序执行一次
|
||||
|
||||
## 服务间通信方式
|
||||
|
||||
配置文件里通过**服务名**互访,例如:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
default:
|
||||
link: "mysql:u_iotlcm:...@tcp(mysql:3306)/db_iotlcm"
|
||||
upstream:
|
||||
proxy:
|
||||
address: "upstream-proxy:8510"
|
||||
```
|
||||
|
||||
这些名称在单机 Compose 中由 Docker 网络解析。**切换到二进制部署或分离部署时必须改为实际地址**(`127.0.0.1` 或内网 IP),这是最容易遗漏的一步。
|
||||
|
||||
::: danger 最常见的部署失败原因
|
||||
只改了数据库地址,忘了改 `upstream-proxy:8510`、`upstream-chan:8110` 这类服务名。症状是服务能启动、数据库也通,但批次执行、智能诊断全部超时。
|
||||
:::
|
||||
@@ -0,0 +1,321 @@
|
||||
---
|
||||
title: 二进制部署
|
||||
description: 直接编译 Go 程序运行,不依赖 Docker
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 二进制部署
|
||||
|
||||
不使用容器,把各服务编译为可执行文件,用 systemd 托管。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 服务器不允许安装 Docker
|
||||
- 需要把服务纳入既有的 systemd / 进程管理体系统一运维
|
||||
- 需要针对特定 CPU 架构或内核参数做优化
|
||||
|
||||
## 构建环境
|
||||
|
||||
| 项 | 要求 |
|
||||
| --- | --- |
|
||||
| Go | 按各服务 `go.mod` 要求,最低 1.23(见下表) |
|
||||
| GCC | admin-svr 需要(`CGO_ENABLED=1`,内存库用 SQLite) |
|
||||
| 系统 | 建议与目标服务器同发行版,避免 libc 差异 |
|
||||
|
||||
```bash
|
||||
# Debian / Ubuntu
|
||||
apt-get install -y build-essential
|
||||
|
||||
# CentOS / Rocky
|
||||
yum groupinstall -y "Development Tools"
|
||||
```
|
||||
|
||||
::: tip 其余服务可静态编译
|
||||
除 admin-svr 外,其余服务 `CGO_ENABLED=0`,产物为静态二进制,可直接拷到任意同架构 Linux 运行。
|
||||
:::
|
||||
|
||||
## 各服务构建参数
|
||||
|
||||
| 服务 | 仓库 | Go 版本 | CGO | 构建目录 | 产物名 | 监听端口 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| admin-svr | `iotlcm-admin-svr` | 1.23 | **需开启** | `server/` | `iotlcmadmin_svc` | 8008 |
|
||||
| openapi | `iotlcm-openapi` | 1.23 | 关闭 | 根目录 | `iotlcm-openapi` | 8000 |
|
||||
| worker-svr | `iotlcm-worker-svr` | 1.25 | 关闭 | 根目录 | `iotlcm-worker-svr` | 无(队列消费) |
|
||||
| upstream-chan | `iotlcm-upstream-chan` | 1.25 | 关闭 | 根目录 | `iotlcmupstreamchan_svc` | 8110 / 8310 |
|
||||
| upstream-proxy | `iotlcm-upstream-proxy` | 1.25 | 关闭 | 根目录 | `iotlcmupstreamproxy_svc` | 8510 |
|
||||
| app-server | `iotlcm-app` | 1.23 | 关闭 | `server/` | `iotlcm-app-server` | 8010 |
|
||||
| sms | `iotlcm-sms` | 1.25 | 关闭 | 根目录 | `iotlcm-sms` | 11190 / 11200 |
|
||||
| bot | `iotlcm-bot` | 1.23 | 关闭 | `cmd/iotlcm-bot` | `iotlcm-bot` | 无 |
|
||||
|
||||
::: warning 仓库里的 Dockerfile 不能直接照搬
|
||||
部分仓库的 Dockerfile 基础镜像指向私有仓库(见[自建镜像](build-image)),二进制构建不受影响,但 `FROM` 行需要替换。
|
||||
:::
|
||||
|
||||
## 构建
|
||||
|
||||
### admin-svr(需要 CGO)
|
||||
|
||||
```bash
|
||||
git clone https://git.5iot.com/iotlcm-public/iotlcm-admin-svr.git
|
||||
cd iotlcm-admin-svr/server
|
||||
|
||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath \
|
||||
-ldflags "-s -w \
|
||||
-X main.BuildVersion=$(git describe --tags --always) \
|
||||
-X main.CommitID=$(git rev-parse --short HEAD) \
|
||||
-X main.BuildTime=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
|
||||
-o iotlcmadmin_svc .
|
||||
```
|
||||
|
||||
### 其余服务(静态编译)
|
||||
|
||||
```bash
|
||||
git clone https://git.5iot.com/iotlcm-public/iotlcm-openapi.git
|
||||
cd iotlcm-openapi
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath \
|
||||
-ldflags "-s -w \
|
||||
-X main.BuildVersion=$(git describe --tags --always) \
|
||||
-X main.CommitID=$(git rev-parse --short HEAD) \
|
||||
-X main.BuildTime=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
|
||||
-o iotlcm-openapi .
|
||||
```
|
||||
|
||||
替换仓库名与产物名即可用于其他服务,`bot` 需要指定子目录:
|
||||
|
||||
```bash
|
||||
cd iotlcm-bot
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o iotlcm-bot ./cmd/iotlcm-bot
|
||||
```
|
||||
|
||||
### 交叉编译说明
|
||||
|
||||
| 服务 | 能否交叉编译 |
|
||||
| --- | --- |
|
||||
| admin-svr(CGO) | ❌ 需在目标架构上编译,或用对应架构的交叉工具链 |
|
||||
| 其余(静态) | ✅ 可直接 `GOOS=linux GOARCH=arm64` |
|
||||
|
||||
查看服务器架构:
|
||||
|
||||
```bash
|
||||
uname -m # x86_64 → amd64;aarch64 → arm64
|
||||
```
|
||||
|
||||
## 部署布局
|
||||
|
||||
```text
|
||||
/opt/iotlcm/
|
||||
├── admin-svr/
|
||||
│ ├── iotlcmadmin_svc
|
||||
│ ├── config/config.yaml ← 从 config/admin-svr 复制
|
||||
│ ├── logs/
|
||||
│ ├── upload/
|
||||
│ └── cache/
|
||||
├── openapi/
|
||||
│ ├── iotlcm-openapi
|
||||
│ ├── config/config.yaml
|
||||
│ └── logs/
|
||||
├── worker-svr/
|
||||
│ ├── iotlcm-worker-svr
|
||||
│ ├── config/config.yaml
|
||||
│ └── logs/
|
||||
├── upstream-chan/
|
||||
├── upstream-proxy/
|
||||
├── app-server/
|
||||
│ ├── iotlcm-app-server
|
||||
│ ├── config/config.yaml
|
||||
│ └── resource/
|
||||
│ ├── log/server/
|
||||
│ └── upload/
|
||||
├── sms/
|
||||
└── bot/
|
||||
```
|
||||
|
||||
**配置文件放 `config/config.yaml`**(相对工作目录),这是 GoFrame 的默认搜索路径。启动时工作目录必须在服务目录内。
|
||||
|
||||
### 目录准备
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/iotlcm/{admin-svr,openapi,worker-svr,upstream-chan,upstream-proxy,app-server,sms,bot}
|
||||
sudo useradd -r -s /sbin/nologin iotlcm
|
||||
sudo chown -R iotlcm:iotlcm /opt/iotlcm
|
||||
```
|
||||
|
||||
## 修改配置中的服务地址
|
||||
|
||||
容器部署时配置里写的是服务名(`mysql:3306`、`upstream-proxy:8510`),二进制部署必须改为实际地址:
|
||||
|
||||
```yaml
|
||||
# admin-svr / openapi / worker-svr / app-server
|
||||
database:
|
||||
default:
|
||||
link: "mysql:u_iotlcm:<口令>@tcp(127.0.0.1:3306)/db_iotlcm?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
|
||||
redis:
|
||||
default:
|
||||
address: "127.0.0.1:6379"
|
||||
rabbitmq:
|
||||
dsn: "amqp://<用户>:<口令>@127.0.0.1:5672/"
|
||||
|
||||
# upstream-chan / upstream-proxy
|
||||
registry:
|
||||
address: "127.0.0.1:8500"
|
||||
|
||||
# 上游服务地址
|
||||
upstream:
|
||||
proxy:
|
||||
address: "127.0.0.1:8510"
|
||||
gateway:
|
||||
address: "127.0.0.1:8110"
|
||||
```
|
||||
|
||||
::: danger logger.path 也要改
|
||||
配置里的 `logger.path` 在容器版本中是 `/app/logs`,二进制部署要改为实际路径,例如 `/opt/iotlcm/admin-svr/logs`,否则会因目录不存在启动失败。
|
||||
:::
|
||||
|
||||
## systemd 托管
|
||||
|
||||
以为 `admin-svr` 为例,保存为 `/etc/systemd/system/iotlcm-admin-svr.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=IotLCM Admin Service
|
||||
After=network-online.target mysql.service redis.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=iotlcm
|
||||
Group=iotlcm
|
||||
WorkingDirectory=/opt/iotlcm/admin-svr
|
||||
ExecStart=/opt/iotlcm/admin-svr/iotlcmadmin_svc
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
LimitNOFILE=65535
|
||||
|
||||
# 环境变量(JWT 密钥等敏感值建议放独立文件)
|
||||
EnvironmentFile=-/etc/iotlcm/admin-svr.env
|
||||
|
||||
# 日志交给 journald,也可用配置里的文件日志
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
# 安全加固
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/iotlcm/admin-svr
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
`/etc/iotlcm/admin-svr.env`:
|
||||
|
||||
```bash
|
||||
JWT_SIGNING_KEY=<与其它服务一致的密钥>
|
||||
```
|
||||
|
||||
启用:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now iotlcm-admin-svr
|
||||
sudo systemctl status iotlcm-admin-svr
|
||||
```
|
||||
|
||||
### 各服务的差异点
|
||||
|
||||
| 服务 | 特殊处理 |
|
||||
| --- | --- |
|
||||
| admin-svr | 需 `ReadWritePaths` 包含 upload 与 logs 目录 |
|
||||
| worker-svr | 无监听端口,`After` 需包含 rabbitmq |
|
||||
| upstream-chan / proxy | `After` 需包含 consul |
|
||||
| app-server | 需 `resource/log/server` 与 `resource/upload` 可写 |
|
||||
| bot | `ExecStart` 需带参数:`/opt/iotlcm/bot/iotlcm-bot watch --config /opt/iotlcm/bot/config/config.yaml` |
|
||||
|
||||
### 启动顺序
|
||||
|
||||
```text
|
||||
MySQL / Redis / RabbitMQ / Consul
|
||||
↓
|
||||
admin-svr、openapi、worker-svr、upstream-chan、upstream-proxy、app-server、sms
|
||||
↓
|
||||
admin-ui、app-web、docs(静态文件,由 nginx 托管)
|
||||
```
|
||||
|
||||
用 systemd 依赖表达:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
After=iotlcm-admin-svr.service
|
||||
Requires=iotlcm-admin-svr.service
|
||||
```
|
||||
|
||||
## 静态前端
|
||||
|
||||
`admin-ui`、`app-web`、`docs` 是静态文件,二进制部署时不需要跑容器,把构建产物交给 nginx:
|
||||
|
||||
```bash
|
||||
# admin-ui:需 Node 构建
|
||||
cd iotlcm-admin-ui/vue
|
||||
npm ci && npm run build
|
||||
sudo cp -r apps/web-antd/dist /var/www/iotlcm/admin-ui
|
||||
```
|
||||
|
||||
nginx 配置参考部署仓的 `config/admin-ui/default.conf` 与 `config/nginx/iotlcm.conf`,把 `proxy_pass http://admin-svr:8008` 改为 `http://127.0.0.1:8008`。
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
# 进程状态
|
||||
systemctl status iotlcm-admin-svr
|
||||
journalctl -u iotlcm-admin-svr -n 50 --no-pager
|
||||
|
||||
# 端口监听
|
||||
ss -lntp | grep -E '8008|8000|8510|8110'
|
||||
|
||||
# 接口
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8008/api.json
|
||||
```
|
||||
|
||||
## 与容器部署的差异
|
||||
|
||||
| 项 | 容器部署 | 二进制部署 |
|
||||
| --- | --- | --- |
|
||||
| 配置路径 | `/app/config`(挂载) | `<服务目录>/config` |
|
||||
| 日志路径 | `/app/logs` | 需在配置里改成实际路径 |
|
||||
| 服务互访 | 服务名(Docker DNS) | `127.0.0.1` 或内网 IP |
|
||||
| 进程管理 | Docker restart policy | systemd |
|
||||
| 升级 | 换镜像 + `up -d` | 停服务 → 换二进制 → 起服务 |
|
||||
| 时区 | 镜像已内置 | 依赖宿主机 tzdata |
|
||||
|
||||
## 升级流程
|
||||
|
||||
```bash
|
||||
# 1. 备份配置与程序
|
||||
sudo tar czf /backup/iotlcm-admin-svr-$(date +%F).tar.gz -C /opt/iotlcm admin-svr
|
||||
|
||||
# 2. 停服务
|
||||
sudo systemctl stop iotlcm-admin-svr
|
||||
|
||||
# 3. 替换二进制
|
||||
sudo cp iotlcmadmin_svc /opt/iotlcm/admin-svr/
|
||||
sudo chown iotlcm:iotlcm /opt/iotlcm/admin-svr/iotlcmadmin_svc
|
||||
|
||||
# 4. 起服务并验证
|
||||
sudo systemctl start iotlcm-admin-svr
|
||||
sudo systemctl status iotlcm-admin-svr
|
||||
```
|
||||
|
||||
## 回滚
|
||||
|
||||
保留上一版二进制,回滚只需换回:
|
||||
|
||||
```bash
|
||||
sudo systemctl stop iotlcm-admin-svr
|
||||
sudo cp /opt/iotlcm/admin-svr/iotlcmadmin_svc.bak /opt/iotlcm/admin-svr/iotlcmadmin_svc
|
||||
sudo systemctl start iotlcm-admin-svr
|
||||
```
|
||||
@@ -0,0 +1,292 @@
|
||||
---
|
||||
title: 自建镜像
|
||||
description: 从源码构建各服务的 Docker 镜像
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 自建镜像
|
||||
|
||||
平台镜像由官方仓库分发。如有定制需求(改代码、加依赖、适配私有基础镜像),可从源码自行构建。
|
||||
|
||||
## 构建环境
|
||||
|
||||
| 项 | 要求 |
|
||||
| --- | --- |
|
||||
| Docker | 20.10 以上 |
|
||||
| 网络 | 需访问基础镜像仓库与 Go/npm 依赖源 |
|
||||
| 磁盘 | 单服务构建约 1~2 GB(含构建缓存) |
|
||||
|
||||
## 各服务构建文件
|
||||
|
||||
| 服务 | 仓库 | Dockerfile | 构建上下文 |
|
||||
| --- | --- | --- | --- |
|
||||
| admin-svr | `iotlcm-admin-svr` | `server/build/Dockerfile` | `server/` |
|
||||
| openapi | `iotlcm-openapi` | `build/Dockerfile` | 仓库根目录 |
|
||||
| worker-svr | `iotlcm-worker-svr` | `build/Dockerfile` | 仓库根目录 |
|
||||
| upstream-chan | `iotlcm-upstream-chan` | `build/Dockerfile` | 仓库根目录 |
|
||||
| upstream-proxy | `iotlcm-upstream-proxy` | `build/Dockerfile` | 仓库根目录 |
|
||||
| app-server | `iotlcm-app` | `server/Dockerfile` | `server/` |
|
||||
| app-web | `iotlcm-app` | `app/Dockerfile` | `app/` |
|
||||
| admin-ui | `iotlcm-admin-ui` | `vue/build/Dockerfile` | `vue/` |
|
||||
| sms | `iotlcm-sms` | `build/Dockerfile` | 仓库根目录 |
|
||||
| bot | `iotlcm-bot` | `Dockerfile` | 仓库根目录 |
|
||||
| docs | `iotlcm-docs` | `Dockerfile` | 仓库根目录 |
|
||||
| onelink-mock | `iotlcm-onelink-mock` | `Dockerfile` | 仓库根目录 |
|
||||
|
||||
## ⚠️ 先处理私有基础镜像
|
||||
|
||||
**当前部分仓库的 Dockerfile 仍指向私有镜像仓库**,外部环境无法拉取,直接构建会失败:
|
||||
|
||||
| 仓库 | 文件 | 问题 |
|
||||
| --- | --- | --- |
|
||||
| `iotlcm-openapi` | `manifest/docker/Dockerfile` | `FROM` 指向私有仓库 |
|
||||
| `iotlcm-app` | `app/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
|
||||
| `iotlcm-app` | `server/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
|
||||
| `iotlcm-admin-ui` | `vue/build/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
|
||||
| `iotlcm-admin-ui` | `vue/build/DockerfileNoBuild` | `FROM` 指向私有仓库 |
|
||||
|
||||
构建前把这些 `FROM` 改为公共镜像:
|
||||
|
||||
```dockerfile
|
||||
# 修改前(私有仓库,外部不可达)
|
||||
FROM crpi-xxxxxxxx.cn-beijing.personal.cr.aliyuncs.com/xxx/golangalpine:1.25.7-alpine3.23 AS builder
|
||||
|
||||
# 修改后(公共镜像)
|
||||
FROM golang:1.25-alpine AS builder
|
||||
```
|
||||
|
||||
```dockerfile
|
||||
FROM crpi-xxxxxxxx.cn-beijing.personal.cr.aliyuncs.com/xxx/nginx:1.30.0-alpine
|
||||
|
||||
# 改为
|
||||
FROM nginx:1.30-alpine
|
||||
```
|
||||
|
||||
::: tip 其余仓库的 Dockerfile 已可用
|
||||
`admin-svr`、`worker-svr`、`upstream-chan`、`upstream-proxy`、`sms`、`bot`、`docs`、`onelink-mock` 的 Dockerfile 基础镜像均为公共镜像,可直接构建。
|
||||
:::
|
||||
|
||||
## 构建命令
|
||||
|
||||
### 后端服务
|
||||
|
||||
```bash
|
||||
COMMIT=$(git rev-parse --short HEAD)
|
||||
VERSION=$(git describe --tags --always)
|
||||
BUILD_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
docker build \
|
||||
--build-arg BuildVersion=$VERSION \
|
||||
--build-arg CommitID=$COMMIT \
|
||||
--build-arg BuildTime=$BUILD_TIME \
|
||||
-t your-registry/iotlcm/iotlcm:iotlcm-openapi-$VERSION \
|
||||
.
|
||||
```
|
||||
|
||||
`admin-svr` 与 `app-server` 需指定上下文目录:
|
||||
|
||||
```bash
|
||||
cd iotlcm-admin-svr
|
||||
docker build \
|
||||
--build-arg BuildVersion=$VERSION \
|
||||
--build-arg CommitID=$COMMIT \
|
||||
--build-arg BuildTime=$BUILD_TIME \
|
||||
-t your-registry/iotlcm/iotlcm:iotlcm-admin-svr-$VERSION \
|
||||
-f server/build/Dockerfile server/
|
||||
```
|
||||
|
||||
### 前端服务
|
||||
|
||||
```bash
|
||||
cd iotlcm-admin-ui/vue
|
||||
docker build -t your-registry/iotlcm/iotlcm:iotlcm-admin-ui-$VERSION -f build/Dockerfile .
|
||||
```
|
||||
|
||||
```bash
|
||||
cd iotlcm-app/app
|
||||
docker build -t your-registry/iotlcm/iotlcm:iotlcm-app-web-$VERSION .
|
||||
```
|
||||
|
||||
### 构建参数说明
|
||||
|
||||
| 参数 | 用途 |
|
||||
| --- | --- |
|
||||
| `BuildVersion` | 写入二进制,可通过版本接口查询 |
|
||||
| `CommitID` | 提交号,便于追溯 |
|
||||
| `BuildTime` | 构建时间 |
|
||||
|
||||
`openapi`、`worker-svr` 等仓库支持用 `GOPROXY` 构建参数替换代理:
|
||||
|
||||
```bash
|
||||
docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .
|
||||
```
|
||||
|
||||
## 镜像命名规则
|
||||
|
||||
官方仓库把**所有镜像放在同一个镜像仓库** `iotlcm/iotlcm` 下,靠 tag 区分服务与版本:
|
||||
|
||||
```text
|
||||
<Registry>/iotlcm/iotlcm:<服务名>-<版本>
|
||||
```
|
||||
|
||||
| 服务 | tag | 完整示例 |
|
||||
| --- | --- | --- |
|
||||
| admin-svr | `iotlcm-admin-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4` |
|
||||
| admin-ui | `iotlcm-admin-ui-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4` |
|
||||
| openapi | `iotlcm-openapi-<TAG>` | `.../iotlcm/iotlcm:iotlcm-openapi-v1.0.4` |
|
||||
| app-server | `iotlcm-app-server-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-server-v1.0.2` |
|
||||
| app-web | `iotlcm-app-web-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-web-v1.0.2` |
|
||||
| upstream-chan | `iotlcm-upstream-chan-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4` |
|
||||
| upstream-proxy | `iotlcm-upstream-proxy-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-proxy-v1.0.4` |
|
||||
| worker-svr | `iotlcm-worker-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4` |
|
||||
| sms | `iotlcm-sms-<TAG>` | `.../iotlcm/iotlcm:iotlcm-sms-v1.0.4` |
|
||||
| bot | `iotlcm-bot-<TAG>` | `.../iotlcm/iotlcm:iotlcm-bot-v1.0.4` |
|
||||
| docs | `iotlcm-docs-<TAG>` | `.../iotlcm/iotlcm:iotlcm-docs-v1.0.4` |
|
||||
| onelink-mock | `iotlcm-onelink-mock-<TAG>` | `.../iotlcm/iotlcm:iotlcm-onelink-mock-v1.0.4` |
|
||||
|
||||
基础组件(`mysql`、`redis`、`rabbitmq`、`consul`、`nginx`)不需要构建,从上游同步到同一仓库即可,tag 为 `<组件>-<版本>`,见下节。
|
||||
|
||||
::: warning tag 必须带上服务名
|
||||
同一个仓库里 tag 不能重复。只用版本号(如 `v1.0.4`)会让 12 个服务互相覆盖,因此 tag 固定为 `<服务名>-<版本>`。
|
||||
:::
|
||||
|
||||
## 同步基础组件
|
||||
|
||||
平台编排不再从 Docker Hub 直连基础组件(公网不稳定,且 `8.0` 这类 tag 会随补丁升级而漂)。部署前先把五个镜像同步到官方仓库:
|
||||
|
||||
```bash
|
||||
REPO=crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com
|
||||
|
||||
docker login --username=sdqmx $REPO
|
||||
|
||||
# 按 docker-compose.yml 中固定的版本拉取并推送
|
||||
sync_image() {
|
||||
local src=$1 dst=$2
|
||||
docker pull "$src"
|
||||
docker tag "$src" "$REPO/iotlcm/iotlcm:$dst"
|
||||
docker push "$REPO/iotlcm/iotlcm:$dst"
|
||||
}
|
||||
|
||||
# 参数:<上游镜像> <目标 tag(组件名-版本)>
|
||||
sync_image mysql:8.0 mysql-8.0
|
||||
sync_image redis:7.2 redis-7.2
|
||||
sync_image rabbitmq:4.3.5-management rabbitmq-4.3.5-management
|
||||
sync_image hashicorp/consul:1.22 consul-1.22
|
||||
sync_image nginx:1.30 nginx-1.30
|
||||
```
|
||||
|
||||
::: warning 同步时把版本号固定到补丁位
|
||||
`mysql:8.0` 这类浮动 tag 会随上游发新补丁而变化。建议同步时锁定到具体补丁版(如 `mysql:8.0.43`),tag 写成 `mysql-8.0.43`,并把 `docker-compose.yml` 里对应的 `image` 同步改掉,保证多台机器拉到的镜像完全一致。
|
||||
:::
|
||||
|
||||
同步完成后校验:
|
||||
|
||||
```bash
|
||||
for tag in mysql-8.0 redis-7.2 rabbitmq-4.3.5-management consul-1.22 nginx-1.30; do
|
||||
docker manifest inspect "$REPO/iotlcm/iotlcm:$tag" >/dev/null 2>&1 \
|
||||
&& echo "✅ $tag" || echo "❌ $tag"
|
||||
done
|
||||
```
|
||||
|
||||
## 推送到自己的仓库
|
||||
|
||||
自建镜像不需要推送回官方仓库。推到自有仓库后,把 `docker-compose.yml` 里的镜像前缀改指向它:
|
||||
|
||||
```bash
|
||||
REG=harbor.internal.example.com
|
||||
TAG=1.0.3
|
||||
|
||||
docker tag iotlcm-openapi:local $REG/iotlcm/iotlcm:iotlcm-openapi-$TAG
|
||||
docker login $REG
|
||||
docker push $REG/iotlcm/iotlcm:iotlcm-openapi-$TAG
|
||||
```
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml —— 逐个替换 image 前缀
|
||||
image: 'harbor.internal.example.com/iotlcm/iotlcm:iotlcm-openapi-v1.0.4'
|
||||
```
|
||||
|
||||
::: tip 只覆盖部分服务
|
||||
只想替换其中一两个服务时,把对应镜像推到自有仓库,其余保留官方地址。此时把 `docker-compose.yml` 里那几个 `image` 单独写成完整地址,其余保持官方仓库前缀即可。
|
||||
:::
|
||||
|
||||
## 批量构建脚本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO=${REPO:?请设置 REPO}
|
||||
TAG=${TAG:-$(date +%Y%m%d)}
|
||||
WORKDIR=${WORKDIR:-./repos}
|
||||
|
||||
# 服务:仓库:上下文:产物镜像名
|
||||
SERVICES=(
|
||||
"admin-svr:iotlcm-admin-svr:server/:iotlcm-admin-svr"
|
||||
"openapi:iotlcm-openapi:.:iotlcm-openapi"
|
||||
"worker-svr:iotlcm-worker-svr:.:iotlcm-worker-svr"
|
||||
"upstream-chan:iotlcm-upstream-chan:.:iotlcm-upstream-chan"
|
||||
"upstream-proxy:iotlcm-upstream-proxy:.:iotlcm-upstream-proxy"
|
||||
"app-server:iotlcm-app:server/:iotlcm-app-server"
|
||||
"sms:iotlcm-sms:.:iotlcm-sms"
|
||||
"bot:iotlcm-bot:.:iotlcm-bot"
|
||||
)
|
||||
|
||||
mkdir -p "$WORKDIR"
|
||||
|
||||
for item in "${SERVICES[@]}"; do
|
||||
IFS=':' read -r name repo ctx image <<< "$item"
|
||||
echo "=== 构建 $name ==="
|
||||
|
||||
if [ ! -d "$WORKDIR/$repo" ]; then
|
||||
git clone "https://git.5iot.com/iotlcm-public/$repo.git" "$WORKDIR/$repo"
|
||||
fi
|
||||
|
||||
VERSION=$(git -C "$WORKDIR/$repo" describe --tags --always 2>/dev/null || echo unknown)
|
||||
COMMIT=$(git -C "$WORKDIR/$repo" rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||
|
||||
find "$WORKDIR/$repo" -name Dockerfile -exec \
|
||||
sed -i 's#FROM crpi-[^ ]*/\([a-z]*\):\([0-9.]*-alpine[0-9.]*\)#FROM \1:\2#' {} \;
|
||||
|
||||
docker build \
|
||||
--build-arg BuildVersion="$VERSION" \
|
||||
--build-arg CommitID="$COMMIT" \
|
||||
--build-arg BuildTime="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
|
||||
-t "$REPO/iotlcm/$image:$TAG" \
|
||||
-f "$WORKDIR/$repo/${ctx}Dockerfile" \
|
||||
"$WORKDIR/$repo/$ctx"
|
||||
done
|
||||
|
||||
echo "全部完成,镜像标签:$REPO/iotlcm/*:$TAG"
|
||||
```
|
||||
|
||||
::: warning 脚本里的 sed 只能应急
|
||||
上面的 `sed` 是粗略替换,把私有仓库地址映射为公共镜像名。仅适用于地址格式规整的情况,用前先 `git diff` 确认改对了。正式做法应把 Dockerfile 的修正提交回仓库。
|
||||
:::
|
||||
|
||||
## 验证构建结果
|
||||
|
||||
```bash
|
||||
# 镜像大小
|
||||
docker images | grep iotlcm
|
||||
|
||||
# 二进制能否启动
|
||||
docker run --rm your-registry/iotlcm/iotlcm:iotlcm-openapi-$TAG --help 2>&1 | head
|
||||
|
||||
# 检查架构
|
||||
docker inspect your-registry/iotlcm/iotlcm:iotlcm-openapi-$TAG \
|
||||
--format '{{.Architecture}}/{{.Os}}'
|
||||
```
|
||||
|
||||
构建后按[启动与验证](install)的清单做一次完整部署验证,不要只看镜像是否构建成功。
|
||||
|
||||
## 常见问题
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| `pull access denied` / `unauthorized` | Dockerfile 里是私有仓库地址 | 按上文替换 `FROM` |
|
||||
| `go mod download` 超时 | 网络无法访问 Go 模块代理 | 加 `--build-arg GOPROXY=https://goproxy.cn,direct` |
|
||||
| `npm ci` 失败 | 前端仓库 lock 文件与 package.json 不一致 | 改用 `npm install`,并注意提交 lock 变更 |
|
||||
| 镜像体积过大 | 未使用多阶段构建,或缓存未清理 | 确认用的是仓库自带的 Dockerfile(均为多阶段) |
|
||||
| 容器启动后立即退出 | 镜像内缺少配置文件 | 容器认为 `config/config.yaml` 必须存在,需挂载或用占位配置 |
|
||||
| `exec format error` | 构建架构与运行架构不一致 | 加 `--platform linux/amd64` |
|
||||
@@ -0,0 +1,189 @@
|
||||
---
|
||||
title: 配置说明
|
||||
description: 部署配置的结构、默认值与修改方式
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 配置说明
|
||||
|
||||
平台配置分两层,**都随仓库分发、都带可用默认值**,无需复制或初始化即可启动:
|
||||
|
||||
| 层 | 位置 | 管什么 |
|
||||
| --- | --- | --- |
|
||||
| 编排层 | `docker-compose.yml` | 镜像地址、端口、数据库/Redis/MQ 口令、容器编排 |
|
||||
| 应用层 | `config/<服务>/config.yaml` | 业务参数、连接串、功能开关 |
|
||||
|
||||
::: warning 配置里是字面值,不是环境变量
|
||||
配置文件与编排文件里直接写的是具体值,**不使用 `${VAR}` 占位**。
|
||||
|
||||
原因:GoFrame 不会展开配置文件中的 `${VAR}`(环境变量只用于覆盖配置键,不参与文件内文本替换),用占位符会导致连接串变成字面量 `${X}`,服务直接连不上库。
|
||||
|
||||
代价是改口令要改多处,见下方「改口令的正确姿势」。
|
||||
:::
|
||||
|
||||
## 编排层:`docker-compose.yml`
|
||||
|
||||
### 镜像地址
|
||||
|
||||
```yaml
|
||||
image: 'crpi-.../iotlcm/iotlcm:mysql-8.0'
|
||||
image: 'crpi-.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4'
|
||||
```
|
||||
|
||||
所有镜像(基础组件 + 业务服务)都在**同一个镜像仓库** `<Registry>/iotlcm/iotlcm` 下,靠 tag 区分:
|
||||
|
||||
| tag 形式 | 示例 |
|
||||
| --- | --- |
|
||||
| 基础组件:`<组件>-<版本>` | `mysql-8.0`、`redis-7.2`、`nginx-1.30` |
|
||||
| 业务服务:`<服务>-<版本>` | `iotlcm-admin-svr-v1.0.4`、`iotlcm-app-server-v1.0.2` |
|
||||
|
||||
改用自有仓库时,全局替换 Registry 前缀即可。
|
||||
|
||||
### 默认口令
|
||||
|
||||
| 变量 | 默认值 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `MYSQL_ROOT_PASSWORD` | 见文件 | MySQL root |
|
||||
| `MYSQL_DATABASE` | `db_iotlcm` | 库名 |
|
||||
| `RABBITMQ_DEFAULT_PASS` | 见文件 | RabbitMQ |
|
||||
| redis `--requirepass` | 见文件 | Redis |
|
||||
|
||||
### 端口
|
||||
|
||||
| 端口 | 服务 | 对外 |
|
||||
| --- | --- | --- |
|
||||
| 8008 | admin-svr | 经反向代理 |
|
||||
| 8009 | admin-ui | **浏览器直接访问这个** |
|
||||
| 8000 | openapi | 经反向代理 |
|
||||
| 8010 / 8018 | app-server / app-web | 经反向代理 |
|
||||
| 8019 | docs | 经反向代理 |
|
||||
| 8080 | onelink-mock | 仅联调 |
|
||||
| 3306 / 6379 / 5672 / 8500 | 基础组件 | 仅内网 |
|
||||
| 8110 / 8310 / 8510 | chan / proxy | 仅内网 |
|
||||
| 11190 / 11200 | sms | 仅内网 |
|
||||
|
||||
改端口直接改 `docker-compose.yml` 中对应 `ports` 的冒号左侧。
|
||||
|
||||
## 应用层:`config/`
|
||||
|
||||
```
|
||||
config/
|
||||
├── admin-svr/config.yaml
|
||||
├── admin-ui/default.conf
|
||||
├── app-server/config.yaml
|
||||
├── bot/config.yaml
|
||||
├── nginx/iotlcm.conf
|
||||
├── openapi/config.yaml
|
||||
├── sms/config.yaml
|
||||
├── upstream-chan/config.yaml
|
||||
├── upstream-proxy/config.yaml
|
||||
└── worker-svr/config.yaml
|
||||
```
|
||||
|
||||
### 各服务要点
|
||||
|
||||
#### admin-svr(管理端后端)
|
||||
|
||||
| 项 | 默认情况 |
|
||||
| --- | --- |
|
||||
| `database.default.link` | 指向 `mysql:3306`,口令与应用账号一致 |
|
||||
| `database.legacyCard.link` | 空,不使用旧平台库 |
|
||||
| `jwt.signingKey` | 已填固定密钥,需与 openapi、app-server 一致 |
|
||||
| `warning.sms.enabled` | `true`,未配短信服务商时建议改 `false` |
|
||||
| `warning.wechat.enabled` | `false` |
|
||||
| `register.email.*` | 空,不发邮件 |
|
||||
| `tsdb.tdengine.dsn` | 空,不启用时序库 |
|
||||
| `alipay.*` | 空,不启用支付 |
|
||||
| `upstream.appDiagnosisToken` | 已填,需与 upstream-proxy 一致 |
|
||||
|
||||
#### openapi(对外 API)
|
||||
|
||||
`jwt.signingKey` 与 admin-svr 一致;`database.default.link` 同库。建议另建只读账号用于生产。
|
||||
|
||||
#### worker-svr(异步任务)
|
||||
|
||||
| 项 | 说明 |
|
||||
| --- | --- |
|
||||
| `batch.inbound.upstreamConcurrency` | 默认 300,机器弱或上游配额小时调低 |
|
||||
| `batch.inbound.upstreamRateLimit` | 运营商限流,**必须按上游实际配额设置**,调大易被封禁 |
|
||||
| `tsdb.tdengine.dsn` | 空,不启用时序库 |
|
||||
|
||||
#### upstream-chan(运营商对接)
|
||||
|
||||
| 项 | 说明 |
|
||||
| --- | --- |
|
||||
| `registry.address` | `consul:8500` |
|
||||
| `dependencies.sim_yd_mock_v5.url` | 指向挡板 `http://onelink-mock:8080/v5/`,接真实运营商时删除本段 |
|
||||
|
||||
运营商通道本身(AppKey、AppSecret、接口地址)在平台「通道管理」中录入,存数据库,不在本文件。
|
||||
|
||||
#### upstream-proxy(通道缓存转发)
|
||||
|
||||
| 项 | 说明 |
|
||||
| --- | --- |
|
||||
| `appDiagnosis.internalToken` | 与 admin-svr 的 `upstream.appDiagnosisToken` 一致 |
|
||||
| `gateway.batchTimeout` | 默认 20m,**worker 侧超时必须大于此值** |
|
||||
|
||||
#### app-server(小程序后端)
|
||||
|
||||
`jwt.signingKey` 同上;`payment.wechat.enabled` 默认 `false`;`appCardRecognition.enabled` 默认 `false`(拍照识别需自备视觉模型 API)。
|
||||
|
||||
#### sms(短信服务)
|
||||
|
||||
| 项 | 默认 |
|
||||
| --- | --- |
|
||||
| `serviceType` | `1`(阿里云) |
|
||||
| `accessKeyId` / `accessKeySecret` | 空,需自行填写 |
|
||||
| `signName` | `示例签名` |
|
||||
|
||||
不填则无法发送验证码,其余功能正常。
|
||||
|
||||
#### bot(飞书通知)
|
||||
|
||||
`feishu.appId` / `appSecret` / `chatId` 均为空。不启动该容器即可。
|
||||
|
||||
#### admin-ui / nginx
|
||||
|
||||
`admin-ui/default.conf` 把 `/api/` 反代到 `admin-svr:8008`;`nginx/iotlcm.conf` 按域名分流,需把 `your-domain.example.com` 换成实际域名。
|
||||
|
||||
## 改口令的正确姿势
|
||||
|
||||
改数据库口令时,以下**三处必须同时改**,漏一处就起不来:
|
||||
|
||||
| 位置 | 内容 |
|
||||
| --- | --- |
|
||||
| `docker-compose.yml` | `MYSQL_ROOT_PASSWORD`、redis `--requirepass`、`RABBITMQ_DEFAULT_PASS` |
|
||||
| `config/*/config.yaml` | 各服务的 `database.default.link`、`redis.default.pass`、`rabbitmq.dsn` |
|
||||
| `schema/04-users.sh`、`schema/05-reset-passwords.sh` | 脚本内的 `APP_PASSWORD` / `ADMIN_PASSWORD` / `ROOT_PASSWORD` |
|
||||
|
||||
::: tip 一次性全局替换
|
||||
```bash
|
||||
OLD='原口令'
|
||||
NEW='新口令'
|
||||
|
||||
# 编排 + 配置
|
||||
grep -rl "$OLD" docker-compose.yml config/ | xargs sed -i "s/$OLD/$NEW/g"
|
||||
# 初始化脚本
|
||||
grep -rl "$OLD" schema/ | xargs sed -i "s/$OLD/$NEW/g"
|
||||
```
|
||||
:::
|
||||
|
||||
::: danger 口令不能含 @ : / 等字符
|
||||
连接串格式为 `mysql:用户:口令@tcp(主机:端口)/库`,口令里的 `@`、`:`、`/` 会被解析成分隔符,导致连接失败。用字母数字组合。
|
||||
:::
|
||||
|
||||
## 配置自检
|
||||
|
||||
```bash
|
||||
# 编排语法
|
||||
docker compose config --quiet
|
||||
|
||||
# 看某个服务最终的配置展开
|
||||
docker compose config | sed -n '/admin-svr:/,/^ [a-z]/p'
|
||||
```
|
||||
|
||||
配置语法错误会导致服务启动后立刻退出,看日志定位:
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=50 admin-svr
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: 部署概览
|
||||
description: IotLCM 平台的部署方式、构成与前置条件
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 部署概览
|
||||
|
||||
IotLCM 平台通过 Docker Compose 在单机上部署。部署编排与数据库初始化脚本在独立仓库:
|
||||
|
||||
```bash
|
||||
git clone https://git.5iot.com/iotlcm-public/iotlcm-deploy.git
|
||||
cd iotlcm-deploy
|
||||
```
|
||||
|
||||
```text
|
||||
iotlcm-deploy/
|
||||
├── docker-compose.yml # 服务编排(含默认口令与端口)
|
||||
├── config/ # 各服务配置,10 份,已带可用默认值
|
||||
└── schema/ # 数据库初始化脚本,13 个,首次启动自动执行
|
||||
```
|
||||
|
||||
## 部署内容
|
||||
|
||||
| 类别 | 服务 |
|
||||
| --- | --- |
|
||||
| 基础组件 | MySQL、Redis、RabbitMQ、Consul |
|
||||
| 管理端 | admin-svr(后端)、admin-ui(前端) |
|
||||
| 对外接口 | openapi |
|
||||
| 小程序 / H5 | app-server(后端)、app-web(前端) |
|
||||
| 运营商对接 | upstream-chan(协议适配)、upstream-proxy(缓存转发) |
|
||||
| 异步任务 | worker-svr(批次执行、全量同步) |
|
||||
| 配套 | sms(短信)、docs(本文档站) |
|
||||
| 可选 | onelink-mock(运营商接口挡板)、bot(飞书通知)、nginx(反向代理) |
|
||||
|
||||
## 部署流程
|
||||
|
||||
```bash
|
||||
git clone <部署仓地址> iotlcm-deploy
|
||||
cd iotlcm-deploy
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
三步,不需要复制或修改任何文件——配置与编排都随仓库分发且带可用默认值。首次启动自动建库并导入 schema,约 1~2 分钟。
|
||||
|
||||
启动后访问 `http://<宿主机IP>:8009`,账号 `admin`,口令见 `docker-compose.yml`。
|
||||
|
||||
::: warning 默认口令只用于试用
|
||||
正式部署前必须修改数据库、Redis、RabbitMQ、admin 账号口令与 JWT 密钥,详见[配置说明](configuration)。
|
||||
:::
|
||||
|
||||
## 文档导航
|
||||
|
||||
| 文档 | 内容 |
|
||||
| --- | --- |
|
||||
| [环境准备](prerequisites) | 硬件、系统、Docker 版本、镜像仓库、端口规划 |
|
||||
| [配置说明](configuration) | `docker-compose.yml` 与 `config/` 各项含义 |
|
||||
| [启动与验证](install) | 启动流程、验证清单、首次登录 |
|
||||
| [其他部署方式](alternatives) | 二进制、自建镜像、分离、离线 |
|
||||
| [可选服务](optional) | 接口挡板、飞书通知、反向代理 |
|
||||
| [升级与备份](operations) | 版本固定、升级回滚、数据备份 |
|
||||
| [故障排查](troubleshooting) | 常见问题与处理 |
|
||||
|
||||
## 关键约束
|
||||
|
||||
::: warning JWT 密钥必须三处一致
|
||||
`admin-svr`、`openapi`、`app-server` 共用同一个 `JWT_SIGNING_KEY`。不一致的表现是能登录管理端,但调用外部 API 或小程序端提示未授权。
|
||||
|
||||
更换该值会使已签发的全部登录态与接口令牌立即失效,需在维护窗口操作。
|
||||
:::
|
||||
|
||||
::: tip 镜像标签不要用 latest
|
||||
生产环境必须把 `TAG` 固定到具体版本号。`latest` 会随仓库推送变化,同一份编排在不同时间拉到的镜像不同,出问题无法定位,也无法明确回滚。
|
||||
:::
|
||||
|
||||
::: danger 修改 schema 脚本不会自动生效
|
||||
数据库初始化只在 `data/mysql` 为空时执行。改了 `schema/` 下的脚本必须清空数据卷才会重跑,而清空会丢失全部数据。生产环境禁止这样操作。
|
||||
:::
|
||||
|
||||
## 服务端口
|
||||
|
||||
默认端口规划:
|
||||
|
||||
| 端口 | 服务 | 是否对外 |
|
||||
| --- | --- | --- |
|
||||
| 8000 | openapi | 经反向代理 |
|
||||
| 8008 | admin-svr | 经反向代理 |
|
||||
| 8009 | admin-ui | 经反向代理 |
|
||||
| 8010 | app-server | 经反向代理 |
|
||||
| 8018 | app-web | 经反向代理 |
|
||||
| 8019 | docs | 经反向代理 |
|
||||
| 3306 / 6379 / 5672 / 8500 | MySQL / Redis / RabbitMQ / Consul | 仅内网 |
|
||||
| 8110 / 8310 / 8510 | upstream-chan / upstream-proxy | 仅内网 |
|
||||
| 11190 / 11200 | sms | 仅内网 |
|
||||
| 8080 | onelink-mock | 仅联调 |
|
||||
|
||||
端口冲突时修改 `docker-compose.yml` 中对应变量,不要直接改 `docker-compose.yml`。
|
||||
@@ -0,0 +1,187 @@
|
||||
---
|
||||
title: 启动与验证
|
||||
description: 首次启动流程、状态检查与验证清单
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 启动与验证
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
git clone <部署仓地址> iotlcm-deploy
|
||||
cd iotlcm-deploy
|
||||
|
||||
# 首次部署建议前台启动,便于观察初始化过程
|
||||
docker compose up
|
||||
|
||||
# 确认无误后转后台
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
不需要复制或修改任何文件,配置已带可用默认值。
|
||||
|
||||
### 首次启动发生什么
|
||||
|
||||
1. **MySQL 建库并导入 schema**(约 1~2 分钟)
|
||||
`schema/` 下 13 个脚本按文件名顺序执行:建表 → 结构补齐 → 创建应用账号 → 重置种子账号口令 → 导入通道模板/字典/菜单等种子数据
|
||||
2. MySQL 通过健康检查后,`admin-svr`、`openapi`、`worker-svr` 依次启动
|
||||
3. 各服务向 Consul 注册
|
||||
|
||||
观察初始化进度:
|
||||
|
||||
```bash
|
||||
docker compose logs -f mysql
|
||||
```
|
||||
|
||||
看到 `[initdb] 应用账号 u_iotlcm 已创建` 与 `[initdb] 种子账号口令已统一重置` 表示初始化完成。
|
||||
|
||||
::: warning 初始化失败需清空数据卷
|
||||
MySQL 只在 `data/mysql` 为空时执行 `schema/`。若中途失败,改完脚本必须清空卷才会重跑:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
sudo rm -rf ./data/mysql
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
这会删除全部数据,仅限首次部署阶段使用。
|
||||
:::
|
||||
|
||||
## 检查状态
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
期望结果:
|
||||
|
||||
| 服务 | 状态 |
|
||||
| --- | --- |
|
||||
| iotlcm-mysql | Up (healthy) |
|
||||
| iotlcm-redis | Up (healthy) |
|
||||
| iotlcm-rabbitmq | Up (healthy) |
|
||||
| iotlcm-consul | Up |
|
||||
| iotlcm-admin-svr | Up |
|
||||
| iotlcm-admin-ui | Up |
|
||||
| iotlcm-openapi | Up |
|
||||
| iotlcm-upstream-chan | Up |
|
||||
| iotlcm-upstream-proxy | Up |
|
||||
| iotlcm-worker-svr | Up |
|
||||
| iotlcm-app-server | Up |
|
||||
| iotlcm-app-web | Up |
|
||||
| iotlcm-sms | Up (healthy) |
|
||||
| iotlcm-docs | Up |
|
||||
|
||||
出现 `Restarting` 或 `Exited` 需查日志:
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=100 <服务名>
|
||||
```
|
||||
|
||||
## 验证清单
|
||||
|
||||
按顺序逐项确认,任一步失败先解决再继续。
|
||||
|
||||
### 1. 数据库表数量
|
||||
|
||||
```bash
|
||||
docker compose exec mysql sh -c \
|
||||
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e \
|
||||
"SELECT COUNT(*) AS tables FROM information_schema.tables WHERE table_schema=\"db_iotlcm\";"'
|
||||
```
|
||||
|
||||
预期 `tables` = **89**。
|
||||
|
||||
### 2. 种子数据
|
||||
|
||||
```bash
|
||||
docker compose exec mysql sh -c \
|
||||
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm -e \
|
||||
"SELECT (SELECT COUNT(*) FROM sys_menu) AS menus, (SELECT COUNT(*) FROM sys_user) AS users;"'
|
||||
```
|
||||
|
||||
预期菜单约 **447** 条,账号 **18** 个。
|
||||
|
||||
### 3. 管理端后端
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8008/api.json
|
||||
```
|
||||
|
||||
预期 `200`。
|
||||
|
||||
### 4. 管理端前端
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8009/
|
||||
```
|
||||
|
||||
预期 `200`。
|
||||
|
||||
### 5. 对外 API
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/api.json
|
||||
```
|
||||
|
||||
预期 `200`。
|
||||
|
||||
### 6. 短信服务
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11190/healthz
|
||||
```
|
||||
|
||||
预期 `200`。
|
||||
|
||||
### 7. 浏览器登录
|
||||
|
||||
访问 `http://<宿主机IP>:8009`:
|
||||
|
||||
| 项 | 值 |
|
||||
| --- | --- |
|
||||
| 账号 | `admin` |
|
||||
| 口令 | `docker-compose.yml` 中 admin 的默认口令(首次登录后立即修改) |
|
||||
|
||||
能进入首页工作台、左侧菜单完整显示即部署成功。
|
||||
|
||||
## 首次登录后必做
|
||||
|
||||
1. **修改 admin 口令**:右上角个人中心 → 安全设置
|
||||
2. **处理其余种子账号**:`user02`、`2admin` 等 17 个账号默认与 admin 同一口令,不使用请停用或删除
|
||||
3. **确认字典与菜单加载**:进入「卡信息」页,确认筛选下拉选项不为空
|
||||
|
||||
## 后端自检
|
||||
|
||||
```bash
|
||||
# admin-svr 是否连上数据库
|
||||
docker compose logs admin-svr | grep -iE 'error|panic' | tail -20
|
||||
|
||||
# worker-svr 是否连上 RabbitMQ
|
||||
docker compose logs worker-svr | tail -30
|
||||
|
||||
# upstream-chan 是否连上 Redis 与 Consul
|
||||
docker compose logs upstream-chan | tail -30
|
||||
```
|
||||
|
||||
正常日志不应出现 `connection refused`、`access denied`、`panic`。
|
||||
|
||||
## 停止与重启
|
||||
|
||||
```bash
|
||||
docker compose stop # 停止,保留数据
|
||||
docker compose restart # 重启全部
|
||||
docker compose restart admin-svr # 重启单个
|
||||
docker compose down # 停止并删除容器(./data 不受影响)
|
||||
```
|
||||
|
||||
::: danger 不要执行 down -v
|
||||
`-v` 会删除命名数据卷。本编排数据都在 `./data` 宿主机目录,但 `-v` 仍可能影响同项目的其他卷。停止服务用 `down` 即可。
|
||||
:::
|
||||
|
||||
## 下一步
|
||||
|
||||
- 需要运营商挡板联调 → [可选服务](optional)
|
||||
- 需要域名访问 → [可选服务](optional) 的反向代理章节
|
||||
- 遇到报错 → [故障排查](troubleshooting)
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
title: 离线部署
|
||||
description: 服务器无法访问镜像仓库时的部署方式
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 离线部署
|
||||
|
||||
服务器无法访问外部镜像仓库时使用。核心思路:**在有网机器上导出镜像 → 传到目标机 → 导入**。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 生产服务器在内网,不能访问外网
|
||||
- 镜像仓库在内网不可达,或需要走审批才能拉取
|
||||
- 需要把某一批镜像固化下来,保证多台机器部署一致
|
||||
|
||||
## 一、确定镜像清单
|
||||
|
||||
```bash
|
||||
docker compose config --images | sort -u
|
||||
```
|
||||
|
||||
输出示例(含可选 profile 时用 `--profile` 指定):
|
||||
|
||||
```text
|
||||
registry.example.com/iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4
|
||||
registry.example.com/iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4
|
||||
registry.example.com/iotlcm/iotlcm:iotlcm-app-server-v1.0.2
|
||||
...
|
||||
registry.example.com/iotlcm/iotlcm:mysql-8.0
|
||||
registry.example.com/iotlcm/iotlcm:redis-7.2
|
||||
```
|
||||
|
||||
把清单保存下来,作为这次部署的镜像基线:
|
||||
|
||||
```bash
|
||||
docker compose config --images | sort -u > images.txt
|
||||
```
|
||||
|
||||
## 二、在有网机器导出
|
||||
|
||||
```bash
|
||||
# 拉取全部镜像
|
||||
docker compose pull
|
||||
|
||||
# 导出为单个压缩包
|
||||
docker save $(cat images.txt) | gzip > iotlcm-images-$(date +%Y%m%d).tar.gz
|
||||
```
|
||||
|
||||
单包体积参考:全部 14 个服务约 **1.5~2 GB**(压缩后)。
|
||||
|
||||
若目标机磁盘紧张,可按服务分包:
|
||||
|
||||
```bash
|
||||
for img in $(cat images.txt); do
|
||||
name=$(echo "$img" | tr '/:' '__')
|
||||
docker save "$img" | gzip > "img-${name}.tar.gz"
|
||||
done
|
||||
```
|
||||
|
||||
## 三、传输
|
||||
|
||||
```bash
|
||||
scp iotlcm-images-*.tar.gz user@target-host:/opt/iotlcm/
|
||||
```
|
||||
|
||||
无网络直连时用移动介质拷入,注意校验完整性:
|
||||
|
||||
```bash
|
||||
# 源端生成校验值
|
||||
sha256sum iotlcm-images-*.tar.gz > images.sha256
|
||||
|
||||
# 目标端校验
|
||||
sha256sum -c images.sha256
|
||||
```
|
||||
|
||||
## 四、在目标机导入
|
||||
|
||||
```bash
|
||||
gunzip -c iotlcm-images-*.tar.gz | docker load
|
||||
```
|
||||
|
||||
分包的逐个导入:
|
||||
|
||||
```bash
|
||||
for f in img-*.tar.gz; do gunzip -c "$f" | docker load; done
|
||||
```
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
docker images | grep iotlcm
|
||||
```
|
||||
|
||||
## 五、部署
|
||||
|
||||
导入后镜像已在本机,compose 不会再尝试拉取:
|
||||
|
||||
```bash
|
||||
# 把 docker-compose.yml 里的镜像地址与 TAG 改成与导入的镜像一致
|
||||
cp -r config.example config
|
||||
vi config/*/config.yaml
|
||||
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
::: warning REGISTRY 与 TAG 必须与导入的镜像名一致
|
||||
`docker load` 只恢复镜像名,不会改标签。若 `docker-compose.yml` 里写 `TAG=latest` 而导入的是 `1.0.3`,compose 会认为镜像不存在并尝试拉取,离线环境直接失败。
|
||||
|
||||
用这条命令核对:
|
||||
|
||||
```bash
|
||||
docker compose config --images | sort -u > expected.txt
|
||||
docker images --format '{{.Repository}}:{{.Tag}}' | sort -u > actual.txt
|
||||
comm -23 expected.txt actual.txt # 输出为空才说明镜像齐全
|
||||
```
|
||||
:::
|
||||
|
||||
## 六、长期方案:内网镜像仓库
|
||||
|
||||
如果需要经常部署或多机部署,建议搭内网镜像仓库,而不是每次传 tar 包。
|
||||
|
||||
### 同步镜像
|
||||
|
||||
```bash
|
||||
# 在能访问外网的机器
|
||||
for img in $(cat images.txt); do
|
||||
docker pull "$img"
|
||||
newimg="${img/registry.example.com/harbor.internal.example.com}"
|
||||
docker tag "$img" "$newimg"
|
||||
docker push "$newimg"
|
||||
done
|
||||
```
|
||||
|
||||
然后目标机的 `docker-compose.yml` 改为:
|
||||
|
||||
```bash
|
||||
REGISTRY=harbor.internal.example.com
|
||||
```
|
||||
|
||||
### 差异更新
|
||||
|
||||
版本升级时只需推送变化的镜像:
|
||||
|
||||
```bash
|
||||
docker compose pull
|
||||
# 对比 images.txt 找出新增/变更的标签,只同步这些
|
||||
```
|
||||
|
||||
## 七、常见问题
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| `docker compose up` 卡在 `Pulling` | 镜像标签不匹配,compose 尝试联网拉取 | 按上文核对 `expected.txt` 与 `actual.txt` |
|
||||
| `no space left on device` | 镜像包 + 解压后镜像双份占盘 | 导入后删除 tar 包,或换挂载点 |
|
||||
| `load` 后 `docker images` 为空 | 压缩包损坏 | 用 `sha256sum -c` 校验 |
|
||||
| 容器启动报 `exec format error` | 架构不匹配(x86 镜像跑在 ARM) | 在目标架构上重新导出 |
|
||||
| 导出包过大 | 包含多个无用层 | 用 `docker save` 只导出必要标签 |
|
||||
|
||||
## 八、离线环境的其他准备
|
||||
|
||||
镜像只是第一步,以下也需提前准备:
|
||||
|
||||
| 资源 | 说明 |
|
||||
| --- | --- |
|
||||
| 部署仓代码 | `git clone` 或直接打包 `iotlcm-deploy/` 目录带过去 |
|
||||
| 配置模板 | 已包含在部署仓 `config/` |
|
||||
| 数据库脚本 | 已包含在部署仓 `schema/` |
|
||||
| Docker 与 Compose | 目标机需预先安装,离线安装包需单独准备 |
|
||||
| 时区数据 | 若使用 `Asia/Shanghai`,确认容器内 tzdata 完整(官方镜像已内置) |
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: 升级与备份
|
||||
description: 版本固定、升级回滚、数据备份与日志清理
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 升级与备份
|
||||
|
||||
## 版本固定
|
||||
|
||||
生产环境**必须固定镜像标签**,不要用 `latest`:
|
||||
|
||||
- `latest` 会随仓库推送变化,同一份编排在不同时间拉到的镜像不同
|
||||
- 出问题无法确定运行版本
|
||||
- 回滚时没有明确目标
|
||||
|
||||
`docker-compose.yml` 中指定:
|
||||
|
||||
```bash
|
||||
TAG=1.0.3
|
||||
```
|
||||
|
||||
记录当前部署版本:
|
||||
|
||||
```bash
|
||||
echo "$(date '+%Y-%m-%d %H:%M') 镜像=$(grep -m1 'iotlcm-admin-svr:' docker-compose.yml)" >> deploy-history.log
|
||||
```
|
||||
|
||||
## 升级
|
||||
|
||||
```bash
|
||||
# 1. 备份(不要跳过,见下节)
|
||||
./backup.sh
|
||||
|
||||
# 2. 拉新镜像
|
||||
vi docker-compose.yml # 修改镜像 TAG
|
||||
docker compose pull
|
||||
|
||||
# 3. 更新
|
||||
docker compose up -d
|
||||
|
||||
# 4. 验证
|
||||
docker compose ps
|
||||
docker compose logs --tail=50 admin-svr
|
||||
```
|
||||
|
||||
滚动更新单个服务减少中断:
|
||||
|
||||
```bash
|
||||
docker compose up -d --no-deps admin-svr
|
||||
```
|
||||
|
||||
## 回滚
|
||||
|
||||
```bash
|
||||
vi docker-compose.yml # TAG 改回旧版本
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
::: warning 数据库变更不能随代码回滚
|
||||
若新版本含数据库结构变更,回滚代码后数据库仍是新结构。升级前确认变更可逆,或保留升级前的完整备份。
|
||||
:::
|
||||
|
||||
## 数据库重建
|
||||
|
||||
`schema/` 脚本只在 `data/mysql` 为空时执行:
|
||||
|
||||
| 情况 | 是否需要重建 |
|
||||
| --- | --- |
|
||||
| 升级应用镜像 | 否 |
|
||||
| 修改 `config/` | 否,重启对应服务即可 |
|
||||
| 修改 `schema/` 脚本 | 是(会丢数据) |
|
||||
| 首次初始化失败 | 是 |
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
sudo rm -rf ./data/mysql
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
::: danger 生产环境禁止清空 data/mysql
|
||||
生产数据必须通过数据库工具单独备份与迁移,不能依赖 schema 重建。
|
||||
:::
|
||||
|
||||
## 备份
|
||||
|
||||
### 备份脚本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
STAMP=$(date '+%Y%m%d-%H%M%S')
|
||||
DEST="./backups/${STAMP}"
|
||||
mkdir -p "$DEST"
|
||||
|
||||
echo "[1/3] 导出数据库..."
|
||||
docker compose exec -T mysql sh -c \
|
||||
'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers --events db_iotlcm' \
|
||||
| gzip > "${DEST}/db_iotlcm.sql.gz"
|
||||
|
||||
echo "[2/3] 备份配置..."
|
||||
tar czf "${DEST}/config.tar.gz" config/ docker-compose.yml
|
||||
|
||||
echo "[3/3] 备份上传文件..."
|
||||
tar czf "${DEST}/upload.tar.gz" data/upload/ 2>/dev/null || echo " 上传目录为空,跳过"
|
||||
|
||||
echo "完成:${DEST}"
|
||||
ls -lh "$DEST"
|
||||
```
|
||||
|
||||
```bash
|
||||
chmod +x backup.sh && ./backup.sh
|
||||
```
|
||||
|
||||
### 只备份数据库
|
||||
|
||||
```bash
|
||||
docker compose exec -T mysql sh -c \
|
||||
'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction db_iotlcm' \
|
||||
| gzip > "db_iotlcm-$(date +%Y%m%d).sql.gz"
|
||||
```
|
||||
|
||||
### 恢复
|
||||
|
||||
```bash
|
||||
gunzip -c db_iotlcm-20260101.sql.gz | \
|
||||
docker compose exec -T mysql sh -c \
|
||||
'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" db_iotlcm'
|
||||
```
|
||||
|
||||
### 备份内容与优先级
|
||||
|
||||
| 内容 | 位置 | 重要性 |
|
||||
| --- | --- | --- |
|
||||
| 数据库 | MySQL 容器内 `db_iotlcm` | **最高**,业务数据全部在此 |
|
||||
| 配置 | `config/` + `docker-compose.yml` | 高,丢失需重新配置 |
|
||||
| 上传文件 | `data/upload/` | 中,附件与图片 |
|
||||
| Redis 数据 | `data/redis/` | 低,缓存可重建 |
|
||||
| RabbitMQ 队列 | `data/rabbitmq/` | 低,未消费消息会丢失 |
|
||||
| 日志 | `data/logs/` | 低,排障用 |
|
||||
|
||||
### 备份策略
|
||||
|
||||
| 场景 | 频率 | 保留 |
|
||||
| --- | --- | --- |
|
||||
| 生产 | 每日一次 + 升级前 | 30 天 |
|
||||
| 试用 | 有数据变更时 | 7 天 |
|
||||
|
||||
备份文件应转存到异机或对象存储,不要与数据库同盘。
|
||||
|
||||
## 日志清理
|
||||
|
||||
日志写在 `data/logs/` 下按服务分目录,应用侧已按小时轮转(`{Y-m-d-H}.log`)。清理旧日志:
|
||||
|
||||
```bash
|
||||
# 删除 7 天前的日志
|
||||
find ./data/logs -name '*.log' -mtime +7 -delete
|
||||
```
|
||||
|
||||
建议配置 cron 定期执行:
|
||||
|
||||
```cron
|
||||
0 3 * * * root find /path/to/iotlcm-deploy/data/logs -name '*.log' -mtime +7 -delete
|
||||
```
|
||||
|
||||
## 监控
|
||||
|
||||
基础组件自带端点:
|
||||
|
||||
| 服务 | 端点 |
|
||||
| --- | --- |
|
||||
| RabbitMQ | `http://<host>:15672` 管理界面 |
|
||||
| Consul | `http://<host>:8500/ui` |
|
||||
| MySQL | `docker compose exec mysql mysqladmin status` |
|
||||
|
||||
建议告警指标:
|
||||
|
||||
- MySQL 连接数与磁盘使用率
|
||||
- Redis 内存使用率
|
||||
- RabbitMQ 队列积压(`iotlcm.batch.execute`、`iotlcm.app.business`)
|
||||
- worker-svr 全量同步任务失败数
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
title: 可选服务
|
||||
description: 运营商接口挡板、飞书通知与反向代理
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 可选服务
|
||||
|
||||
以下服务默认不启动,按需通过 profile 开启。
|
||||
|
||||
## 运营商接口挡板
|
||||
|
||||
### 用途
|
||||
|
||||
模拟中国移动 OneLink 接口。**没有真实运营商环境时,靠它跑通完整链路**:
|
||||
|
||||
```text
|
||||
admin-svr → worker-svr → upstream-chan → onelink-mock
|
||||
```
|
||||
|
||||
不开挡板时,`upstream-chan` 会尝试连接真实运营商接口。没有凭据则批次执行、状态同步、流量查询全部失败。
|
||||
|
||||
### 启动
|
||||
|
||||
```bash
|
||||
docker compose --profile with-mock up -d
|
||||
```
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/api.json
|
||||
```
|
||||
|
||||
预期 `200`。接口文档在 `http://127.0.0.1:8080/swagger`。
|
||||
|
||||
### 配置
|
||||
|
||||
挡板自身无需配置,数据存在容器内 SQLite(`./data/mock/onelink_mock.db`)。
|
||||
|
||||
`upstream-chan` 配置中已预置指向:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
sim_yd_mock_v5:
|
||||
url: "http://onelink-mock:8080/v5/"
|
||||
```
|
||||
|
||||
数据库种子脚本 `schema/08-seed-mock-channel.sql` 已插入 Mock 通道(`channel_yd_mock`)并指向该地址。
|
||||
|
||||
`app-server` 若使用挡板,把 `upstream.onelinkMock.enabled` 改为 `true`,`baseUrl` 填 `http://onelink-mock:8080/v5`。
|
||||
|
||||
### 造测试卡
|
||||
|
||||
挡板支持生成模拟卡片,用于验证查询、开停复机、续费流程。接口见挡板 Swagger 页面。
|
||||
|
||||
### 切换到真实运营商
|
||||
|
||||
1. 删除或注释 `upstream-chan` 配置中的 `sim_yd_mock_v5` 段
|
||||
2. 在平台「通道管理」录入真实运营商通道(AppKey、AppSecret、接口地址)
|
||||
3. 停用 Mock 通道,避免卡片误挂
|
||||
|
||||
```bash
|
||||
docker compose --profile with-mock stop onelink-mock
|
||||
```
|
||||
|
||||
## 飞书通知
|
||||
|
||||
### 用途
|
||||
|
||||
把全量同步完成、失败告警等运维事件推送到飞书群。
|
||||
|
||||
### 准备
|
||||
|
||||
在飞书开放平台创建自建应用,获取 `appId`、`appSecret` 与目标群的 `chatId`,填入 `docker-compose.yml`:
|
||||
|
||||
```bash
|
||||
FEISHU_APP_ID=cli_xxxxxxxx
|
||||
FEISHU_APP_SECRET=xxxxxxxx
|
||||
FEISHU_CHAT_ID=oc_xxxxxxxx
|
||||
```
|
||||
|
||||
### 启动
|
||||
|
||||
```bash
|
||||
docker compose --profile with-bot up -d
|
||||
```
|
||||
|
||||
bot 读取 `worker-svr` 生成的全量同步归档(`./data/logs/worker-svr/full-sync`),该目录有数据后才会产生通知。
|
||||
|
||||
## 反向代理
|
||||
|
||||
### 用途
|
||||
|
||||
把分散在各端口的服务统一到域名下按路径分流。宿主机已有 nginx 或面板时不需要。
|
||||
|
||||
### 域名与分流
|
||||
|
||||
编辑 `config/nginx/iotlcm.conf`:
|
||||
|
||||
| 域名 | 分流到 |
|
||||
| --- | --- |
|
||||
| `api.your-domain.example.com` | openapi(`/openapi/v3/`、`/route/open/`) |
|
||||
| `admin.your-domain.example.com` | 管理端前端 + `/docs/` 文档站 |
|
||||
| `app.your-domain.example.com` | 小程序前端 + `/app/api/v1/` 后端 |
|
||||
|
||||
### 启动
|
||||
|
||||
```bash
|
||||
docker compose --profile with-nginx up -d
|
||||
```
|
||||
|
||||
默认监听 80,改 `docker-compose.yml` 的 `NGINX_HTTP_PORT` 可调整。
|
||||
|
||||
### 使用宿主机 nginx
|
||||
|
||||
不启动容器时,把 `config/nginx/iotlcm.conf` 中的 `upstream` 与 `location` 段落复制到宿主机 nginx 配置。
|
||||
|
||||
::: warning 宿主机 nginx 无法解析容器名
|
||||
需要把 `upstream` 里的服务名换成宿主机地址:
|
||||
|
||||
```nginx
|
||||
# 容器内写法
|
||||
server admin-svr:8008;
|
||||
# 宿主机写法
|
||||
server 127.0.0.1:8008;
|
||||
```
|
||||
:::
|
||||
|
||||
### 启用 HTTPS
|
||||
|
||||
自带配置未包含证书,启用需三步:
|
||||
|
||||
1. 准备证书,放入 `data/certs/`
|
||||
2. 在 server 块加入 SSL 配置:
|
||||
|
||||
```nginx
|
||||
listen 443 ssl;
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
```
|
||||
|
||||
3. 给 nginx 服务增加证书目录挂载,并加 80 → 443 跳转:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./data/certs:/etc/nginx/certs:ro
|
||||
```
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name admin.your-domain.example.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
## 组合启动
|
||||
|
||||
多个 profile 可同时启用:
|
||||
|
||||
```bash
|
||||
docker compose --profile with-mock --profile with-nginx up -d
|
||||
```
|
||||
|
||||
查看指定 profile 下的服务清单:
|
||||
|
||||
```bash
|
||||
docker compose --profile with-mock config --services
|
||||
```
|
||||
@@ -0,0 +1,134 @@
|
||||
---
|
||||
title: 环境准备
|
||||
description: 部署 IotLCM 所需的硬件、系统、Docker 与网络条件
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 环境准备
|
||||
|
||||
## 硬件要求
|
||||
|
||||
按数据规模选择:
|
||||
|
||||
| 场景 | CPU | 内存 | 磁盘 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 试用 / 演示 | 2 核 | 4 GB | 40 GB | 少量卡片,建议停用定时同步任务 |
|
||||
| 生产(中小规模) | 4 核 | 8 GB | 200 GB | 万级卡片,开启全量同步 |
|
||||
| 生产(大规模) | 8 核+ | 16 GB+ | 500 GB+ | 十万级卡片,建议数据库独立部署 |
|
||||
|
||||
磁盘估算:MySQL 数据约 1~2 KB/张卡;日志随同步任务量增长,建议单独挂载并配置轮转。
|
||||
|
||||
生产 8 GB 场景的内存分配参考:
|
||||
|
||||
| 服务 | 占用 |
|
||||
| --- | --- |
|
||||
| MySQL | 1~2 GB |
|
||||
| Redis | 200~500 MB |
|
||||
| RabbitMQ | 300~500 MB |
|
||||
| worker-svr | 500 MB~1 GB(与并发同步卡片数相关) |
|
||||
| 其余业务服务 | 各 100~200 MB |
|
||||
|
||||
## 系统要求
|
||||
|
||||
| 项 | 要求 |
|
||||
| --- | --- |
|
||||
| 操作系统 | Linux x86_64(CentOS 7+ / Ubuntu 20.04+ / Debian 11+) |
|
||||
| 内核 | 3.10 以上 |
|
||||
| 文件系统 | ext4 / xfs |
|
||||
| 时区 | 建议 `Asia/Shanghai`,与 `docker-compose.yml` 的 `TZ` 保持一致 |
|
||||
|
||||
## Docker 要求
|
||||
|
||||
| 组件 | 最低版本 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| Docker Engine | 20.10 | 需支持 `healthcheck` 与 `profiles` |
|
||||
| Docker Compose | v2.0(插件版) | 使用 `docker compose` 而非 `docker-compose` |
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
docker version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
`docker compose version` 需 ≥ v2.0。若只有 `docker-compose`(v1),请升级到 Compose V2 插件。
|
||||
|
||||
## 镜像仓库
|
||||
|
||||
平台镜像发布在阿里云容器镜像服务,**仓库类型为公开,无需登录即可拉取**。
|
||||
|
||||
| 项 | 值 |
|
||||
| --- | --- |
|
||||
| 公网地址 | `crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com` |
|
||||
| VPC 地址 | `crpi-qflrqrxoohlkkvgc-vpc.cn-chengdu.personal.cr.aliyuncs.com` |
|
||||
| 命名空间 | `iotlcm` |
|
||||
| 地域 | 西南1(成都) |
|
||||
|
||||
**所有镜像都放在同一个镜像仓库** `iotlcm/iotlcm` 下,靠 tag 区分服务与版本:
|
||||
|
||||
```text
|
||||
crpi-.../iotlcm/iotlcm:mysql-8.0
|
||||
crpi-.../iotlcm/iotlcm:redis-7.2
|
||||
crpi-.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4
|
||||
crpi-.../iotlcm/iotlcm:iotlcm-openapi-v1.0.4
|
||||
crpi-.../iotlcm/iotlcm:iotlcm-app-server-v1.0.2
|
||||
```
|
||||
|
||||
tag 格式统一为 **`<服务名>-<版本>`**。
|
||||
|
||||
地址结构是 `<Registry>/<命名空间>/<镜像仓库>:<tag>`,即:
|
||||
|
||||
```text
|
||||
crpi-...cn-chengdu.personal.cr.aliyuncs.com / iotlcm / iotlcm : iotlcm-admin-svr-v1.0.4
|
||||
↑ 命名空间 ↑ 镜像仓库 ↑ tag
|
||||
```
|
||||
|
||||
`docker-compose.yml` 里的镜像地址已预置为上述公网地址,一般无需修改。
|
||||
|
||||
::: tip 基础组件也在同一仓库
|
||||
MySQL、Redis、RabbitMQ、Consul、nginx 同样在这个仓库里(`mysql-8.0`、`redis-7.2` 等 tag),不从 Docker Hub 直连(公网不稳定且 tag 会漂)。版本已在编排文件中固定。
|
||||
:::
|
||||
|
||||
::: tip 阿里云 VPC 地址
|
||||
若部署在阿里云同地域 ECS 上,可把编排文件里的 Registry 前缀改为 VPC 地址,走内网流量、不消耗公网带宽:
|
||||
|
||||
```yaml
|
||||
image: 'crpi-qflrqrxoohlkkvgc-vpc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:mysql-8.0'
|
||||
```
|
||||
:::
|
||||
|
||||
## 网络与防火墙
|
||||
|
||||
仅需对外开放反向代理端口(默认 80 / 443),其余服务端口应限制为内网访问。
|
||||
|
||||
```bash
|
||||
# 示例:firewalld 仅放行 80/443
|
||||
firewall-cmd --permanent --add-service=http
|
||||
firewall-cmd --permanent --add-service=https
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
::: tip 默认只监听本机
|
||||
`admin-svr`、`admin-ui`、`openapi`、`app-server`、`app-web`、`docs` 默认绑定 `127.0.0.1`,只能本机访问。对外需通过反向代理,见[可选服务](optional)。
|
||||
:::
|
||||
|
||||
## 外部依赖(可选)
|
||||
|
||||
以下能力需要外部账号或服务,**不影响平台主流程**:
|
||||
|
||||
| 服务 | 用途 | 不配置的影响 |
|
||||
| --- | --- | --- |
|
||||
| 短信服务商 | 发送验证码 | 无法发送短信 |
|
||||
| 微信支付 | 在线支付 | 支付功能不可用 |
|
||||
| 飞书 | 运维通知 | 不启动 bot 即可 |
|
||||
|
||||
::: tip 时序库已内置
|
||||
流量历史使用的 TDengine **已包含在编排里**,由 `tdengine-init` 服务在首次启动时自动建库建表,无需额外申请或配置:
|
||||
|
||||
| 库 | 分片 | 保留 | 超级表 |
|
||||
| --- | --- | --- | --- |
|
||||
| `iotlcm_daily` | 7 天 | 3 年 | `t_sim_card_usage_daily` |
|
||||
| `iotlcm_monthly` | 30 天 | 3 年 | `t_sim_card_usage_monthly` |
|
||||
|
||||
编排使用的版本是 `3.3.6.13`(公开镜像可得),生产为 `3.4.0.9`。如需与生产完全一致,可自行构建镜像并替换 `docker-compose.yml` 中的 tag。
|
||||
:::
|
||||
@@ -0,0 +1,181 @@
|
||||
---
|
||||
title: 分离部署
|
||||
description: 中间件独立部署,业务服务单独一台或多台
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 分离部署
|
||||
|
||||
把 MySQL、Redis、RabbitMQ、Consul 放到独立主机或使用云托管,业务服务单独部署。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 数据库已有云托管实例(RDS、云 Redis、云 MQ)
|
||||
- 中间件已有独立集群,不希望在业务机上再跑一套
|
||||
- 业务机配置较低,不适合同时承载数据库
|
||||
|
||||
## 部署结构
|
||||
|
||||
```text
|
||||
┌─────────────────────┐ ┌──────────────────────┐
|
||||
│ 中间件主机 / 云服务 │ │ 业务主机 │
|
||||
│ │ │ │
|
||||
│ MySQL :3306 │◄───────┤ admin-svr :8008 │
|
||||
│ Redis :6379 │ │ admin-ui :80 │
|
||||
│ RabbitMQ :5672 │ │ openapi :8000 │
|
||||
│ Consul :8500 │ │ app-server :8010 │
|
||||
│ │ │ worker-svr │
|
||||
└─────────────────────┘ │ upstream-chan/-proxy│
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
## 一、准备中间件
|
||||
|
||||
在中间件侧完成(云托管通常已就绪):
|
||||
|
||||
| 组件 | 需准备 |
|
||||
| --- | --- |
|
||||
| MySQL | 建库 `db_iotlcm`、执行 `schema/` 脚本、建应用账号 `u_iotlcm` |
|
||||
| Redis | 开启认证,记录地址与口令 |
|
||||
| RabbitMQ | 建用户与 vhost,记录地址与口令 |
|
||||
| Consul | 单机模式即可,记录地址 |
|
||||
|
||||
### 初始化数据库
|
||||
|
||||
在能连到目标 MySQL 的机器上执行:
|
||||
|
||||
```bash
|
||||
# 按文件名顺序执行全部脚本
|
||||
for f in schema/0*.sql; do
|
||||
echo "执行 $f"
|
||||
mysql -h <MySQL地址> -uroot -p<口令> < "$f"
|
||||
done
|
||||
```
|
||||
|
||||
::: warning `.sh` 脚本需单独执行
|
||||
`schema/04-users.sh` 与 `schema/05-reset-passwords.sh` 依赖环境变量,不能直接重定向执行:
|
||||
|
||||
```bash
|
||||
MYSQL_ROOT_PASSWORD='<口令>' MYSQL_APP_PASSWORD='<应用口令>' \
|
||||
MYSQL_HOST=<地址> bash schema/04-users.sh
|
||||
```
|
||||
:::
|
||||
|
||||
## 二、准备业务机 Compose
|
||||
|
||||
复制 `docker-compose.yml`,删除四个中间件服务(`mysql`、`redis`、`rabbitmq`、`consul`),并移除各处 `depends_on` 对它们的引用。
|
||||
|
||||
```yaml
|
||||
# docker-compose.app.yml
|
||||
services:
|
||||
admin-svr:
|
||||
image: '${REGISTRY}/iotlcm/iotlcm:iotlcm-admin-svr-${TAG}'
|
||||
# ... 保留 ports / volumes / environment
|
||||
# 删除 depends_on 中对 mysql/redis/rabbitmq 的依赖
|
||||
```
|
||||
|
||||
::: tip 网络要求
|
||||
业务机必须能访问中间件主机的对应端口。若有防火墙,放行:
|
||||
|
||||
- 业务机 → 中间件:3306、6379、5672、8500
|
||||
- 外部 → 业务机:仅反向代理端口(80 / 443)
|
||||
:::
|
||||
|
||||
## 三、修改配置指向外部地址
|
||||
|
||||
这是本节最关键的一步。所有配置里以**服务名**书写的地址都要改成实际地址。
|
||||
|
||||
### admin-svr
|
||||
|
||||
```yaml
|
||||
database:
|
||||
default:
|
||||
link: "mysql:u_iotlcm:<口令>@tcp(<MySQL地址>:3306)/db_iotlcm?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
|
||||
|
||||
redis:
|
||||
default:
|
||||
address: "<Redis地址>:6379"
|
||||
pass: "<Redis口令>"
|
||||
|
||||
rabbitmq:
|
||||
dsn: "amqp://<用户>:<口令>@<RabbitMQ地址>:5672/"
|
||||
```
|
||||
|
||||
### openapi
|
||||
|
||||
同上三处:`database.default.link`、`redis.default.address`、`rabbitmq.dsn`。
|
||||
|
||||
### worker-svr
|
||||
|
||||
同上三处。注意 `tsdb.tdengine.dsn` 若使用 TDengine 也要改为实际地址。
|
||||
|
||||
### app-server
|
||||
|
||||
改 `database.default.link` 与 `redis.default`。
|
||||
|
||||
### upstream-chan / upstream-proxy
|
||||
|
||||
```yaml
|
||||
registry:
|
||||
type: "consul"
|
||||
address: "<Consul地址>:8500"
|
||||
```
|
||||
|
||||
同时改 `database.default.link` 与 `redis.default`。
|
||||
|
||||
### 服务互访地址
|
||||
|
||||
若业务服务分散在多台机器,`admin-svr`、`worker-svr`、`app-server` 配置里的上游地址也要改:
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
proxy:
|
||||
address: "<upstream-proxy所在主机>:8510"
|
||||
gateway:
|
||||
address: "<upstream-chan所在主机>:8110"
|
||||
```
|
||||
|
||||
`upstream-proxy` 的 `gateway.address` 同理。
|
||||
|
||||
::: danger 最容易出的错
|
||||
只改数据库地址,忘了改 `upstream-proxy:8510`、`upstream-chan:8110` 这类服务名。症状是服务能启动、数据库也通,但批次执行、智能诊断全部超时。
|
||||
:::
|
||||
|
||||
## 四、启动
|
||||
|
||||
```bash
|
||||
# 按需修改 docker-compose.app.yml 的镜像地址、端口与口令
|
||||
cp -r config.example config
|
||||
vi config/*/config.yaml # 按上节改为外部地址
|
||||
|
||||
docker compose -f docker-compose.app.yml up -d
|
||||
```
|
||||
|
||||
此时 `docker-compose.yml` 中的 `MYSQL_*`、`REDIS_PASSWORD`、`RABBITMQ_PASSWORD` 不再用于启动中间件容器,但仍作为配置中的变量来源,**取值要与中间件实际口令一致**。
|
||||
|
||||
## 五、验证
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.app.yml ps
|
||||
|
||||
# 数据库连通性
|
||||
docker compose -f docker-compose.app.yml exec admin-svr sh -c 'nc -zv <MySQL地址> 3306'
|
||||
docker compose -f docker-compose.app.yml exec admin-svr sh -c 'nc -zv <Redis地址> 6379'
|
||||
|
||||
# Redis 认证
|
||||
docker compose -f docker-compose.app.yml exec admin-svr sh -c 'redis-cli -h <地址> -a "<口令>" ping'
|
||||
|
||||
# 服务日志
|
||||
docker compose -f docker-compose.app.yml logs --tail=50 admin-svr
|
||||
```
|
||||
|
||||
浏览器登录管理端,确认能查卡、能发起业务办理。
|
||||
|
||||
## 六、注意事项
|
||||
|
||||
| 项 | 说明 |
|
||||
| --- | --- |
|
||||
| 网络延迟 | 业务机到中间件的延迟建议 < 5ms。跨地域部署会造成批次执行明显变慢 |
|
||||
| 连接池 | 外部数据库延迟较高时,适当调大 `database.default.maxIdle` 与 `maxOpen` |
|
||||
| 备份 | 分离后数据库备份由中间件侧负责,本仓的 `backup.sh` 需调整连接参数 |
|
||||
| 时钟 | 各机器需开启 NTP 同步,否则全量同步的时间窗口判断会出错 |
|
||||
@@ -0,0 +1,253 @@
|
||||
---
|
||||
title: 故障排查
|
||||
description: 部署与运行中的常见问题及处理
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 故障排查
|
||||
|
||||
## 通用排查
|
||||
|
||||
```bash
|
||||
docker compose ps # 容器状态
|
||||
docker compose logs --tail=100 <服务> # 最近日志
|
||||
docker compose logs -f <服务> # 跟随日志
|
||||
docker compose exec <服务> sh # 进容器
|
||||
```
|
||||
|
||||
## 启动类
|
||||
|
||||
### MySQL 一直不 healthy
|
||||
|
||||
**现象**:`iotlcm-mysql` 反复重启或一直 `starting`,其余服务不启动。
|
||||
|
||||
```bash
|
||||
docker compose logs mysql | tail -50
|
||||
```
|
||||
|
||||
| 日志关键词 | 原因 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| `Access denied` | root 口令与旧数据卷不一致 | 改 `docker-compose.yml` 与卷内一致,或清空 `data/mysql` 重建 |
|
||||
| `Can't create/write to file` | 目录权限 | `sudo chown -R 999:999 data/mysql` |
|
||||
| `No space left on device` | 磁盘满 | 清理磁盘 |
|
||||
| `Can't open shared library` | 架构不匹配 | 确认镜像与宿主机架构一致 |
|
||||
|
||||
### schema 初始化失败
|
||||
|
||||
```bash
|
||||
docker compose logs mysql | grep -iE 'error' | head -30
|
||||
```
|
||||
|
||||
| 原因 | 处理 |
|
||||
| --- | --- |
|
||||
| `MYSQL_APP_PASSWORD` 未设置,`04-users.sh` 退出 | 补上后清空 `data/mysql` 重建 |
|
||||
| `MYSQL_ADMIN_PASSWORD` 未设置,`05-reset-passwords.sh` 退出 | 同上 |
|
||||
| SQL 脚本语法问题 | 单独执行该脚本定位 |
|
||||
|
||||
::: 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: connection refused` | MySQL 未就绪 | 等 MySQL healthy |
|
||||
| `Access denied for user 'u_iotlcm'` | 应用账号口令与库内不一致 | 确认 `docker-compose.yml` 的 `MYSQL_APP_PASSWORD` 与初始化时一致 |
|
||||
| `panic: unsupported provider xxx` | 通道模板与镜像版本不匹配 | 确认镜像版本与 schema 版本配套 |
|
||||
|
||||
## 功能类
|
||||
|
||||
### 登录后提示未授权 / 接口 401
|
||||
|
||||
**原因**:`JWT_SIGNING_KEY` 在服务间不一致。
|
||||
|
||||
确认 `admin-svr`、`openapi`、`app-server` 三份配置的 `jwt.signingKey` 都引用 `${JWT_SIGNING_KEY}` 且 `docker-compose.yml` 有值,然后重启:
|
||||
|
||||
```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
|
||||
docker compose logs tdengine-init | tail -20 # 建库建表是否成功
|
||||
docker compose exec tdengine taos -s "show databases;"
|
||||
```
|
||||
|
||||
常见原因与处理:
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| `tdengine-init` 报 MISS | 建表失败 | 看该容器日志里的 `DB error`;改 `tdengine-init/init.sql` 后重跑 `docker compose up -d tdengine-init` |
|
||||
| worker 日志报库表不存在 | 未执行建表 | 同上,确保 init 容器退出码为 0 |
|
||||
| 连接超时 | 地址/端口不对 | 配置里应为 `ws(tdengine:6041)`,容器内用服务名而非 IP |
|
||||
|
||||
建表脚本全部带 `IF NOT EXISTS`,可重复执行。
|
||||
|
||||
### 短信发不出去
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11190/healthz
|
||||
docker compose logs sms --tail=50
|
||||
```
|
||||
|
||||
| 报错 | 原因 |
|
||||
| --- | --- |
|
||||
| `InvalidAccessKeyId` | `docker-compose.yml` 中 AK 未填或错误 |
|
||||
| `isv.SMS_SIGNATURE_ILLEGAL` | `signName` 与服务商审核签名不一致 |
|
||||
| `isv.SMS_TEMPLATE_ILLEGAL` | 模板 ID 未审核通过 |
|
||||
| `缺少 config.templateId` | 场景模板库未配置,调用方需自带 templateId |
|
||||
|
||||
### 图片上传失败 / 不显示
|
||||
|
||||
```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. 演示 / 试用环境直接停用定时任务:
|
||||
|
||||
```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` | 清理旧日志,见[升级与备份](operations) |
|
||||
| `data/upload` | 检查异常大的上传文件 |
|
||||
| `data/mysql` | 数据库增长正常,关注 binlog |
|
||||
| `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:<端口>`
|
||||
|
||||
::: danger 不要直接把端口改成 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
|
||||
```
|
||||
Reference in New Issue
Block a user