本文目录导读:

这是一个关于Shell脚本配置容器迁移方案的详细指南,我将分步骤介绍如何编写脚本来实现容器(以Docker为例)的迁移,包括导出、传输、导入和启动流程。
核心迁移思路
容器迁移主要分为三个阶段:
- 源端: 停止容器 → 提交为镜像 → 导出为tar文件
- 传输: 将tar文件传输到目标服务器
- 目标端: 导入镜像 → 启动新容器
基础迁移脚本模板
单容器迁移脚本
#!/bin/bash
# 容器迁移脚本 - migrate_container.sh
# 配置变量
CONTAINER_NAME="myapp"
IMAGE_NAME="myapp-image"
IMAGE_TAG="migrated-$(date +%Y%m%d%H%M%S)"
TAR_FILE="${IMAGE_NAME}_${IMAGE_TAG}.tar"
TARGET_USER="root"
TARGET_HOST="192.168.1.100"
TARGET_PATH="/tmp/"
TARGET_DIR="/opt/containers"
# 错误处理函数
handle_error() {
echo "错误: $1"
exit 1
}
# 步骤1:验证源容器是否存在
echo "===== 步骤1: 验证源容器 ====="
if ! docker inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
handle_error "容器 $CONTAINER_NAME 不存在"
fi
# 步骤2:停止容器
echo "===== 步骤2: 停止容器 ====="
docker stop "$CONTAINER_NAME" || echo "容器已经在停止状态"
# 步骤3:提交容器为镜像
echo "===== 步骤3: 提交镜像 ====="
docker commit "$CONTAINER_NAME" "${IMAGE_NAME}:${IMAGE_TAG}" || \
handle_error "镜像提交失败"
# 步骤4:导出镜像为文件
echo "===== 步骤4: 导出镜像 ====="
docker save -o "$TAR_FILE" "${IMAGE_NAME}:${IMAGE_TAG}" || \
handle_error "镜像导出失败"
# 步骤5:传输到目标服务器
echo "===== 步骤5: 传输镜像 ====="
scp "$TAR_FILE" "${TARGET_USER}@${TARGET_HOST}:${TARGET_PATH}" || \
handle_error "镜像传输失败"
# 步骤6:在目标服务器上执行导入和启动
echo "===== 步骤6: 远程执行导入 ====="
ssh "${TARGET_USER}@${TARGET_HOST}" "
# 创建目标目录
mkdir -p $TARGET_DIR
# 导入镜像
docker load -i ${TARGET_PATH}${TAR_FILE} || exit 1
# 重新创建容器(根据原始容器配置调整)
docker run -d \\
--name ${CONTAINER_NAME} \\
--restart unless-stopped \\
-p 8080:80 \\ # 修改为实际端口映射
${IMAGE_NAME}:${IMAGE_TAG}
# 清理tar文件
rm -f ${TARGET_PATH}${TAR_FILE}
" || handle_error "远程导入失败"
# 步骤7:清理本地临时文件
echo "===== 步骤7: 清理临时文件 ====="
rm -f "$TAR_FILE"
docker rmi "${IMAGE_NAME}:${IMAGE_TAG}"
echo "===== 迁移完成 ====="
echo "容器 $CONTAINER_NAME 已成功迁移到 $TARGET_HOST"
增强版迁移脚本(支持多容器和配置持久化)
#!/bin/bash
# 增强版容器迁移脚本 - advanced_migrate.sh
# 配置区 - 可修改为配置文件读取
CONFIG_FILE="migration.conf"
# 默认配置
declare -A CONTAINERS=(
["nginx-web"]="nginx:latest:80:80"
["mysql-db"]="mysql:8.0:3306:3306"
["redis-cache"]="redis:alpine:6379:6379"
)
TARGET_HOST="192.168.1.200"
TARGET_USER="root"
BACKUP_DIR="/tmp/container_backup_$(date +%Y%m%d_%H%M%S)"
TARGET_BASE_DIR="/opt/containers"
VOLUME_BACKUP_ENABLED=true
# 加载配置文件
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 函数:备份容器卷
backup_volumes() {
local container=$1
local backup_path=$2
log "备份卷数据: $container"
# 获取容器的挂载点信息
local volumes=$(docker inspect "$container" | jq -r '.[0].Mounts[] | select(.Type=="bind" or .Type=="volume") | .Source')
if [ -z "$volumes" ]; then
log "没有发现卷"
return 0
fi
# 使用临时容器来打包卷数据
docker run --rm \
--volumes-from "$container" \
-v "${backup_path}:/backup" \
alpine tar czf "/backup/${container}_volumes.tar.gz" \
/data 2>/dev/null || true
}
# 函数:迁移单个容器
migrate_container() {
local container=$1
local image=$2
local host_port=$3
local container_port=$4
local tar_file="${container}_$(date +%Y%m%d%H%M%S).tar"
log "开始迁移容器: $container"
# 停止并提交
docker stop "$container" 2>/dev/null
docker commit "$container" "${container}:migrated" || {
log "ERROR: 提交失败: $container"
return 1
}
# 导出
docker save -o "${BACKUP_DIR}/${tar_file}" "${container}:migrated" || {
log "ERROR: 导出失败: $container"
return 1
}
# 备份卷
if [ "$VOLUME_BACKUP_ENABLED" = true ]; then
backup_volumes "$container" "$BACKUP_DIR"
fi
# 传输到目标
scp "${BACKUP_DIR}/${tar_file}" "${TARGET_USER}@${TARGET_HOST}:${TARGET_BASE_DIR}/" || {
log "ERROR: 传输失败: $container"
return 1
}
# 远程导入和启动
ssh "${TARGET_USER}@${TARGET_HOST}" "
log() { echo \"[\$(date '+%Y-%m-%d %H:%M:%S')] \$1\"; }
log '加载镜像: ${container}'
docker load -i ${TARGET_BASE_DIR}/${tar_file}
# 创建卷目录
mkdir -p ${TARGET_BASE_DIR}/volumes/${container}
# 启动容器 - 这里需要根据实际情况调整参数
docker run -d \\
--name ${container} \\
--restart unless-stopped \\
-p ${host_port}:${container_port} \\
-v ${TARGET_BASE_DIR}/volumes/${container}:/data \\
${container}:migrated
# 清理
rm -f ${TARGET_BASE_DIR}/${tar_file}
" || {
log "ERROR: 远程执行失败: $container"
return 1
}
# 清理本地
rm -f "${BACKUP_DIR}/${tar_file}"
docker rmi "${container}:migrated" 2>/dev/null
log "迁移完成: $container"
return 0
}
# 主迁移流程
main() {
log "启动容器迁移流程"
# 创建本地备份目录
mkdir -p "$BACKUP_DIR"
# 创建目标目录
ssh "${TARGET_USER}@${TARGET_HOST}" "mkdir -p ${TARGET_BASE_DIR}/volumes"
# 遍历并迁移所有容器
for container in "${!CONTAINERS[@]}"; do
IFS=':' read -r image host_port container_port <<< "${CONTAINERS[$container]}"
migrate_container "$container" "$image" "$host_port" "$container_port"
done
# 清理备份目录
rm -rf "$BACKUP_DIR"
log "所有容器迁移完成"
}
# 执行主函数
main
高级配置与优化
配置文件示例 (migration.conf)
# migration.conf - 容器迁移配置文件
# 容器配置: [容器名]=镜像:主机端口:容器端口
CONTAINERS=(
["nginx-web"]="nginx:latest:80:80"
["mysql-db"]="mysql:8.0:3306:3306"
["redis-cache"]="redis:alpine:6379:6379"
)
# 目标服务器配置
TARGET_HOST="192.168.1.100"
TARGET_USER="root"
TARGET_BASE_DIR="/opt/containers"
# 迁移选项
VOLUME_BACKUP_ENABLED=true
KEEP_SOURCE_CONTAINER=false
COMPRESS_LEVEL=6 # gzip压缩级别 1-9
# 网络配置
NETWORK_MODE="bridge"
DEFAULT_DNS="8.8.8.8"
# 资源限制 (可选)
MEMORY_LIMIT="1g"
CPU_LIMIT="2"
并行迁移脚本
#!/bin/bash
# 并行迁移脚本 - parallel_migrate.sh
PARALLEL_LIMIT=3 # 并行执行数量
# 任务队列
declare -a TASKS
# 添加迁移任务
for container in "${!CONTAINERS[@]}"; do
TASKS+=("$container")
done
# 并行执行函数
run_parallel() {
local running=0
local index=0
while [ $index -lt ${#TASKS[@]} ]; do
if [ $running -lt $PARALLEL_LIMIT ]; then
migrate_container "${TASKS[$index]}" &
((running++))
((index++))
else
wait -n
((running--))
fi
done
# 等待所有任务完成
wait
}
# 执行并行迁移
run_parallel
故障恢复脚本
#!/bin/bash
# 故障恢复脚本 - rollback.sh
TARGET_HOST="192.168.1.100"
BACKUP_DIR="/opt/container_backups"
# 回滚到指定版本
rollback_to_version() {
local container=$1
local timestamp=$2
local tar_file="${container}_${timestamp}.tar"
ssh "$TARGET_HOST" "
# 停止并删除当前容器
docker stop $container 2>/dev/null
docker rm $container 2>/dev/null
# 加载备份镜像
docker load -i ${BACKUP_DIR}/${tar_file}
# 启动旧版本
docker run -d --name $container --restart unless-stopped \\
$(cat /opt/container_configs/${container}_config.txt) \\
${container}:${timestamp}
"
}
使用注意事项
- 数据一致性:确保在停止容器后提交,避免数据不一致
- 网络配置:注意检查目标服务器的网络环境和端口占用
- 资源限制:确认目标服务器有足够资源运行容器
- 日志保留:迁移过程详细记录日志,便于问题追踪
- 测试验证:生产环境迁移前务必在测试环境验证
安全性建议
- 使用SSH密钥认证而非密码
- 对传输的文件进行校验(MD5/SHA256)
- 敏感数据考虑加密传输
- 迁移后清理临时文件和中间镜像
这个脚本框架可以根据你的具体环境进行调整和扩展,记得在实际使用前充分测试,并根据需要添加错误处理和重试机制。