docs: 部署手册适配一键部署,去掉环境变量与示例配置

部署仓已改为开箱即用:config/ 直接是配置文件、口令等值内置在
docker-compose.yml、不再有 .env。

同步调整:
- 概览:部署流程改为 git clone + docker compose up -d 三步
- 配置说明:重写为「编排层 + 应用层」,说明为何不用环境变量
  (GoFrame 不展开配置文件中的 ${VAR})、改口令需同步三处、
  口令不能含 @ : / 等字符
- 启动与验证:去掉准备步骤,schema 脚本数改为 13 个
- 环境准备:镜像仓库改为描述编排文件中的实际地址
- 升级备份/离线/分离/自建镜像:.env 相关操作改为修改编排文件
This commit is contained in:
MiFaZhan
2026-10-29 09:20:09 +08:00
parent f169afc1bc
commit 73adad97f3
11 changed files with 244 additions and 168 deletions
+2 -2
View File
@@ -109,7 +109,7 @@ uname -m # x86_64 → amd64;aarch64 → arm64
/opt/iotlcm/ /opt/iotlcm/
├── admin-svr/ ├── admin-svr/
│ ├── iotlcmadmin_svc │ ├── iotlcmadmin_svc
│ ├── config/config.yaml ← 从 config.example/admin-svr 复制 │ ├── config/config.yaml ← 从 config/admin-svr 复制
│ ├── logs/ │ ├── logs/
│ ├── upload/ │ ├── upload/
│ └── cache/ │ └── cache/
@@ -265,7 +265,7 @@ npm ci && npm run build
sudo cp -r apps/web-antd/dist /var/www/iotlcm/admin-ui sudo cp -r apps/web-antd/dist /var/www/iotlcm/admin-ui
``` ```
nginx 配置参考部署仓的 `config.example/admin-ui/default.conf` 与 `config.example/nginx/iotlcm.conf`,把 `proxy_pass http://admin-svr:8008` 改为 `http://127.0.0.1:8008`。 nginx 配置参考部署仓的 `config/admin-ui/default.conf` 与 `config/nginx/iotlcm.conf`,把 `proxy_pass http://admin-svr:8008` 改为 `http://127.0.0.1:8008`。
## 验证 ## 验证
+69 -27
View File
@@ -123,49 +123,91 @@ docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .
## 镜像命名规则 ## 镜像命名规则
部署编排按以下名称拉取镜像,自建时需保持一致: 部署编排按以下名称拉取镜像,自建时需保持一致。`<镜像仓库>` 即官方仓库前缀 `crpi-qflrqrxoohllkvgc.cn-chengdu.personal.cr.aliyuncs.com`:
| 服务 | 镜像名 | | 服务 | 镜像名 |
| --- | --- | | --- | --- |
| admin-svr | `<REGISTRY>/iotlcm/iotlcm-admin-svr:<TAG>` | | admin-svr | `<镜像仓库>/iotlcm/iotlcm-admin-svr:<TAG>` |
| admin-ui | `<REGISTRY>/iotlcm/iotlcm-admin-ui:<TAG>` | | admin-ui | `<镜像仓库>/iotlcm/iotlcm-admin-ui:<TAG>` |
| openapi | `<REGISTRY>/iotlcm/iotlcm-openapi:<TAG>` | | openapi | `<镜像仓库>/iotlcm/iotlcm-openapi:<TAG>` |
| app-server | `<REGISTRY>/iotlcm/iotlcm-app-server:<TAG>` | | app-server | `<镜像仓库>/iotlcm/iotlcm-app-server:<TAG>` |
| app-web | `<REGISTRY>/iotlcm/iotlcm-app-web:<TAG>` | | app-web | `<镜像仓库>/iotlcm/iotlcm-app-web:<TAG>` |
| upstream-chan | `<REGISTRY>/iotlcm/iotlcm-upstream-chan:<TAG>` | | upstream-chan | `<镜像仓库>/iotlcm/iotlcm-upstream-chan:<TAG>` |
| upstream-proxy | `<REGISTRY>/iotlcm/iotlcm-upstream-proxy:<TAG>` | | upstream-proxy | `<镜像仓库>/iotlcm/iotlcm-upstream-proxy:<TAG>` |
| worker-svr | `<REGISTRY>/iotlcm/iotlcm-worker-svr:<TAG>` | | worker-svr | `<镜像仓库>/iotlcm/iotlcm-worker-svr:<TAG>` |
| sms | `<REGISTRY>/iotlcm/iotlcm-sms:<TAG>` | | sms | `<镜像仓库>/iotlcm/iotlcm-sms:<TAG>` |
| bot | `<REGISTRY>/iotlcm/iotlcm-bot:<TAG>` | | bot | `<镜像仓库>/iotlcm/iotlcm-bot:<TAG>` |
| docs | `<REGISTRY>/iotlcm/iotlcm-docs:<TAG>` | | docs | `<镜像仓库>/iotlcm/iotlcm-docs:<TAG>` |
| onelink-mock | `<REGISTRY>/iotlcm/iotlcm-onelink-mock:<TAG>` | | onelink-mock | `<镜像仓库>/iotlcm/iotlcm-onelink-mock:<TAG>` |
基础组件(`library/mysql`、`library/redis` 等)直接使用上游官方镜像,无需自建。 基础组件(`mysql`、`redis`、`rabbitmq`、`consul`、`nginx`)不需要构建,只需从上游同步到 `<镜像仓库>/library/`,见下节。
## 同步基础组件
平台编排不再从 Docker Hub 直连基础组件(公网不稳定,且 `8.0` 这类 tag 会随补丁升级而漂)。部署前先把五个镜像同步到官方仓库:
```bash
REPO=crpi-qflrqrxoohllkvgc.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/library/$dst"
docker push "$REPO/library/$dst"
}
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`),并把 `docker-compose.yml` 的 `MYSQL_VERSION` 同步改为 `8.0.43`,保证多台机器拉到的镜像完全一致。
:::
同步完成后校验:
```bash
for img in mysql:8.0 redis:7.2 rabbitmq:4.3.5-management consul:1.22 nginx:1.30; do
docker manifest inspect "$REPO/library/$img" >/dev/null 2>&1 \
&& echo "✅ $img" || echo "❌ $img"
done
```
## 推送到自己的仓库 ## 推送到自己的仓库
```bash 自建镜像不需要推送回官方仓库。推到自有仓库后,把 `docker-compose.yml` 里的镜像前缀改指向它:
REGISTRY=harbor.internal.example.com
TAG=1.0.3
docker tag iotlcm-openapi:local $REGISTRY/iotlcm/iotlcm-openapi:$TAG
docker push $REGISTRY/iotlcm/iotlcm-openapi:$TAG
```
部署时 `.env` 改为:
```bash ```bash
REGISTRY=harbor.internal.example.com REG=harbor.internal.example.com
TAG=1.0.3 TAG=1.0.3
docker tag iotlcm-openapi:local $REG/iotlcm/iotlcm-openapi:$TAG
docker login $REG
docker push $REG/iotlcm/iotlcm-openapi:$TAG
``` ```
```yaml
# docker-compose.yml —— 逐个替换 image 前缀
image: 'harbor.internal.example.com/iotlcm/iotlcm-openapi:1.0.3'
```
::: tip 只覆盖部分服务
只想替换其中一两个服务时,把对应镜像推到自有仓库,其余保留官方地址。此时把 `docker-compose.yml` 里那几个 `image` 单独写成完整地址,其余保持官方仓库前缀即可。
:::
## 批量构建脚本 ## 批量构建脚本
```bash ```bash
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
REGISTRY=${REGISTRY:?请设置 REGISTRY} REPO=${REPO:?请设置 REPO}
TAG=${TAG:-$(date +%Y%m%d)} TAG=${TAG:-$(date +%Y%m%d)}
WORKDIR=${WORKDIR:-./repos} WORKDIR=${WORKDIR:-./repos}
@@ -201,12 +243,12 @@ for item in "${SERVICES[@]}"; do
--build-arg BuildVersion="$VERSION" \ --build-arg BuildVersion="$VERSION" \
--build-arg CommitID="$COMMIT" \ --build-arg CommitID="$COMMIT" \
--build-arg BuildTime="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ --build-arg BuildTime="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
-t "$REGISTRY/iotlcm/$image:$TAG" \ -t "$REPO/iotlcm/$image:$TAG" \
-f "$WORKDIR/$repo/${ctx}Dockerfile" \ -f "$WORKDIR/$repo/${ctx}Dockerfile" \
"$WORKDIR/$repo/$ctx" "$WORKDIR/$repo/$ctx"
done done
echo "全部完成,镜像标签:$REGISTRY/iotlcm/*:$TAG" echo "全部完成,镜像标签:$REPO/iotlcm/*:$TAG"
``` ```
::: warning 脚本里的 sed 只能应急 ::: warning 脚本里的 sed 只能应急
+100 -93
View File
@@ -1,68 +1,66 @@
--- ---
title: 配置说明 title: 配置说明
description: 部署环境变量与应用配置文件的填写方式 description: 部署配置的结构、默认值与修改方式
outline: deep outline: deep
--- ---
# 配置说明 # 配置说明
平台配置分两层: 平台配置分两层,**都随仓库分发、都带可用默认值**,无需复制或初始化即可启动:
| 层 | 位置 | 管什么 | 是否入库 | | 层 | 位置 | 管什么 |
| --- | --- | --- | --- | | --- | --- | --- |
| 部署层 | `.env` | 镜像仓库、口令、端口、外部服务地址 | 否 | | 编排层 | `docker-compose.yml` | 镜像地址、端口、数据库/Redis/MQ 口令、容器编排 |
| 应用层 | `config/<服务>/config.yaml` | 业务参数、连接串、功能开关 | 否 | | 应用层 | `config/<服务>/config.yaml` | 业务参数、连接串、功能开关 |
两者分别由 `.env.example` 与 `config.example/` 复制而来,`.gitignore` 已排除。**不要把自己的配置提交回仓库。** ::: warning 配置里是字面值,不是环境变量
配置文件与编排文件里直接写的是具体值,**不使用 `${VAR}` 占位**。
## 一、环境变量 原因:GoFrame 不会展开配置文件中的 `${VAR}`(环境变量只用于覆盖配置键,不参与文件内文本替换),用占位符会导致连接串变成字面量 `${X}`,服务直接连不上库。
```bash 代价是改口令要改多处,见下方「改口令的正确姿势」。
cp .env.example .env :::
## 编排层:`docker-compose.yml`
### 镜像地址
```yaml
image: 'crpi-qflrqrxoohllkvgc.cn-chengdu.personal.cr.aliyuncs.com/library/mysql:8.0'
image: 'crpi-qflrqrxoohllkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm-admin-svr:latest'
``` ```
### 必填项 - 基础组件在 `<镜像仓库>/library/` 下
- 业务镜像在 `<镜像仓库>/iotlcm/` 下
| 变量 | 说明 | 改用自有仓库时,全局替换前缀即可。
| --- | --- |
| `REGISTRY` | 镜像仓库地址,不含协议与末尾斜杠 |
| `TAG` | 镜像标签,生产用具体版本号 |
| `MYSQL_ROOT_PASSWORD` | MySQL root 口令 |
| `MYSQL_APP_PASSWORD` | 应用账号 `u_iotlcm` 的口令 |
| `MYSQL_ADMIN_PASSWORD` | 平台 admin 账号初始口令 |
| `REDIS_PASSWORD` | Redis 口令 |
| `RABBITMQ_PASSWORD` | RabbitMQ 口令 |
| `JWT_SIGNING_KEY` | JWT 签名密钥 |
任一项未设置时 `docker compose` 会直接报错退出,不会用空值或弱口令启动。 ### 默认口令
生成强口令: | 变量 | 默认值 | 用途 |
| --- | --- | --- |
```bash | `MYSQL_ROOT_PASSWORD` | 见文件 | MySQL root |
openssl rand -base64 24 # 各类密码 | `MYSQL_DATABASE` | `db_iotlcm` | 库名 |
openssl rand -hex 32 # JWT_SIGNING_KEY | `RABBITMQ_DEFAULT_PASS` | 见文件 | RabbitMQ |
``` | redis `--requirepass` | 见文件 | Redis |
### 可选项
| 变量 | 不填的影响 |
| --- | --- |
| `DIAGNOSIS_INTERNAL_TOKEN` | 控制台「智能诊断」不可用 |
| `SMS_ACCESS_KEY_ID` / `SMS_ACCESS_KEY_SECRET` | 无法发送短信验证码 |
| `FEISHU_APP_ID` / `FEISHU_APP_SECRET` / `FEISHU_CHAT_ID` | 不启动 bot 即可 |
| `TDENGINE_*` | 历史流量查询不可用,主流程正常 |
### 端口 ### 端口
全部端口变量见 `.env.example`,均有默认值。改端口只改 `.env`。 | 端口 | 服务 | 对外 |
| --- | --- | --- |
| 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` 的冒号左侧。
```bash ## 应用层:`config/`
cp -r config.example config
```
得到 10 份:
``` ```
config/ config/
@@ -78,100 +76,109 @@ config/
└── worker-svr/config.yaml └── worker-svr/config.yaml
``` ```
所有配置支持 `${VAR}` 与 `${VAR:default}` 占位,由 compose 从 `.env` 注入。**口令类字段保持占位引用即可,不要写死明文。** ### 各服务要点
### 各服务配置要点
#### admin-svr(管理端后端) #### admin-svr(管理端后端)
| 项 | 说明 | | 项 | 默认情况 |
| --- | --- | | --- | --- |
| `database.default.link` | 库连接串,已用 `${MYSQL_APP_PASSWORD}` 拼好 | | `database.default.link` | 指向 `mysql:3306`,口令与应用账号一致 |
| `database.legacyCard.link` | 旧平台库,没有就留空 | | `database.legacyCard.link` | 空,不使用旧平台库 |
| `jwt.signingKey` | 保持 `${JWT_SIGNING_KEY}` | | `jwt.signingKey` | 已填固定密钥,需与 openapi、app-server 一致 |
| `warning.sms.enabled` | 不用短信改 `false` | | `warning.sms.enabled` | `true`,未配短信服务商时建议改 `false` |
| `warning.wechat.*` | 不用微信订阅消息改 `false` 并留空 | | `warning.wechat.enabled` | `false` |
| `register.email.*` | 邮件验证码的 SMTP,不用则留空 | | `register.email.*` | 空,不发邮件 |
| `tsdb.tdengine.dsn` | 不用时序库则留空 | | `tsdb.tdengine.dsn` | 空,不启用时序库 |
| `alipay.*` | 不用支付宝整段删除 | | `alipay.*` | 空,不启用支付 |
| `upstream.appDiagnosisToken` | 与 proxy 的 `appDiagnosis.internalToken` 一致 | | `upstream.appDiagnosisToken` | 已填,需与 upstream-proxy 一致 |
#### openapi(对外 API) #### openapi(对外 API)
| 项 | 说明 | `jwt.signingKey` 与 admin-svr 一致;`database.default.link` 同库。建议另建只读账号用于生产。
| --- | --- |
| `jwt.signingKey` | 与 admin-svr 一致 |
| `database.default.link` | 建议单独建只读账号隔离 |
#### worker-svr(异步任务) #### worker-svr(异步任务)
| 项 | 说明 | | 项 | 说明 |
| --- | --- | | --- | --- |
| `batch.inbound.upstreamRateLimit` | **运营商接口限流,必须按上游实际配额设置**,调太大会被限流或封禁 | | `batch.inbound.upstreamConcurrency` | 默认 300,机器弱或上游配额小时调低 |
| `rabbitmq.batchExecute.maxRetries` | 批次执行失败重试次数 | | `batch.inbound.upstreamRateLimit` | 运营商限流,**必须按上游实际配额设置**,调大易被封禁 |
| `tsdb.tdengine.dsn` | 不用时序库则留空 | | `tsdb.tdengine.dsn` | 空,不启用时序库 |
#### upstream-chan(运营商对接) #### upstream-chan(运营商对接)
| 项 | 说明 | | 项 | 说明 |
| --- | --- | | --- | --- |
| `registry.address` | Consul 地址,默认 `consul:8500` | | `registry.address` | `consul:8500` |
| `dependencies.sim_yd_mock_v5.url` | 挡板地址,对接真实运营商时删除本段 | | `dependencies.sim_yd_mock_v5.url` | 指向挡板 `http://onelink-mock:8080/v5/`,接真实运营商时删除本段 |
| `dependencies.token_refresh.skew_seconds` | 运营商 Token 提前刷新窗口 |
运营商通道本身(AppKey、AppSecret、接口地址)不在本文件配置,而是在平台「通道管理」中录入,存数据库。 运营商通道本身(AppKey、AppSecret、接口地址)在平台「通道管理」中录入,存数据库,不在本文件。
#### upstream-proxy(通道缓存转发) #### upstream-proxy(通道缓存转发)
| 项 | 说明 | | 项 | 说明 |
| --- | --- | | --- | --- |
| `appDiagnosis.internalToken` | 与 admin-svr 的 `upstream.appDiagnosisToken` 一致 | | `appDiagnosis.internalToken` | 与 admin-svr 的 `upstream.appDiagnosisToken` 一致 |
| `gateway.batchTimeout` | 批量转发总预算上限,**worker 侧超时必须大于此值** | | `gateway.batchTimeout` | 默认 20m,**worker 侧超时必须大于此值** |
#### app-server(小程序后端) #### app-server(小程序后端)
| 项 | 说明 | `jwt.signingKey` 同上;`payment.wechat.enabled` 默认 `false`;`appCardRecognition.enabled` 默认 `false`(拍照识别需自备视觉模型 API)。
| --- | --- |
| `jwt.signingKey` | 与 admin-svr 一致 |
| `payment.wechat.*` | 不用支付把 `enabled` 改 `false` |
| `appCardRecognition.*` | 拍照识别卡号,不用改 `false` |
| `upstream.onelinkMock.*` | 挡板联调配置,切真实运营商时 `enabled: false` |
#### sms(短信服务) #### sms(短信服务)
| 项 | 说明 | | 项 | 默认 |
| --- | --- | | --- | --- |
| `serviceType` | `1`=阿里云,`2`=腾讯云 | | `serviceType` | `1`(阿里云) |
| `accessKeyId` / `accessKeySecret` | 从 `.env` 注入 | | `accessKeyId` / `accessKeySecret` | 空,需自行填写 |
| `signName` | 短信签名,需服务商审核通过 | | `signName` | `示例签名` |
#### bot(飞书通知,可选) 不填则无法发送验证码,其余功能正常。
`feishu.appId`、`appSecret`、`chatId` 全部从 `.env` 注入。 #### bot(飞书通知)
#### admin-ui(管理端 nginx) `feishu.appId` / `appSecret` / `chatId` 均为空。不启动该容器即可。
`default.conf` 中 `/api/` 反代到 `admin-svr:8008`,`/upload/` 指向共享上传目录。一般无需修改。 #### admin-ui / nginx
#### nginx(对外入口,可选) `admin-ui/default.conf` 把 `/api/` 反代到 `admin-svr:8008`;`nginx/iotlcm.conf` 按域名分流,需把 `your-domain.example.com` 换成实际域名。
`iotlcm.conf` 按域名分三组 server:`api.*` → openapi,`admin.*` → 管理端与文档站,`app.*` → 小程序端。**把示例域名替换为实际域名。** ## 改口令的正确姿势
不用自带 nginx 时,把其中 `location` 段落复制到宿主机 nginx 配置即可。 改数据库口令时,以下**三处必须同时改**,漏一处就起不来:
## 三、配置自检 | 位置 | 内容 |
| --- | --- |
| `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 ```bash
# 检查展开结果(不启动容器) # 编排语法
docker compose config
# 只检查必填项是否齐全
docker compose config --quiet docker compose config --quiet
# 看某个服务最终的配置展开
docker compose config | sed -n '/admin-svr:/,/^ [a-z]/p'
``` ```
配置语法错误会导致服务启动后立即退出,用日志定位: 配置语法错误会导致服务启动后立刻退出,看日志定位:
```bash ```bash
docker compose logs --tail=50 admin-svr docker compose logs --tail=50 admin-svr
+17 -11
View File
@@ -15,10 +15,9 @@ cd iotlcm-deploy
```text ```text
iotlcm-deploy/ iotlcm-deploy/
├── docker-compose.yml # 服务编排 ├── docker-compose.yml # 服务编排(含默认口令与端口)
├── .env.example # 环境变量模板 ├── config/ # 各服务配置,10 份,已带可用默认值
├── config.example/ # 应用配置模板(10 份) └── schema/ # 数据库初始化脚本,13 个,首次启动自动执行
└── schema/ # 数据库初始化脚本(12 个)
``` ```
## 部署内容 ## 部署内容
@@ -37,20 +36,27 @@ iotlcm-deploy/
## 部署流程 ## 部署流程
```bash ```bash
cp .env.example .env # 1. 环境变量 git clone <部署仓地址> iotlcm-deploy
cp -r config.example config # 2. 应用配置 cd iotlcm-deploy
docker compose up -d # 3. 启动 docker compose up -d
``` ```
首次启动自动建库并导入 schema,约 1~2 分钟。 三步,不需要复制或修改任何文件——配置与编排都随仓库分发且带可用默认值。首次启动自动建库并导入 schema,约 1~2 分钟。
启动后访问 `http://<宿主机IP>:8009`,账号 `admin`,口令见 `docker-compose.yml`。
::: warning 默认口令只用于试用
正式部署前必须修改数据库、Redis、RabbitMQ、admin 账号口令与 JWT 密钥,详见[配置说明](configuration)。
:::
## 文档导航 ## 文档导航
| 文档 | 内容 | | 文档 | 内容 |
| --- | --- | | --- | --- |
| [环境准备](prerequisites) | 硬件、系统、Docker 版本、端口规划 | | [环境准备](prerequisites) | 硬件、系统、Docker 版本、镜像仓库、端口规划 |
| [配置说明](configuration) | `.env` 与 `config/` 各项含义 | | [配置说明](configuration) | `docker-compose.yml` 与 `config/` 各项含义 |
| [启动与验证](install) | 启动流程、验证清单、首次登录 | | [启动与验证](install) | 启动流程、验证清单、首次登录 |
| [其他部署方式](alternatives) | 二进制、自建镜像、分离、离线 |
| [可选服务](optional) | 接口挡板、飞书通知、反向代理 | | [可选服务](optional) | 接口挡板、飞书通知、反向代理 |
| [升级与备份](operations) | 版本固定、升级回滚、数据备份 | | [升级与备份](operations) | 版本固定、升级回滚、数据备份 |
| [故障排查](troubleshooting) | 常见问题与处理 | | [故障排查](troubleshooting) | 常见问题与处理 |
@@ -88,4 +94,4 @@ docker compose up -d # 3. 启动
| 11190 / 11200 | sms | 仅内网 | | 11190 / 11200 | sms | 仅内网 |
| 8080 | onelink-mock | 仅联调 | | 8080 | onelink-mock | 仅联调 |
端口冲突时修改 `.env` 中对应变量,不要直接改 `docker-compose.yml`。 端口冲突时修改 `docker-compose.yml` 中对应变量,不要直接改 `docker-compose.yml`。
+7 -2
View File
@@ -9,6 +9,9 @@ outline: deep
## 启动 ## 启动
```bash ```bash
git clone <部署仓地址> iotlcm-deploy
cd iotlcm-deploy
# 首次部署建议前台启动,便于观察初始化过程 # 首次部署建议前台启动,便于观察初始化过程
docker compose up docker compose up
@@ -16,10 +19,12 @@ docker compose up
docker compose up -d docker compose up -d
``` ```
不需要复制或修改任何文件,配置已带可用默认值。
### 首次启动发生什么 ### 首次启动发生什么
1. **MySQL 建库并导入 schema**(约 1~2 分钟) 1. **MySQL 建库并导入 schema**(约 1~2 分钟)
`schema/` 下 12 个脚本按文件名顺序执行:建表 → 结构补齐 → 创建应用账号 → 重置种子账号口令 → 导入通道模板/字典/菜单等种子数据 `schema/` 下 13 个脚本按文件名顺序执行:建表 → 结构补齐 → 创建应用账号 → 重置种子账号口令 → 导入通道模板/字典/菜单等种子数据
2. MySQL 通过健康检查后,`admin-svr`、`openapi`、`worker-svr` 依次启动 2. MySQL 通过健康检查后,`admin-svr`、`openapi`、`worker-svr` 依次启动
3. 各服务向 Consul 注册 3. 各服务向 Consul 注册
@@ -137,7 +142,7 @@ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:11190/healthz
| 项 | 值 | | 项 | 值 |
| --- | --- | | --- | --- |
| 账号 | `admin` | | 账号 | `admin` |
| 口令 | `.env` 中的 `MYSQL_ADMIN_PASSWORD` | | 口令 | `docker-compose.yml` 中 admin 的默认口令(首次登录后立即修改) |
能进入首页工作台、左侧菜单完整显示即部署成功。 能进入首页工作台、左侧菜单完整显示即部署成功。
+4 -5
View File
@@ -97,8 +97,7 @@ docker images | grep iotlcm
导入后镜像已在本机,compose 不会再尝试拉取: 导入后镜像已在本机,compose 不会再尝试拉取:
```bash ```bash
cp .env.example .env # 把 docker-compose.yml 里的镜像地址与 TAG 改成与导入的镜像一致
vi .env # REGISTRY 与 TAG 需与导入的镜像标签完全一致
cp -r config.example config cp -r config.example config
vi config/*/config.yaml vi config/*/config.yaml
@@ -106,7 +105,7 @@ docker compose up -d
``` ```
::: warning REGISTRY 与 TAG 必须与导入的镜像名一致 ::: warning REGISTRY 与 TAG 必须与导入的镜像名一致
`docker load` 只恢复镜像名,不会改标签。若 `.env` 里写 `TAG=latest` 而导入的是 `1.0.3`,compose 会认为镜像不存在并尝试拉取,离线环境直接失败。 `docker load` 只恢复镜像名,不会改标签。若 `docker-compose.yml` 里写 `TAG=latest` 而导入的是 `1.0.3`,compose 会认为镜像不存在并尝试拉取,离线环境直接失败。
用这条命令核对: 用这条命令核对:
@@ -133,7 +132,7 @@ for img in $(cat images.txt); do
done done
``` ```
然后目标机的 `.env` 改为: 然后目标机的 `docker-compose.yml` 改为:
```bash ```bash
REGISTRY=harbor.internal.example.com REGISTRY=harbor.internal.example.com
@@ -165,7 +164,7 @@ docker compose pull
| 资源 | 说明 | | 资源 | 说明 |
| --- | --- | | --- | --- |
| 部署仓代码 | `git clone` 或直接打包 `iotlcm-deploy/` 目录带过去 | | 部署仓代码 | `git clone` 或直接打包 `iotlcm-deploy/` 目录带过去 |
| 配置模板 | 已包含在部署仓 `config.example/` | | 配置模板 | 已包含在部署仓 `config/` |
| 数据库脚本 | 已包含在部署仓 `schema/` | | 数据库脚本 | 已包含在部署仓 `schema/` |
| Docker 与 Compose | 目标机需预先安装,离线安装包需单独准备 | | Docker 与 Compose | 目标机需预先安装,离线安装包需单独准备 |
| 时区数据 | 若使用 `Asia/Shanghai`,确认容器内 tzdata 完整(官方镜像已内置) | | 时区数据 | 若使用 `Asia/Shanghai`,确认容器内 tzdata 完整(官方镜像已内置) |
+6 -6
View File
@@ -14,7 +14,7 @@ outline: deep
- 出问题无法确定运行版本 - 出问题无法确定运行版本
- 回滚时没有明确目标 - 回滚时没有明确目标
`.env` 中指定: `docker-compose.yml` 中指定:
```bash ```bash
TAG=1.0.3 TAG=1.0.3
@@ -23,7 +23,7 @@ TAG=1.0.3
记录当前部署版本: 记录当前部署版本:
```bash ```bash
echo "$(date '+%Y-%m-%d %H:%M') TAG=$(grep '^TAG=' .env | cut -d= -f2)" >> deploy-history.log echo "$(date '+%Y-%m-%d %H:%M') 镜像=$(grep -m1 'iotlcm-admin-svr:' docker-compose.yml)" >> deploy-history.log
``` ```
## 升级 ## 升级
@@ -33,7 +33,7 @@ echo "$(date '+%Y-%m-%d %H:%M') TAG=$(grep '^TAG=' .env | cut -d= -f2)" >> depl
./backup.sh ./backup.sh
# 2. 拉新镜像 # 2. 拉新镜像
vi .env # 修改 TAG vi docker-compose.yml # 修改镜像 TAG
docker compose pull docker compose pull
# 3. 更新 # 3. 更新
@@ -53,7 +53,7 @@ docker compose up -d --no-deps admin-svr
## 回滚 ## 回滚
```bash ```bash
vi .env # TAG 改回旧版本 vi docker-compose.yml # TAG 改回旧版本
docker compose pull docker compose pull
docker compose up -d docker compose up -d
``` ```
@@ -102,7 +102,7 @@ docker compose exec -T mysql sh -c \
| gzip > "${DEST}/db_iotlcm.sql.gz" | gzip > "${DEST}/db_iotlcm.sql.gz"
echo "[2/3] 备份配置..." echo "[2/3] 备份配置..."
tar czf "${DEST}/config.tar.gz" config/ .env tar czf "${DEST}/config.tar.gz" config/ docker-compose.yml
echo "[3/3] 备份上传文件..." echo "[3/3] 备份上传文件..."
tar czf "${DEST}/upload.tar.gz" data/upload/ 2>/dev/null || echo " 上传目录为空,跳过" tar czf "${DEST}/upload.tar.gz" data/upload/ 2>/dev/null || echo " 上传目录为空,跳过"
@@ -136,7 +136,7 @@ gunzip -c db_iotlcm-20260101.sql.gz | \
| 内容 | 位置 | 重要性 | | 内容 | 位置 | 重要性 |
| --- | --- | --- | | --- | --- | --- |
| 数据库 | MySQL 容器内 `db_iotlcm` | **最高**,业务数据全部在此 | | 数据库 | MySQL 容器内 `db_iotlcm` | **最高**,业务数据全部在此 |
| 配置 | `config/` + `.env` | 高,丢失需重新配置 | | 配置 | `config/` + `docker-compose.yml` | 高,丢失需重新配置 |
| 上传文件 | `data/upload/` | 中,附件与图片 | | 上传文件 | `data/upload/` | 中,附件与图片 |
| Redis 数据 | `data/redis/` | 低,缓存可重建 | | Redis 数据 | `data/redis/` | 低,缓存可重建 |
| RabbitMQ 队列 | `data/rabbitmq/` | 低,未消费消息会丢失 | | RabbitMQ 队列 | `data/rabbitmq/` | 低,未消费消息会丢失 |
+2 -2
View File
@@ -72,7 +72,7 @@ docker compose --profile with-mock stop onelink-mock
### 准备 ### 准备
在飞书开放平台创建自建应用,获取 `appId`、`appSecret` 与目标群的 `chatId`,填入 `.env`: 在飞书开放平台创建自建应用,获取 `appId`、`appSecret` 与目标群的 `chatId`,填入 `docker-compose.yml`:
```bash ```bash
FEISHU_APP_ID=cli_xxxxxxxx FEISHU_APP_ID=cli_xxxxxxxx
@@ -110,7 +110,7 @@ bot 读取 `worker-svr` 生成的全量同步归档(`./data/logs/worker-svr/fu
docker compose --profile with-nginx up -d docker compose --profile with-nginx up -d
``` ```
默认监听 80,改 `.env` 的 `NGINX_HTTP_PORT` 可调整。 默认监听 80,改 `docker-compose.yml` 的 `NGINX_HTTP_PORT` 可调整。
### 使用宿主机 nginx ### 使用宿主机 nginx
+30 -12
View File
@@ -35,7 +35,7 @@ outline: deep
| 操作系统 | Linux x86_64(CentOS 7+ / Ubuntu 20.04+ / Debian 11+) | | 操作系统 | Linux x86_64(CentOS 7+ / Ubuntu 20.04+ / Debian 11+) |
| 内核 | 3.10 以上 | | 内核 | 3.10 以上 |
| 文件系统 | ext4 / xfs | | 文件系统 | ext4 / xfs |
| 时区 | 建议 `Asia/Shanghai`,与 `.env` 的 `TZ` 保持一致 | | 时区 | 建议 `Asia/Shanghai`,与 `docker-compose.yml` 的 `TZ` 保持一致 |
## Docker 要求 ## Docker 要求
@@ -55,22 +55,40 @@ docker compose version
## 镜像仓库 ## 镜像仓库
平台镜像由官方镜像仓库分发,需要: 平台镜像发布在阿里云容器镜像服务,**仓库类型为公开,无需登录即可拉取**。
1. 能访问镜像仓库地址(内网仓库需先配置网络或代理) | 项 | 值 |
2. 具备拉取权限 | --- | --- |
| 公网地址 | `crpi-qflrqrxoohllkvgc.cn-chengdu.personal.cr.aliyuncs.com` |
| VPC 地址 | `crpi-qflrqrxoohllkvgc-vpc.cn-chengdu.personal.cr.aliyuncs.com` |
| 命名空间 | `iotlcm` |
| 地域 | 西南1(成都) |
```bash 业务镜像路径为:
docker login <镜像仓库地址>
```text
crpi-qflrqrxoohllkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm-admin-svr:1.0.3
crpi-qflrqrxoohllkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm-openapi:1.0.3
...
``` ```
`.env` 的 `REGISTRY` 填写仓库地址,不含 `https://` 与末尾斜杠: `docker-compose.yml` 里的镜像地址已预置为上述公网地址,一般无需修改。
```bash ::: tip 基础组件也在同一仓库
REGISTRY=registry.example.com MySQL、Redis、RabbitMQ、Consul、nginx 同样托管在 `library/` 命名空间下,不从 Docker Hub 直连(公网不稳定且 tag 会漂)。版本已在编排文件中固定,**必须与仓库里实际同步的标签一致**。
:::
::: warning 基础组件需先同步
部署前 `library/` 下必须已有这五个镜像,否则拉取失败。同步方法见[自建镜像](build-image)的「同步基础组件」一节。
:::
::: tip 阿里云 VPC 地址
若部署在阿里云同地域 ECS 上,可把编排文件里的镜像前缀改为 VPC 地址,走内网流量、不消耗公网带宽:
```yaml
image: 'crpi-qflrqrxoohllkvgc-vpc.cn-chengdu.personal.cr.aliyuncs.com/library/mysql:8.0'
``` ```
:::
基础组件从 `<REGISTRY>/library/` 拉取(mysql、redis、rabbitmq、consul、nginx),业务镜像从 `<REGISTRY>/iotlcm/` 拉取。
## 网络与防火墙 ## 网络与防火墙
@@ -96,7 +114,7 @@ firewall-cmd --reload
| 微信支付 | 在线支付 | 支付功能不可用 | | 微信支付 | 在线支付 | 支付功能不可用 |
| 飞书 | 运维通知 | 不启动 bot 即可 | | 飞书 | 运维通知 | 不启动 bot 即可 |
TDengine 需开启 WebSocket 接口(默认 6041),在 `.env` 中配置: TDengine 需开启 WebSocket 接口(默认 6041),在 `docker-compose.yml` 中配置:
```bash ```bash
TDENGINE_HOST=tdengine.example.com TDENGINE_HOST=tdengine.example.com
+2 -3
View File
@@ -144,15 +144,14 @@ upstream:
## 四、启动 ## 四、启动
```bash ```bash
cp .env.example .env # 按需修改 docker-compose.app.yml 的镜像地址、端口与口令
vi .env # REGISTRY、TAG、各类口令
cp -r config.example config cp -r config.example config
vi config/*/config.yaml # 按上节改为外部地址 vi config/*/config.yaml # 按上节改为外部地址
docker compose -f docker-compose.app.yml up -d docker compose -f docker-compose.app.yml up -d
``` ```
此时 `.env` 中的 `MYSQL_*`、`REDIS_PASSWORD`、`RABBITMQ_PASSWORD` 不再用于启动中间件容器,但仍作为配置中的变量来源,**取值要与中间件实际口令一致**。 此时 `docker-compose.yml` 中的 `MYSQL_*`、`REDIS_PASSWORD`、`RABBITMQ_PASSWORD` 不再用于启动中间件容器,但仍作为配置中的变量来源,**取值要与中间件实际口令一致**。
## 五、验证 ## 五、验证
+5 -5
View File
@@ -27,7 +27,7 @@ docker compose logs mysql | tail -50
| 日志关键词 | 原因 | 处理 | | 日志关键词 | 原因 | 处理 |
| --- | --- | --- | | --- | --- | --- |
| `Access denied` | root 口令与旧数据卷不一致 | 改 `.env` 与卷内一致,或清空 `data/mysql` 重建 | | `Access denied` | root 口令与旧数据卷不一致 | 改 `docker-compose.yml` 与卷内一致,或清空 `data/mysql` 重建 |
| `Can't create/write to file` | 目录权限 | `sudo chown -R 999:999 data/mysql` | | `Can't create/write to file` | 目录权限 | `sudo chown -R 999:999 data/mysql` |
| `No space left on device` | 磁盘满 | 清理磁盘 | | `No space left on device` | 磁盘满 | 清理磁盘 |
| `Can't open shared library` | 架构不匹配 | 确认镜像与宿主机架构一致 | | `Can't open shared library` | 架构不匹配 | 确认镜像与宿主机架构一致 |
@@ -62,7 +62,7 @@ docker compose logs --tail=80 admin-svr
| --- | --- | --- | | --- | --- | --- |
| `possible config files "config" not found` | `config/` 不存在或挂载失败 | `cp -r config.example config`,确认 `config/admin-svr/config.yaml` 存在 | | `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 | | `dial tcp mysql:3306: connection refused` | MySQL 未就绪 | 等 MySQL healthy |
| `Access denied for user 'u_iotlcm'` | 应用账号口令与库内不一致 | 确认 `.env` 的 `MYSQL_APP_PASSWORD` 与初始化时一致 | | `Access denied for user 'u_iotlcm'` | 应用账号口令与库内不一致 | 确认 `docker-compose.yml` 的 `MYSQL_APP_PASSWORD` 与初始化时一致 |
| `panic: unsupported provider xxx` | 通道模板与镜像版本不匹配 | 确认镜像版本与 schema 版本配套 | | `panic: unsupported provider xxx` | 通道模板与镜像版本不匹配 | 确认镜像版本与 schema 版本配套 |
## 功能类 ## 功能类
@@ -71,7 +71,7 @@ docker compose logs --tail=80 admin-svr
**原因**:`JWT_SIGNING_KEY` 在服务间不一致。 **原因**:`JWT_SIGNING_KEY` 在服务间不一致。
确认 `admin-svr`、`openapi`、`app-server` 三份配置的 `jwt.signingKey` 都引用 `${JWT_SIGNING_KEY}` 且 `.env` 有值,然后重启: 确认 `admin-svr`、`openapi`、`app-server` 三份配置的 `jwt.signingKey` 都引用 `${JWT_SIGNING_KEY}` 且 `docker-compose.yml` 有值,然后重启:
```bash ```bash
docker compose restart admin-svr openapi app-server docker compose restart admin-svr openapi app-server
@@ -149,7 +149,7 @@ docker compose logs upstream-chan --tail=50
docker compose logs worker-svr | grep -i 'tdengine\|taos' | tail -20 docker compose logs worker-svr | grep -i 'tdengine\|taos' | tail -20
``` ```
确认 `.env` 的 `TDENGINE_*` 已填,且 WebSocket 端口(默认 6041)可达。不用该功能留空即可。 确认 `docker-compose.yml` 的 `TDENGINE_*` 已填,且 WebSocket 端口(默认 6041)可达。不用该功能留空即可。
### 短信发不出去 ### 短信发不出去
@@ -160,7 +160,7 @@ docker compose logs sms --tail=50
| 报错 | 原因 | | 报错 | 原因 |
| --- | --- | | --- | --- |
| `InvalidAccessKeyId` | `.env` 中 AK 未填或错误 | | `InvalidAccessKeyId` | `docker-compose.yml` 中 AK 未填或错误 |
| `isv.SMS_SIGNATURE_ILLEGAL` | `signName` 与服务商审核签名不一致 | | `isv.SMS_SIGNATURE_ILLEGAL` | `signName` 与服务商审核签名不一致 |
| `isv.SMS_TEMPLATE_ILLEGAL` | 模板 ID 未审核通过 | | `isv.SMS_TEMPLATE_ILLEGAL` | 模板 ID 未审核通过 |
| `缺少 config.templateId` | 场景模板库未配置,调用方需自带 templateId | | `缺少 config.templateId` | 场景模板库未配置,调用方需自带 templateId |