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