Files
iotlcm/docker-compose.yml
T
MiFaZhan d84f93ce0a feat: 内置 TDengine 时序库,自动建库建表
此前编排不含时序库,首页流量/用量图表、单卡流量历史等依赖时序数据的功能
不可用。现补上:

- 新增 tdengine 服务(镜像 iotlcm/iotlcm:tdengine-3.3.6.13)
  端口 6041(taosAdapter/WebSocket,应用用这个)+ 6030,数据卷 ./data/tdengine
- 新增 tdengine-init 一次性服务:等 tdengine 健康后建库建表,并校验超级表存在,
  缺表则以非零码退出
- 建库建表 SQL 取自生产环境 SHOW CREATE STABLE(仅结构无数据):
  iotlcm_daily   DURATION 7d  KEEP 1095d  ms  -> t_sim_card_usage_daily
  iotlcm_monthly DURATION 30d KEEP 1095d  ms  -> t_sim_card_usage_monthly
- 三个服务的 tsdb.tdengine 配置指向 tdengine:6041(admin-svr / app-server / worker-svr)

踩坑记录:taos -f 是按行执行语句的,多行 CREATE STABLE 会报
Incomplete SQL statement,因此 SQL 必须单行;且 taos 出错时退出码仍为 0,
故 run.sh 额外做超级表校验。

已验证:初始化 4 条语句全部 Create OK、0 error;按 worker 的 INSERT 形式
(USING ... TAGS)写入成功、按 admin 的查询形式(SELECT ts,usage_mb,package_code,iccid)
可读回;整栈 15 个服务全 Up,admin-svr/openapi/sms/docs 均 200,worker 无时序库报错。
2026-10-30 10:02:32 +08:00

432 lines
12 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: iotlcm
# ============================================================
# IotLCM 单机部署编排
#
# 快速开始:
# 1. docker compose up -d
# 配置文件和初始口令均已内置;正式部署前按 README.md 修改默认口令。
#
# 首次启动会自动建库并导入 schema(见 schema/README.md)。
#
# 可选服务(默认不启动):
# docker compose --profile with-mock up -d # 运营商接口挡板
# docker compose --profile with-nginx up -d # 自带反向代理
# ============================================================
services:
# ---------------- 基础组件 ----------------
mysql:
# 基础组件统走同一镜像仓库(`<REGISTRY>/iotlcm/`),版本在此固定
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:mysql-8.0'
container_name: iotlcm-mysql
restart: always
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: 'Iotlcm2026Default'
MYSQL_DATABASE: 'db_iotlcm'
TZ: 'Asia/Shanghai'
volumes:
- ./data/mysql:/var/lib/mysql
# 仅在 data/mysql 为空(首次启动)时执行 schema/init.sql
- ./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-SHELL", "MYSQL_PWD=\"$$MYSQL_ROOT_PASSWORD\" mysqladmin ping -h 127.0.0.1 -u root --silent"]
interval: 10s
timeout: 5s
retries: 10
start_period: 40s
redis:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:redis-7.2'
container_name: iotlcm-redis
restart: always
ports:
- "6379:6379"
environment:
TZ: 'Asia/Shanghai'
command:
- redis-server
- --requirepass
- 'Iotlcm2026Default'
- --appendonly
- "yes"
volumes:
- ./data/redis:/data
networks:
- iotlcm-network
healthcheck:
test: ["CMD", "redis-cli", "-a", "Iotlcm2026Default", "ping"]
interval: 10s
timeout: 5s
retries: 5
rabbitmq:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:rabbitmq-4.3.5-management'
container_name: iotlcm-rabbitmq
hostname: rabbitmq
restart: always
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: 'iotlcm'
RABBITMQ_DEFAULT_PASS: 'Iotlcm2026Default'
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
# 时序数据库:存流量历史(daily / monthly),worker-svr 全量同步写这里
tdengine:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:tdengine-3.3.6.13'
container_name: iotlcm-tdengine
restart: always
ports:
- "${TDENGINE_PORT:-6041}:6041" # taosAdapter(WebSocket,应用用这个)
- "${TDENGINE_NATIVE_PORT:-6030}:6030"
environment:
TZ: '${TZ:-Asia/Shanghai}'
volumes:
- ./data/tdengine/data:/var/lib/taos
- ./data/tdengine/log:/var/log/taos
networks:
- iotlcm-network
healthcheck:
test: ["CMD-SHELL", "curl -sf http://127.0.0.1:6041/-/ping || exit 1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s
# 建库建表:业务代码不负责建 TDengine 库表,由本服务在 tdengine 健康后执行一次
tdengine-init:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:tdengine-3.3.6.13'
container_name: iotlcm-tdengine-init
restart: "no"
entrypoint: ["sh", "/init/run.sh"]
volumes:
- ./tdengine-init:/init:ro
depends_on:
tdengine:
condition: service_healthy
networks:
- iotlcm-network
consul:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:consul-1.22'
container_name: iotlcm-consul
restart: always
ports:
- "8500:8500"
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4'
container_name: iotlcm-admin-svr
restart: always
ports:
- "127.0.0.1:8008:8008"
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4'
container_name: iotlcm-admin-ui
restart: always
ports:
- "127.0.0.1:8009:80"
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-openapi-v1.0.4'
container_name: iotlcm-openapi
restart: always
ports:
- "127.0.0.1:8000:8000"
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4'
container_name: iotlcm-upstream-chan
restart: always
ports:
- "8110:8110"
- "8310:8310"
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-upstream-proxy-v1.0.4'
container_name: iotlcm-upstream-proxy
restart: always
ports:
- "8510:8510"
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4'
container_name: iotlcm-worker-svr
restart: always
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-app-server-v1.0.2'
container_name: iotlcm-app-server
restart: always
ports:
- "127.0.0.1:8010:8010"
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-app-web-v1.0.2'
container_name: iotlcm-app-web
restart: always
ports:
- "127.0.0.1:8018:80"
environment:
TZ: 'Asia/Shanghai'
volumes:
- ./data/logs/app-web:/var/log/nginx
networks:
- iotlcm-network
sms:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-sms-v1.0.4'
container_name: iotlcm-sms
hostname: iotlcm-sms
restart: always
environment:
TZ: 'Asia/Shanghai'
HTTP_ADDR: ':11190'
GRPC_ADDR: ':11200'
ports:
- "11190:11190"
- "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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-bot-v1.0.4'
container_name: iotlcm-bot
restart: unless-stopped
environment:
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: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-docs-v1.0.4'
container_name: iotlcm-docs
restart: unless-stopped
ports:
- "127.0.0.1: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/upstream-chan 已预置该地址)
onelink-mock:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:iotlcm-onelink-mock-v1.0.4'
container_name: iotlcm-onelink-mock
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
environment:
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/nginx/iotlcm.conf 把 location 段落到宿主机配置。
nginx:
image: 'crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com/iotlcm/iotlcm:nginx-1.30'
container_name: iotlcm-nginx
restart: always
ports:
- "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