init: IotLCM 开源版部署仓

内容:
- docker-compose.yml:14 个基础/业务服务 + 3 个可选 profile(with-mock 挡板、with-bot、with-nginx)
- .env.example:集中管理镜像仓库、口令、端口;必填项缺失时 compose 直接报错退出
- config.example/:10 份配置模板,全部为占位值与注释说明
- schema/:12 个初始化脚本,按文件名顺序由 MySQL initdb 自动执行

脱敏处理:
- 生产/内网 IP、镜像仓库地址、公司名、真实域名全部清除
- 演示账号保留 18 个(出库与划拨依赖账号与部门树的关联),
  昵称/邮箱/手机号/登录IP/头像改为占位或编造值
- 数据库账号与 admin 口令改为初始化时按 .env 注入计算
- 通道模板与 Mock 通道的上游密钥置空
- Mock 通道地址指向 compose 内 onelink-mock 服务

部署时用 --profile with-mock 启动运营商接口挡板,即可在无真实运营商环境时跑通全链路。
This commit is contained in:
MiFaZhan
2026-10-28 13:40:12 +08:00
commit bc2c7bcc6b
26 changed files with 7264 additions and 0 deletions
+397
View File
@@ -0,0 +1,397 @@
name: iotlcm
# ============================================================
# IotLCM 单机部署编排
#
# 快速开始:
# 1. cp .env.example .env # 填写镜像仓库与各项口令
# 2. cp -r config.example config # 逐份填写配置(见各文件内注释)
# 3. docker compose up -d
#
# 首次启动会自动建库并导入 schema(见 schema/README.md)。
#
# 可选服务(默认不启动):
# docker compose --profile with-mock up -d # 运营商接口挡板
# docker compose --profile with-nginx up -d # 自带反向代理
# ============================================================
services:
# ---------------- 基础组件 ----------------
mysql:
image: '${REGISTRY:-your-registry}/library/mysql:8.0'
container_name: iotlcm-mysql
restart: always
ports:
- "${MYSQL_PORT:-3306}:3306"
environment:
MYSQL_ROOT_PASSWORD: '${MYSQL_ROOT_PASSWORD:?必须在 .env 中设置}'
MYSQL_DATABASE: '${MYSQL_DATABASE:-db_iotlcm}'
MYSQL_APP_PASSWORD: '${MYSQL_APP_PASSWORD:?必须在 .env 中设置}'
MYSQL_ADMIN_PASSWORD: '${MYSQL_ADMIN_PASSWORD:?必须在 .env 中设置}'
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./data/mysql:/var/lib/mysql
# 仅在 data/mysql 为空(首次启动)时执行,按文件名顺序
- ./schema:/docker-entrypoint-initdb.d:ro
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_general_ci
- --default-time-zone=+08:00
networks:
- iotlcm-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 40s
redis:
image: '${REGISTRY:-your-registry}/library/redis:7.2'
container_name: iotlcm-redis
restart: always
ports:
- "${REDIS_PORT:-6379}:6379"
environment:
TZ: '${TZ:-Asia/Shanghai}'
command:
- redis-server
- --requirepass
- '${REDIS_PASSWORD:?必须在 .env 中设置}'
- --appendonly
- "yes"
volumes:
- ./data/redis:/data
networks:
- iotlcm-network
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
rabbitmq:
image: '${REGISTRY:-your-registry}/library/rabbitmq:4.3.5-management'
container_name: iotlcm-rabbitmq
hostname: rabbitmq
restart: always
ports:
- "${RABBITMQ_PORT:-5672}:5672"
- "${RABBITMQ_MGMT_PORT:-15672}:15672"
environment:
RABBITMQ_DEFAULT_USER: '${RABBITMQ_USER:-iotlcm}'
RABBITMQ_DEFAULT_PASS: '${RABBITMQ_PASSWORD:?必须在 .env 中设置}'
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./data/rabbitmq:/var/lib/rabbitmq
networks:
- iotlcm-network
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 30s
consul:
image: '${REGISTRY:-your-registry}/library/consul:1.22'
container_name: iotlcm-consul
restart: always
ports:
- "${CONSUL_PORT:-8500}:8500"
environment:
TZ: '${TZ:-Asia/Shanghai}'
CONSUL_LOCAL_CONFIG: |
{
"node_name": "iotlcm-node",
"bind_addr": "0.0.0.0",
"client_addr": "0.0.0.0",
"datacenter": "dc1",
"data_dir": "/consul/data",
"server": true,
"bootstrap_expect": 1,
"ui": true,
"connect": { "enabled": true }
}
volumes:
- ./data/consul:/consul/data
networks:
- iotlcm-network
# ---------------- 业务服务 ----------------
admin-svr:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-admin-svr:${TAG:-latest}'
container_name: iotlcm-admin-svr
restart: always
ports:
- "127.0.0.1:${ADMIN_SVR_PORT:-8008}:8008"
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./config/admin-svr:/app/config:ro
- ./data/logs/admin-svr:/app/logs
- ./data/upload:/app/upload
- ./data/upload:/app/resource/upload
networks:
- iotlcm-network
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
admin-ui:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-admin-ui:${TAG:-latest}'
container_name: iotlcm-admin-ui
restart: always
ports:
- "127.0.0.1:${ADMIN_UI_PORT:-8009}:80"
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./config/admin-ui/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./data/logs/admin-ui:/var/log/nginx
- ./data/upload:/app/upload
networks:
- iotlcm-network
depends_on:
- admin-svr
openapi:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-openapi:${TAG:-latest}'
container_name: iotlcm-openapi
restart: always
ports:
- "127.0.0.1:${OPENAPI_PORT:-8000}:8000"
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./config/openapi:/app/config:ro
- ./data/logs/openapi:/app/logs
networks:
- iotlcm-network
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
upstream-chan:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-upstream-chan:${TAG:-latest}'
container_name: iotlcm-upstream-chan
restart: always
ports:
- "${CHAN_HTTP_PORT:-8110}:8110"
- "${CHAN_GRPC_PORT:-8310}:8310"
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./config/upstream-chan:/app/config:ro
- ./data/logs/upstream-chan:/app/logs
- ./data/upload:/app/upload
- ./data/upload:/app/resource/upload
networks:
- iotlcm-network
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
consul:
condition: service_started
upstream-proxy:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-upstream-proxy:${TAG:-latest}'
container_name: iotlcm-upstream-proxy
restart: always
ports:
- "${PROXY_PORT:-8510}:8510"
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./config/upstream-proxy:/app/config:ro
- ./data/logs/upstream-proxy:/app/logs
- ./data/upload:/app/upload
- ./data/upload:/app/resource/upload
- ./data/tmp:/app/tmp
networks:
- iotlcm-network
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
consul:
condition: service_started
worker-svr:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-worker-svr:${TAG:-latest}'
container_name: iotlcm-worker-svr
restart: always
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./config/worker-svr:/app/config:ro
- ./data/logs/worker-svr:/app/logs
- ./data/logs/worker-svr/full-sync:/app/logs/full-sync
- ./data/upload:/app/upload
- ./data/upload:/app/resource/upload
networks:
- iotlcm-network
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
app-server:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-app-server:${TAG:-latest}'
container_name: iotlcm-app-server
restart: always
ports:
- "127.0.0.1:${APP_SERVER_PORT:-8010}:8010"
environment:
TZ: '${TZ:-Asia/Shanghai}'
APP_UPLOAD_DIR: /app/resource/upload
volumes:
- ./config/app-server:/app/config:ro
- ./data/upload:/app/resource/upload
- ./data/logs/app-server:/app/resource/log/server
networks:
- iotlcm-network
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
app-web:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-app-web:${TAG:-latest}'
container_name: iotlcm-app-web
restart: always
ports:
- "127.0.0.1:${APP_WEB_PORT:-8018}:80"
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./data/logs/app-web:/var/log/nginx
networks:
- iotlcm-network
sms:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-sms:${TAG:-latest}'
container_name: iotlcm-sms
hostname: iotlcm-sms
restart: always
environment:
TZ: '${TZ:-Asia/Shanghai}'
HTTP_ADDR: ':11190'
GRPC_ADDR: ':11200'
ports:
- "${SMS_HTTP_PORT:-11190}:11190"
- "${SMS_GRPC_PORT:-11200}:11200"
volumes:
- ./config/sms:/app/config:ro
- ./data/logs/sms:/app/logs
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1:11190/healthz"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
networks:
- iotlcm-network
bot:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-bot:${TAG:-latest}'
container_name: iotlcm-bot
restart: unless-stopped
environment:
TZ: '${TZ:-Asia/Shanghai}'
command: ["watch", "--config", "/app/config/config.yaml"]
volumes:
- ./config/bot/config.yaml:/app/config/config.yaml:ro
- ./data/logs/worker-svr/full-sync:/data/full-sync:ro
- ./data/bot-state:/data/state
networks:
- iotlcm-network
profiles:
- with-bot
docs:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-docs:${TAG:-latest}'
container_name: iotlcm-docs
restart: unless-stopped
ports:
- "127.0.0.1:${DOCS_PORT:-8019}:80"
networks:
- iotlcm-network
# ---------------- 可选:运营商接口挡板 ----------------
# 模拟中国移动 OneLink 接口,用于在没有真实运营商环境时联调整条链路:
# admin-svr → worker-svr → upstream-chan → onelink-mock
#
# 启用:docker compose --profile with-mock up -d
# 启用后 upstream-chan 的 sim_yd_mock_v5.url 指向 http://onelink-mock:8080/v5/
# (config.example/upstream-chan 已预置该地址)
onelink-mock:
image: '${REGISTRY:-your-registry}/iotlcm/iotlcm-onelink-mock:${TAG:-latest}'
container_name: iotlcm-onelink-mock
restart: unless-stopped
ports:
- "127.0.0.1:${MOCK_PORT:-8080}:8080"
environment:
TZ: '${TZ:-Asia/Shanghai}'
BAFFLE_ADDR: ':8080'
BAFFLE_SQLITE_PATH: /app/data/onelink_mock.db
volumes:
- ./data/mock:/app/data
- ./data/logs/mock:/app/logs
networks:
- iotlcm-network
healthcheck:
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1:8080/api.json"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
profiles:
- with-mock
# ---------------- 可选:反向代理 ----------------
# 若宿主机已有 nginx / 面板,可不启动本容器,
# 参考 config.example/nginx/iotlcm.conf 把 location 段落到宿主机配置。
nginx:
image: '${REGISTRY:-your-registry}/library/nginx:1.30'
container_name: iotlcm-nginx
restart: always
ports:
- "${NGINX_HTTP_PORT:-80}:80"
volumes:
- ./config/nginx:/etc/nginx/conf.d:ro
- ./data/logs/nginx:/var/log/nginx
- ./data/upload:/app/upload
networks:
- iotlcm-network
depends_on:
- admin-ui
- openapi
- app-web
- docs
profiles:
- with-nginx
networks:
iotlcm-network:
driver: bridge
name: iotlcm-network