近来有一个需求,需要备份数据库以供审计。思路很简单,直接写个sh脚本并用Java程序调用即可,说干就干

1、backup.sh脚本

#!/bin/bash
host="127.0.0.1"
port="3306"
user="user"
password="password"
# 备份数据库名
db="dbName"
# 仅仅备份结构的表,多表使用|隔开
noDataTable="table"
# 备份文件名
backupFile=/var/lib/mysql-backup/${db}-$(date +%Y%m%d%H%M%S).sql

echo "开始备份数据库:${db}"

# 备份表结构与数据,-N是去掉标题头
tables=$(mysql -N -h${host} -P${port} -u${user} -p${password} -e "show tables from ${db}" | grep -Ev ${noDataTable})
mysqldump -h${host} -P${port} -u${user} -p${password} ${db} ${tables} >> ${backupFile}

# 仅备份表结构
mysqldump -d -h${host} -P${port} -u${user} -p${password} ${db} ${noDataTable} >> ${backupFile}
echo "数据库备份完成"

接着问题就来了,使用mysql是使用docker部署的,那么如何在宿主机执行mysql容器的脚本呢?很简单,思路是将脚本目录挂载到mysql容器中,然后使用docker exec命令执行内部的备份脚本。思路有了,继续往下执行

2、挂载

在这里插入图片描述
上边是我的docker-compose配置,框起来就是重点。贴出文字版

services:
  mysql:
    image: mysql:5.7.34
    restart: always
    container_name: mysql
    #容器内赋予root权限
    privileged: true
    ports:
      - 13306:3306
    environment:
      MYSQL_ROOT_PASSWORD: password
      TZ: Asia/Shanghai
    command:
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --lower_case_table_names=1
      --max_allowed_packet=128M
      --innodb_strict_mode=0
      --sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
    volumes:
      - ./mysql/conf:/etc/mysql
      - ./mysql/data:/var/lib/mysql
      - ./mysql/logs:/var/log/mysql
      - ./mysql/backup:/var/lib/mysql-backup

./mysql/backup是我宿主机上备份脚本backup.sh的目录,将其挂载到mysql容器内部的/var/lib/mysql-backup中,然后就能通过命令去运行了
docker exec mysql /var/lib/mysql-backup/backup.sh
然后发现还有问题,Java应用也是通过Docker部署的,那么如何跨容器去调用脚本呢?我先把备份脚本目录挂载进Java容器中,这样,Java容器与mysql容器共同挂载了./mysql/backup目录,为了方便,我们将容器内部目录统一设置为/var/lib/mysql-backup。这样,我们在Java容器中也能访问到备份脚本backup.sh了,下面我们进入java容器检验一下,docker exec -it youth sh,进入脚本目录cd /var/lib/mysql-backup在这里插入图片描述
发现已经脚本存在了,那么我们执行一下sh /var/lib/mysql-backup/backup.sh
在这里插入图片描述
找不到mysql及mysqldump命令?原来这个是Java容器,里边根本没有安装Mysql,那么这条路就行不通了,我们可以换一种新的思路,也就是在Java容器中跨容器调用Mysql的脚本。思路有了,接着开干。

1、进入Java容器

docker exec -it youth sh

2、通过Docker执行Mysql备份脚本

docker exec mysql /var/lib/mysql-backup/backup.sh

在这里插入图片描述

3、没有docker指令

所以我们需要在Java容器内部挂载宿主机的docker,贴出构建镜像的dockerfile

FROM openjdk:8-jdk-alpine
MAINTAINER SYH
ENV PARAMS="-Xms64m -Xmx128m"
ENV TIME_ZONE=PRC
#时区定义
RUN ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone

RUN mkdir -p /data/logs

ARG JAR_FILE

COPY target/${JAR_FILE} /app.jar

ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app.jar $PARAMS"]

4、挂载docker.sock文件

具体解析查看大佬的文章docker.sock详解,贴出docker-compose文件

youth:
    image: 127.0.0.1:5000/youth_dev:latest
    restart: always
    container_name: youth
    ports:
      - 8080:8080
    volumes:
      - /etc/localtime:/etc/localtime:ro
      #在容器内部可以使用docker
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /usr/bin/docker:/usr/bin/docker:ro
      - /etc/docker/daemon.json:/etc/docker/daemon.json:ro
    logging:
       options:
         max-file: "2"
         max-size: "1024m"

5、重启Java容器

这里需要注意第3步重新构建完镜像后,记得删除本地的镜像,我是使用私人docker仓库的,踩了一个坑,若是镜像标签一致,且本地已有镜像的时候,不会再去从私人仓库拉取镜像,而是继续采用本地的镜像,这就造成镜像还是老的版本,或者改下镜像标签的话也可以,这样他就每次都从私人仓库拉取新镜像了。启动Java容器后,进入容器内部,使用docker -v指令,确保容器内部已有docker
在这里插入图片描述

6、看看能否查询到宿主机的容器

docker ps

在这里插入图片描述

7、不忘初心,执行我们的数据库备份脚本

docker exec mysql /var/lib/mysql-backup/backup.sh

在这里插入图片描述

8、大功告成

此时只需要Java代码中实现执行docker exec mysql /var/lib/mysql-backup/backup.sh指令即可,下面贴出初步实现代码,该方法很不安全,只是在测试环境验证而已,注意生产环境上不能直接以参数的形式写入bash命令。

package com.syh.youth.controller;

import cn.hutool.core.util.RuntimeUtil;
import com.syh.youth.domain.Res;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author Lenovo
 */
@RestController
@RequestMapping("/shell")
public class ShellController {

    @GetMapping("/exec")
    public Res<String> exec(String command) {
        List<String> strings = RuntimeUtil.execForLines(command);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < strings.size(); i++) {
            sb.append(strings.get(i)).append(" ");
        }
        return Res.ok(sb.toString());
    }
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