在1核2G的“小钢炮”服务器上,搭建ThingsBoard+EMQX物联网平台实战
手里只有一台1核2G的入门级云服务器,却想搭建一个完整的物联网平台来测试自己的智能硬件项目?这听起来像是让一辆小排量家用车去跑拉力赛,但事实证明,只要调校得当,它不仅能跑,还能跑得相当稳健。今天,我们就来挑战这个极限,用Docker在1核2G的服务器上,部署ThingsBoard物联网平台和EMQX消息中间件。这不是一篇照搬官方文档的教程,而是融合了多次“爆内存”教训后,总结出的资源压榨方案和避坑指南。你会发现,限制往往能激发出最精巧的解决方案。
1. 战前准备:理解我们的“战场”与武器
在开始敲命令之前,我们必须清醒地认识手中的资源。1核2G的云服务器,意味着什么?2GB内存,除去操作系统(约200-300MB)和必要的系统进程,留给我们的“自由内存”可能只有1.5GB左右。而我们要部署的ThingsBoard、PostgreSQL数据库、EMQX以及tb-gateway,每一个都不是省油的灯。
为什么不用ThingsBoard自带的数据库? 官方Docker镜像确实提供了All-in-One的选项,内部集成了PostgreSQL。但在极限资源环境下,这常常是第一个“坑”。集成环境虽然方便,但进程间资源隔离性差,一个组件异常可能拖垮整个容器。更重要的是,在资源紧张时,我们需要更精细的控制权——比如为数据库单独设置内存限制,或者方便地备份、迁移数据。因此,我们的核心策略是:解耦与隔离。为每个核心服务(数据库、应用、消息中间件)分配独立的容器,通过Docker的资源限制功能进行管控。
注意:本文所有操作均基于Linux服务器(如CentOS 7/8或Ubuntu 20.04+),并假设你已具备基础的Linux命令和Docker使用知识。请确保你的云服务器安全组已开放后续需要用到的端口。
首先,检查并确保Docker已正确安装。如果尚未安装,可以使用以下命令快速安装:
# 以CentOS为例,安装Docker
sudo yum install -y yum-utils
sudo yum-config-manager –add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
sudo systemctl enable docker
2. 基石先行:部署轻量级PostgreSQL数据库
数据库是平台的基石,必须优先确保其稳定。我们不使用ThingsBoard镜像内置的数据库,而是独立部署一个PostgreSQL容器,并对其进行内存限制。
创建数据持久化目录:为了避免容器重启后数据丢失,我们首先创建宿主机上的数据目录。
mkdir -p /opt/iot-stack/postgres/data
启动PostgreSQL容器:这里的关键是使用 –memory 和 –memory-swap 参数来限制容器使用的最大内存。
docker run -d \\
–name postgres-for-tb \\
–restart=unless-stopped \\
–memory=512m \\
–memory-swap=512m \\
-e POSTGRES_PASSWORD=your_strong_password \\
-e POSTGRES_DB=thingsboard \\
-p 5432:5432 \\
-v /opt/iot-stack/postgres/data:/var/lib/postgresql/data \\
postgres:13-alpine
命令解析:
- –memory=512m:限制该容器最多使用512MB的物理内存。
- –memory-swap=512m:将内存交换(Swap)总量也限制为512MB,这实际上意味着禁止使用Swap。在内存极度紧张的环境下,频繁的Swap交换会导致I/O瓶颈,系统卡死。我们宁可让容器因内存不足(OOM)被终止,也不希望整个服务器失去响应。
- postgres:13-alpine:我们选择了基于Alpine Linux的镜像版本。alpine镜像体积通常只有标准镜像的几分之一,非常适合资源受限的环境。
- -v …:/var/lib/postgresql/data:将容器内的数据目录挂载到宿主机,实现数据持久化。
启动后,你可以进入容器验证数据库是否正常运行:
docker exec -it postgres-for-tb psql -U postgres -d thingsboard
如果成功进入psql命令行,输入 \\l 查看数据库列表,应该能看到名为 thingsboard 的数据库。退出请使用 \\q。
3. 核心部署:优化ThingsBoard容器配置
ThingsBoard是资源消耗的大户。我们将通过环境变量和Docker Compose来精细控制其行为。
首先,安装Docker Compose。虽然我们可以用多个docker run命令,但Compose能更好地管理多容器应用的依赖关系和配置。
# 下载Docker Compose二进制文件(请检查官网获取最新版本号)
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose –version # 验证安装
创建专属的Docker Compose文件:在/opt/iot-stack目录下创建docker-compose.yml。
version: '3.8'
services:
thingsboard:
image: thingsboard/tb-postgres:3.6.1 # 建议使用稳定版本,而非latest
container_name: thingsboard-app
restart: unless-stopped
mem_limit: 800m # 限制ThingsBoard容器内存
ports:
– "8080:9090" # Web UI
– "1884:1883" # MQTT端口,映射到非标准的1884,避免与后续EMQX冲突
– "7070:7070" # HTTP API
– "5683-5688:5683-5688/udp" # CoAP协议端口
environment:
– TB_QUEUE_TYPE=in-memory # 使用内存队列,降低复杂度。生产环境可考虑kafka或rabbitmq。
– SPRING_DATASOURCE_URL=jdbc:postgresql://你的服务器IP:5432/thingsboard
– SPRING_DATASOURCE_USERNAME=postgres
– SPRING_DATASOURCE_PASSWORD=your_strong_password
– JAVA_OPTS=-Xms256m -Xmx512m -XX:MaxRAM=800m # 关键!直接控制JVM堆内存
depends_on:
– postgres-for-tb
networks:
– iot-network
postgres-for-tb:
image: postgres:13-alpine
container_name: postgres-for-tb
restart: unless-stopped
mem_limit: 512m
environment:
– POSTGRES_PASSWORD=your_strong_password
– POSTGRES_DB=thingsboard
volumes:
– ./postgres/data:/var/lib/postgresql/data
networks:
– iot-network
networks:
iot-network:
driver: bridge
核心优化点解析:
- -Xms256m:设置JVM初始堆内存为256MB。
- -Xmx512m:设置JVM最大堆内存为512MB。这确保了ThingsBoard的Java进程不会无节制地吞噬内存。
- -XX:MaxRAM=800m:告知JVM它可用的最大RAM约为800MB,帮助其更好地规划内存使用。
启动与验证:
cd /opt/iot-stack
docker-compose up -d # -d 表示后台运行
docker-compose logs -f thingsboard-app # 查看启动日志,关注是否有数据库连接成功和启动完成的提示
当看到日志中出现“Started ThingsBoard Server Application”之类的信息时,访问 http://你的服务器IP:8080,应该能看到ThingsBoard的登录界面。默认账号:sysadmin@thingsboard.org,密码:sysadmin。
4. 消息中枢:部署并精简EMQX
EMQX作为高性能的MQTT消息中间件,是我们连接海量设备的关键。同样,我们需要一个轻量化的部署方案。
创建EMQX的持久化目录:
mkdir -p /opt/iot-stack/emqx/data /opt/iot-stack/emqx/etc /opt/iot-stack/emqx/log
编写EMQX的Docker启动命令或将其加入Compose。为了更灵活地控制启动参数,我们这里使用单独的docker run命令,并同样进行严格的内存限制。
docker run -d \\
–name emqx-broker \\
–restart=unless-stopped \\
–memory=400m \\
–memory-swap=400m \\
-p 1883:1883 \\
-p 8083:8083 \\
-p 8084:8084 \\
-p 8883:8883 \\
-p 18083:18083 \\
-v /opt/iot-stack/emqx/etc:/opt/emqx/etc \\
-v /opt/iot-stack/emqx/data:/opt/emqx/data \\
-v /opt/iot-stack/emqx/log:/opt/emqx/log \\
-e EMQX_NAME=emqx@iot \\
-e EMQX_NODE__NAME=emqx@iot \\
-e EMQX_LISTENERS__TCP__EXTERNAL__MAX_CONNECTIONS=1024 \\ # 限制最大连接数,防止过载
emqx/emqx:5.0.9
部署要点:
- 内存限制:同样限制在400MB,并禁用Swap。
- 端口:1883是标准的MQTT端口,供设备连接。18083是EMQX的管理控制台端口。
- 环境变量:我们通过环境变量限制了最大TCP连接数(1024),这对于一个测试或小型生产环境已经足够,同时避免了因连接数过多导致的内存溢出。
- 版本选择:选择了相对稳定的5.0.9版本。EMQX 5.x在资源管理和集群方面有较大改进。
启动后,访问 http://你的服务器IP:18083,使用默认账号admin和密码public登录管理控制台。你可以在这里查看客户端连接、消息流量等信息。
5. 连接桥梁:配置tb-gateway实现设备接入
现在,ThingsBoard和EMQX都已就位,但它们之间是独立的。tb-gateway的作用就是作为一座桥梁,将连接到EMQX的设备数据,转发到ThingsBoard进行管理和可视化。
启动tb-gateway容器:
docker run -d \\
–name tb-gateway \\
–restart=unless-stopped \\
–memory=256m \\
-p 9090:9090 \\
-v /opt/iot-stack/tb-gateway/config:/thingsboard_gateway/config \\
-v /opt/iot-stack/tb-gateway/extensions:/thingsboard_gateway/extensions \\
-v /opt/iot-stack/tb-gateway/logs:/thingsboard_gateway/logs \\
thingsboard/tb-gateway
关键步骤:配置tb-gateway。配置文件的修改是核心。进入宿主机挂载的配置目录:
cd /opt/iot-stack/tb-gateway/config
你需要修改两个主要文件:tb_gateway.yaml 和 mqtt.json。
首先,配置 tb_gateway.yaml,主要设置与ThingsBoard的连接:
thingsboard:
host: 你的服务器IP
port: 8080
remoteShell: false
remoteConfiguration: false
security:
accessToken: YOUR_GATEWAY_ACCESS_TOKEN # 需要在ThingsBoard中创建设备并获取
storage:
type: memory
read_records_count: 100
max_records_count: 100000
connectors:
– name: MQTT Broker Connector
type: mqtt
configuration: mqtt.json
然后,配置 mqtt.json,设置与EMQX的连接及数据映射规则:
{
"broker": {
"name": "Default EMQX Broker",
"host": "你的服务器IP",
"port": 1883,
"security": {
"type": "anonymous"
}
},
"mapping": [
{
"topicFilter": "sensor/+/telemetry",
"converter": {
"type": "json",
"deviceNameTopicExpression": "(?<=sensor/)[^/]+(?=/telemetry)",
"deviceTypeTopicExpression": "Thermometer",
"timeout": 60000,
"attributes": [
{
"type": "string",
"key": "model",
"value": "default"
}
],
"timeseries": [
{
"type": "double",
"key": "temperature",
"value": "${temperature}"
},
{
"type": "double",
"key": "humidity",
"value": "${humidity}"
}
]
}
}
]
}
这个配置示例表示:tb-gateway会订阅EMQX上匹配 sensor/+/telemetry 主题的消息(例如sensor/device001/telemetry),并将消息中的temperature和humidity字段作为时序数据,转发给ThingsBoard。设备名将从主题中提取(device001)。
最后,重启tb-gateway容器使配置生效:
docker restart tb-gateway
docker logs -f tb-gateway # 查看日志,确认连接成功
6. 压力测试与监控:确保系统稳定运行
部署完成只是第一步,在低配环境下,持续的监控和压力测试至关重要。
使用Docker内置命令监控资源:
docker stats –no-stream # 查看各容器实时资源占用(CPU、内存、网络IO)
定期运行此命令,观察在模拟设备连接和数据上报时,各个容器的内存使用是否接近我们设定的上限。
模拟MQTT设备进行压力测试:你可以使用 mosquitto_pub 命令行工具或编写简单的Python脚本,模拟设备向EMQX发布消息。
# 安装mosquitto客户端(在另一台机器或本机)
# Ubuntu: sudo apt install mosquitto-clients
# CentOS: sudo yum install mosquitto
# 模拟设备发布数据到EMQX
mosquitto_pub -h 你的服务器IP -p 1883 -t "sensor/testDevice/telemetry" -m '{"temperature": 25.5, "humidity": 60}'
在ThingsBoard中验证数据:登录ThingsBoard,在“设备”页面应该能看到名为testDevice的设备,并在其最新遥测数据中看到上传的温度和湿度值。
设置内存告警:虽然我们限制了容器内存,但最坏的情况是某个容器发生内存泄漏,不断触发OOM Killer。可以在宿主机上配置简单的监控脚本,或者使用云服务商自带的监控告警功能,当系统内存使用率持续超过85%时发送通知。
整个部署流程走下来,你会发现,在1核2G的服务器上运行这样一个物联网平台栈,虽然捉襟见肘,但通过严格的资源限制、选择轻量级镜像、优化应用配置(尤其是JVM)、以及合理的端口与网络规划,是完全可行的。这套方案特别适合个人开发者进行原型验证、学习测试,或者运行一个设备数量不多(几十到上百)的小型项目。当你的项目真正需要走向生产环境时,首要任务可能就是升级服务器配置了。不过,这段在资源极限下的部署经历,会让你对每个组件的资源消耗有更深刻的理解,这本身就是一笔宝贵的财富。




