IoTDB实战:用Docker Compose搭建工业级时序数据库集群(含Grafana监控)
IoTDB实战:用Docker Compose搭建工业级时序数据库集群(含Grafana监控)
最近在帮一个做智慧工厂的朋友搭建数据中台,他们产线上几千个传感器,数据量上来后,原先用的MySQL加Redis的方案彻底扛不住了,查询慢得像蜗牛,存储成本也高得吓人。折腾了一圈,最后把目光锁定在了时序数据库上。Apache IoTDB这个名字在工业物联网圈子里越来越响,它原生为时序数据设计的架构,特别是对高基数场景的友好度,让我们最终决定用它来重构整个数据底座。
但问题来了,朋友那边的运维团队规模不大,对传统复杂的集群部署和运维有点发怵。他们需要一套能快速上线、易于管理、并且能直观看到运行状态的方案。这不,容器化部署就成了不二之选。今天,我就把这次用Docker Compose搭建一个生产可用的IoTDB集群,并集成Prometheus和Grafana监控的完整过程梳理出来。这套方案不仅部署简单,通过编排文件就能一键拉起整个环境,更重要的是,它包含了数据持久化、负载均衡接入以及可视化的监控看板,非常适合中小团队快速构建一个稳定、可观测的工业物联网数据平台。
1. 环境准备与架构设计
在动手敲命令之前,我们得先理清楚要搭建的是一个什么样的系统。对于生产环境,单点故障是绝对不能接受的,所以集群部署是基础。同时,考虑到数据的安全性和服务的可访问性,我们需要规划好存储、网络和访问入口。
我设计的这个最小化生产架构包含以下核心组件:
- 一个三节点的IoTDB集群:包含一个ConfigNode和两个DataNode。ConfigNode负责集群元数据管理,DataNode负责实际的数据存储和查询。这个配置能提供基本的高可用能力,即使一个DataNode宕机,集群依然可以提供服务。
- 负载均衡器(Nginx):对外提供统一的访问入口,将客户端的写入和查询请求分发到后端的多个DataNode上,避免单点压力过大,也方便后续的节点扩缩容。
- 监控栈(Prometheus + Grafana):Prometheus负责抓取IoTDB集群各个节点暴露的JMX指标,Grafana则用于将指标数据可视化,让我们能一目了然地看到集群的写入吞吐、查询延迟、内存使用、磁盘IO等关键状态。
- Docker Compose:作为所有服务的编排工具,通过一个
docker-compose.yml文件定义和运行所有容器,管理它们之间的依赖、网络和存储卷。
整个服务的网络拓扑和访问关系,可以参考下面这个简化的示意图来理解:
| 组件 | 容器内端口 | 映射宿主机端口 | 主要作用 |
|---|---|---|---|
| iotdb-confignode | 10710, 10720 | 不映射 | 集群配置与元数据管理 |
| iotdb-datanode-1 | 6667, 10730, 10740, 10750 | 6667 (仅用于测试) | 数据存储与查询节点1 |
| iotdb-datanode-2 | 6667, 10730, 10740, 10750 | 不映射 | 数据存储与查询节点2 |
| nginx | 80 | 8080 | 负载均衡,对外提供统一端口 |
| prometheus | 9090 | 9090 | 指标抓取与存储 |
| grafana | 3000 | 3000 | 监控数据可视化 |
注意:在生产环境中,通常不会将DataNode的服务端口(如6667)直接映射到宿主机。所有外部访问都应通过Nginx负载均衡器进行,这样更安全,也便于管理。上表中为
iotdb-datanode-1映射6667端口,仅仅是为了方便我们后续进行一些直接的连接测试。
接下来是具体的环境要求。你需要一台至少满足以下配置的Linux服务器(我使用的是Ubuntu 22.04 LTS):
- CPU: 4核或以上(集群组件较多)
- 内存: 8GB或以上(建议16GB以获得更好体验)
- 磁盘: 100GB可用空间(为时序数据增长预留)
- 软件: 已安装Docker Engine和Docker Compose插件。
检查Docker环境是否就绪:
docker --version
docker compose version
如果都能正确输出版本信息,那么准备工作就完成了。
2. 编写Docker Compose编排文件
这是整个部署的核心。我们将所有服务的定义、配置、依赖关系和存储卷都集中在一个docker-compose.yml文件里。我把它分成了几个部分来讲解。
首先,定义顶层的网络和卷。我们创建一个独立的Docker网络iotdb-net,让所有服务在同一个网络内互通,避免复杂的端口映射。同时,为每个需要持久化数据的服务创建对应的命名卷。
version: '3.8'
networks:
iotdb-net:
driver: bridge
volumes:
iotdb-confignode-data:
iotdb-datanode-1-data:
iotdb-datanode-2-data:
prometheus-data:
grafana-data:
接下来是IoTDB集群部分的定义。这里的关键在于正确设置环境变量,特别是IOTDB_CLUSTER_CONFIG_NODES,它告诉每个节点ConfigNode的地址。同时,我们将数据和日志目录挂载到宿主机卷,确保容器重启后数据不丢失。
services:
iotdb-confignode:
image: apache/iotdb:1.3.1-cluster
container_name: iotdb-confignode
hostname: iotdb-confignode
environment:
- IOTDB_CLUSTER_CONFIG_NODES=iotdb-confignode:10710
- IOTDB_CLUSTER_ROLE=ConfigNode
networks:
- iotdb-net
volumes:
- iotdb-confignode-data:/iotdb/data
- ./logs/confignode:/iotdb/logs
restart: unless-stopped
iotdb-datanode-1:
image: apache/iotdb:1.3.1-cluster
container_name: iotdb-datanode-1
hostname: iotdb-datanode-1
environment:
- IOTDB_CLUSTER_CONFIG_NODES=iotdb-confignode:10710
- IOTDB_CLUSTER_ROLE=DataNode
- IOTDB_CLUSTER_DATA_NODE_CONSENSUS_PROTOCOL_CLASS=org.apache.iotdb.consensus.ratis.RatisConsensus
depends_on:
- iotdb-confignode
networks:
- iotdb-net
volumes:
- iotdb-datanode-1-data:/iotdb/data
- ./logs/datanode1:/iotdb/logs
ports:
- "6667:6667" # 仅为测试暴露,生产环境应关闭
restart: unless-stopped
iotdb-datanode-2:
image: apache/iotdb:1.3.1-cluster
container_name: iotdb-datanode-2
hostname: iotdb-datanode-2
environment:
- IOTDB_CLUSTER_CONFIG_NODES=iotdb-confignode:10710
- IOTDB_CLUSTER_ROLE=DataNode
- IOTDB_CLUSTER_DATA_NODE_CONSENSUS_PROTOCOL_CLASS=org.apache.iotdb.consensus.ratis.RatisConsensus
depends_on:
- iotdb-confignode
networks:
- iotdb-net
volumes:
- iotdb-datanode-2-data:/iotdb/data
- ./logs/datanode2:/iotdb/logs
restart: unless-stopped
然后是Nginx负载均衡器。我们需要编写一个简单的Nginx配置文件,将流量轮询(round-robin)到两个DataNode。这里使用upstream模块定义后端服务器组。
# 创建目录并编写nginx配置文件
mkdir -p nginx/conf.d
cat > nginx/conf.d/iotdb.conf << EOF
upstream iotdb_backend {
server iotdb-datanode-1:6667;
server iotdb-datanode-2:6667;
}
server {
listen 80;
location / {
proxy_pass http://iotdb_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
EOF
在docker-compose.yml中继续添加Nginx服务:
nginx:
image: nginx:alpine
container_name: iotdb-nginx
depends_on:
- iotdb-datanode-1
- iotdb-datanode-2
networks:
- iotdb-net
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
ports:
- "8080:80"
restart: unless-stopped
最后是监控部分。Prometheus需要配置抓取目标,即两个DataNode的JMX Exporter端口(IoTDB集群镜像默认会启动JMX Exporter在10750端口)。Grafana则直接使用官方镜像,并预置IoTDB的监控面板。
prometheus:
image: prom/prometheus
container_name: iotdb-prometheus
networks:
- iotdb-net
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
restart: unless-stopped
grafana:
image: grafana/grafana
container_name: iotdb-grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123 # 请务必修改!
networks:
- iotdb-net
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
ports:
- "3000:3000"
depends_on:
- prometheus
restart: unless-stopped
对应的Prometheus配置文件prometheus.yml如下:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'iotdb-cluster'
static_configs:
- targets:
- 'iotdb-datanode-1:10750'
- 'iotdb-datanode-2:10750'
labels:
group: 'iotdb-production'
至此,完整的docker-compose.yml文件就组合完毕了。你可以将所有代码块按顺序合并到一个文件中。
3. 启动集群与基础验证
万事俱备,现在可以启动我们的“全家桶”了。进入存放docker-compose.yml文件的目录,执行一条命令:
docker compose up -d
-d参数表示在后台运行。Docker Compose会按照依赖顺序拉取镜像(如果本地没有)并启动所有容器。你可以用以下命令观察启动状态:
docker compose ps
当所有服务的状态(STATUS)都显示为“Up”时,说明启动成功。如果某个服务启动失败,可以查看其日志定位问题:
docker compose logs -f iotdb-confignode # 查看指定容器的日志
集群启动后,我们需要验证其是否正常工作。首先,检查集群节点状态。我们可以进入任意一个DataNode容器内部,使用IoTDB自带的CLI工具连接本地服务,并执行集群状态查询。
# 进入datanode-1容器
docker exec -it iotdb-datanode-1 bash
# 在容器内启动CLI,连接到本地的DataNode(默认端口6667)
/iotdb/sbin/start-cli.sh -h 127.0.0.1 -p 6667
# 在CLI中执行查看集群节点的命令
IoTDB> show cluster;
如果一切正常,你应该能看到类似下面的输出,显示一个ConfigNode和两个DataNode,且状态均为“Running”:
+------+---------------------+-------+---------+--------+
|NodeID| NodeType | Status|Internal | ... |
+------+---------------------+-------+---------+--------+
| 0| ConfigNode|Running|iotdb-...| |
| 1| DataNode|Running|iotdb-...| |
| 2| DataNode|Running|iotdb-...| |
+------+---------------------+-------+---------+--------+
接下来,测试通过Nginx负载均衡器写入和查询数据。退出容器,在宿主机上,我们可以使用任何兼容的客户端,这里用curl模拟一个简单的HTTP API请求(IoTDB支持RESTful接口),目标是Nginx暴露的8080端口。
# 创建一个存储组
curl -X POST http://localhost:8080/rest/v1/query -H "Content-Type: application/json" -d '{"sql": "CREATE DATABASE root.ln"}'
# 插入一条数据
curl -X POST http://localhost:8080/rest/v1/insert -H "Content-Type: application/json" -d '{
"timestamps": [1733865600000],
"measurements": ["temperature", "status"],
"values": [[25.5], [1]],
"isAligned": false,
"device": "root.ln.device1"
}'
# 查询数据
curl -X POST http://localhost:8080/rest/v1/query -H "Content-Type: application/json" -d '{"sql": "SELECT * FROM root.ln.device1"}'
如果这些命令都能成功返回(通常返回{"code":200,"message":"Execute successfully"}之类的JSON),那么恭喜你,一个具备负载均衡能力的IoTDB生产集群已经搭建成功,并且可以正常对外提供服务了。
4. 配置Grafana监控看板
集群跑起来了,但我们不能当“瞎子”。监控是生产环境的眼睛。接下来,我们让Grafana展示Prometheus抓取到的IoTDB指标。
首先,打开浏览器,访问 http://你的服务器IP:3000,使用默认用户名admin和我们在compose文件中设置的密码(示例中是admin123)登录Grafana。
第一步,添加数据源。
- 点击左侧齿轮图标进入“Configuration” -> “Data Sources”。
- 点击“Add data source”,选择“Prometheus”。
- 在URL一栏填写
http://prometheus:9090(注意,这里用的是Docker服务名,因为在同一网络内)。 - 点击“Save & test”,如果显示“Data source is working”,说明连接成功。
第二步,导入IoTDB监控仪表板。 Grafana社区有官方和用户贡献的仪表板模板。我们可以直接导入一个现成的。这里我们使用一个较为全面的IoTDB集群监控模板。
- 点击左侧“+”号 -> “Import”。
- 在“Import via grafana.com”框中,输入仪表板ID
18450,然后点击Load。 - 在下一步中,选择我们刚刚添加的Prometheus数据源,然后点击“Import”。
瞬间,一个专业的IoTDB集群监控看板就出现在你面前。这个看板通常包含以下几个关键视图:
- 集群概览:显示集群节点数量、状态、总时间序列数等。
- 写入性能:每秒写入点数、写入延迟分布。
- 查询性能:查询QPS、查询延迟。
- 系统资源:JVM内存使用情况(堆内存、非堆内存)、GC次数与时间。
- 存储与文件:数据文件大小、TsFile数量、压缩率。
你可以根据自己的关注点,调整面板或创建新的仪表板。例如,为重要的业务指标(如特定产线的传感器数据写入速率)单独创建一个面板。
提示:监控告警是另一个重要话题。你可以在Grafana中为关键指标(如节点宕机、写入延迟过高、内存使用率超过阈值)配置告警规则,并集成到钉钉、企业微信或邮件中,实现主动运维。
5. 生产环境调优与运维要点
基础集群搭好了,监控也有了,但要真正用于生产,还有一些细节需要打磨和注意。这里分享几个我们在实际项目中踩过坑后总结的经验。
数据持久化与备份策略
Docker卷虽然实现了数据持久化,但并非备份。你必须建立定期备份机制。IoTDB提供了export和load工具进行逻辑备份。可以编写一个脚本,定期执行导出命令,并将备份文件同步到远程存储或对象存储中。
# 示例:在容器内执行全量导出(需根据实际路径调整)
docker exec iotdb-datanode-1 /iotdb/tools/export-csv.sh -h localhost -p 6667 -u root -pw root -td /iotdb/backup/export -q "SELECT ** FROM root.**"
性能调优参数
默认配置适合起步,但针对高负载场景可能需要调整。关键配置位于每个节点的iotdb-system.properties和iotdb-datanode.properties文件。你可以通过挂载卷的方式覆盖容器内的默认配置。
max_heaped_memory_in_bytes:调整JVM堆内存大小,建议设置为物理内存的50%-70%。concurrent_writing_time_partition:调整写入并发相关的分区数,在高并发写入时可适当增加。tsfile_size_threshold:单个TsFile文件的大小阈值,影响查询性能和合并(compaction)频率。
调整配置的步骤是:先将容器内的配置文件复制到宿主机,修改后再通过volumes映射回去。切记,修改集群配置后,需要重启对应节点才能生效。
集群扩容与缩容 业务增长后,横向扩展是必经之路。IoTDB集群扩容相对平滑。
- 扩容DataNode:在
docker-compose.yml中仿照现有DataNode增加一个iotdb-datanode-3的服务定义,确保其IOTDB_CLUSTER_CONFIG_NODES指向正确的ConfigNode。然后启动新服务docker compose up -d iotdb-datanode-3。新节点会自动加入集群。之后,你可能需要调整数据分片(默认自动管理)或重新平衡负载。 - 缩容:缩容需要谨慎。首先确保该DataNode上的数据已经通过系统内部的副本机制或其他方式迁移到了其他节点(IoTDB的Ratis共识协议提供了数据冗余)。然后,可以通过CLI执行
remove-datanode指令将该节点安全地从集群中移除,最后再停止并删除对应的容器和卷。
日常运维命令 掌握几个常用的Docker Compose命令能让运维更轻松:
docker compose logs -f [服务名]:实时追踪某个服务的日志,排查问题时非常有用。docker compose exec [服务名] bash:进入某个正在运行的容器内部。docker compose restart [服务名]:重启某个服务,例如在修改配置后。docker compose down:停止并移除所有容器、网络(但保留数据卷)。docker compose down -v会同时删除数据卷,生产环境慎用!
最后,再强调一个安全实践:务必修改所有默认密码!包括IoTDB的root用户密码(在CLI中使用ALTER USER root SET PASSWORD 'new_password')、Grafana的admin密码,以及确保你的服务器防火墙只开放了必要的端口(如8080, 3000, 9090)。
更多推荐



所有评论(0)