Docker 镜像拉取与导出脚本
功能概述
该脚本用于批量拉取 Docker 镜像并导出为 tar 文件,导出成功后自动删除本地镜像以释放磁盘空间。
主要特性
- ✅ 批量拉取 Docker 镜像
- ✅ 自动导出为 tar 文件
- ✅ 导出成功后自动删除本地镜像(释放磁盘空间)
- ✅ 彩色日志输出,清晰易读
- ✅ 详细的成功/失败统计
- ✅ 显示磁盘空间使用情况
前置要求
- 已安装 Docker
- 具有 Docker 操作权限
- 确保有足够的磁盘空间存储导出的 tar 文件
使用方法
1. 基本使用(使用默认配置)
./pull_and_save_images.sh
默认配置:
– 镜像列表文件:images.txt
– 保存目录:./images
2. 指定镜像列表文件
./pull_and_save_images.sh -f my_images.txt
3. 指定保存目录
./pull_and_save_images.sh -d /backup/docker_images
4. 同时指定文件和目录
./pull_and_save_images.sh -f my_images.txt -d /backup/docker_images
5. 查看帮助信息
./pull_and_save_images.sh -h
镜像列表文件格式
创建一个文本文件(如 images.txt),每行一个镜像地址:
# 这是注释行,会被忽略
reg-hub.gzeport.com/jsb/gzeport-sso/gzeport-sso-web:202603181336-23
reg-hub.gzeport.com/jsb/managemodule/gzeport-manage-web:202603181152-66
reg-hub.gzeport.com/jsb/managemodule/gzeport-manage-server:202603181152-66
# 空行会被忽略
reg-hub.gzeport.com/jsb/datamanage/gzeport-datamanage-web:202603181045-34
注意事项:
– 每行一个完整的镜像地址(包含仓库地址和标签)
– 以 # 开头的行为注释,会被忽略
– 空行会被自动跳过
命令行参数说明
| 参数 | 简写 | 说明 | 默认值 |
|---|---|---|---|
| –file | -f | 指定镜像列表文件 | images.txt |
| –dir | -d | 指定保存目录 | ./images |
| –help | -h | 显示帮助信息 | – |
工作流程
1. 读取镜像列表文件
↓
2. 拉取 Docker 镜像
↓
3. 导出为 tar 文件
↓
4. 自动删除本地镜像(释放磁盘空间)
↓
5. 显示统计信息和磁盘使用情况
输出文件命名规则
导出的 tar 文件命名格式:<镜像名>-<标签>.tar
示例:
– 镜像:reg-hub.gzeport.com/jsb/gzeport-sso/gzeport-sso-web:202603181336-23
– 文件名:gzeport-sso-web-202603181336-23.tar
日志输出示例
[INFO] 2026-03-18 13:45:20 - Docker已安装,版本: Docker version 24.0.7
[INFO] 2026-03-18 13:45:20 - 从文件读取镜像列表: images.txt
[INFO] 2026-03-18 13:45:20 - 开始处理 5 个镜像
================================
[INFO] 2026-03-18 13:45:21 - 开始拉取镜像: reg-hub.gzeport.com/jsb/gzeport-sso/gzeport-sso-web:202603181336-23
[INFO] 2026-03-18 13:45:35 - 镜像拉取成功: reg-hub.gzeport.com/jsb/gzeport-sso/gzeport-sso-web:202603181336-23
[INFO] 2026-03-18 13:45:35 - 开始保存镜像到: ./images/gzeport-sso-web-202603181336-23.tar
[INFO] 2026-03-18 13:45:50 - 镜像保存成功: ./images/gzeport-sso-web-202603181336-23.tar
[INFO] 2026-03-18 13:45:50 - 文件大小: 812M
[INFO] 2026-03-18 13:45:50 - 删除本地镜像以释放磁盘空间: reg-hub.gzeport.com/jsb/gzeport-sso/gzeport-sso-web:202603181336-23
[INFO] 2026-03-18 13:45:51 - 镜像删除成功: reg-hub.gzeport.com/jsb/gzeport-sso/gzeport-sso-web:202603181336-23
--------------------------------
================================
[INFO] 2026-03-18 14:09:03 - 镜像拉取成功: reg-hub.gzeport.com/jsb/gzeport-gzsw-server/gzeport-openapi-wechat:202603181401-78
[INFO] 2026-03-18 14:09:03 - 开始保存镜像到: ./images/gzeport-openapi-wechat-202603181401-78.tar
[INFO] 2026-03-18 14:09:08 - 镜像保存成功: ./images/gzeport-openapi-wechat-202603181401-78.tar
[INFO] 2026-03-18 14:09:08 - 文件大小: 567M
[INFO] 2026-03-18 14:09:08 - 删除本地镜像以释放磁盘空间: reg-hub.gzeport.com/jsb/gzeport-gzsw-server/gzeport-openapi-wechat:202603181401-78
[INFO] 2026-03-18 14:09:08 - 镜像删除成功: reg-hub.gzeport.com/jsb/gzeport-gzsw-server/gzeport-openapi-wechat:202603181401-78
--------------------------------
================================
[INFO] 2026-03-18 14:09:08 - 处理完成!
[INFO] 2026-03-18 14:09:08 - 总计: 5, 成功: 5, 失败: 0
[INFO] 2026-03-18 14:09:08 - 当前磁盘空间使用情况:
/dev/mapper/klas-root 91G 44G 47G 49% /
[INFO] 2026-03-18 14:09:08 - 已保存的镜像文件:
常见问题
1. 镜像删除失败怎么办?
如果镜像正在被容器使用,删除会失败并显示警告,但不影响整体流程。可以手动停止相关容器后再删除:
# 查看使用该镜像的容器
docker ps -a --filter ancestor=<镜像名>
# 停止并删除容器
docker stop <容器ID>
docker rm <容器ID>
# 删除镜像
docker rmi <镜像名>
2. 如何恢复导出的镜像?
使用 docker load 命令:
docker load -i ./images/gzeport-sso-web-202603181336-23.tar
3. 磁盘空间不足怎么办?
- 确保保存目录所在分区有足够空间
- 脚本会在导出后自动删除本地镜像,释放空间
- 可以分批处理镜像,避免同时处理过多
4. 如何验证导出的 tar 文件是否完整?
# 查看 tar 文件内容
tar -tvf ./images/gzeport-sso-web-202603181336-23.tar
# 或者尝试加载镜像
docker load -i ./images/gzeport-sso-web-202603181336-23.tar
示例场景
场景 1:备份生产环境镜像
# 1. 创建镜像列表
cat > prod_images.txt <<EOF
reg-hub.gzeport.com/jsb/gzeport-sso/gzeport-sso-web:202603181336-23
reg-hub.gzeport.com/jsb/managemodule/gzeport-manage-web:202603181152-66
EOF
# 2. 执行备份到指定目录
./pull_and_save_images.sh -f prod_images.txt -d /backup/prod_images
# 3. 查看备份结果
ls -lh /backup/prod_images/
场景 2:定时自动备份(Crontab)
# 编辑 crontab
crontab -e
# 添加定时任务(每天凌晨 2 点执行)
0 2 * * * cd /path/to/script && ./pull_and_save_images.sh > /var/log/docker_backup_$(date +\%Y\%m\%d).log 2>&1
下面是脚本
#!/bin/bash
# 镜像拉取并保存脚本
# 功能:批量拉取Docker镜像并保存为tar文件
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 无颜色
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
# 检查Docker是否安装
check_docker() {
if ! command -v docker &> /dev/null; then
log_error "Docker未安装,请先安装Docker"
exit 1
fi
log_info "Docker已安装,版本: $(docker --version)"
}
# 拉取并保存单个镜像
pull_and_save_image() {
local image=$1
local save_dir=$2
# 提取镜像名称(去除仓库地址和标签)
local image_name=$(echo "$image" | awk -F'/' '{print $NF}' | sed 's/:/-/g')
local tar_file="${save_dir}/${image_name}.tar"
log_info "开始拉取镜像: $image"
# 拉取镜像
if docker pull "$image"; then
log_info "镜像拉取成功: $image"
else
log_error "镜像拉取失败: $image"
return 1
fi
# 保存镜像为tar文件
log_info "开始保存镜像到: $tar_file"
if docker save -o "$tar_file" "$image"; then
log_info "镜像保存成功: $tar_file"
# 显示文件大小
local file_size=$(du -h "$tar_file" | awk '{print $1}')
log_info "文件大小: $file_size"
# 导出成功后删除本地镜像以释放磁盘空间
log_info "删除本地镜像以释放磁盘空间: $image"
if docker rmi "$image" > /dev/null 2>&1; then
log_info "镜像删除成功: $image"
else
log_warn "镜像删除失败(可能被其他容器使用): $image"
fi
else
log_error "镜像保存失败: $image"
return 1
fi
return 0
}
# 从文件读取镜像列表
read_images_from_file() {
local file=$1
local images=()
# 检查文件是否存在
if [ ! -f "$file" ]; then
log_error "镜像列表文件不存在: $file"
exit 1
fi
# 读取文件,过滤空行和注释行
while IFS= read -r line || [ -n "$line" ]; do
# 去除首尾空格
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
# 跳过空行和以#开头的注释行
if [ -n "$line" ] && [[ ! "$line" =~ ^# ]]; then
images+=("$line")
fi
done < "$file"
# 返回镜像数组
printf '%s\n' "${images[@]}"
}
# 主函数
main() {
# 检查Docker
check_docker
# 默认镜像列表文件
local image_list_file="images.txt"
# 保存目录(默认为当前目录下的images文件夹)
local save_dir="./images"
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-f|--file)
image_list_file="$2"
shift 2
;;
-d|--dir)
save_dir="$2"
shift 2
;;
-h|--help)
echo "使用方法: $0 [选项]"
echo "选项:"
echo " -f, --file <文件> 指定镜像列表文件 (默认: images.txt)"
echo " -d, --dir <目录> 指定保存目录 (默认: ./images)"
echo " -h, --help 显示帮助信息"
exit 0
;;
*)
log_error "未知参数: $1"
echo "使用 -h 或 --help 查看帮助信息"
exit 1
;;
esac
done
# 从文件读取镜像列表
log_info "从文件读取镜像列表: $image_list_file"
mapfile -t images < <(read_images_from_file "$image_list_file")
# 检查是否读取到镜像
if [ ${#images[@]} -eq 0 ]; then
log_error "镜像列表为空,请检查文件: $image_list_file"
exit 1
fi
# 创建保存目录
if [ ! -d "$save_dir" ]; then
mkdir -p "$save_dir"
log_info "创建保存目录: $save_dir"
fi
# 统计信息
local total=${#images[@]}
local success=0
local failed=0
log_info "开始处理 $total 个镜像"
echo "================================"
# 遍历镜像列表
for image in "${images[@]}"; do
if pull_and_save_image "$image" "$save_dir"; then
((success++))
else
((failed++))
fi
echo "--------------------------------"
done
# 输出统计结果
echo "================================"
log_info "处理完成!"
log_info "总计: $total, 成功: $success, 失败: $failed"
# 显示磁盘空间使用情况
log_info "当前磁盘空间使用情况:"
df -h | grep -E "Filesystem|/$"
# 列出保存的文件
if [ $success -gt 0 ]; then
log_info "已保存的镜像文件:"
ls -lh "$save_dir"/*.tar 2>/dev/null
fi
}
# 执行主函数
main "$@"