Java跨Docker容器备份数据库数据
- 前置背景
- 思路整理
- 编写备份脚本
- 容器启动
- 检验效果
- Java容器
- MySQL容器
- Java代码执行备份
我的个人博客:Lichg,欢迎大家访问。
前置背景
- 在我们的开发部署场景中,通常多数使用Docker进行部署。当你的数据库和项目都使用Docker进行部署,此时我想要通过Java程序进行数据备份,那么就无法实现,因为是两个相互独立的容器。
- 在本篇文章中,我提供我的解决方法仅供参考。
思路整理
- 因为你的两个Docker容器是相互独立的,你的Java容器要操作MySQL,所以你的Java容器要具备可以执行MySQL命令的能力
- 但是,MySQL和Java是隔离的,无法直接使用MySQL命令备份,所以就想到使用docker的exec命令去操作MySQL容器让他备份。
- 备份完事以后,备份的文件还在MySQL容器中,宿主机也看不到想要的备份的SQL脚本,所以MySQL容器要挂载数据卷,把备份的SQL脚本备份出来。
编写备份脚本
本脚本我是从别人那里拿的,根据自己的需求修改即可
#!/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挂载进去。其他的根据自己的需求修改就行。
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:
version: '3'
services:
mysql:
image: mysql:5.7
container_name: mysql
volumes:
- mysql-conf:/etc/mysql/conf.d
- mysql-data:/var/lib/mysql
# 数据库备份脚本存储路径,映射进去:这里是把你准备的备份脚本从宿主机映射到MySQL容器中,让他可以执行
- /export/shell/mysql:/home/shell/mysql
# 数据库的备份文件挂在地址:这里是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容器中:
docker exec -it Java容器名 bash
- 进来以后可以直接使用下面的命令,查看是否挂载Docker成功:
docker -v
- 出现版本号即为成功
- 如果你出现了
权限不足
的问题,你需要退回到你的宿主机内,给docker.sock
进行权限修改,没有的话直接省略此步骤即可:
chmod -R 777 /var/run/docker.sock
- 在
Java容器
中查看你的MySQL版本,如果正常出现版本号基本就没什么问题了:
# 注意这里没有 -it 参数
docker exec mysql mysql -V
MySQL容器
- 进入MySQL容器:
docker exec -it mysql bash
- 找到你自己设置的挂载备份脚本的路径,查看脚本是否挂载成功:
- 你可以直接执行下你的脚本,看下效果
sh 你的脚本名称
- 可以到你的备份目录查看下备份的效果,这里有其他的是因为我的程序设置的每七天自动备份一次:
- 到你的宿主机挂载的脚本备份路径查看:
- 这里还是有一个可能存在的坑:
如果你发现你备份的数据SQL脚本,分明是18号备份的但是SQL的脚本文件的名称上却是17号,这是因为:你的数据库备份脚本中,有一个参数是获取当前系统的时间,那么就说明你的MySQL容器中的时间跟宿主机的时间不一致,运行容器时没有对时区进行挂载数据卷。
Java代码执行备份
代码执行这里我是使用了xxl-job
定时任务去做,每七天备份一次,其他的实现方式可以根据你们自己的需求去进行更改。
@XxlJob("mysqlBackup")
public void mysqlBackup(){
//获取Runtime实例
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;
//此时就可以对获取的结果in进行操作了,可以使用in.readline()逐步获取每一行的结果内容
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);
}
}
}
}