Docker Compose编排物联网平台后端:Nginx+PHP-FPM+EMQX+InfluxDB一键部署实战
Docker Compose编排物联网平台后端NginxPHP-FPMEMQXInfluxDB一键部署实战物联网平台后端涉及的服务组件多手动部署一次就得折腾半天。Nginx做反向代理PHP-FPM跑业务APIEMQX处理MQTT设备连接InfluxDB存时序数据Redis做缓存。每个组件单独装、单独配环境一换全得重来。Docker Compose能把这套东西用一份YAML文件定义清楚一条命令拉起来。本文分享沧州虎王科技在物联网平台部署中使用的Docker Compose编排方案经过生产环境验证可以直接拿去改。### 整体架构平台后端的服务拓扑| 服务 | 镜像 | 端口 | 职责 || — | — | — | — || Nginx | nginx:1.25-alpine | 80, 443 | 反向代理静态资源SSL终结 || PHP-FPM | php:8.2-fpm-alpine | 9000 | 业务API设备管理数据查询 || EMQX | emqx/emqx:5.4 | 1883, 8083, 18083 | MQTT Broker设备接入 || InfluxDB | influxdb:2.7 | 8086 | 时序数据存储 || Redis | redis:7-alpine | 6379 | 会话缓存设备状态 |Nginx对外暴露80/443端口所有外部请求经过它。设备端MQTT连接走1883端口直达EMQXWeb端WebSocket走Nginx代理到EMQX的8083端口。PHP-FPM只对内部网络开放不直接暴露。docker-compose.yml完整配置yamlversion: 3.8services: nginx: image: nginx:1.25-alpine container_name: iot-nginx restart: unless-stopped ports: - 80:80 - 443:443 volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./src:/var/www/html:ro - ./logs/nginx:/var/log/nginx - ./ssl:/etc/nginx/ssl:ro depends_on: - php-fpm networks: - iot-net php-fpm: build: context: ./php dockerfile: Dockerfile container_name: iot-php restart: unless-stopped volumes: - ./src:/var/www/html - ./php/php.ini:/usr/local/etc/php/conf.d/custom.ini:ro environment: - DB_HOSTinfluxdb - DB_PORT8086 - DB_TOKEN${INFLUX_TOKEN} - DB_ORG${INFLUX_ORG} - DB_BUCKET${INFLUX_BUCKET} - REDIS_HOSTredis - REDIS_PORT6379 - MQTT_HOSTemqx - MQTT_PORT1883 depends_on: - influxdb - redis - emqx networks: - iot-net emqx: image: emqx/emqx:5.4 container_name: iot-emqx restart: unless-stopped ports: - 1883:1883 - 8083:8083 - 18083:18083 environment: - EMQX_NAMEiot-emqx - EMQX_HOSTemqx - EMQX_DASHBOARD__DEFAULT_PASSWORD${EMQX_DASHBOARD_PASSWORD} volumes: - emqx-data:/opt/emqx/data - emqx-etc:/opt/emqx/etc networks: - iot-net influxdb: image: influxdb:2.7 container_name: iot-influxdb restart: unless-stopped ports: - 8086:8086 environment: - DOCKER_INFLUXDB_INIT_MODEsetup - DOCKER_INFLUXDB_INIT_USERNAME${INFLUX_USER} - DOCKER_INFLUXDB_INIT_PASSWORD${INFLUX_PASSWORD} - DOCKER_INFLUXDB_INIT_ORG${INFLUX_ORG} - DOCKER_INFLUXDB_INIT_BUCKET${INFLUX_BUCKET} - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN${INFLUX_TOKEN} volumes: - influxdb-data:/var/lib/influxdb2 networks: - iot-net redis: image: redis:7-alpine container_name: iot-redis restart: unless-stopped command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru ports: - 6379:6379 volumes: - redis-data:/data networks: - iot-netvolumes: emqx-data: emqx-etc: influxdb-data: redis-data:networks: iot-net: driver: bridge配置里有几个设计决策值得说明。第一PHP-FPM用自定义Dockerfile构建因为官方镜像不带PDO、Redis等扩展需要额外安装。第二所有敏感信息通过环境变量注入变量值放在同目录的.env文件里不写进docker-compose.yml。第三Nginx和PHP-FPM共享同一个代码卷./src但Nginx以只读方式挂载:ro防止Nginx意外修改代码。PHP-FPM的DockerfiledockerfileFROM php:8.2-fpm-alpineRUN apk add --no-cache \ libzip-dev \ libxml2-dev \ oniguruma-dev \ docker-php-ext-install \ pdo_mysql \ pdo \ simplexml \ zip \ opcache \ pecl install redis influxdb \ docker-php-ext-enable redis influxdbCOPY php.ini /usr/local/etc/php/conf.d/custom.iniWORKDIR /var/www/html这个Dockerfile基于Alpine Linux最终镜像体积约120MB。opcache默认启用对PHP API性能提升明显。influxdb扩展是PHP的原生InfluxDB客户端比用cURL手写HTTP请求效率高。### Nginx反向代理配置Nginx需要处理三类请求PHP API请求、MQTT WebSocket连接、静态资源。nginxupstream php-fpm { server php-fpm:9000;}upstream emqx-ws { server emqx:8083;}server { listen 80; server_name iot.example.com; location /static/ { root /var/www/html; expires 30d; access_log off; } location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php-fpm; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; fastcgi_read_timeout 30s; } location /mqtt { proxy_pass http://emqx-ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 300s; }}MQTT over WebSocket的配置最容易出错。proxy_set_header Upgrade和Connection这两个头必须加否则WebSocket握手会失败。proxy_read_timeout设到300秒因为MQTT连接是长连接默认的60秒超时会导致设备频繁断开重连。### .env环境变量文件bashINFLUX_USERadminINFLUX_PASSWORDChangeMeInProduction2026INFLUX_ORGhuwangkejiINFLUX_BUCKETiot_dataINFLUX_TOKENyour-influxdb-token-hereEMQX_DASHBOARD_PASSWORDChangeMeAlso2026.env文件不要提交到Git仓库加到.gitignore里。生产环境的密码和Token应该通过CI/CD流水线注入而不是写在文件里。一键启动与验证bashdocker compose up -ddocker compose psdocker compose logs -f php-fpm启动后用curl验证各服务是否正常bashcurl http://localhost/healthcurl http://localhost:8086/healthcurl http://localhost:18083/api/v5/statusdocker exec iot-redis redis-cli ping### PHP连接各服务的代码示例php?phpfunction writeDeviceData($deviceId, $field, $value) { $client new InfluxDB\Client(getenv(DB_HOST), getenv(DB_PORT)); $database $client-selectDB(getenv(DB_BUCKET)); $point new InfluxDB\Point( device_metrics, null, [device_id $deviceId], [$field floatval($value)], time() ); $database-writePoints([$point], InfluxDB\Database::PRECISION_SECONDS);}function setDeviceOnline($deviceId) { $redis new Redis(); $redis-connect(getenv(REDIS_HOST), getenv(REDIS_PORT)); $redis-setex(device:online:$deviceId, 300, json_encode([ last_seen date(c), ip $_SERVER[REMOTE_ADDR] ?? unknown ]));}function sendMqttCommand($deviceId, $command) { $mqtt new PhpMqtt\Client\MqttClient( getenv(MQTT_HOST), intval(getenv(MQTT_PORT)), php-backend- . uniqid() ); $mqtt-connect(); $mqtt-publish(device/$deviceId/cmd, json_encode($command), 1); $mqtt-disconnect();}这段代码展示了三个后端服务的调用方式。InfluxDB写数据点Redis设带过期时间的在线状态MQTT发布控制指令。实际项目中MQTT连接应该用连接池复用不要每次请求都新建连接。### 生产环境优化建议第一给PHP-FPM配置合理的进程数。默认的dynamic模式下pm.max_children设为50对于中小规模物联网平台够用。如果设备连接数超过5000建议改用static模式并把pm.max_children提到100-200同时给容器分配2GB以上内存。第二EMQX的连接数上限需要调整。默认配置支持10万连接但内存只有2GB的话实际能撑3-5万。在emqx.conf里修改node.process_limit和listeners.tcp.default.max_connections。第三InfluxDB的存储空间规划。1000台设备每秒上报一个数据点一天8640万条约占5GB存储。用InfluxDB的连续查询做自动降采样把超过7天的原始数据降精度到1分钟聚合。第四日志收集用ELK或者Loki别让日志文件在容器里无限增长。在docker-compose.yml里给每个服务加logging配置限制日志大小yamllogging: driver: json-file options: max-size: 10m max-file: 3虎王科技的物联网平台开源项目和更多部署实战经验可以在GitHubgithub.com/huwangkeji找到博客heicat.com上有Docker和PHP相关的技术笔记。