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.sqlecho "===== 导出成功,开始传输 ====="#压缩成tar.gz包cd $BACKUPtar -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.7container_name: mysqlvolumes:- 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:roenvironment:- MYSQL_ROOT_PASSWORD=自行设置你的数据库密码ports:- "3306:3306"mem_limit: 512mvolumes: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);}}}}