一. 基础知识

在 Docker 中,推送镜像到 Docker Hub 是一个常见操作,但需要注意的是,Docker 镜像本身设计用来包含应用程序和其依赖的环境,而不是用来包含数据。数据通常应该通过卷(Volumes)或绑定挂载(Bind Mounts)来管理,以保持容器的无状态性。这样做的好处是可以保证容器的可移植性和灵活性。

然而,如果确实需要将数据一起与镜像打包,比如在某些特定的情况下(例如,为了方便快速部署或测试),可以通过在构建镜像时将数据复制到镜像中来实现。这里是推送包含数据的 Docker 镜像到 Docker Hub 的步骤:

1.1 准备 Dockerfile

需要一个 Dockerfile 来定义如何构建包含数据的 Docker 镜像。例如:

# 使用官方的 MySQL 镜像作为基础镜像
FROM mysql:5.7

# 将数据目录复制到镜像中
COPY ./data /var/lib/mysql/data

1.2 构建镜像

使用下面的命令在包含 Dockerfile 的目录中构建新的 Docker 镜像:

docker build -t yourusername/myimage:tag

这里,yourusername/myimage:tag 应该替换为希望用来在 Docker Hub 上标识您的镜像的用户名、镜像名称和标签。例如,如果 Docker Hub 用户名是 john_doe,可能想要将镜像命名为 mysql_with_data,并给这个特定构建添加一个标签如 latest:

docker build -t john_doe/mysql_with_data:latest .

1.3 登录到 Docker Hub

在推送镜像之前,需要使用 Docker Hub 凭据登录:

docker login

1.4 推送镜像到 Docker Hub

现在可以将构建好的镜像推送到 Docker Hub:

docker push john_doe/mysql_with_data:latest

这条命令会将镜像上传到 Docker Hub,使得其他人或其他系统可以下载和使用你的镜像。

1.5 验证镜像

完成推送后,可以在 Docker Hub 的网站上查看镜像,确保它已经正确上传。

1.6 注意事项

  1. 数据持久性与安全性:将数据打包进 Docker 镜像并不是管理数据的最佳实践,尤其是对于生产环境。这种做法可能会导致数据安全问题,且不利于数据的更新和管理。建议只在特定的情况下,如测试、演示或非常特定的部署需求中使用。
  2. 镜像大小:将数据包含在镜像中可能会显著增加镜像的大小,这会影响推送和拉取镜像的时间,同时也会增加存储成本。
  3. 数据更新:如果数据有更新,需要重新构建和推送整个镜像。这可能会导致版本控制和回滚操作复杂化。

二. 具体案例

如我要将我服务器上的3个容器和数据,定时上传到 Docker Hub 中作为备份的话,要怎么做呢?

这里容器分别是 nginx、mysql、wordpress,对原有 Docker Hub 上的镜像重新打标记,后缀 latest 改为 当前日期,并上传最新的镜像。

2.1 目录结构

├── log
│   └── wordpress
│       └── logfile_20240424.log
├── upload_worpress_images.sh
└── wordpress
    ├── wp_mysql
    │   ├── Dockerfile
    │   └── run_script.sh
    ├── wp_nginx
    │   ├── Dockerfile
    │   └── run_script.sh
    └── wp_wordpress
        ├── Dockerfile
        └── run_script.sh

其中,log 是记录定时日志的目录;
upload_worpress_images.sh 是上传镜像文件到 Docker Hub 的脚本;
wordpress 是存储对应容器数据、Dockerfile 和生成镜像脚本的目录。

2.2 脚本参考

2.2.1 upload_worpress_images.sh


#!/bin/bash

# 配置变量
USERNAME="bluebro"
REPOSITORY="blue_repository"
DATE_TAG=$(date +%Y%m%d)

# 定义要处理的容器和对应的镜像名
declare -A containers
containers=(["wp_nginx"]="wp_nginx" ["wp_mysql"]="wp_mysql" ["wp_wordpress"]="wp_wordpress")
# containers=(["wp_mysql"]="wp_mysql")

# 登录 Docker Hub
echo "===> Logging into Docker Hub..."
docker login # 确保事先配置好了 Docker 的认证信息,或在这里提供用户名和密码

# 循环遍历容器,处理镜像
for container in "${!containers[@]}"; do
    image_base="$USERNAME/$REPOSITORY:${containers[$container]}"
    image_now="${image_base}-now"
    image_latest="${image_base}-latest"
    image_date_tag="${image_base}-$DATE_TAG"

    echo "===> Processing container: $container..."

    # 获取本地 now 镜像ID
    now_image_id=$(docker images -q $image_now)

    # 检查是否获取到本地 now 镜像ID, 有则删除
    if [ ! -z "$now_image_id" ]; then
        echo "===> Removing local image: $image_now"
        echo "===> docker rmi $image_now"
        docker rmi $image_now
    else
        echo "===> No local image to remove: $image_now"
    fi

    #

    # 获取本地 latest 镜像ID
    latest_image_id=$(docker images -q $image_latest)

    # 检查是否获取到本地 latest 镜像ID, 有则删除
    if [ ! -z "$latest_image_id" ]; then
        echo "===> Removing local image: $image_latest"
        echo "===> docker rmi $image_latest"
        docker rmi $image_latest
    else
        echo "===> No local image to remove: $image_latest"
    fi

    # 从 Docker Hub 拉取最新的镜像,重新标记 tag 并上传到
    echo "===> docker pull $image_latest"
    if docker pull $image_latest; then
        echo "===> Retagging $image_latest to $image_date_tag..."

        echo "===> docker tag $image_latest $image_date_tag"
        docker tag $image_latest $image_date_tag

        echo "===> docker push $image_date_tag"
        docker push $image_date_tag

        echo "===> docker rmi $image_latest $image_date_tag"
        docker rmi $image_latest $image_date_tag # 清理拉取和重标记的镜像
    fi

    # 提交容器为最新镜像并推送
    echo "===> Committing container $container to $image_latest"

    # 构建基础镜像
    echo "===> docker commit $container $image_now"
    docker commit $container $image_now

    # 调用自定义生成的镜像脚本
    echo "===> sh /opt/cmd/wordpress/$container/run_script.sh"
    sh /opt/cmd/wordpress/$container/run_script.sh

    # 将镜像推送到 docker hub
    echo "===> docker push $image_latest"
    docker push $image_latest

