Java跨Docker容器备份数据库数据
前置背景
- 在我们的开发部署场景中,通常多数使用Docker进行部署。当你的数据库和项目都使用Docker进行部署,此时我想要通过Java程序进行数据备份,那么就无法实现,因为是两个相互独立的容器。
- 在本篇文章中,我提供我的解决方法仅供参考。
思路整理
- 因为你的两个Docker容器是相互独立的,你的Java容器要操作MySQL,所以你的Java容器要具备可以执行MySQL命令的能力
- 但是,MySQL和Java是隔离的,无法直接使用MySQL命令备份,所以就想到使用docker的exec命令去操作MySQL容器让他备份。
- 备份完事以后,备份的文件还在MySQL容器中,宿主机也看不到想要的备份的SQL脚本,所以MySQL容器要挂载数据卷,把备份的SQL脚本备份出来。
编写备份脚本
本脚本我是从别人那里拿的,根据自己的需求修改即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #!/bin/bash #备份路径 BACKUP=/backups/mysql #当前时间 DATETIME=$(date +%Y-%m-%d) echo "===== 备份开始 =====" #数据库名称 DATABASE=你的数据库名 #数据库地址 HOST=数据库地址 #数据库用户名 DB_USER=用户名 #数据库密码 DB_PW=密码 #创建备份目录 [ ! -d "${BACKUP}/$DATABASE" ] && mkdir -p "${BACKUP}/$DATABASE" echo "备份文件存放于${BACKUP}/$DATABASE/$DATABASE-$DATETIME.sql" #开始备份 mysqldump -h ${HOST} -u${DB_USER} -p${DB_PW} ${DATABASE} > ${BACKUP}/$DATABASE/$DATABASE-$DATETIME.sql echo "===== 导出成功,开始传输 =====" #压缩成tar.gz包 cd $BACKUP tar -zcvf $DATABASE.tar.gz $DATABASE #备份到服务器B #scp $DATABASE-$DATETIME.sql root@ip:/home/mysqlBackup #删除备份目录 如果取消注释此命令 会删除sql脚本文件 只保留打包完成后的压缩包 # rm -rf ${BACKUP}/$DATABASE/$DATETIME #删除10天(不含)前备份的数据,这边可以自行更改 find $BACKUP -mtime +10 -name "*.tar.gz" -exec rm -rf {} \; echo "===== 数据库备份到服务器成功 ====="
|
容器启动
这里如果你的容器已经启动了也没关系,直接跑一遍docker命令即可。
Java:
这里最主要的是数据卷的绑定,因为你的Java容器需要可以使用docker命令,所以你得把docker挂载进去。其他的根据自己的需求修改就行。
1 2 3 4 5 6 7
| docker run -d \ -v $JOB_NAME-data:/tmp \ --net=host \ -e PARAMS="--spring.profiles.active=prod" \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/bin/docker:/usr/bin/docker \ --name $JOB_NAME $JOB_NAME
|
MySQL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| version: '3'
services: mysql: image: mysql:5.7 container_name: mysql volumes: - mysql-conf:/etc/mysql/conf.d - mysql-data:/var/lib/mysql - /export/shell/mysql:/home/shell/mysql - /export/backups/mysql/57:/backups/mysql - /etc/localtime:/etc/localtime:ro environment: - MYSQL_ROOT_PASSWORD=自行设置你的数据库密码 ports: - "3306:3306" mem_limit: 512m
volumes: mysql-conf: mysql-data:
|
检验效果
Java容器
- 先进入到你的Java容器中:
1
| docker exec -it Java容器名 bash
|
- 进来以后可以直接使用下面的命令,查看是否挂载Docker成功:
- 出现版本号即为成功

- 如果你出现了
权限不足
的问题,你需要退回到你的宿主机内,给docker.sock
进行权限修改,没有的话直接省略此步骤即可:
1
| chmod -R 777 /var/run/docker.sock
|
- 在
Java容器
中查看你的MySQL版本,如果正常出现版本号基本就没什么问题了:
1 2
| # 注意这里没有 -it 参数 docker exec mysql mysql -V
|

MySQL容器
- 进入MySQL容器:
1
| docker exec -it mysql bash
|
- 找到你自己设置的挂载备份脚本的路径,查看脚本是否挂载成功:

- 你可以直接执行下你的脚本,看下效果

- 可以到你的备份目录查看下备份的效果,这里有其他的是因为我的程序设置的每七天自动备份一次:

- 到你的宿主机挂载的脚本备份路径查看:


- 这里还是有一个可能存在的坑:
如果你发现你备份的数据SQL脚本,分明是18号备份的但是SQL的脚本文件的名称上却是17号,这是因为:你的数据库备份脚本中,有一个参数是获取当前系统的时间,那么就说明你的MySQL容器中的时间跟宿主机的时间不一致,运行容器时没有对时区进行挂载数据卷。

Java代码执行备份
代码执行这里我是使用了xxl-job
定时任务去做,每七天备份一次,其他的实现方式可以根据你们自己的需求去进行更改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @XxlJob("mysqlBackup") public void mysqlBackup(){ Runtime runtime = Runtime.getRuntime(); String command = "docker exec mysql sh /home/shell/mysql/course_compete.sh"; BufferedReader bufferedReader = null; try { Process exec = runtime.exec(command); bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream())); String line; while ((line = bufferedReader.readLine()) != null){ log.info("获取到的行数据:{}", line); } } catch (IOException e) { log.error("竞赛模块数据库备份异常"); throw new RuntimeException(e); }finally { if(bufferedReader != null){ try { bufferedReader.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
|