Java跨Docker容器备份数据库数据

Java跨Docker容器备份数据库数据

    • 前置背景
    • 思路整理
    • 编写备份脚本
    • 容器启动
    • 检验效果
      • Java容器
      • MySQL容器
    • Java代码执行备份

我的个人博客:Lichg,欢迎大家访问。

前置背景

  • 在我们的开发部署场景中,通常多数使用Docker进行部署。当你的数据库和项目都使用Docker进行部署,此时我想要通过Java程序进行数据备份,那么就无法实现,因为是两个相互独立的容器。
  • 在本篇文章中,我提供我的解决方法仅供参考。

思路整理

  1. 因为你的两个Docker容器是相互独立的,你的Java容器要操作MySQL,所以你的Java容器要具备可以执行MySQL命令的能力
  2. 但是,MySQL和Java是隔离的,无法直接使用MySQL命令备份,所以就想到使用docker的exec命令去操作MySQL容器让他备份。
  3. 备份完事以后,备份的文件还在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容器

  1. 先进入到你的Java容器中:
docker exec -it Java容器名 bash
  1. 进来以后可以直接使用下面的命令,查看是否挂载Docker成功:
docker -v
  1. 出现版本号即为成功

image-20240527104710429

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

image-20240527111754983

MySQL容器

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

image-20240527112516583

  1. 你可以直接执行下你的脚本,看下效果
sh 你的脚本名称

image-20240527113328530

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

image-20240527113507659

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

image-20240527113846237

image-20240527113918053

  1. 这里还是有一个可能存在的坑:

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

