Docker部署物联网设备管理系统实战:从架构到一键部署
为什么用Docker部署物联网系统
物联网项目的部署环境五花八门:客户机房的服务器、工控机、树莓派、云服务器。每个环境的操作系统版本、依赖库版本都不一样。
Docker解决的就是"在我电脑上能跑"的问题。
把你的物联网平台、MQTT Broker、数据库、管理后端全部容器化,任何装了Docker的机器上docker-compose up一把启动,不用管底层的OS差异。
这在实际项目中意味着什么?你在公司开发机上调试好的系统,部署到客户机房时不用再花两天时间装环境、排依赖。镜像拉下来,配置文件改一改,跑起来。
整体架构
一个完整的物联网设备管理系统,Docker化后的架构:
docker-compose.yml
├── emqx # MQTT Broker
├── mysql # 业务数据库
├── redis # 缓存
├── go-worker # MQTT消息处理微服务
├── php-api # 管理API + Web界面
└── nginx # 反向代理
所有组件跑在同一台服务器上,通过Docker内部网络通信。
完整docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: iot_mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: iot_platform
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- iot_net
redis:
image: redis:7-alpine
container_name: iot_redis
restart: always
command: redis-server --requirepass ${REDIS_PASSWORD}
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- iot_net
emqx:
image: emqx/emqx:5.8
container_name: iot_emqx
restart: always
environment:
EMQX_NAME: iot_emqx
EMQX_HOST: 127.0.0.1
EMQX_DASHBOARD__DEFAULT_PASSWORD: ${EMQX_DASHBOARD_PASSWORD}
ports:
- "1883:1883" # MQTT TCP
- "8083:8083" # WebSocket
- "18083:18083" # Dashboard
volumes:
- emqx_data:/opt/emqx/data
networks:
- iot_net
go-worker:
build: ./go-worker
container_name: iot_worker
restart: always
depends_on:
- emqx
- mysql
- redis
environment:
MQTT_BROKER: emqx:1883
MYSQL_DSN: root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/iot_platform
REDIS_ADDR: redis:6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
networks:
- iot_net
php-api:
build: ./php-api
container_name: iot_api
restart: always
depends_on:
- mysql
- redis
environment:
DB_HOST: mysql
DB_DATABASE: iot_platform
DB_USERNAME: root
DB_PASSWORD: ${MYSQL_ROOT_PASSWORD}
REDIS_HOST: redis
REDIS_PASSWORD: ${REDIS_PASSWORD}
volumes:
- ./php-api/src:/var/www/html
networks:
- iot_net
nginx:
image: nginx:alpine
container_name: iot_nginx
restart: always
depends_on:
- php-api
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./php-api/src:/var/www/html
networks:
- iot_net
volumes:
mysql_data:
redis_data:
emqx_data:
networks:
iot_net:
driver: bridge
.env配置
MYSQL_ROOT_PASSWORD=your_secure_password_here
REDIS_PASSWORD=your_redis_password_here
EMQX_DASHBOARD_PASSWORD=your_emqx_password_here
Go Worker关键代码
Go Worker负责订阅MQTT消息,处理设备上报数据并写入数据库:
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/signal"
"syscall"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/joho/godotenv"
_ "github.com/go-sql-driver/mysql"
"github.com/gomodule/redigo/redis"
"database/sql"
)
type Telemetry struct {
DeviceID string `json:"device_id"`
Temp float64 `json:"temp"`
Hum float64 `json:"hum"`
Voltage float64 `json:"voltage"`
}
var db *sql.DB
var redisConn redis.Conn
func main() {
godotenv.Load()
// 初始化MySQL
dsn := os.Getenv("MYSQL_DSN")
db, _ = sql.Open("mysql", dsn)
defer db.Close()
// 初始化Redis
redisAddr := os.Getenv("REDIS_ADDR")
redisConn, _ = redis.Dial("tcp", redisAddr)
defer redisConn.Close()
// 连接MQTT
opts := mqtt.NewClientOptions()
opts.AddBroker(os.Getenv("MQTT_BROKER"))
opts.SetClientID("go-worker-001")
opts.OnConnect = func(c mqtt.Client) {
c.Subscribe("devices/+/+/telemetry", 1, handleMessage)
log.Println("Subscribed to telemetry topic")
}
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
log.Fatal(token.Error())
}
// 等待退出信号
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
}
func handleMessage(client mqtt.Client, msg mqtt.Message) {
var t Telemetry
if err := json.Unmarshal(msg.Payload(), &t); err != nil {
return
}
// 写入MySQL
_, err := db.Exec(
"INSERT INTO device_telemetry (device_id, temp, hum, voltage, created_at) VALUES (?, ?, ?, ?, NOW())",
t.DeviceID, t.Temp, t.Hum, t.Voltage,
)
if err != nil {
log.Printf("MySQL insert error: %v", err)
}
// 更新Redis缓存(最新状态)
redisConn.Do("HSET", "device:"+t.DeviceID, "temp", t.Temp, "hum", t.Hum, "voltage", t.Voltage)
redisConn.Do("HSET", "device:"+t.DeviceID, "last_seen", "NOW()")
// 告警判断
if t.Temp > 80 {
redisConn.Do("LPUSH", "alerts", fmt.Sprintf(`{"device":"%s","type":"high_temp","value":%.1f}`, t.DeviceID, t.Temp))
}
}
Nginx配置
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php-api:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# WebSocket代理(设备实时通信)
location /ws {
proxy_pass http://emqx:8083;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
部署步骤
# 1. 克隆代码
git clone https://github.com/your-repo/iot-platform.git
cd iot-platform
# 2. 配置环境变量
cp .env.example .env
nano .env # 修改密码
# 3. 启动所有服务
docker-compose up -d
# 4. 初始化数据库
docker exec -it iot_mysql mysql -uroot -p iot_platform < sql/init.sql
# 5. 检查服务状态
docker-compose ps
docker-compose logs -f
整个过程5分钟。
性能与资源占用
在一台4核8G的云服务器上实测:
| 服务 | 内存占用 | CPU占用 |
|---|---|---|
| MySQL | ~500MB | <5% |
| Redis | ~30MB | <2% |
| EMQX | ~200MB | <10% |
| Go Worker | ~50MB | <5% |
| PHP-FPM | ~100MB | <5% |
| Nginx | ~20MB | <1% |
| 总计 | ~900MB | <30% |
500台设备每5秒上报一次数据,系统负载轻松hold住。
实际踩坑
坑1:MySQL时区
Docker MySQL默认UTC,但业务需要东八区。在docker-compose.yml中加:
environment:
TZ: Asia/Shanghai
或者直接在MySQL配置文件中设置default-time-zone='+08:00'。
坑2:EMQX WebSocket跨域
前端通过WebSocket连接EMQX时可能遇到CORS问题。在EMQX配置中:
emqx:
environment:
EMQX_LISTENER__WS__DEFAULT__ACCESS_CONTROL__ALLOW_ORIGINS: "*"
坑3:Docker日志撑爆磁盘
容器默认不限制日志大小。加上:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
总结
Docker化部署物联网系统,核心好处是环境一致性和快速交付。你在本地调好的系统,到客户现场5分钟部署完,不需要在现场折腾环境问题。
虎王科技的设备管理平台就是基于这套架构,目前稳定运行中。Docker Compose文件和部分代码在GitHub上:github.com/huwangkeji
有问题欢迎评论区交流。
沧州虎王科技,专注物联网软硬件开发、通信设备、嵌入式系统与设备管理平台。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)