done

echo "===> Images processing complete."

2.2.2 wp_mysql

2.2.2.1 Dockerfile

# 使用 mysql 的基础镜像
FROM bluebro/blue_repository:wp_mysql-now

# 将你的数据库数据复制到镜像中
# 一定是Mysql数据卷的路径!!!
COPY mysql_data/wp_mysql/ /var/lib/mysql

2.2.2.2 run_script.sh

#!/bin/bash

DIR='/opt/cmd/wordpress/wp_mysql'
mkdir -p $DIR

# 进入目标目录
echo "===> cd $DIR"
cd $DIR

# 检查目录是否存在,如果存在则删除
if [ -d "mysql_data" ]; then
    echo "===> mysql_data directory exists. Removing..."
    echo "===> rm -rf ./mysql_data"
    rm -rf ./mysql_data
fi

# 创建新的目录
echo "===> mkdir mysql_data"
mkdir mysql_data

# 将原始挂载卷文件复制到当前目录下
echo "===> cp -r /data/wordpress/wp_mysql ./mysql_data"
cp -r /data/wordpress/wp_mysql/ ./mysql_data

# 通过 Dockerfile 构建新镜像
echo "===> docker build -t bluebro/blue_repository:wp_mysql-latest ."
docker build -t bluebro/blue_repository:wp_mysql-latest .

2.2.3 wp_nginx

2.2.3.1 Dockerfile

# 使用 nginx 的基础镜像
FROM bluebro/blue_repository:wp_nginx-now

# 将你的数据复制到镜像中
# 一定是 nginx 数据卷的路径!!!
COPY nginx_data/wp_nginx/confs/nginx.conf /etc/nginx/nginx.conf
COPY nginx_data/wp_nginx/htmls/           /usr/share/nginx/html
COPY nginx_data/wp_nginx/logs/            /var/log/nginx
COPY nginx_data/wp_nginx/certs/           /etc/nginx/certs

2.2.3.2 run_script.sh

#!/bin/bash

DIR='/opt/cmd/wordpress/wp_nginx'
mkdir -p $DIR
# 进入目标目录
echo "===> cd $DIR"
cd $DIR

# 检查目录是否存在,如果存在则删除
if [ -d "nginx_data" ]; then
    echo "===> nginx_data directory exists. Removing..."
    echo "===> rm -rf ./nginx_data"
    rm -rf ./nginx_data
fi

# 创建新的目录
echo "===> mkdir nginx_data"
mkdir nginx_data

# 将原始挂载卷文件复制到当前目录下
echo "===> cp -r /data/wordpress/wp_nginx ./nginx_data"
cp -r /data/wordpress/wp_nginx/ ./nginx_data

# 通过 Dockerfile 构建新镜像
echo "===> docker build -t bluebro/blue_repository:wp_nginx-latest ."
docker build -t bluebro/blue_repository:wp_nginx-latest .

2.2.4 wp_wordpress

2.2.4.1 Dockerfile

# 使用 wordpress 的基础镜像
FROM bluebro/blue_repository:wp_wordpress-now

# 将你的数据复制到镜像中
# 一定是 wordpress 数据文件的路径!!!
COPY wordpress_data/wp_wordpress/ /var/www/html

2.2.4.2 run_script.sh

#!/bin/bash

DIR='/opt/cmd/wordpress/wp_wordpress'
mkdir -p $DIR
# 进入目标目录
echo "===> cd $DIR"
cd $DIR

# 检查目录是否存在,如果存在则删除
if [ -d "wordpress_data" ]; then
    echo "===> wordpress_data directory exists. Removing..."
    echo "===> rm -rf ./wordpress_data"
    rm -rf ./wordpress_data
fi

# 创建新的目录
echo "===> mkdir wordpress_data"
mkdir wordpress_data

# 将原始挂载卷文件复制到当前目录下
echo "===> cp -r /data/wordpress/wp_wordpress ./wordpress_data"
cp -r /data/wordpress/wp_wordpress/ ./wordpress_data

# 通过 Dockerfile 构建新镜像
echo "===> docker build -t bluebro/blue_repository:wp_wordpress-latest ."
docker build -t bluebro/blue_repository:wp_wordpress-latest .

2.3 验证 Docker Hub

三. 使用 crontab 定时任务脚本

这里不过多介绍用法。

0 1 * * 1 bash /opt/cmd/upload_worpress_images.sh \
> /opt/cmd/log/wordpress/logfile_$(date "+\%Y\%m\%d").log 2>&1