image-20240527114446397

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);
                }
            }
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/651733.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu22.04之扩展并挂载4T硬盘(二百三十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

Java | Leetcode Java题解之第115题不同的子序列

题目&#xff1a; 题解&#xff1a; class Solution {public int numDistinct(String s, String t) {int m s.length(), n t.length();if (m < n) {return 0;}int[][] dp new int[m 1][n 1];for (int i 0; i < m; i) {dp[i][n] 1;}for (int i m - 1; i > 0; …

文刻创作ai工具官网免费工具

文刻创作ai工具官网免费工具 Docshttps://iimenvrieak.feishu.cn/docx/O0UedptjbonN4UxyEy7cPlZknYc 文刻是一种可以帮助用户进行创作的AI工具。 它使用自然语言处理和机器学习技术&#xff0c;可以生成文章、故事、诗歌等文本内容。 用户可以通过输入一些关键词或指定一定的…

MobaXterm连接eNSP设备

1、开启一台交换机 2、右键设置查看交换机串口号&#xff08;2000&#xff09; 3、打开MBX&#xff0c;点击session。 4、配置MBX 5、右键点击 6、配置为force off&#xff0c;点击回车就可以看到效果了

Golang | Leetcode Golang题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; func connect(root *Node) *Node {if root nil {return root}// 每次循环从该层的最左侧节点开始for leftmost : root; leftmost.Left ! nil; leftmost leftmost.Left {// 通过 Next 遍历这一层节点&#xff0c;为下一层的节点更新 Next …

损失函数篇 | YOLOv8更换损失函数之Inner-IoU | 通过辅助边界框计算IoU损失

前言:Hello大家好,我是小哥谈。损失函数是机器学习中用来衡量模型预测值与真实值之间差异的函数。在训练模型时,我们希望通过不断调整模型参数,使得损失函数的值最小化,从而使得模型的预测值更加接近真实值。为弥补现有IoU损失函数在不同的检测任务中的泛化能力较弱且收敛…

HTTPS加密过程

今天我们说https具体工作原理。 HTTPS概念 HTTPS是一种网络协议&#xff0c;传统的HTTP是明文传输&#xff0c;非常 不安全&#xff0c;所以HTTPS是基于HTTP基础上进行加密传输内容。 HTTPS使用加密传输方式 第一种是非对称加密&#xff0c;是前期建立连接时候使用的数据加密…

Golang | Leetcode Golang题解之第115题不同的子序列

题目&#xff1a; 题解&#xff1a; func numDistinct(s, t string) int {m, n : len(s), len(t)if m < n {return 0}dp : make([][]int, m1)for i : range dp {dp[i] make([]int, n1)dp[i][n] 1}for i : m - 1; i > 0; i-- {for j : n - 1; j > 0; j-- {if s[i] …

R实验 正交试验设计与一元线性回归分析

实验目的&#xff1a; 掌握正交试验设计记号的意义&#xff1b;掌握正交试验设计的直观分析和方差分析&#xff1b;掌握一元线性回归模型的相关概念&#xff1b;掌握最小二乘法的思想&#xff1b;掌握一元线性回归方程的显著性检验和预测。 实验内容&#xff1a; &#xff11;…

Python | Leetcode Python题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; class Solution:def connect(self, root: Node) -> Node:if not root:return root# 从根节点开始leftmost rootwhile leftmost.left:# 遍历这一层节点组织成的链表&#xff0c;为下一层的节点更新 next 指针head leftmostwhile head:#…

mumu 模拟器安装

1.下载安装 下载地址 Win 历史版本&#xff1a;http://mumu.163.com/update/win/Mac 历史 版本&#xff1a;http://mumu.163.com/20200515/25905_880858.html 2.设置为竖屏 在设置中心--界面设置页面设置宽720&#xff0c;高1280&#xff0c;DPI为240&#xff0c;如下图所示。…

Go语言之GORM框架(三)——Hook(钩子)与Gorm的高级查询

Hook(钩子) 和我们在gin框架中讲解的Hook函数一样&#xff0c;我们也可以在定义Hook结构体&#xff0c;完成一些操作&#xff0c;相关接口声明如下&#xff1a; type CreateUser interface { //创建对象时使用的HookBeforeCreate() errorBeforeSave() errorAfterCreate() …

C++习题(1)

一、题目描述&#xff1a; 二、代码展示&#xff1a; #include <iostream> #include <iomanip> using namespace std; struct Student{char name[20];int id;int age;float score; }; int main() {int n;cin>>n;Student student[n];float sum0.0;for(int i0…

小易大数据:大数据报告查询领域的黑马,这些优势让你无法忽视!

随着大数据技术被运用到各行各业&#xff0c;风控领域也不例外&#xff0c;形成了基于大数据技术的大数据信用&#xff0c;也就是我们常说的大数据报告或者网贷大数据&#xff0c;在众多的查询平台中&#xff0c;小易大数据平台在市面上是比较受欢迎的&#xff0c;那在小易平台…

JAVASE2

封装的步骤&#xff1a; 1、所有属性私有化&#xff0c;使用private关键字进行修饰&#xff0c;private表示私有的&#xff0c;修饰的所有数据只能在本类中访问 2、对外提供简单入口&#xff1a;比如说被private修饰的成员变量&#xff0c;在其他类中只能通过getXxx/setXxx方法…

Linux之多进程

c程序获取进程pid和ppid 在 Linux 系统中管理进程使用树型管理方式每个进程都需要与其他某一个进程建立 父子关系, 对应的进程则叫做 父进程 Linux 系统会为每个进程分配 id , 这个 id 作为当前进程的唯一标识, 当进程结束, 则会回收 进程的 id 与 父进程的 id 分别通过 getpi…

马斯克:AI时代人人高收入,不需要工作,商品服务不再短缺,可能性80%

当前人工智能现状和未来如何&#xff1f;AI时代下&#xff0c;人类未来会发生哪些变化&#xff1f; 埃隆马斯克&#xff08;Elon Musk&#xff09;在2024 VivaTech大会上分享了关于地球未来的诸多愿景。 投资作业本课代表摘录了其中的要点&#xff0c;分享给大家&#xff1a…

c语言基础:数组的运用以及在内存中的地址的理解

目录 目录&#xff1a; 1.数组作为函数参数 2.数组在内存中的存储 2.1数组名是什么&#xff1f; 2.2下面我们来探讨二维数组的各个名字表示什么 二维数组的首元素地址是什么呢&#xff1f; *arr表示的是什么呢 &#xff1f;&#xff08;arr是二维数组&#xff09; 1.数组作…

C语言 | Leetcode C语言题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; struct Node* connect(struct Node* root) {if (root NULL) {return root;}// 从根节点开始struct Node* leftmost root;while (leftmost->left ! NULL) {// 遍历这一层节点组织成的链表&#xff0c;为下一层的节点更新 next 指针stru…

echarts学习篇

一、使用echarts 1.引入 Apache ECharts <!DOCTYPE html> <html> <head> <meta charset"utf-8" /> <!-- 引入刚刚下载的 ECharts 文件 --> <script src"echarts.js"></script> </head> </html> 2.…