跳转到内容

环境迁移指南

环境迁移指南

本节介绍如何在不同环境中迁移完整的 IoT 栈,包括开发环境、测试环境和生产环境之间的数据迁移。学习完成后,您将能够:

  • 理解环境迁移的完整流程
  • 使用脚本自动化迁移过程
  • 向客户解释环境迁移的可行性和方法
  • 处理迁移过程中的常见问题
场景 1: 开发 → 测试
┌──────────┐ ┌──────────┐
│ 笔记本 │ ──→ │ 测试服务器 │
│ (开发者) │ │ (QA) │
└──────────┘ └──────────┘
场景 2: 演示 → 生产
┌──────────┐ ┌──────────┐
│ 演示环境 │ ──→ │ 生产服务器 │
│ (PoC) │ │ (客户) │
└──────────┘ └──────────┘
场景 3: 本地 → 云端
┌──────────┐ ┌──────────┐
│ 本地服务器 │ ──→ │ 云服务器 │
│ (办公室) │ │ (AWS/阿里云)│
└──────────┘ └──────────┘
场景 4: 灾难恢复
┌──────────┐ ┌──────────┐
│ 主服务器 │ ──→ │ 备用服务器 │
│ (主节点) │ │ (备节点) │
└──────────┘ └──────────┘

完整的迁移脚本 migrate-iot-stack.sh

#!/bin/bash
# IoT Stack 环境迁移脚本
set -e
# 配置
SOURCE_DIR="/opt/iot-stack"
BACKUP_FILE="/tmp/iot-migration-$(date +%Y%m%d).tar.gz"
echo "=== IoT Stack Migration Script ==="
# 1. 备份源环境
backup_source() {
echo "[1/4] Backing up source environment..."
# 备份配置和数据
mkdir -p /tmp/iot-backup
# 备份 docker-compose.yml
cp ${SOURCE_DIR}/docker-compose.yml /tmp/iot-backup/
# 备份所有数据卷
docker run --rm \
-v nodered_data:/source \
-v /tmp/iot-backup:/backup \
alpine tar czf /backup/nodered_data.tar.gz -C /source .
docker run --rm \
-v influxdb_data:/source \
-v /tmp/iot-backup:/backup \
alpine tar czf /backup/influxdb_data.tar.gz -C /source .
docker run --rm \
-v grafana_data:/source \
-v /tmp/iot-backup:/backup \
alpine tar czf /backup/grafana_data.tar.gz -C /source .
# 导出容器列表
docker ps --format '{{.Names}}' > /tmp/iot-backup/container-list.txt
echo " ✓ Source backup completed"
}
# 2. 打包迁移文件
package_migration() {
echo "[2/4] Packaging migration archive..."
tar czf ${BACKUP_FILE} -C /tmp iot-backup
echo " ✓ Migration package: ${BACKUP_FILE}"
echo " ✓ Size: $(du -h ${BACKUP_FILE} | cut -f1)"
}
# 3. 恢复目标环境
restore_target() {
echo "[3/4] Restoring to target environment..."
# 解压迁移包
tar xzf ${BACKUP_FILE} -C /tmp
# 恢复 docker-compose.yml
cp /tmp/iot-backup/docker-compose.yml ${SOURCE_DIR}/
# 恢复数据卷
docker run --rm \
-v nodered_data:/target \
-v /tmp/iot-backup:/backup \
alpine tar xzf /backup/nodered_data.tar.gz -C /target
docker run --rm \
-v influxdb_data:/target \
-v /tmp/iot-backup:/backup \
alpine tar xzf /backup/influxdb_data.tar.gz -C /target
docker run --rm \
-v grafana_data:/target \
-v /tmp/iot-backup:/backup \
alpine tar xzf /backup/grafana_data.tar.gz -C /target
echo " ✓ Target restore completed"
}
# 4. 验证迁移
verify_migration() {
echo "[4/4] Verifying migration..."
# 启动服务
cd ${SOURCE_DIR}
docker-compose up -d
# 等待服务就绪
sleep 10
# 检查服务状态
local services=("nodered" "mosquitto" "influxdb" "grafana")
for service in "${services[@]}"; do
if docker ps --format '{{.Names}}' | grep -q "${service}"; then
echo " ✓ ${service}: Running"
else
echo " ✗ ${service}: Not found - may need manual check"
fi
done
echo " ✓ Migration verification completed"
}
# 主流程
case "${1:-}" in
backup)
backup_source
package_migration
echo "Backup migration package: ${BACKUP_FILE}"
;;
restore)
if [ -z "${2:-}" ]; then
echo "Error: Please provide migration package path"
echo "Usage: ./migrate-iot-stack.sh restore <backup-file.tar.gz>"
exit 1
fi
BACKUP_FILE=$2
restore_target
verify_migration
;;
*)
echo "Usage:"
echo " ./migrate-iot-stack.sh backup # 在源环境执行备份"
echo " ./migrate-iot-stack.sh restore <file> # 在目标环境执行恢复"
echo ""
echo "Example:"
echo " # 源环境:创建迁移包"
echo " ./migrate-iot-stack.sh backup"
echo " scp /tmp/iot-migration-20260517.tar.gz user@target-server:/tmp/"
echo ""
echo " # 目标环境:恢复"
echo " ./migrate-iot-stack.sh restore /tmp/iot-migration-20260517.tar.gz"
;;
esac

