**Docker 容器化** 的企业级能耗系统运维 本方案重点解决你最关心的三个核心问题: 1. **数据安全性:** 容器删了数据还在。 2. **版本迭代:** 数据库加字段怎么自动生效,不需人工介入。 3. **灾难恢复:** 如何自动备份,以及炸机后如何恢复。 --- ### 一、 运维目录结构设计 (Standard Directory Layout) 首先,规范服务器上的目录结构。**所有数据和配置必须挂载到宿主机**,严禁散落在容器内部。 ```text /opt/ems-platform ├── docker-compose.yml # 核心编排文件 ├── .env # 环境变量(密码、密钥) ├── /configs # 配置文件挂载 │ ├── nginx/conf.d/default.conf │ └── emqx/emqx.conf ├── /data # 【核心】数据持久化目录 (自动生成) │ ├── postgres/ # PG 数据 │ ├── taos/ # TDengine 数据 │ └── redis/ # Redis 数据 ├── /backups # 【核心】自动备份落地目录 │ ├── postgres/ │ └── tdengine/ ├── /db-migrations # 数据库升级脚本 (SQL文件) │ ├── 000001_init.up.sql │ └── 000002_add_field.up.sql └── /scripts # 运维脚本 └── backup_tdengine.sh ``` --- ### 二、 数据库变更方案 (Schema Migration) **场景:** 业务迭代,需要在 `device` 表新增字段 `last_maintenance_date`。 **原则:** **禁止**进入数据库手动执行 `ALTER TABLE`,必须代码化管理。 #### 1. 选型 使用 **Golang-Migrate**。它能保证数据库结构的版本与代码版本严格一致。 #### 2. 工作流 1. **开发阶段:** 开发者在代码库生成 `000002_add_field.up.sql`。 2. **部署阶段:** Go 服务启动时,会自动检测并执行这个 SQL。 #### 3. 代码集成 (main.go) 在你的 Go 后端入口加入自动迁移逻辑: ```go import ( "github.com/golang-migrate/migrate/v4" _ "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" ) func initSchema(dbUrl string) { // 指向容器内的 SQL 文件目录 m, err := migrate.New("file:///app/migrations", dbUrl) if err != nil { log.Fatal("迁移初始化失败:", err) } // 自动执行 Up 操作,追平最新版本 if err := m.Up(); err != nil && err != migrate.ErrNoChange { log.Fatal("数据库升级失败:", err) } log.Println("数据库结构已升级到最新版本") } ``` #### 4. Dockerfile 调整 确保 SQL 文件被打入镜像: ```dockerfile # ... build stage ... COPY ./db/migrations /app/migrations CMD ["./ems-server"] ``` --- ### 三、 自动化备份方案 (Auto Backup) #### 1. PostgreSQL 自动备份 (Sidecar 模式) 在 `docker-compose.yml` 中增加一个专用容器,它是 PG 的“僚机”,负责每天半夜把数据打包出来。 ```yaml # 数据库服务 postgres: image: postgres:15 volumes: - ./data/postgres:/var/lib/postgresql/data # 数据持久化 # ... # 备份服务 (Sidecar) pg-backup: image: prodrigestivill/postgres-backup-local restart: always environment: - POSTGRES_HOST=postgres - POSTGRES_DB=ems_db - POSTGRES_USER=ems_user - POSTGRES_PASSWORD=${DB_PASSWORD} - SCHEDULE=@daily # 每天 00:00 备份 - BACKUP_KEEP_DAYS=7 # 只保留最近 7 天 - BACKUP_DIR=/backups volumes: - ./backups/postgres:/backups # 映射到宿主机 depends_on: - postgres ``` #### 2. TDengine 自动备份 (Host Script 模式) 由于 TDengine 数据量极大,且官方暂无 Sidecar 镜像,建议使用宿主机 Crontab 调度。 **脚本:`/opt/ems-platform/scripts/backup_td.sh**` ```bash #!/bin/bash # 设置变量 CONTAINER_NAME="ems-tdengine" BACKUP_ROOT="/opt/ems-platform/backups/tdengine" DATE=$(date +%Y%m%d_%H%M) TARGET_DIR="$BACKUP_ROOT/$DATE" mkdir -p $TARGET_DIR # 1. 调用容器内的 taosdump 工具导出元数据和数据 echo "Starting TDengine backup..." docker exec $CONTAINER_NAME taosdump -o /tmp/dump_out -D power_db # 2. 将备份文件从容器复制到宿主机 docker cp $CONTAINER_NAME:/tmp/dump_out/. $TARGET_DIR/ # 3. 清理容器内临时文件 docker exec $CONTAINER_NAME rm -rf /tmp/dump_out # 4. 压缩 (时序数据通常很大,建议压缩) cd $BACKUP_ROOT tar -czf $DATE.tar.gz $DATE rm -rf $DATE # 5. 清理超过 30 天的备份 find $BACKUP_ROOT -name "*.tar.gz" -mtime +30 -delete echo "Backup completed: $TARGET_DIR" ``` **加入宿主机 Crontab:** ```bash # 每天凌晨 2 点执行 0 2 * * * /bin/bash /opt/ems-platform/scripts/backup_td.sh >> /var/log/ems_backup.log 2>&1 ``` --- ### 四、 灾难恢复演练 (Disaster Recovery) 假设服务器硬盘损坏,或者有人误删库,如何通过备份文件恢复? #### 1. 恢复 PostgreSQL ```bash # 1. 停止后端服务,防止写入 docker stop ems-server # 2. 获取备份文件 (例如昨天的备份) BACKUP_FILE="./backups/postgres/ems_db-20231027.sql.gz" # 3. 强行恢复 (Drop 现有库并重建) gunzip -c $BACKUP_FILE | docker exec -i ems-postgres psql -U ems_user -d ems_db ``` #### 2. 恢复 TDengine ```bash # 1. 解压备份 tar -xzf 20231027_0200.tar.gz cd 20231027_0200 # 2. 拷贝进容器 docker cp . ems-tdengine:/tmp/restore_data # 3. 执行恢复 docker exec ems-tdengine taosdump -i /tmp/restore_data ``` --- ### 五、 完整的 Docker Compose 配置 这是集成了上述所有理念的最终配置。 ```yaml version: '3.8' services: # ---------------------------------------- # 业务服务 # ---------------------------------------- app-server: image: my-ems/server:latest restart: always env_file: .env volumes: - ./configs/app.yaml:/app/config.yaml - ./logs:/app/logs depends_on: - postgres - tdengine - redis nginx: image: nginx:alpine ports: ["80:80", "443:443"] volumes: - ./configs/nginx:/etc/nginx/conf.d - ./html:/usr/share/nginx/html - ./certs:/etc/nginx/certs # ---------------------------------------- # 数据服务 (带持久化) # ---------------------------------------- postgres: image: postgres:15 container_name: ems-postgres restart: always environment: POSTGRES_DB: ems_db POSTGRES_USER: ems_user POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - ./data/postgres:/var/lib/postgresql/data # 数据持久化 - /etc/localtime:/etc/localtime:ro tdengine: image: tdengine/tdengine:3.0 container_name: ems-tdengine ports: ["6030:6030"] volumes: - ./data/taos/data:/var/lib/taos # 数据持久化 - ./data/taos/log:/var/log/taos - /etc/localtime:/etc/localtime:ro redis: image: redis:7 volumes: - ./data/redis:/data # 数据持久化 # ---------------------------------------- # 运维服务 (Sidecar) # ---------------------------------------- pg-backup: image: prodrigestivill/postgres-backup-local restart: always environment: - POSTGRES_HOST=postgres - POSTGRES_DB=ems_db - POSTGRES_USER=ems_user - POSTGRES_PASSWORD=${DB_PASSWORD} - SCHEDULE=@daily - BACKUP_KEEP_DAYS=7 - BACKUP_DIR=/backups volumes: - ./backups/postgres:/backups depends_on: - postgres ``` ### 六、 日常运维命令手册 (Cheat Sheet) 1. **发布新版本(含数据库变更):** ```bash # 这一步会自动拉取新镜像,重建容器,启动时 Go 程序会自动执行 SQL 迁移 docker-compose up -d --build app-server ``` 2. **查看实时日志:** ```bash docker-compose logs -f --tail=100 app-server ``` 3. **检查数据库状态:** ```bash # 进 PG docker exec -it ems-postgres psql -U ems_user -d ems_db # 进 TDengine docker exec -it ems-tdengine taos ```