当无法使用脚本时的分步手动迁移:

源环境导出:

Terminal window
# 1. 导出 docker-compose.yml 和配置文件
tar czf iot-config.tar.gz docker-compose.yml mosquitto/config/
# 2. 导出 Node-RED 数据
docker run --rm \
-v nodered_data:/source \
-v $(pwd):/backup \
alpine tar czf /backup/nodered-data.tar.gz -C /source .
# 3. 导出 InfluxDB 数据
docker exec influxdb influx backup /tmp/influx-backup --token ${TOKEN}
docker cp influxdb:/tmp/influx-backup ./influx-backup
# 4. 导出 Grafana 数据
docker cp grafana:/var/lib/grafana/grafana.db ./grafana.db

目标环境导入:

Terminal window
# 1. 复制文件到目标服务器
scp iot-config.tar.gz user@target-server:/opt/iot/
scp nodered-data.tar.gz user@target-server:/tmp/
scp -r influx-backup user@target-server:/tmp/
scp grafana.db user@target-server:/tmp/
# 2. 恢复配置
cd /opt/iot
tar xzf iot-config.tar.gz
# 3. 创建数据卷
docker volume create nodered_data
docker volume create influxdb_data
docker volume create grafana_data
# 4. 恢复数据
docker run --rm \
-v nodered_data:/target \
-v /tmp:/backup \
alpine tar xzf /backup/nodered-data.tar.gz -C /target
# 5. 启动服务
docker-compose up -d

创建一个中心化的配置文件 iot-environment.yml

# IoT 环境配置文件
environment:
name: "production"
type: "production" # development, staging, production, demo
# 网络配置
network:
name: "iot-network"
subnet: "172.20.0.0/16"
# 存储配置
storage:
base_path: "/opt/iot-stack"
backup_path: "/opt/backups/iot"
retention_days: 30
# 服务配置
services:
mosquitto:
port: 1883
ws_port: 9001
allow_anonymous: false
require_authentication: true
nodered:
port: 1880
auth_enabled: true
timezone: "Asia/Shanghai"
influxdb:
port: 8086
org: "iot-demo"
bucket: "nodered"
grafana:
port: 3000
admin_user: "admin"
  • 确认源环境所有服务运行正常
  • 执行一次完整备份
  • 记录所有服务的版本号
  • 导出所有配置文件
  • 记录网络配置和端口信息
  • 确认目标服务器满足系统要求
  • 目标服务器已安装 Docker 和 Docker Compose
  • 停止源环境的数据写入(可选)
  • 打包并传输备份文件
  • 确保传输过程中数据完整性(MD5 校验)
Terminal window
# 校验文件完整性
md5sum iot-migration-20260517.tar.gz > checksum.md5
scp iot-migration-20260517.tar.gz checksum.md5 user@target:/tmp/
cd /tmp && md5sum -c checksum.md5
  • 验证所有服务正常运行
  • 验证数据完整性
  • 测试 MQTT 消息收发
  • 验证 Node-RED Flow 功能
  • 确认 Grafana 仪表板显示正常
  • 更新 DNS 或连接配置
  • 保留源环境 1-2 天以备回滚

问题: 目标环境端口被占用

解决方案:

# 修改 docker-compose.yml 端口映射
services:
nodered:
ports:
- "1881:1880" # 改为其他端口

问题: Bind Mount 路径权限不足

解决方案:

Terminal window
# 创建目录并设置权限
mkdir -p /opt/iot-stack/{nodered,influxdb,grafana}
chown -R 1000:1000 /opt/iot-stack/nodered
chown -R 472:472 /opt/iot-stack/grafana

问题: 不同版本容器不兼容

解决方案:

# 使用相同版本标签
services:
influxdb:
image: influxdb:2.7 # 而不是 latest

推荐做法:

  • 使用同一版本标签避免兼容性问题
  • 迁移前完整备份
  • 迁移后全面验证
  • 准备回滚方案
  • 记录迁移过程和配置变更

避免做法:

  • 跳过备份直接迁移
  • 使用 latest 标签
  • 忽略权限设置
  • 在业务高峰期迁移
  • 迁移后立即删除源环境
  1. 环境迁移是 IoT 项目交付的常见需求
  2. 脚本化迁移简化整个过程
  3. 数据卷的迁移是核心关注点
  4. 完整的迁移检查清单确保零失误
  5. 保留回滚方案降低风险