一、容器的存储介绍

1.1 容器存储方式

Docker容器有三种主要的存储方式:

1.1.1 分层存储(Layer Storage)
# 容器镜像的分层结构示例
FROM ubuntu:22.04          # 基础层
RUN apt-get update         # 新的只读层
RUN apt-get install nginx  # 新的只读层
COPY app /var/www/html     # 新的只读层
CMD ["nginx", "-g", "daemon off;"]  # 新的只读层

特点:

  • 只读层(RO Layers):镜像的每一层都是只读的
  • 可写层(RW Layer):容器运行时新增的读写层
  • 分层共享:多个容器可以共享相同的只读层
1.1.2 绑定挂载(Bind Mounts)
# 将主机目录挂载到容器
docker run -v /host/path:/container/path nginx

# 或使用--mount参数
docker run --mount type=bind,source=/host/path,target=/container/path nginx
1.1.3 数据卷(Volumes)
# 创建数据卷
docker volume create my-volume

# 使用数据卷
docker run -v my-volume:/container/path nginx

# 查看数据卷信息
docker volume inspect my-volume
1.1.4 tmpfs挂载
# 使用内存作为临时存储
docker run --tmpfs /tmp nginx

# 限制tmpfs大小
docker run --tmpfs /tmp:size=100m,mode=1777 nginx

1.2 数据卷的特性

1.2.1 数据卷的优势
# docker-compose.yml中数据卷的使用示例
version: '3.8'
services:
  web:
    image: nginx
    volumes:
      - web-data:/usr/share/nginx/html
      - ./config:/etc/nginx/conf.d
      
volumes:
  web-data:
    driver: local
    driver_opts:
      type: none
      device: /data/web
      o: bind

核心特性:

  1. 持久化:数据卷独立于容器生命周期
  2. 共享性:多个容器可共享同一数据卷
  3. 可移植性:支持备份、恢复和迁移
  4. 性能:本地存储,性能优于绑定挂载
  5. 管理:可通过Docker命令管理
1.2.2 数据卷生命周期管理
# 数据卷的完整生命周期命令
# 1. 创建
docker volume create myapp-data

# 2. 查看
docker volume ls
docker volume inspect myapp-data

# 3. 使用
docker run -d --name app -v myapp-data:/data myapp

# 4. 备份
docker run --rm -v myapp-data:/source -v $(pwd):/backup ubuntu tar czf /backup/backup.tar.gz -C /source .

# 5. 恢复
docker run --rm -v myapp-data:/target -v $(pwd):/backup ubuntu tar xzf /backup/backup.tar.gz -C /target

# 6. 清理
docker volume rm myapp-data
docker volume prune  # 清理未使用的数据卷

1.3 容器的存储图示

容器存储结构示意图:
┌─────────────────────────────────────────────────────────┐
│                        Container                         │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │  Read/Write │  │    tmpfs    │  │ Bind Mount  │     │
│  │    Layer    │  │   Mount     │  │   /host     │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────┐│
│  │               Container Data Volumes                ││
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────┐  ││
│  │  │  Volume A    │  │  Volume B    │  │ Volume C │  ││
│  │  │/var/lib/mysql│  │/var/www/html │  │  /logs   │  ││
│  │  └──────────────┘  └──────────────┘  └──────────┘  ││
│  └─────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │  Image      │  │  Image      │  │  Image      │     │
│  │  Layer 3    │  │  Layer 2    │  │  Layer 1    │     │
│  │  (RO)       │  │  (RO)       │  │  (RO)       │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘

1.4 迁移方式简介

1.4.1 容器迁移的三种方式
迁移方式 适用场景 优点 缺点
镜像迁移 应用代码+环境迁移 简单快速,完整环境 不包含运行时数据
数据卷迁移 数据库、文件等持久化数据 数据完整,独立迁移 需配合容器迁移
完整容器迁移 开发环境、测试环境 完全一致,开箱即用 体积大,效率低
1.4.2 迁移工具对比
# 1. Docker原生命令
docker save -o myimage.tar myimage:tag          # 保存镜像
docker load -i myimage.tar                      # 加载镜像
docker export -o mycontainer.tar mycontainer    # 导出容器
docker import mycontainer.tar                   # 导入容器

# 2. Docker Compose迁移
docker-compose config > docker-compose-export.yml  # 导出配置
docker-compose up -d                              # 重新部署

# 3. 第三方工具
# docker-migrate: https://github.com/docker/migrate
# Portainer: 图形化迁移工具

二、本次实践介绍

2.1 本次实践简介

实践目标

将运行在源服务器上的Web应用容器(包含Nginx服务、网站文件和MySQL数据库)完整迁移到目标服务器,确保服务无缝切换。

实践场景
  • 场景1:服务器硬件升级迁移
  • 场景2:开发环境到生产环境迁移
  • 场景3:数据中心迁移
技术要点
迁移组件:
  - Web服务器: Nginx 1.24
  - 应用代码: Node.js应用
  - 数据库: MySQL 8.0
  - 配置文件: nginx.conf, my.cnf
  - 持久化数据: 数据库数据、上传文件

2.2 本次实践环境介绍

2.2.1 源服务器环境
# 环境检查脚本
#!/bin/bash
echo "=== 源服务器环境检查 ==="
echo "1. Docker版本:"
docker --version
echo ""

echo "2. 运行中的容器:"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
echo ""

echo "3. 容器详情:"
for container in $(docker ps -q); do
    echo "容器ID: $container"
    docker inspect $container | grep -A5 Mounts
    echo ""
done

echo "4. 磁盘使用情况:"
df -h | grep -E "(Filesystem|/var/lib/docker)"
2.2.2 目标服务器环境
# 目标服务器准备脚本
#!/bin/bash
echo "=== 目标服务器准备 ==="

# 1. 安装Docker
if ! command -v docker &> /dev/null; then
    echo "安装Docker..."
    curl -fsSL https://get.docker.com -o get-docker.sh
    sudo sh get-docker.sh
    sudo usermod -aG docker $USER
fi

# 2. 安装Docker Compose
if ! command -v docker-compose &> /dev/null; then
    echo "安装Docker Compose..."
    sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    sudo chmod +x /usr/local/bin/docker-compose
fi

# 3. 创建目录结构
echo "创建数据目录..."
sudo mkdir -p /data/docker/{volumes,backups,configs}
sudo chmod -R 755 /data/docker

# 4. 配置Docker
echo "配置Docker..."
sudo tee /etc/docker/daemon.json <<EOF
{
  "data-root": "/data/docker",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn"
  ]
}
EOF

# 5. 重启Docker
sudo systemctl restart docker
sudo systemctl enable docker

echo "环境准备完成!"
2.2.3 网络架构
迁移网络架构示意图:

源服务器 (192.168.1.100)                 目标服务器 (192.168.1.200)
├── Docker容器                          ├── Docker容器(迁移后)
│   ├── nginx:1.24 (80:8080)           │   ├── nginx:1.24 (80:8080)
│   ├── mysql:8.0 (3306:3306)          │   ├── mysql:8.0 (3306:3306)
│   └── app:nodejs (3000:3000)         │   └── app:nodejs (3000:3000)
├── 数据卷                              ├── 数据卷(迁移后)
│   ├── mysql-data                     │   ├── mysql-data
│   └── app-data                       │   └── app-data
└── 配置文件                            └── 配置文件(迁移后)
    ├── nginx.conf                         ├── nginx.conf
    └── my.cnf                             └── my.cnf

三、A类实验内容

A类模拟试验1:基础容器迁移

3.1.1 实验目标

迁移一个简单的Nginx Web服务器容器,包含自定义首页和配置文件。

3.1.2 实验步骤
# 步骤1:在源服务器创建测试容器
echo "=== 创建测试容器 ==="

# 创建测试目录
mkdir -p ~/nginx-test/{html,conf}

# 创建自定义首页
cat > ~/nginx-test/html/index.html <<EOF
<!DOCTYPE html>
<html>
<head>
    <title>迁移测试页面</title>
</head>
<body>
    <h1>Hello from Source Server!</h1>
    <p>服务器时间: <span id="time"></span></p>
    <script>
        document.getElementById('time').textContent = new Date().toLocaleString();
    </script>
</body>
</html>
EOF

# 创建自定义配置
cat > ~/nginx-test/conf/nginx.conf <<EOF
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
                    '\$status \$body_bytes_sent "\$http_referer" '
                    '"\$http_user_agent" "\$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
        }

        location /status {
            stub_status on;
            access_log off;
        }
    }
}
EOF

# 运行容器
docker run -d \
  --name nginx-test \
  -p 8080:80 \
  -v ~/nginx-test/html:/usr/share/nginx/html \
  -v ~/nginx-test/conf/nginx.conf:/etc/nginx/nginx.conf \
  nginx:1.24

echo "测试容器已启动,访问 http://localhost:8080 验证"

A类模拟试验2:数据卷容器迁移

3.2.1 实验目标

迁移包含数据卷的MySQL容器,确保数据完整性。

3.2.2 实验步骤
# 步骤1:创建带数据卷的MySQL容器
echo "=== 创建MySQL测试容器 ==="

# 创建数据卷
docker volume create mysql-test-data

# 运行MySQL容器
docker run -d \
  --name mysql-test \
  -e MYSQL_ROOT_PASSWORD=Test@123 \
  -e MYSQL_DATABASE=migration_test \
  -e MYSQL_USER=testuser \
  -e MYSQL_PASSWORD=Test@123 \
  -v mysql-test-data:/var/lib/mysql \
  mysql:8.0

# 等待MySQL启动
sleep 30

# 创建测试数据
docker exec mysql-test mysql -u root -pTest@123 -e "
CREATE DATABASE IF NOT EXISTS migration_db;
USE migration_db;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (username, email) VALUES
    ('alice', 'alice@example.com'),
    ('bob', 'bob@example.com'),
    ('charlie', 'charlie@example.com');

SELECT * FROM users;
"

echo "MySQL容器已创建,包含测试数据"

A类模拟试验3:多容器应用迁移

3.3.1 实验目标

迁移由多个容器组成的完整应用(Web + API + Database)。

3.3.2 实验步骤
# 步骤1:使用Docker Compose创建多容器应用
echo "=== 创建多容器应用 ==="

# 创建项目目录
mkdir -p ~/multi-app/{web,api,database}

# 创建docker-compose.yml
cat > ~/multi-app/docker-compose.yml <<EOF
version: '3.8'

services:
  database:
    image: postgres:15
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: App@123
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - app-network

  api:
    build: ./api
    environment:
      DATABASE_URL: postgresql://appuser:App@123@database:5432/appdb
    ports:
      - "3000:3000"
    depends_on:
      - database
    networks:
      - app-network

  web:
    image: nginx:1.24
    ports:
      - "80:80"
    volumes:
      - ./web/html:/usr/share/nginx/html
      - ./web/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - api
    networks:
      - app-network

volumes:
  postgres-data:

networks:
  app-network:
    driver: bridge
EOF

# 创建API Dockerfile
cat > ~/multi-app/api/Dockerfile <<EOF
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
EOF

# 创建API代码
cat > ~/multi-app/api/index.js <<EOF
const express = require('express');
const { Pool } = require('pg');
const app = express();
const port = 3000;

const pool = new Pool({
  user: 'appuser',
  host: 'database',
  database: 'appdb',
  password: 'App@123',
  port: 5432,
});

app.get('/api/users', async (req, res) => {
  try {
    const result = await pool.query('SELECT * FROM users');
    res.json(result.rows);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

app.listen(port, () => {
  console.log(\`API server listening at http://localhost:\${port}\`);
});
EOF

# 创建数据库初始化脚本
cat > ~/multi-app/database/init.sql <<EOF
CREATE TABLE IF NOT EXISTS users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (name, email) VALUES
    ('迁移测试用户1', 'test1@example.com'),
    ('迁移测试用户2', 'test2@example.com'),
    ('迁移测试用户3', 'test3@example.com')
ON CONFLICT (email) DO NOTHING;
EOF

# 创建Web页面
cat > ~/multi-app/web/html/index.html <<EOF
<!DOCTYPE html>
<html>
<head>
    <title>多容器应用</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; }
        .user { border: 1px solid #ccc; padding: 10px; margin: 5px; }
    </style>
</head>
<body>
    <h1>多容器应用迁移测试</h1>
    <div id="users"></div>
    <div id="status"></div>
    
    <script>
        async function loadUsers() {
            try {
                const response = await fetch('http://localhost:3000/api/users');
                const users = await response.json();
                const container = document.getElementById('users');
                container.innerHTML = '<h2>用户列表:</h2>' + 
                    users.map(u => \`
                        <div class="user">
                            <strong>\${u.name}</strong><br>
                            Email: \${u.email}<br>
                            创建时间: \${new Date(u.created_at).toLocaleString()}
                        </div>
                    \`).join('');
            } catch (error) {
                console.error('加载用户失败:', error);
            }
        }
        
        async function checkHealth() {
            try {
                const response = await fetch('http://localhost:3000/health');
                const status = await response.json();
                document.getElementById('status').innerHTML = \`
                    <h3>系统状态</h3>
                    <p>状态: \${status.status}</p>
                    <p>时间: \${new Date(status.timestamp).toLocaleString()}</p>
                \`;
            } catch (error) {
                document.getElementById('status').innerHTML = 
                    '<p style="color: red">API服务不可用</p>';
            }
        }
        
        // 初始加载
        loadUsers();
        checkHealth();
        
        // 每10秒刷新一次
        setInterval(() => {
            loadUsers();
            checkHealth();
        }, 10000);
    </script>
</body>
</html>
EOF

# 创建Nginx配置
cat > ~/multi-app/web/nginx.conf <<EOF
server {
    listen 80;
    server_name localhost;
    
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files \$uri \$uri/ =404;
    }
    
    location /api {
        proxy_pass http://api:3000;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
    }
}
EOF

# 启动应用
cd ~/multi-app
docker-compose up -d

echo "多容器应用已启动,访问 http://localhost 验证"

四、迁移源服务器操作

4.1 查看迁移容器状态

#!/bin/bash
echo "=== 源服务器容器状态检查 ==="
echo ""

# 1. 查看所有容器状态
echo "1. 当前运行中的容器:"
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" | tee containers-status.txt

echo ""
echo "2. 所有容器(包括停止的):"
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}\t{{.CreatedAt}}" | tee all-containers.txt

echo ""
echo "3. 容器资源使用情况:"
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}" | tee containers-resources.txt

echo ""
echo "4. 检查容器数据卷:"
for container in $(docker ps -q); do
    container_name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    echo "容器: $container_name"
    
    # 检查挂载点
    mounts=$(docker inspect --format='{{range .Mounts}}{{.Type}} {{.Source}}:{{.Destination}} {{end}}' $container)
    if [ -n "$mounts" ]; then
        echo "  挂载: $mounts"
    else
        echo "  无挂载"
    fi
    
    # 检查数据卷
    volumes=$(docker inspect --format='{{range .Config.Volumes}}{{.}} {{end}}' $container)
    if [ -n "$volumes" ]; then
        echo "  数据卷: $volumes"
    fi
    
    echo ""
done | tee containers-volumes.txt

echo ""
echo "5. 网络配置检查:"
echo "容器网络配置:" > containers-network.txt
for container in $(docker ps -q); do
    container_name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    echo "容器: $container_name" >> containers-network.txt
    docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}} {{.Gateway}} {{end}}' $container >> containers-network.txt
    echo "" >> containers-network.txt
done
cat containers-network.txt

echo ""
echo "6. 环境变量检查:"
echo "关键环境变量:" > containers-env.txt
for container in $(docker ps -q); do
    container_name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    echo "容器: $container_name" >> containers-env.txt
    docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' $container | grep -E "(PASS|KEY|SECRET|TOKEN|URL|DB|HOST)" >> containers-env.txt
    echo "" >> containers-env.txt
done
cat containers-env.txt

echo ""
echo "=== 检查完成 ==="
echo "报告文件已保存:"
ls -la *-status.txt *-containers.txt *-resources.txt *-volumes.txt *-network.txt *-env.txt

4.2 将容器转化为镜像

#!/bin/bash
echo "=== 容器转镜像操作 ==="
echo ""

# 创建备份目录
BACKUP_DIR="/backup/docker-migration-$(date +%Y%m%d-%H%M%S)"
mkdir -p $BACKUP_DIR
cd $BACKUP_DIR

echo "备份目录: $BACKUP_DIR"
echo ""

# 方法1:通过commit创建镜像
echo "方法1:使用docker commit保存容器状态"
for container in $(docker ps -q); do
    container_name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    image_name="migration/${container_name}:$(date +%Y%m%d)"
    
    echo "正在保存容器: $container_name -> $image_name"
    
    # 暂停容器(可选,确保数据一致性)
    # docker pause $container
    
    # 提交为镜像
    docker commit \
        --author "Migration Tool" \
        --message "Migrated from $container_name on $(date)" \
        $container \
        $image_name
    
    # 恢复容器
    # docker unpause $container
    
    # 验证镜像
    docker inspect $image_name > "${container_name}-image.json"
    
    echo "  已保存: $image_name"
    echo "  镜像信息: ${container_name}-image.json"
    echo ""
done

# 方法2:通过Dockerfile重建镜像
echo "方法2:生成Dockerfile用于重建"
for container in $(docker ps -q); do
    container_name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    
    echo "为容器 $container_name 生成Dockerfile..."
    
    # 获取基础镜像
    base_image=$(docker inspect --format='{{.Config.Image}}' $container)
    
    # 获取环境变量
    env_vars=$(docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' $container | grep -v "^$")
    
    # 获取工作目录
    workdir=$(docker inspect --format='{{.Config.WorkingDir}}' $container)
    
    # 获取命令
    cmd=$(docker inspect --format='{{json .Config.Cmd}}' $container)
    entrypoint=$(docker inspect --format='{{json .Config.Entrypoint}}' $container)
    
    # 生成Dockerfile
    cat > "${container_name}.Dockerfile" <<EOF
# Dockerfile for $container_name
# Generated by migration tool on $(date)
# Source container: $container_name

FROM $base_image

# 环境变量
$(echo "$env_vars" | while read var; do
    echo "ENV $var"
done)

# 工作目录
$(if [ -n "$workdir" ] && [ "$workdir" != "<no value>" ]; then
    echo "WORKDIR $workdir"
fi)

# 复制变更(需要额外处理)
# 这里需要手动添加需要复制的文件

# 入口点
$(if [ "$entrypoint" != "null" ]; then
    echo "ENTRYPOINT $entrypoint"
fi)

# 命令
$(if [ "$cmd" != "null" ]; then
    echo "CMD $cmd"
fi)
EOF
    
    echo "  已生成: ${container_name}.Dockerfile"
    
    # 提取容器中的文件变更
    echo "  提取容器文件变更..."
    docker diff $container > "${container_name}-diff.txt"
    
    # 保存容器配置
    docker inspect $container > "${container_name}-config.json"
    
    echo ""
done

echo "=== 容器转镜像完成 ==="
echo "所有文件保存在: $BACKUP_DIR"
ls -la $BACKUP_DIR/

4.3 将生成镜像保存为tar文件

#!/bin/bash
echo "=== 镜像打包操作 ==="
echo ""

BACKUP_DIR="/backup/docker-migration-$(date +%Y%m%d-%H%M%S)"
mkdir -p $BACKUP_DIR/images
cd $BACKUP_DIR

echo "打包目录: $BACKUP_DIR/images"
echo ""

# 方法1:打包所有镜像
echo "方法1:打包所有相关镜像"
echo "收集镜像列表..."

# 获取所有运行中容器使用的镜像
IMAGE_LIST=""
for container in $(docker ps -q); do
    image_id=$(docker inspect --format='{{.Image}}' $container)
    image_name=$(docker inspect --format='{{.Config.Image}}' $container)
    IMAGE_LIST="$IMAGE_LIST $image_name"
done

# 去重
IMAGE_LIST=$(echo $IMAGE_LIST | tr ' ' '\n' | sort -u | tr '\n' ' ')

echo "需要打包的镜像:"
echo "$IMAGE_LIST"

# 保存每个镜像
for image in $IMAGE_LIST; do
    echo ""
    echo "打包镜像: $image"
    
    # 清理镜像名称中的非法字符
    safe_name=$(echo $image | sed 's/[\/:]/-/g')
    tar_file="images/${safe_name}.tar"
    
    # 保存镜像
    docker save -o $tar_file $image
    
    # 计算大小
    file_size=$(du -h $tar_file | cut -f1)
    
    # 验证文件
    if tar -tf $tar_file >/dev/null 2>&1; then
        echo "  ✓ 已保存: $tar_file ($file_size)"
        
        # 生成校验和
        md5sum $tar_file > "${tar_file}.md5"
        sha256sum $tar_file > "${tar_file}.sha256"
        
        # 保存镜像信息
        docker inspect $image > "images/${safe_name}-info.json"
    else
        echo "  ✗ 保存失败: $tar_file"
    fi
done

# 方法2:打包迁移创建的镜像
echo ""
echo "方法2:打包迁移创建的镜像"
MIGRATION_IMAGES=$(docker images --filter "reference=migration/*" --format "{{.Repository}}:{{.Tag}}")

if [ -n "$MIGRATION_IMAGES" ]; then
    echo "迁移镜像列表:"
    echo "$MIGRATION_IMAGES"
    
    for image in $MIGRATION_IMAGES; do
        echo ""
        echo "打包迁移镜像: $image"
        
        safe_name=$(echo $image | sed 's/[\/:]/-/g')
        tar_file="images/migration-${safe_name}.tar"
        
        docker save -o $tar_file $image
        
        file_size=$(du -h $tar_file | cut -f1)
        echo "  ✓ 已保存: $tar_file ($file_size)"
        
        # 校验和
        md5sum $tar_file > "${tar_file}.md5"
        sha256sum $tar_file > "${tar_file}.sha256"
    done
else
    echo "未找到迁移镜像"
fi

# 方法3:打包所有镜像(完整备份)
echo ""
echo "方法3:完整镜像备份"
ALL_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "<none>")

echo "完整镜像列表(共 $(echo "$ALL_IMAGES" | wc -l) 个)"

# 可以选择性打包,这里只打包前5个作为示例
echo "$ALL_IMAGES" | head -5 | while read image; do
    if [ -n "$image" ]; then
        safe_name=$(echo $image | sed 's/[\/:]/-/g')
        tar_file="images/full-${safe_name}.tar"
        
        echo "打包: $image"
        docker save -o $tar_file $image 2>/dev/null
        
        if [ $? -eq 0 ]; then
            file_size=$(du -h $tar_file 2>/dev/null | cut -f1)
            echo "  ✓ $tar_file ($file_size)"
        fi
    fi
done

# 创建打包清单
echo ""
echo "创建打包清单..."
cat > images/MANIFEST.md <<EOF
# Docker镜像打包清单

## 打包信息
- 打包时间: $(date)
- 源服务器: $(hostname)
- Docker版本: $(docker --version)

## 镜像清单
$(for tar_file in images/*.tar; do
    if [ -f "$tar_file" ]; then
        echo "### $(basename $tar_file)"
        echo "- 文件: \`$(basename $tar_file)\`"
        echo "- 大小: $(du -h $tar_file | cut -f1)"
        echo "- MD5: \`$(cat ${tar_file}.md5 2>/dev/null | cut -d' ' -f1 || echo "N/A")\`"
        echo "- SHA256: \`$(cat ${tar_file}.sha256 2>/dev/null | cut -d' ' -f1 || echo "N/A")\`"
        echo ""
    fi
done)

## 容器状态
\`\`\`
$(docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}")
\`\`\`

## 数据卷
$(docker volume ls --format "{{.Name}}" | while read volume; do
    echo "- $volume"
done)
EOF

echo ""
echo "=== 镜像打包完成 ==="
echo "总计打包文件:"
ls -lh images/*.tar | wc -l
echo "总大小:"
du -sh images/
echo ""
echo "详细清单: $BACKUP_DIR/images/MANIFEST.md"

4.4 将镜像包拷贝到新服务器

#!/bin/bash
echo "=== 镜像传输操作 ==="
echo ""

BACKUP_DIR="/backup/docker-migration-$(date +%Y%m%d)"
TARGET_SERVER="user@target-server-ip"
TARGET_PATH="/backup/docker-migration"

# 检查参数
if [ $# -ge 1 ]; then
    TARGET_SERVER="$1"
fi

if [ $# -ge 2 ]; then
    TARGET_PATH="$2"
fi

echo "传输配置:"
echo "  源目录: $BACKUP_DIR"
echo "  目标服务器: $TARGET_SERVER"
echo "  目标路径: $TARGET_PATH"
echo ""

# 方法1:使用scp传输
echo "方法1:使用SCP传输"
echo "正在压缩备份文件..."

# 压缩备份目录
tar_filename="docker-migration-$(date +%Y%m%d-%H%M%S).tar.gz"
cd $(dirname $BACKUP_DIR)
tar -czf $tar_filename $(basename $BACKUP_DIR)

echo "压缩完成: $tar_filename ($(du -h $tar_filename | cut -f1))"
echo ""

echo "开始传输到目标服务器..."
echo "目标: $TARGET_SERVER:$TARGET_PATH"

# 检查目标服务器目录
ssh $TARGET_SERVER "mkdir -p $TARGET_PATH"

# 传输文件
echo "传输中..."
scp $tar_filename $TARGET_SERVER:$TARGET_PATH/

if [ $? -eq 0 ]; then
    echo "✓ 传输成功"
    
    # 在目标服务器解压
    echo "在目标服务器解压..."
    ssh $TARGET_SERVER "
        cd $TARGET_PATH
        echo '解压文件...'
        tar -xzf $(basename $tar_filename)
        echo '清理压缩包...'
        rm -f $(basename $tar_filename)
        echo '设置权限...'
        chmod -R 755 $(basename $BACKUP_DIR)
        echo '完成!'
    "
    
    # 验证传输
    echo "验证传输..."
    local_md5=$(md5sum $tar_filename | cut -d' ' -f1)
    remote_md5=$(ssh $TARGET_SERVER "cd $TARGET_PATH && md5sum $(basename $tar_filename) 2>/dev/null | cut -d' ' -f1 || echo 'not found'")
    
    if [ "$local_md5" = "$remote_md5" ] || [ "$remote_md5" = "not found" ]; then
        echo "✓ 传输验证通过"
    else
        echo "⚠ MD5校验不匹配"
        echo "  本地: $local_md5"
        echo "  远程: $remote_md5"
    fi
else
    echo "✗ 传输失败"
    exit 1
fi

# 方法2:使用rsync(增量传输)
echo ""
echo "方法2:使用rsync增量传输"
echo "正在同步镜像目录..."

rsync -avz --progress \
    $BACKUP_DIR/images/ \
    $TARGET_SERVER:$TARGET_PATH/images-rsync/

if [ $? -eq 0 ]; then
    echo "✓ rsync同步完成"
    
    # 验证文件数量
    local_count=$(find $BACKUP_DIR/images -name "*.tar" | wc -l)
    remote_count=$(ssh $TARGET_SERVER "find $TARGET_PATH/images-rsync -name '*.tar' 2>/dev/null | wc -l")
    
    echo "文件数量对比:"
    echo "  本地: $local_count 个tar文件"
    echo "  远程: $remote_count 个tar文件"
    
    if [ $local_count -eq $remote_count ]; then
        echo "✓ 文件数量一致"
    else
        echo "⚠ 文件数量不一致"
    fi
else
    echo "✗ rsync同步失败"
fi

# 方法3:使用curl上传到HTTP服务器
echo ""
echo "方法3:上传到HTTP服务器(可选)"
read -p "是否上传到HTTP服务器?(y/N): " upload_choice

if [ "$upload_choice" = "y" ] || [ "$upload_choice" = "Y" ]; then
    read -p "请输入HTTP上传URL: " upload_url
    
    if [ -n "$upload_url" ]; then
        echo "上传 $tar_filename$upload_url"
        
        # 使用curl上传
        curl -X POST \
             -F "file=@$tar_filename" \
             -F "hostname=$(hostname)" \
             -F "timestamp=$(date)" \
             $upload_url
        
        if [ $? -eq 0 ]; then
            echo "✓ HTTP上传成功"
        else
            echo "✗ HTTP上传失败"
        fi
    fi
fi

# 创建传输报告
echo ""
echo "创建传输报告..."
cat > transfer-report.md <<EOF
# Docker镜像传输报告

## 传输信息
- 传输时间: $(date)
- 源服务器: $(hostname)
- 目标服务器: $TARGET_SERVER
- 目标路径: $TARGET_PATH

## 传输文件
- 压缩包: $tar_filename
- 大小: $(du -h $tar_filename | cut -f1)
- MD5: $(md5sum $tar_filename | cut -d' ' -f1)

## 传输方式
1. SCP传输: $(if [ $? -eq 0 ]; then echo "成功"; else echo "失败"; fi)
2. rsync同步: $(if command -v rsync &> /dev/null; then echo "已执行"; else echo "未执行"; fi)

## 验证信息
\`\`\`
本地文件数: $(find $BACKUP_DIR/images -name "*.tar" 2>/dev/null | wc -l)
远程文件数: $(ssh $TARGET_SERVER "find $TARGET_PATH -name '*.tar' 2>/dev/null | wc -l")
\`\`\`

## 后续步骤
1. 在目标服务器加载镜像: \`docker load -i <tar文件>\`
2. 恢复数据卷(如果需要)
3. 启动容器
EOF

echo ""
echo "=== 镜像传输完成 ==="
echo "传输报告: transfer-report.md"
echo "所有文件已传输到: $TARGET_SERVER:$TARGET_PATH"

五、迁移目的服务器操作

5.1 生成本地镜像

#!/bin/bash
echo "=== 目标服务器镜像恢复 ==="
echo ""

MIGRATION_DIR="/backup/docker-migration-$(date +%Y%m%d)"
if [ $# -ge 1 ]; then
    MIGRATION_DIR="$1"
fi

if [ ! -d "$MIGRATION_DIR" ]; then
    echo "错误: 迁移目录不存在: $MIGRATION_DIR"
    echo "请先传输备份文件到目标服务器"
    exit 1
fi

cd "$MIGRATION_DIR"

echo "迁移目录: $(pwd)"
echo ""

# 检查镜像文件
echo "检查镜像文件..."
IMAGE_FILES=$(find . -name "*.tar" -type f | sort)

if [ -z "$IMAGE_FILES" ]; then
    echo "未找到镜像文件"
    exit 1
fi

echo "找到 $(echo "$IMAGE_FILES" | wc -l) 个镜像文件:"
echo "$IMAGE_FILES"
echo ""

# 创建镜像恢复目录
RESTORE_DIR="restored-images-$(date +%Y%m%d-%H%M%S)"
mkdir -p $RESTORE_DIR
cd $RESTORE_DIR

echo "开始恢复镜像..."
echo "恢复目录: $(pwd)"
echo ""

# 方法1:逐个加载镜像
echo "方法1:逐个加载镜像"
success_count=0
fail_count=0

for tar_file in $IMAGE_FILES; do
    echo ""
    echo "加载镜像: $tar_file"
    
    # 获取镜像名
    base_name=$(basename "$tar_file")
    
    # 验证文件完整性
    if ! tar -tf "../$tar_file" >/dev/null 2>&1; then
        echo "  ✗ 文件损坏或格式错误"
        fail_count=$((fail_count + 1))
        continue
    fi
    
    # 加载镜像
    echo "  加载中..."
    start_time=$(date +%s)
    
    docker load -i "../$tar_file"
    
    if [ $? -eq 0 ]; then
        end_time=$(date +%s)
        duration=$((end_time - start_time))
        
        # 获取加载的镜像名
        loaded_image=$(docker images --format "{{.Repository}}:{{.Tag}}" | tail -1)
        
        echo "  ✓ 加载成功: $loaded_image (耗时: ${duration}s)"
        
        # 记录到清单
        echo "$(date) | $base_name | $loaded_image | 成功" >> load-success.log
        
        success_count=$((success_count + 1))
        
        # 标记镜像标签
        docker tag $loaded_image "restored/${loaded_image##*/}"
        
    else
        echo "  ✗ 加载失败"
        echo "$(date) | $base_name | N/A | 失败" >> load-fail.log
        fail_count=$((fail_count + 1))
    fi
done

# 方法2:批量验证镜像
echo ""
echo "方法2:验证恢复的镜像"
echo "恢复的镜像列表:"

docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedAt}}" | grep -E "(restored|migration)" > restored-images.txt

cat restored-images.txt
echo ""

# 检查镜像完整性
echo "检查镜像完整性..."
for image in $(docker images --filter "reference=restored/*" --format "{{.Repository}}:{{.Tag}}"); do
    echo "验证镜像: $image"
    
    # 尝试创建临时容器验证
    if docker run --rm $image echo "验证成功" 2>/dev/null; then
        echo "  ✓ 镜像可正常运行"
        echo "$image | 验证通过" >> image-validation.log
    else
        echo "  ⚠ 镜像运行测试失败"
        echo "$image | 验证失败" >> image-validation.log
    fi
done

# 方法3:重构镜像(如果需要)
echo ""
echo "方法3:从Dockerfile重构镜像(可选)"

DOCKERFILES=$(find .. -name "*.Dockerfile" -type f)

if [ -n "$DOCKERFILES" ]; then
    echo "找到 $(echo "$DOCKERFILES" | wc -l) 个Dockerfile"
    
    for dockerfile in $DOCKERFILES; do
        echo ""
        echo "重构: $(basename $dockerfile)"
        
        # 提取镜像名
        image_name="rebuilt/$(basename $dockerfile .Dockerfile):latest"
        
        # 检查是否有对应的diff文件
        diff_file="../$(basename $dockerfile .Dockerfile)-diff.txt"
        
        if [ -f "$diff_file" ]; then
            echo "  发现变更文件: $(basename $diff_file)"
            echo "  变更内容:"
            head -5 "$diff_file"
        fi
        
        # 尝试重构(需要手动处理文件复制)
        echo "  注意: 需要手动处理文件复制步骤"
        echo "  参考Dockerfile: $dockerfile"
    done
else
    echo "未找到Dockerfile文件"
fi

# 创建恢复报告
echo ""
echo "=== 镜像恢复完成 ==="
echo "恢复统计:"
echo "  成功: $success_count 个镜像"
echo "  失败: $fail_count 个镜像"
echo ""
echo "恢复报告:"

cat > restore-report.md <<EOF
# Docker镜像恢复报告

## 恢复信息
- 恢复时间: $(date)
- 目标服务器: $(hostname)
- Docker版本: $(docker --version)
- 恢复目录: $(pwd)

## 恢复统计
- 总镜像文件: $(echo "$IMAGE_FILES" | wc -l) 个
- 成功加载: $success_count 个
- 加载失败: $fail_count 个

## 恢复的镜像
\`\`\`
$(docker images --filter "reference=restored/*" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}")
\`\`\`

## 成功清单
$(if [ -f "load-success.log" ]; then
    echo "\`\`\`"
    cat load-success.log
    echo "\`\`\`"
else
    echo "无成功记录"
fi)

## 失败清单
$(if [ -f "load-fail.log" ] && [ -s "load-fail.log" ]; then
    echo "\`\`\`"
    cat load-fail.log
    echo "\`\`\`"
else
    echo "无失败记录"
fi)

## 验证结果
$(if [ -f "image-validation.log" ]; then
    echo "\`\`\`"
    cat image-validation.log
    echo "\`\`\`"
else
    echo "无验证记录"
fi)

## 后续步骤
1. 检查数据卷是否需要恢复
2. 启动容器: \`docker run --name <name> -p <ports> <image>\`
3. 验证服务运行状态
EOF

cat restore-report.md
echo ""
echo "详细报告保存为: restore-report.md"

5.2 查看本地镜像

#!/bin/bash
echo "=== 本地镜像检查 ==="
echo ""

# 创建检查报告目录
CHECK_DIR="image-check-$(date +%Y%m%d-%H%M%S)"
mkdir -p $CHECK_DIR
cd $CHECK_DIR

echo "检查目录: $(pwd)"
echo ""

# 1. 基本镜像信息
echo "1. 所有镜像列表:"
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedAt}}\t{{.Size}}" > all-images.txt

# 显示关键镜像
echo "关键镜像(恢复的镜像):"
docker images --filter "reference=restored/*" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | tee restored-images.txt

echo ""
echo "迁移相关镜像:"
docker images --filter "reference=migration/*" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | tee migration-images.txt

echo ""
echo "基础镜像:"
docker images --filter "reference=*:latest" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | tee base-images.txt

# 2. 镜像详细信息
echo ""
echo "2. 镜像详细信息检查:"

for image in $(docker images --filter "reference=restored/*" --format "{{.Repository}}:{{.Tag}}"); do
    echo ""
    echo "检查镜像: $image"
    
    # 保存详细信息
    safe_name=$(echo $image | sed 's/[\/:]/-/g')
    docker inspect $image > "${safe_name}-inspect.json"
    
    # 提取关键信息
    echo "  架构: $(docker inspect --format='{{.Architecture}}' $image)"
    echo "  操作系统: $(docker inspect --format='{{.Os}}' $image)"
    echo "  创建时间: $(docker inspect --format='{{.Created}}' $image)"
    echo "  环境变量:"
    docker inspect --format='{{range .Config.Env}}{{println "    " .}}{{end}}' $image | head -5
    
    # 检查层数
    layers=$(docker inspect --format='{{.RootFS.Layers}}' $image | wc -w)
    echo "  层数: $layers"
    
    # 检查暴露端口
    ports=$(docker inspect --format='{{.Config.ExposedPorts}}' $image)
    if [ "$ports" != "map[]" ] && [ "$ports" != "<no value>" ]; then
        echo "  暴露端口: $ports"
    fi
    
    # 检查启动命令
    cmd=$(docker inspect --format='{{.Config.Cmd}}' $image)
    if [ "$cmd" != "<no value>" ] && [ "$cmd" != "[]" ]; then
        echo "  启动命令: $cmd"
    fi
done

# 3. 镜像依赖关系分析
echo ""
echo "3. 镜像依赖关系分析:"

# 找出所有基础镜像
BASE_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "(ubuntu|alpine|centos|debian|nginx|mysql|postgres|node|python)" | sort -u)

echo "基础镜像列表:" > image-dependencies.md
for base in $BASE_IMAGES; do
    echo "- $base" >> image-dependencies.md
    
    # 找出基于此镜像的容器
    dependent_images=$(docker images --format "{{.Repository}}:{{.Tag}}" | xargs -I {} sh -c 'docker inspect --format="{{.Parent}}" {} 2>/dev/null | grep -q "$base" && echo "    - {}"')
    
    if [ -n "$dependent_images" ]; then
        echo "$dependent_images" >> image-dependencies.md
    fi
done

# 4. 镜像安全扫描(简单版本)
echo ""
echo "4. 镜像安全检查:"

for image in $(docker images --filter "reference=restored/*" --format "{{.Repository}}:{{.Tag}}"); do
    echo ""
    echo "扫描: $image"
    
    # 检查是否有root用户
    docker run --rm $image sh -c 'id' 2>/dev/null | grep -q "uid=0" && echo "  ⚠ 以root用户运行"
    
    # 检查是否有敏感文件
    docker run --rm $image sh -c 'find / -name "*.pem" -o -name "*.key" -o -name "*pass*" 2>/dev/null | head -3' | while read file; do
        if [ -n "$file" ]; then
            echo "  ⚠ 发现可能敏感文件: $file"
        fi
    done
    
    # 检查暴露的端口
    docker inspect --format='{{range $p, $conf := .Config.ExposedPorts}}{{$p}} {{end}}' $image | while read port; do
        if [ -n "$port" ]; then
            echo "  ℹ 暴露端口: $port"
        fi
    done
done > image-security.txt

# 5. 镜像存储分析
echo ""
echo "5. 镜像存储分析:"

echo "镜像磁盘使用统计:" > image-storage.txt
docker system df -v >> image-storage.txt

echo "各镜像大小排序:"
docker images --format "table {{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -hr | head -10 | tee top-images-size.txt

# 6. 创建综合报告
echo ""
echo "=== 镜像检查完成 ==="

cat > image-check-report.md <<EOF
# 本地镜像检查报告

## 检查时间
$(date)

## 镜像统计
- 总镜像数: $(docker images | wc -l)
- 恢复的镜像: $(docker images --filter "reference=restored/*" | wc -l)
- 迁移镜像: $(docker images --filter "reference=migration/*" | wc -l)

## 存储使用
\`\`\`
$(docker system df)
\`\`\`

## 最大镜像(Top 10)
$(cat top-images-size.txt | sed 's/^/\- /')

## 恢复镜像详情
$(for image in $(docker images --filter "reference=restored/*" --format "{{.Repository}}:{{.Tag}}"); do
    echo "### $image"
    echo "- ID: \`$(docker inspect --format='{{.Id}}' $image | cut -c1-12)\`"
    echo "- 创建时间: $(docker inspect --format='{{.Created}}' $image)"
    echo "- 大小: $(docker inspect --format='{{.Size}}' $image | numfmt --to=iec)"
    echo "- 层数: $(docker inspect --format='{{.RootFS.Layers}}' $image | wc -w)"
    echo ""
done)

## 安全检查摘要
$(if [ -f "image-security.txt" ]; then
    echo "\`\`\`"
    cat image-security.txt
    echo "\`\`\`"
else
    echo "无安全问题发现"
fi)

## 依赖关系
详细依赖关系见: image-dependencies.md

## 建议
1. 清理未使用的镜像: \`docker image prune\`
2. 考虑使用多阶段构建减小镜像大小
3. 定期更新基础镜像以获取安全补丁
EOF

echo "详细报告已生成:"
ls -la *.md *.txt *.json
echo ""
echo "主报告: image-check-report.md"

5.3 在新服务器上运行容器

#!/bin/bash
echo "=== 在新服务器上运行容器 ==="
echo ""

RUN_DIR="container-run-$(date +%Y%m%d-%H%M%S)"
mkdir -p $RUN_DIR
cd $RUN_DIR

echo "运行目录: $(pwd)"
echo ""

# 1. 检查可用的恢复镜像
echo "1. 可用镜像列表:"
AVAILABLE_IMAGES=$(docker images --filter "reference=restored/*" --format "{{.Repository}}:{{.Tag}}")

if [ -z "$AVAILABLE_IMAGES" ]; then
    echo "未找到恢复的镜像"
    echo "请先执行镜像恢复步骤"
    exit 1
fi

echo "$AVAILABLE_IMAGES"
echo ""

# 2. 创建容器运行配置
echo "2. 创建容器运行配置"

cat > run-containers.sh <<'EOF'
#!/bin/bash
# 容器运行脚本
# 自动从恢复的镜像启动容器

set -e

echo "开始启动容器..."
echo ""

# 定义容器配置
declare -A CONTAINER_CONFIG

# Web服务器容器
CONTAINER_CONFIG[web]="
  --name nginx-restored
  -p 80:80
  -p 443:443
  --restart unless-stopped
  -v web-data:/usr/share/nginx/html
  -v web-logs:/var/log/nginx
  restored/nginx:latest
"

# 数据库容器
CONTAINER_CONFIG[database]="
  --name mysql-restored
  -p 3306:3306
  --restart unless-stopped
  -e MYSQL_ROOT_PASSWORD=Restored@123
  -e MYSQL_DATABASE=restored_db
  -v mysql-data:/var/lib/mysql
  restored/mysql:latest
"

# 应用容器
CONTAINER_CONFIG[app]="
  --name app-restored
  -p 3000:3000
  --restart unless-stopped
  -e DATABASE_URL=mysql://root:Restored@123@mysql-restored:3306/restored_db
  -v app-logs:/app/logs
  restored/app:latest
"

# 创建数据卷
echo "创建数据卷..."
for volume in web-data web-logs mysql-data app-logs; do
    if ! docker volume ls | grep -q $volume; then
        docker volume create $volume
        echo "  创建: $volume"
    else
        echo "  已存在: $volume"
    fi
done

echo ""

# 启动容器
for container in web database app; do
    if docker ps -a --format "{{.Names}}" | grep -q "${container}-restored"; then
        echo "容器 ${container}-restored 已存在,重新创建..."
        docker rm -f ${container}-restored
    fi
    
    echo "启动 ${container}-restored..."
    
    # 使用eval展开配置
    eval "docker run -d ${CONTAINER_CONFIG[$container]}"
    
    if [ $? -eq 0 ]; then
        echo "  ✓ 启动成功"
    else
        echo "  ✗ 启动失败"
    fi
    
    echo ""
done

# 等待服务启动
echo "等待服务启动..."
sleep 10

# 检查容器状态
echo "容器状态:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

echo ""
echo "容器启动完成!"
EOF

chmod +x run-containers.sh

# 3. 创建docker-compose配置
echo "3. 创建docker-compose配置"

cat > docker-compose.restored.yml <<'EOF'
version: '3.8'

services:
  mysql-restored:
    image: restored/mysql:latest
    container_name: mysql-restored
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: Restored@123
      MYSQL_DATABASE: restored_db
      MYSQL_USER: restored_user
      MYSQL_PASSWORD: Restored@123
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - restored-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

  app-restored:
    image: restored/app:latest
    container_name: app-restored
    restart: unless-stopped
    environment:
      DATABASE_URL: mysql://root:Restored@123@mysql-restored:3306/restored_db
      NODE_ENV: production
    ports:
      - "3000:3000"
    volumes:
      - app-data:/app/data
      - app-logs:/app/logs
    depends_on:
      mysql-restored:
        condition: service_healthy
    networks:
      - restored-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  nginx-restored:
    image: restored/nginx:latest
    container_name: nginx-restored
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - web-html:/usr/share/nginx/html
      - nginx-conf:/etc/nginx/conf.d
      - nginx-logs:/var/log/nginx
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - app-restored
    networks:
      - restored-network

volumes:
  mysql-data:
    driver: local
  app-data:
    driver: local
  app-logs:
    driver: local
  web-html:
    driver: local
  nginx-conf:
    driver: local
  nginx-logs:
    driver: local

networks:
  restored-network:
    driver: bridge
EOF

# 4. 创建初始化脚本
echo "4. 创建初始化脚本"

mkdir -p mysql nginx

cat > mysql/init.sql <<'EOF'
-- 初始化数据库
CREATE DATABASE IF NOT EXISTS restored_app;
USE restored_app;

-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 创建产品表
CREATE TABLE IF NOT EXISTS products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    price DECIMAL(10,2) NOT NULL,
    stock INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 插入测试数据
INSERT INTO users (username, email) VALUES
('admin', 'admin@restored.com'),
('user1', 'user1@restored.com'),
('user2', 'user2@restored.com')
ON DUPLICATE KEY UPDATE updated_at = CURRENT_TIMESTAMP;

INSERT INTO products (name, description, price, stock) VALUES
('迁移测试产品1', '从源服务器迁移的产品1', 99.99, 100),
('迁移测试产品2', '从源服务器迁移的产品2', 199.99, 50),
('迁移测试产品3', '从源服务器迁移的产品3', 299.99, 25)
ON DUPLICATE KEY UPDATE price = VALUES(price);

-- 创建视图
CREATE OR REPLACE VIEW user_summary AS
SELECT 
    COUNT(*) as total_users,
    MAX(created_at) as latest_user,
    DATE(created_at) as date_joined,
    COUNT(*) as users_per_day
FROM users
GROUP BY DATE(created_at);
EOF

cat > nginx/default.conf <<'EOF'
server {
    listen 80;
    server_name localhost;
    
    # 重定向到HTTPS(如果配置了SSL)
    # return 301 https://$host$request_uri;
    
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ =404;
    }
    
    location /api {
        proxy_pass http://app-restored:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # WebSocket支持
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    
    location /status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}

# HTTPS配置示例(需要SSL证书)
# server {
#     listen 443 ssl http2;
#     server_name localhost;
#     
#     ssl_certificate /etc/nginx/ssl/server.crt;
#     ssl_certificate_key /etc/nginx/ssl/server.key;
#     
#     # SSL配置...
# }
EOF

# 5. 创建健康检查脚本
echo "5. 创建健康检查脚本"

cat > health-check.sh <<'EOF'
#!/bin/bash
echo "容器健康检查"
echo "=============="
echo ""

# 检查Docker服务
echo "1. Docker服务状态:"
systemctl is-active docker
echo ""

# 检查所有容器状态
echo "2. 所有容器状态:"
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}\t{{.RunningFor}}"
echo ""

# 检查运行中的容器
echo "3. 运行中的容器:"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
echo ""

# 容器资源使用
echo "4. 容器资源使用:"
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.PIDs}}"
echo ""

# 网络检查
echo "5. 网络连接:"
for container in $(docker ps -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $container)
    echo "  $name: $ip"
    
    # 检查端口监听
    echo "  监听端口:"
    docker exec $container netstat -tuln 2>/dev/null | grep LISTEN | awk '{print "    "$4}'
done
echo ""

# 服务连通性检查
echo "6. 服务连通性检查:"

# MySQL检查
if docker ps --format "{{.Names}}" | grep -q mysql; then
    echo "  MySQL:"
    if docker exec mysql-restored mysqladmin ping -h localhost --silent; then
        echo "    ✓ 服务正常"
        # 检查数据库
        db_count=$(docker exec mysql-restored mysql -uroot -pRestored@123 -e "SHOW DATABASES;" 2>/dev/null | grep -c "Database")
        echo "    数据库数量: $db_count"
    else
        echo "    ✗ 服务异常"
    fi
fi

# Web应用检查
if docker ps --format "{{.Names}}" | grep -q app-restored; then
    echo "  Web应用:"
    if curl -s http://localhost:3000/health > /dev/null; then
        echo "    ✓ 服务正常"
        response=$(curl -s http://localhost:3000/health)
        echo "    健康状态: $response"
    else
        echo "    ✗ 服务异常"
    fi
fi

# Nginx检查
if docker ps --format "{{.Names}}" | grep -q nginx-restored; then
    echo "  Nginx:"
    if curl -s http://localhost > /dev/null; then
        echo "    ✓ 服务正常"
        http_code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost)
        echo "    HTTP状态码: $http_code"
    else
        echo "    ✗ 服务异常"
    fi
fi

# 日志检查
echo ""
echo "7. 最近错误日志:"
for container in $(docker ps -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    echo "  $name:"
    docker logs --tail=5 $container 2>&1 | grep -i error | head -3 | sed 's/^/    /'
done

echo ""
echo "健康检查完成!"
EOF

chmod +x health-check.sh

# 6. 创建启动说明
echo "6. 创建启动说明"

cat > README.md <<'EOF'
# 容器运行说明

## 文件结构

.
├── run-containers.sh # 快速启动脚本
├── docker-compose.restored.yml # Docker Compose配置
├── health-check.sh # 健康检查脚本
├── mysql/
│ └── init.sql # 数据库初始化脚本
├── nginx/
│ └── default.conf # Nginx配置
└── README.md # 本说明文件


## 启动方式

### 方式一:使用快速启动脚本
```bash
# 启动所有容器
./run-containers.sh

# 检查状态
./health-check.sh

方式二:使用Docker Compose

# 启动服务
docker-compose -f docker-compose.restored.yml up -d

# 查看日志
docker-compose -f docker-compose.restored.yml logs -f

# 停止服务
docker-compose -f docker-compose.restored.yml down

# 停止并清理数据卷
docker-compose -f docker-compose.restored.yml down -v

方式三:手动启动

# 1. 启动MySQL
docker run -d \
  --name mysql-restored \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=Restored@123 \
  -v mysql-data:/var/lib/mysql \
  restored/mysql:latest

# 2. 启动应用
docker run -d \
  --name app-restored \
  -p 3000:3000 \
  -e DATABASE_URL=mysql://root:Restored@123@mysql-restored:3306/restored_db \
  restored/app:latest

# 3. 启动Nginx
docker run -d \
  --name nginx-restored \
  -p 80:80 \
  --link app-restored:app \
  restored/nginx:latest

服务访问

  • Web界面: http://localhost
  • API服务: http://localhost:3000
  • MySQL: localhost:3306 (root/Restored@123)
  • Nginx状态: http://localhost/status

数据卷

  • mysql-data: MySQL数据存储
  • app-data: 应用数据存储
  • app-logs: 应用日志
  • web-html: 静态文件
  • nginx-conf: Nginx配置
  • nginx-logs: Nginx日志

维护命令

# 查看容器日志
docker logs -f nginx-restored

# 进入容器
docker exec -it mysql-restored mysql -uroot -p

# 备份数据
docker exec mysql-restored mysqldump -uroot -pRestored@123 --all-databases > backup.sql

# 监控资源
docker stats

故障排除

  1. 如果服务启动失败,检查端口是否被占用
  2. 查看容器日志: docker logs <容器名>
  3. 检查容器状态: docker ps -a
  4. 重启容器: docker restart <容器名>

安全建议

  1. 修改默认密码
  2. 配置防火墙规则
  3. 定期更新镜像
  4. 启用SSL/TLS

# 7. 执行启动
echo "7. 执行启动"
echo ""
echo "请选择启动方式:"
echo "1. 使用快速启动脚本"
echo "2. 使用Docker Compose"
echo "3. 手动启动"
echo ""
read -p "请选择 (1-3): " choice

case $choice in
    1)
        echo "使用快速启动脚本..."
        ./run-containers.sh
        ;;
    2)
        echo "使用Docker Compose..."
        docker-compose -f docker-compose.restored.yml up -d
        ;;
    3)
        echo "请参考README.md中的手动启动步骤"
        ;;
    *)
        echo "无效选择"
        ;;
esac

echo ""
echo "=== 容器运行配置完成 ==="
echo "所有配置已保存到: $(pwd)"
echo "请查看README.md获取详细说明"

5.4 查看新容器状态

#!/bin/bash
echo "=== 新容器状态监控 ==="
echo ""

MONITOR_DIR="container-monitor-$(date +%Y%m%d-%H%M%S)"
mkdir -p $MONITOR_DIR
cd $MONITOR_DIR

echo "监控目录: $(pwd)"
echo ""

# 1. 实时状态监控
echo "1. 实时状态监控"
echo "按Ctrl+C退出监控"
echo ""

# 创建监控脚本
cat > realtime-monitor.sh <<'EOF'
#!/bin/bash
# 实时容器监控

INTERVAL=5  # 监控间隔(秒)
DURATION=300  # 监控持续时间(秒),0表示无限

echo "开始容器实时监控 (间隔: ${INTERVAL}s)"
echo ""

end_time=$((SECONDS + DURATION)) if [ $DURATION -gt 0 ]; then end_time=0; fi

while true; do
    clear
    
    echo "================================================"
    echo "容器实时监控 - $(date)"
    echo "================================================"
    echo ""
    
    # 运行容器状态
    echo "运行中的容器:"
    echo "----------------------------------------------------------------"
    docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" | tail -n +2
    echo ""
    
    # 资源使用
    echo "资源使用情况:"
    echo "----------------------------------------------------------------"
    docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
    echo ""
    
    # 网络统计
    echo "网络连接统计:"
    echo "----------------------------------------------------------------"
    for container in $(docker ps -q); do
        name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
        echo "$name:"
        
        # TCP连接数
        tcp_conn=$(docker exec $container sh -c "netstat -an | grep ESTABLISHED | wc -l" 2>/dev/null || echo "N/A")
        echo "  ESTABLISHED连接: $tcp_conn"
        
        # 端口监听
        listening=$(docker exec $container sh -c "netstat -tuln | grep LISTEN | wc -l" 2>/dev/null || echo "N/A")
        echo "  监听端口数: $listening"
    done
    echo ""
    
    # 服务健康检查
    echo "服务健康状态:"
    echo "----------------------------------------------------------------"
    
    # MySQL健康检查
    if docker ps --format "{{.Names}}" | grep -q mysql; then
        if docker exec mysql-restored mysqladmin ping -h localhost --silent 2>/dev/null; then
            echo "✓ MySQL: 健康"
        else
            echo "✗ MySQL: 异常"
        fi
    fi
    
    # Web应用健康检查
    if docker ps --format "{{.Names}}" | grep -q app-restored; then
        if curl -s -f http://localhost:3000/health > /dev/null 2>&1; then
            echo "✓ Web应用: 健康"
        else
            echo "✗ Web应用: 异常"
        fi
    fi
    
    # Nginx健康检查
    if docker ps --format "{{.Names}}" | grep -q nginx; then
        if curl -s -f http://localhost > /dev/null 2>&1; then
            echo "✓ Nginx: 健康"
        else
            echo "✗ Nginx: 异常"
        fi
    fi
    
    echo ""
    
    # 检查监控时间
    if [ $DURATION -gt 0 ] && [ $SECONDS -ge $end_time ]; then
        echo "监控时间到"
        break
    fi
    
    echo "下一次更新: ${INTERVAL}秒后..."
    sleep $INTERVAL
done
EOF

chmod +x realtime-monitor.sh

# 2. 生成状态报告
echo "2. 生成详细状态报告"

cat > generate-report.sh <<'EOF'
#!/bin/bash
echo "生成容器状态报告..."
echo ""

REPORT_FILE="container-status-report-$(date +%Y%m%d-%H%M%S).md"

cat > $REPORT_FILE <<REPORTEOF
# 容器状态报告

## 报告信息
- 生成时间: $(date)
- 服务器: $(hostname)
- Docker版本: $(docker --version)

## 系统概览
\`\`\`
$(docker system df)
\`\`\`

## 容器状态

### 所有容器
\`\`\`
$(docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}\t{{.CreatedAt}}")
\`\`\`

### 运行中的容器
\`\`\`
$(docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.RunningFor}}")
\`\`\`

## 详细容器信息

EOF

# 为每个容器添加详细信息
for container in $(docker ps -a -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    
    cat >> $REPORT_FILE <<CONTAINEREOF
### $name

**基本信息:**
- 容器ID: \`$(docker inspect --format='{{.Id}}' $container | cut -c1-12)\`
- 镜像: \`$(docker inspect --format='{{.Config.Image}}' $container)\`
- 状态: $(docker inspect --format='{{.State.Status}}' $container)
- 运行时长: $(docker inspect --format='{{.State.StartedAt}}' $container | xargs -I {} date -d {} "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "N/A")

**资源限制:**
- 内存限制: $(docker inspect --format='{{.HostConfig.Memory}}' $container | numfmt --to=iec 2>/dev/null || echo "无限制")
- CPU限制: $(docker inspect --format='{{.HostConfig.NanoCpus}}' $container | awk '{if($1>0) print $1/1000000000 " CPUs"; else print "无限制"}')
- 重启策略: $(docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' $container)

**网络配置:**
$(docker inspect --format='{{range .NetworkSettings.Networks}}IP地址: {{.IPAddress}}
网关: {{.Gateway}}
MAC地址: {{.MacAddress}}
{{end}}' $container)

**挂载卷:**
\`\`\`
$(docker inspect --format='{{range .Mounts}}{{.Type}}: {{.Source}} -> {{.Destination}}
{{end}}' $container)
\`\`\`

**环境变量:**
\`\`\`
$(docker inspect --format='{{range .Config.Env}}{{.}}
{{end}}' $container | grep -E "(PASS|KEY|SECRET|DB|HOST|PORT)" | head -10)
\`\`\`

---

CONTAINEREOF
done

# 添加性能数据
cat >> $REPORT_FILE <<PERFORMANCEEOF
## 性能数据

### 当前资源使用
\`\`\`
$(docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}")
\`\`\`

### 日志信息
EOF

# 添加日志摘要
for container in $(docker ps -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    
    cat >> $REPORT_FILE <<LOGEOF
#### $name 最近日志
\`\`\`
$(docker logs --tail=20 $container 2>&1 | tail -5)
\`\`\`

LOGEOF
done

cat >> $REPORT_FILE <<ENDOF
## 建议

1. **资源优化:**
   - 检查内存使用,适当调整限制
   - 监控CPU使用率,考虑增加资源

2. **安全建议:**
   - 检查暴露的端口
   - 验证环境变量安全性
   - 更新基础镜像

3. **维护建议:**
   - 定期清理未使用的容器和镜像
   - 配置日志轮转
   - 设置监控告警

## 检查清单
- [ ] 所有容器正常运行
- [ ] 网络连接正常
- [ ] 存储卷正确挂载
- [ ] 服务端口可访问
- [ ] 日志无异常错误
- [ ] 资源使用在合理范围

---
*报告生成完成*
REPORTEOF

echo "报告已生成: $REPORT_FILE"
EOF

chmod +x generate-report.sh

# 3. 创建告警检查脚本
echo "3. 创建告警检查脚本"

cat > alert-check.sh <<'EOF'
#!/bin/bash
echo "容器告警检查"
echo "============="
echo ""

ALERT=false
ALERT_MESSAGES=()

# 1. 检查容器状态
echo "检查容器状态..."
for container in $(docker ps -a --filter "status=exited" --format "{{.Names}}"); do
    exit_code=$(docker inspect --format='{{.State.ExitCode}}' $container)
    
    if [ $exit_code -ne 0 ]; then
        ALERT=true
        ALERT_MESSAGES+=("容器 $container 异常退出,退出码: $exit_code")
        echo "✗ $container: 异常退出 (code: $exit_code)"
    else
        echo "✓ $container: 正常退出"
    fi
done

# 2. 检查重启次数
echo ""
echo "检查容器重启..."
for container in $(docker ps -a --format "{{.Names}}"); do
    restart_count=$(docker inspect --format='{{.RestartCount}}' $container)
    
    if [ $restart_count -gt 5 ]; then
        ALERT=true
        ALERT_MESSAGES+=("容器 $container 重启次数过多: $restart_count")
        echo "⚠ $container: 重启 $restart_count 次"
    fi
done

# 3. 检查资源使用
echo ""
echo "检查资源使用..."
docker stats --no-stream --format "{{.Name}} {{.CPUPerc}} {{.MemPerc}}" | while read line; do
    name=$(echo $line | awk '{print $1}')
    cpu=$(echo $line | awk '{print $2}' | sed 's/%//')
    mem=$(echo $line | awk '{print $3}' | sed 's/%//')
    
    if [ $(echo "$cpu > 80" | bc) -eq 1 ]; then
        ALERT=true
        ALERT_MESSAGES+=("容器 $name CPU使用率过高: ${cpu}%")
        echo "⚠ $name: CPU使用率 ${cpu}%"
    fi
    
    if [ $(echo "$mem > 80" | bc) -eq 1 ]; then
        ALERT=true
        ALERT_MESSAGES+=("容器 $name 内存使用率过高: ${mem}%")
        echo "⚠ $name: 内存使用率 ${mem}%"
    fi
done

# 4. 检查端口冲突
echo ""
echo "检查端口冲突..."
used_ports=""
for container in $(docker ps -q); do
    ports=$(docker port $container | awk '{print $3}' | cut -d':' -f2)
    for port in $ports; do
        if echo "$used_ports" | grep -q "\b$port\b"; then
            ALERT=true
            ALERT_MESSAGES+=("端口 $port 被多个容器使用")
            echo "⚠ 端口冲突: $port"
        fi
        used_ports="$used_ports $port"
    done
done

# 5. 检查磁盘空间
echo ""
echo "检查磁盘空间..."
docker system df | tail -n +2 | while read line; do
    type=$(echo $line | awk '{print $1}')
    usage=$(echo $line | awk '{print $5}' | sed 's/%//')
    
    if [ $(echo "$usage > 85" | bc) -eq 1 ]; then
        ALERT=true
        ALERT_MESSAGES+=("$type 磁盘使用率过高: ${usage}%")
        echo "⚠ $type: 磁盘使用率 ${usage}%"
    fi
done

# 6. 输出结果
echo ""
echo "========================================="
if [ "$ALERT" = true ]; then
    echo "发现告警!"
    echo ""
    echo "告警信息:"
    for msg in "${ALERT_MESSAGES[@]}"; do
        echo "• $msg"
    done
    
    # 发送告警(示例)
    # echo "发送邮件告警..."
    # echo "${ALERT_MESSAGES[@]}" | mail -s "Docker容器告警" admin@example.com
    
    exit 1
else
    echo "所有检查通过,无告警"
    exit 0
fi
EOF

chmod +x alert-check.sh

# 4. 创建监控面板
echo "4. 创建监控面板"

cat > monitor-dashboard.sh <<'EOF'
#!/bin/bash
# 简易监控面板

clear

while true; do
    echo "╔══════════════════════════════════════════════════════════╗"
    echo "║                  Docker容器监控面板                       ║"
    echo "╠══════════════════════════════════════════════════════════╣"
    echo "║ 选项:                                                    ║"
    echo "║  1. 实时状态监控                                          ║"
    echo "║  2. 生成状态报告                                          ║"
    echo "║  3. 告警检查                                              ║"
    echo "║  4. 查看容器日志                                          ║"
    echo "║  5. 资源使用详情                                          ║"
    echo "║  6. 网络连接查看                                          ║"
    echo "║  7. 数据卷状态                                            ║"
    echo "║  8. 退出                                                  ║"
    echo "╚══════════════════════════════════════════════════════════╝"
    echo ""
    read -p "请选择操作 (1-8): " choice
    
    case $choice in
        1)
            ./realtime-monitor.sh
            ;;
        2)
            ./generate-report.sh
            ;;
        3)
            ./alert-check.sh
            read -p "按回车键继续..."
            ;;
        4)
            echo "选择要查看日志的容器:"
            select container in $(docker ps --format "{{.Names}}") "返回"; do
                if [ "$container" = "返回" ]; then
                    break
                elif [ -n "$container" ]; then
                    echo "查看 $container 的日志 (Ctrl+C退出):"
                    docker logs -f $container
                fi
            done
            ;;
        5)
            echo "资源使用详情:"
            echo "------------------------------------------------"
            docker stats --no-stream
            echo ""
            read -p "按回车键继续..."
            ;;
        6)
            echo "网络连接:"
            echo "------------------------------------------------"
            docker network ls
            echo ""
            echo "容器网络详情:"
            for container in $(docker ps -q); do
                name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
                ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $container)
                echo "$name: $ip"
            done
            echo ""
            read -p "按回车键继续..."
            ;;
        7)
            echo "数据卷状态:"
            echo "------------------------------------------------"
            docker volume ls
            echo ""
            echo "数据卷使用情况:"
            for volume in $(docker volume ls -q); do
                mount=$(docker volume inspect --format='{{.Mountpoint}}' $volume)
                size=$(sudo du -sh $mount 2>/dev/null | cut -f1 || echo "未知")
                echo "$volume: $mount ($size)"
            done
            echo ""
            read -p "按回车键继续..."
            ;;
        8)
            echo "退出监控面板"
            exit 0
            ;;
        *)
            echo "无效选择"
            ;;
    esac
    
    clear
done
EOF

chmod +x monitor-dashboard.sh

# 5. 创建定期检查任务
echo "5. 创建定期检查任务"

cat > setup-cron.sh <<'EOF'
#!/bin/bash
echo "设置定期检查任务"
echo ""

# 创建日志目录
mkdir -p /var/log/docker-monitor

# 添加每日检查任务
(crontab -l 2>/dev/null; echo "# Docker容器每日检查") | crontab -
(crontab -l 2>/dev/null; echo "0 2 * * * $(pwd)/generate-report.sh >> /var/log/docker-monitor/daily-check.log 2>&1") | crontab -

# 添加每小时告警检查
(crontab -l 2>/dev/null; echo "# Docker容器每小时告警检查") | crontab -
(crontab -l 2>/dev/null; echo "0 * * * * $(pwd)/alert-check.sh >> /var/log/docker-monitor/hourly-alert.log 2>&1") | crontab -

# 添加日志轮转配置
cat > /etc/logrotate.d/docker-monitor <<LOGROTATE
/var/log/docker-monitor/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 644 root root
}
LOGROTATE

echo "定期任务已设置:"
echo "1. 每日凌晨2点生成状态报告"
echo "2. 每小时执行告警检查"
echo "3. 日志自动轮转(保留30天)"
echo ""
echo "日志目录: /var/log/docker-monitor"
EOF

chmod +x setup-cron.sh

echo ""
echo "=== 容器状态监控配置完成 ==="
echo "可用的监控工具:"
echo "1. ./realtime-monitor.sh    # 实时监控"
echo "2. ./generate-report.sh     # 生成报告"
echo "3. ./alert-check.sh         # 告警检查"
echo "4. ./monitor-dashboard.sh   # 监控面板"
echo "5. ./setup-cron.sh          # 设置定期任务"
echo ""
echo "监控目录: $(pwd)"

5.5 测试网站内容

#!/bin/bash
echo "=== 网站内容测试 ==="
echo ""

TEST_DIR="website-test-$(date +%Y%m%d-%H%M%S)"
mkdir -p $TEST_DIR
cd $TEST_DIR

echo "测试目录: $(pwd)"
echo ""

# 1. 基础连通性测试
echo "1. 基础连通性测试"
echo "================="

cat > basic-connectivity.sh <<'EOF'
#!/bin/bash
echo "基础连通性测试"
echo ""

# 定义测试端点
declare -A ENDPOINTS=(
    ["首页"]="http://localhost"
    ["API健康检查"]="http://localhost:3000/health"
    ["Nginx状态"]="http://localhost/status"
    ["API用户列表"]="http://localhost:3000/api/users"
)

# 测试函数
test_endpoint() {
    local name=$1
    local url=$2
    
    echo -n "测试 $name ($url)... "
    
    # 使用curl测试
    response=$(curl -s -o /dev/null -w "%{http_code} %{time_total}s" -I $url 2>&1)
    http_code=$(echo $response | awk '{print $1}')
    response_time=$(echo $response | awk '{print $2}')
    
    if [[ $http_code =~ ^2[0-9]{2}$ ]] || [[ $http_code =~ ^3[0-9]{2}$ ]]; then
        echo "✓ 成功 ($http_code, ${response_time}s)"
        return 0
    else
        echo "✗ 失败 ($http_code, ${response_time}s)"
        return 1
    fi
}

# 执行测试
FAIL_COUNT=0
TOTAL_TESTS=0

for name in "${!ENDPOINTS[@]}"; do
    test_endpoint "$name" "${ENDPOINTS[$name]}"
    if [ $? -ne 0 ]; then
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi
    TOTAL_TESTS=$((TOTAL_TESTS + 1))
done

echo ""
echo "测试完成: $TOTAL_TESTS 个测试, $FAIL_COUNT 个失败"

if [ $FAIL_COUNT -eq 0 ]; then
    echo "✓ 所有基础连通性测试通过"
    exit 0
else
    echo "✗ 部分测试失败"
    exit 1
fi
EOF

chmod +x basic-connectivity.sh

# 2. 功能完整性测试
echo "2. 功能完整性测试"
echo "================="

cat > functionality-test.sh <<'EOF'
#!/bin/bash
echo "功能完整性测试"
echo ""

TEST_RESULTS=""

# 测试1: 数据库连接
echo "测试1: 数据库连接"
if docker exec mysql-restored mysql -uroot -pRestored@123 -e "SELECT 1" > /dev/null 2>&1; then
    echo "✓ 数据库连接正常"
    TEST_RESULTS="${TEST_RESULTS}数据库连接: 通过\n"
else
    echo "✗ 数据库连接失败"
    TEST_RESULTS="${TEST_RESULTS}数据库连接: 失败\n"
fi

# 测试2: 数据库数据完整性
echo ""
echo "测试2: 数据库数据完整性"
db_tables=$(docker exec mysql-restored mysql -uroot -pRestored@123 -e "SHOW TABLES FROM restored_db" 2>/dev/null | wc -l)

if [ $db_tables -gt 0 ]; then
    echo "✓ 数据库表存在 ($((db_tables-1)) 个表)"
    
    # 检查用户表数据
    user_count=$(docker exec mysql-restored mysql -uroot -pRestored@123 -e "SELECT COUNT(*) FROM restored_db.users" 2>/dev/null | tail -1)
    if [ $user_count -gt 0 ]; then
        echo "✓ 用户数据存在 ($user_count 个用户)"
        TEST_RESULTS="${TEST_RESULTS}数据库数据: 通过\n"
    else
        echo "⚠ 用户表为空"
        TEST_RESULTS="${TEST_RESULTS}数据库数据: 警告\n"
    fi
else
    echo "✗ 数据库表不存在"
    TEST_RESULTS="${TEST_RESULTS}数据库数据: 失败\n"
fi

# 测试3: API功能测试
echo ""
echo "测试3: API功能测试"

# 测试健康检查端点
if curl -s http://localhost:3000/health | grep -q '"status":"ok"'; then
    echo "✓ API健康检查正常"
    
    # 测试用户API
    users_response=$(curl -s http://localhost:3000/api/users)
    if echo "$users_response" | grep -q "username"; then
        user_count=$(echo "$users_response" | grep -c "username")
        echo "✓ 用户API正常 ($user_count 个用户)"
        TEST_RESULTS="${TEST_RESULTS}API功能: 通过\n"
    else
        echo "⚠ 用户API返回异常"
        TEST_RESULTS="${TEST_RESULTS}API功能: 警告\n"
    fi
else
    echo "✗ API健康检查失败"
    TEST_RESULTS="${TEST_RESULTS}API功能: 失败\n"
fi

# 测试4: 网站内容测试
echo ""
echo "测试4: 网站内容测试"

homepage_content=$(curl -s http://localhost)
if [ -n "$homepage_content" ]; then
    echo "✓ 网站首页可访问"
    
    # 检查页面标题
    if echo "$homepage_content" | grep -q "<title>"; then
        title=$(echo "$homepage_content" | grep -o "<title>[^<]*</title>" | sed 's/<title>//;s/<\/title>//')
        echo "✓ 页面标题: '$title'"
        TEST_RESULTS="${TEST_RESULTS}网站内容: 通过\n"
    else
        echo "⚠ 页面缺少标题"
        TEST_RESULTS="${TEST_RESULTS}网站内容: 警告\n"
    fi
else
    echo "✗ 网站首页无法访问"
    TEST_RESULTS="${TEST_RESULTS}网站内容: 失败\n"
fi

# 测试5: Nginx配置测试
echo ""
echo "测试5: Nginx配置测试"

# 检查Nginx配置文件
if docker exec nginx-restored nginx -t > /dev/null 2>&1; then
    echo "✓ Nginx配置语法正确"
    
    # 检查静态文件服务
    if curl -s -o /dev/null -w "%{http_code}" http://localhost | grep -q "200"; then
        echo "✓ 静态文件服务正常"
        TEST_RESULTS="${TEST_RESULTS}Nginx配置: 通过\n"
    else
        echo "⚠ 静态文件服务异常"
        TEST_RESULTS="${TEST_RESULTS}Nginx配置: 警告\n"
    fi
else
    nginx_error=$(docker exec nginx-restored nginx -t 2>&1)
    echo "✗ Nginx配置错误: $nginx_error"
    TEST_RESULTS="${TEST_RESULTS}Nginx配置: 失败\n"
fi

# 输出测试总结
echo ""
echo "══════════════════════════════════════════════════════════"
echo "功能完整性测试总结"
echo "══════════════════════════════════════════════════════════"
echo -e "$TEST_RESULTS"
echo "══════════════════════════════════════════════════════════"

# 统计结果
pass_count=$(echo -e "$TEST_RESULTS" | grep -c "通过")
warn_count=$(echo -e "$TEST_RESULTS" | grep -c "警告")
fail_count=$(echo -e "$TEST_RESULTS" | grep -c "失败")

echo ""
echo "总计:"
echo "  ✓ 通过: $pass_count"
echo "  ⚠ 警告: $warn_count"
echo "  ✗ 失败: $fail_count"

if [ $fail_count -eq 0 ]; then
    if [ $warn_count -eq 0 ]; then
        echo "✅ 所有功能测试完全通过"
        exit 0
    else
        echo "⚠ 功能测试通过,但有警告需要关注"
        exit 0
    fi
else
    echo "❌ 功能测试存在失败项"
    exit 1
fi
EOF

chmod +x functionality-test.sh

# 3. 性能测试
echo "3. 性能测试"
echo "============"

cat > performance-test.sh <<'EOF'
#!/bin/bash
echo "网站性能测试"
echo ""

PERF_RESULTS="performance-results-$(date +%Y%m%d-%H%M%S).txt"

# 测试参数
CONCURRENT_USERS=10
REQUESTS_PER_USER=100
TEST_URLS="http://localhost http://localhost:3000/health http://localhost:3000/api/users"

echo "性能测试配置:"
echo "  并发用户数: $CONCURRENT_USERS"
echo "  每用户请求数: $REQUESTS_PER_USER"
echo "  测试URL: $TEST_URLS"
echo "  结果文件: $PERF_RESULTS"
echo ""

# 检查是否安装了ab(Apache Bench)
if ! command -v ab &> /dev/null; then
    echo "安装Apache Bench..."
    if command -v apt-get &> /dev/null; then
        sudo apt-get update
        sudo apt-get install -y apache2-utils
    elif command -v yum &> /dev/null; then
        sudo yum install -y httpd-tools
    else
        echo "无法安装ab,跳过性能测试"
        exit 0
    fi
fi

# 性能测试函数
run_performance_test() {
    local url=$1
    local name=$2
    
    echo ""
    echo "测试: $name ($url)"
    echo "------------------------------------------------"
    
    # 运行ab测试
    ab -n $((CONCURRENT_USERS * REQUESTS_PER_USER)) \
       -c $CONCURRENT_USERS \
       -k \
       -q \
       "$url" 2>&1 | tee -a $PERF_RESULTS
    
    # 提取关键指标
    echo "" >> $PERF_RESULTS
    echo "================================================" >> $PERF_RESULTS
    echo "" >> $PERF_RESULTS
}

# 执行性能测试
echo "开始性能测试..." > $PERF_RESULTS
echo "测试时间: $(date)" >> $PERF_RESULTS
echo "" >> $PERF_RESULTS

for url in $TEST_URLS; do
    # 提取主机名和路径作为测试名称
    if [[ $url == "http://localhost" ]]; then
        name="网站首页"
    elif [[ $url == "http://localhost:3000/health" ]]; then
        name="API健康检查"
    elif [[ $url == "http://localhost:3000/api/users" ]]; then
        name="API用户列表"
    else
        name="其他端点"
    fi
    
    run_performance_test "$url" "$name"
    
    # 间隔一下,避免压测太密集
    sleep 2
done

# 分析性能结果
echo ""
echo "性能测试结果分析:"
echo "══════════════════════════════════════════════════════════"

if [ -f "$PERF_RESULTS" ]; then
    # 提取关键指标
    echo "从 $PERF_RESULTS 提取关键指标:"
    echo ""
    
    # 请求成功率
    success_rate=$(grep "Complete requests" $PERF_RESULTS | tail -1 | awk '{print $3"/"$2" ("$3/$2*100"%)"}')
    echo "请求成功率: $success_rate"
    
    # 失败请求数
    failed_requests=$(grep "Failed requests" $PERF_RESULTS | tail -1 | awk '{print $3}')
    echo "失败请求数: $failed_requests"
    
    # 每秒请求数
    requests_per_sec=$(grep "Requests per second" $PERF_RESULTS | tail -1 | awk '{print $4}')
    echo "每秒请求数: $requests_per_sec"
    
    # 平均响应时间
    time_per_request=$(grep "Time per request" $PERF_RESULTS | grep "mean" | tail -1 | awk '{print $4" "$5}')
    echo "平均响应时间: $time_per_request"
    
    # 最长响应时间
    max_time=$(grep "100%" $PERF_RESULTS | tail -1 | awk '{print $2}')
    echo "最长响应时间: ${max_time}ms"
    
    # 传输速率
    transfer_rate=$(grep "Transfer rate" $PERF_RESULTS | tail -1 | awk '{print $3" "$4}')
    echo "传输速率: $transfer_rate"
    
    echo ""
    echo "性能评估:"
    
    # 简单评估逻辑
    rps_num=$(echo $requests_per_sec | sed 's/\..*//')
    if [ $rps_num -gt 100 ]; then
        echo "  ✓ 吞吐量优秀 (>100 req/s)"
    elif [ $rps_num -gt 50 ]; then
        echo "  ✓ 吞吐量良好 (50-100 req/s)"
    elif [ $rps_num -gt 10 ]; then
        echo "  ⚠ 吞吐量一般 (10-50 req/s)"
    else
        echo "  ✗ 吞吐量较低 (<10 req/s)"
    fi
    
    avg_time_num=$(echo $time_per_request | sed 's/\..*//')
    if [ $avg_time_num -lt 100 ]; then
        echo "  ✓ 响应时间优秀 (<100ms)"
    elif [ $avg_time_num -lt 500 ]; then
        echo "  ✓ 响应时间良好 (100-500ms)"
    elif [ $avg_time_num -lt 1000 ]; then
        echo "  ⚠ 响应时间一般 (500-1000ms)"
    else
        echo "  ✗ 响应时间较慢 (>1000ms)"
    fi
    
    if [ $failed_requests -eq 0 ]; then
        echo "  ✓ 请求成功率优秀 (100%)"
    elif [ $failed_requests -lt 10 ]; then
        echo "  ⚠ 请求成功率良好 (<10失败)"
    else
        echo "  ✗ 请求成功率较差 (≥10失败)"
    fi
    
else
    echo "性能测试结果文件不存在"
fi

echo ""
echo "详细性能报告: $PERF_RESULTS"
EOF

chmod +x performance-test.sh

# 4. 安全测试
echo "4. 安全测试"
echo "============"

cat > security-test.sh <<'EOF'
#!/bin/bash
echo "网站安全测试"
echo ""

SECURITY_REPORT="security-report-$(date +%Y%m%d-%H%M%S).md"

cat > $SECURITY_REPORT <<REPORTEOF
# 网站安全测试报告

## 测试信息
- 测试时间: $(date)
- 目标服务器: $(hostname)
- 测试URL: http://localhost

## 1. 信息泄露测试

### HTTP头信息
\`\`\`
$(curl -I http://localhost 2>&1)
\`\`\`

### 敏感文件扫描
扫描常见敏感文件:
REPORTEOF

# 检查常见敏感文件
SENSITIVE_PATHS=(
    "/.git"
    "/.env"
    "/config.php"
    "/phpinfo.php"
    "/admin"
    "/backup"
    "/README.md"
    "/CHANGELOG.md"
)

for path in "${SENSITIVE_PATHS[@]}"; do
    status_code=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost$path")
    
    if [[ $status_code =~ ^2[0-9]{2}$ ]] || [[ $status_code =~ ^3[0-9]{2}$ ]]; then
        echo "- ⚠ \`$path\`: 可访问 ($status_code)" >> $SECURITY_REPORT
    elif [ $status_code -ne 404 ]; then
        echo "- ℹ \`$path\`: 返回 $status_code" >> $SECURITY_REPORT
    fi
done

cat >> $SECURITY_REPORT <<REPORTEOF

## 2. SSL/TLS测试(如果启用HTTPS)

\`\`\`
$(if curl -s -f https://localhost > /dev/null 2>&1; then
    echo "HTTPS已启用"
    echo "证书信息:"
    openssl s_client -connect localhost:443 -servername localhost 2>/dev/null | openssl x509 -noout -dates -subject
else
    echo "HTTPS未启用"
fi)
\`\`\`

## 3. 安全头检查

\`\`\`
$(curl -s -D - http://localhost -o /dev/null | grep -iE "(security|protection|x-)" || echo "未发现安全头")
\`\`\`

## 4. CORS配置检查

\`\`\`
$(curl -s -X OPTIONS -H "Origin: http://evil.com" -D - http://localhost -o /dev/null | grep -i "access-control" || echo "未发现CORS头")
\`\`\`

## 5. 容器安全扫描

### 运行容器列表
\`\`\`
$(docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}")
\`\`\`

### 以root用户运行的容器
\`\`\`
$(for container in $(docker ps -q); do
    user=$(docker exec $container whoami 2>/dev/null || echo "unknown")
    if [ "$user" = "root" ]; then
        name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
        echo "$name: 以root用户运行"
    fi
done)
\`\`\`

### 暴露的端口
\`\`\`
$(for container in $(docker ps -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    ports=$(docker port $container)
    if [ -n "$ports" ]; then
        echo "$name:"
        echo "$ports" | sed 's/^/  /'
    fi
done)
\`\`\`

## 6. 数据库安全

### MySQL配置检查
\`\`\`
$(docker exec mysql-restored mysql -uroot -pRestored@123 -e "SHOW VARIABLES LIKE '%ssl%'; SHOW VARIABLES LIKE '%auth%';" 2>/dev/null || echo "无法连接数据库")
\`\`\`

## 7. 建议的安全改进

### 高优先级
1. **启用HTTPS** - 为网站配置SSL证书
2. **修改默认密码** - 更改数据库默认密码
3. **限制端口暴露** - 只暴露必要的端口

### 中优先级
1. **添加安全头** - 配置CSP、HSTS等安全头
2. **最小权限原则** - 容器不要以root用户运行
3. **定期更新** - 保持镜像和系统更新

### 低优先级
1. **配置WAF** - 添加Web应用防火墙
2. **日志审计** - 配置完整的日志记录和监控
3. **备份策略** - 实施定期备份和恢复测试

## 8. 风险评估

| 风险项 | 等级 | 描述 | 建议 |
|--------|------|------|------|
| HTTP明文传输 | 高 | 所有通信未加密 | 启用HTTPS |
| 默认密码 | 高 | 使用默认数据库密码 | 立即修改 |
| Root用户运行 | 中 | 容器以root运行 | 使用非root用户 |
| 端口暴露过多 | 中 | 暴露不必要的端口 | 限制端口范围 |
| 缺少安全头 | 低 | 缺少安全相关HTTP头 | 添加安全头 |

## 总结

本次安全测试发现了 $(grep -c "⚠" $SECURITY_REPORT) 个警告项和 $(grep -c "高风险" $SECURITY_REPORT) 个高风险项。

**建议立即处理高风险项,特别是启用HTTPS和修改默认密码。**

---
*报告生成完成*
REPORTEOF

echo "安全测试报告已生成: $SECURITY_REPORT"
echo ""
echo "查看报告: cat $SECURITY_REPORT"
EOF

chmod +x security-test.sh

# 5. 综合测试套件
echo "5. 综合测试套件"
echo "================"

cat > comprehensive-test.sh <<'EOF'
#!/bin/bash
echo "网站综合测试套件"
echo "=================="
echo ""

# 创建测试结果目录
RESULTS_DIR="test-results-$(date +%Y%m%d-%H%M%S)"
mkdir -p $RESULTS_DIR

echo "测试结果将保存到: $RESULTS_DIR"
echo ""

# 测试计数器
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
WARNING_TESTS=0

# 运行测试并记录结果
run_test() {
    local test_name=$1
    local test_script=$2
    local test_file="$RESULTS_DIR/${test_name}.log"
    
    echo "运行测试: $test_name"
    echo "----------------------------------------"
    
    # 运行测试脚本
    if [ -f "$test_script" ] && [ -x "$test_script" ]; then
        $test_script > "$test_file" 2>&1
        exit_code=$?
        
        # 分析结果
        if [ $exit_code -eq 0 ]; then
            result="✓ 通过"
            PASSED_TESTS=$((PASSED_TESTS + 1))
        elif [ $exit_code -eq 1 ]; then
            result="✗ 失败"
            FAILED_TESTS=$((FAILED_TESTS + 1))
        else
            result="⚠ 警告"
            WARNING_TESTS=$((WARNING_TESTS + 1))
        fi
        
        echo "$result"
        
        # 显示摘要
        echo "结果摘要:"
        tail -10 "$test_file"
    else
        echo "✗ 测试脚本不存在或不可执行"
        FAILED_TESTS=$((FAILED_TESTS + 1))
    fi
    
    TOTAL_TESTS=$((TOTAL_TESTS + 1))
    echo ""
}

# 执行所有测试
run_test "基础连通性" "./basic-connectivity.sh"
run_test "功能完整性" "./functionality-test.sh"
run_test "性能测试" "./performance-test.sh"
run_test "安全测试" "./security-test.sh"

# 生成综合报告
cat > "$RESULTS_DIR/comprehensive-report.md" <<REPORTEOF
# 网站综合测试报告

## 测试概述
- 测试时间: $(date)
- 测试环境: $(hostname)
- Docker版本: $(docker --version)
- 总测试数: $TOTAL_TESTS

## 测试结果统计
| 测试类型 | 结果 | 详情 |
|----------|------|------|
| 基础连通性 | $(if [ -f "$RESULTS_DIR/基础连通性.log" ]; then tail -1 "$RESULTS_DIR/基础连通性.log"; else echo "未执行"; fi) | [查看详情](#基础连通性) |
| 功能完整性 | $(if [ -f "$RESULTS_DIR/功能完整性.log" ]; then grep -E "^(✅|⚠|❌)" "$RESULTS_DIR/功能完整性.log" | tail -1; else echo "未执行"; fi) | [查看详情](#功能完整性) |
| 性能测试 | $(if [ -f "$RESULTS_DIR/性能测试.log" ]; then grep -E "^(性能评估|吞吐量|响应时间)" "$RESULTS_DIR/性能测试.log" | head -3 | sed 's/^/- /'; else echo "未执行"; fi) | [查看详情](#性能测试) |
| 安全测试 | $(if [ -f "$RESULTS_DIR/安全测试.log" ]; then echo "报告已生成"; else echo "未执行"; fi) | [查看详情](#安全测试) |

## 总体评估

**测试结果:** $PASSED_TESTS 通过, $WARNING_TESTS 警告, $FAILED_TESTS 失败

$(if [ $FAILED_TESTS -eq 0 ]; then
    if [ $WARNING_TESTS -eq 0 ]; then
        echo "✅ **所有测试通过** - 网站运行状态良好"
    else
        echo "⚠ **测试通过但有警告** - 需要注意警告项"
    fi
else
    echo "❌ **存在测试失败** - 需要立即处理"
fi)

## 详细测试结果

### 基础连通性
\`\`\`
$(if [ -f "$RESULTS_DIR/基础连通性.log" ]; then cat "$RESULTS_DIR/基础连通性.log"; else echo "测试未执行"; fi)
\`\`\`

### 功能完整性
\`\`\`
$(if [ -f "$RESULTS_DIR/功能完整性.log" ]; then cat "$RESULTS_DIR/功能完整性.log"; else echo "测试未执行"; fi)
\`\`\`

### 性能测试
\`\`\`
$(if [ -f "$RESULTS_DIR/性能测试.log" ]; then 
    grep -A5 "性能测试结果分析" "$RESULTS_DIR/性能测试.log" || 
    tail -20 "$RESULTS_DIR/性能测试.log"
else 
    echo "测试未执行"; 
fi)
\`\`\`

### 安全测试
安全测试报告: [security-report-*.md]($(ls $RESULTS_DIR/security-report-*.md 2>/dev/null | head -1 | xargs basename 2>/dev/null || echo "未生成"))

## 建议

### 立即处理项
$(if [ $FAILED_TESTS -gt 0 ]; then
    echo "1. 修复失败的测试项"
    grep -l "✗" $RESULTS_DIR/*.log | while read file; do
        echo "   - $(basename $file .log): $(grep "✗" "$file" | head -3 | sed 's/✗/    ✗/')"
    done
else
    echo "无"
fi)

### 建议改进项
$(if [ $WARNING_TESTS -gt 0 ]; then
    echo "1. 处理警告项"
    grep -l "⚠" $RESULTS_DIR/*.log | while read file; do
        echo "   - $(basename $file .log)"
    done
else
    echo "无"
fi)

### 优化建议
1. 根据性能测试结果优化配置
2. 实施安全测试报告中的建议
3. 建立定期测试机制

## 测试文件
所有详细测试日志可在 $RESULTS_DIR 目录查看

---
*报告生成完成*
REPORTEOF

echo ""
echo "══════════════════════════════════════════════════════════"
echo "综合测试完成"
echo "══════════════════════════════════════════════════════════"
echo ""
echo "测试结果:"
echo "  总计: $TOTAL_TESTS 个测试"
echo "  通过: $PASSED_TESTS"
echo "  警告: $WARNING_TESTS"
echo "  失败: $FAILED_TESTS"
echo ""
echo "详细报告: $RESULTS_DIR/comprehensive-report.md"
echo "所有测试日志: $RESULTS_DIR/"
echo ""
echo "══════════════════════════════════════════════════════════"

# 根据测试结果退出
if [ $FAILED_TESTS -eq 0 ]; then
    echo "✅ 测试套件执行完成,网站运行正常"
    exit 0
else
    echo "❌ 测试套件发现失败项,需要处理"
    exit 1
fi
EOF

chmod +x comprehensive-test.sh

# 6. 创建测试说明
echo "6. 创建测试说明"

cat > TESTING.md <<'EOF'
# 网站测试指南

## 测试套件说明

本目录包含完整的网站测试套件,用于验证迁移后网站的运行状态。

### 可用测试脚本

1. **基础测试**
   - `./basic-connectivity.sh` - 基础连通性测试
   - 测试网站是否能正常访问
   - 检查关键端点是否响应

2. **功能测试**
   - `./functionality-test.sh` - 功能完整性测试
   - 验证数据库连接
   - 测试API功能
   - 检查网站内容

3. **性能测试**
   - `./performance-test.sh` - 性能测试
   - 压力测试网站性能
   - 测量响应时间和吞吐量

4. **安全测试**
   - `./security-test.sh` - 安全测试
   - 检查安全漏洞
   - 验证安全配置

5. **综合测试**
   - `./comprehensive-test.sh` - 综合测试套件
   - 运行所有测试并生成报告
   - 提供整体评估

### 快速开始

#### 方法一:运行所有测试
```bash
./comprehensive-test.sh
方法二:分步测试
# 1. 基础测试
./basic-connectivity.sh

# 2. 功能测试
./functionality-test.sh

# 3. 性能测试
./performance-test.sh

# 4. 安全测试
./security-test.sh

测试环境要求

  1. Docker环境

    • Docker已安装并运行
    • 容器已启动并运行
  2. 网络访问

    • 本地网络可访问
    • 端口80、3000、3306已开放
  3. 工具依赖

    • curl (必须)
    • ab (Apache Bench,性能测试需要)
    • mysql-client (可选,用于数据库测试)

测试结果解读

结果代码
  • ✓ 通过 - 测试项完全通过
  • ⚠ 警告 - 测试通过但有需要注意的问题
  • ✗ 失败 - 测试项失败,需要处理
报告文件

所有测试结果都会保存到 test-results-<时间戳>/ 目录:

  • .log 文件 - 详细测试日志
  • .md 文件 - 格式化报告
  • comprehensive-report.md - 综合报告

常见问题

Q1: 性能测试失败

原因: 可能未安装ab工具
解决: sudo apt-get install apache2-utilssudo yum install httpd-tools

Q2: 数据库连接失败

原因: 数据库密码不正确或服务未启动
解决: 检查数据库容器状态和密码配置

Q3: 网站无法访问

原因: 容器未启动或端口被占用
解决:

# 检查容器状态
docker ps

# 检查端口占用
netstat -tuln | grep -E "(80|3000|3306)"

# 重启容器
docker-compose -f docker-compose.restored.yml restart

自动化测试

定期测试
# 添加到crontab,每天凌晨3点运行
0 3 * * * cd /path/to/test && ./comprehensive-test.sh
CI/CD集成
# 在CI/CD流水线中添加测试步骤
- name: 运行网站测试
  run: |
    cd website-test
    ./comprehensive-test.sh

自定义测试

如需添加自定义测试,可修改相应脚本或创建新的测试脚本:

# 示例:创建自定义测试
cat > custom-test.sh <<'CUSTOM'
#!/bin/bash
echo "自定义测试"
# 添加测试逻辑
CUSTOM
chmod +x custom-test.sh

联系方式

如有问题或建议,请联系:

  • 维护者: [你的名字]
  • 邮箱: [你的邮箱]
  • 文档版本: 1.0

最后更新: $(date)
EOF

echo “”
echo “=== 网站测试套件配置完成 ===”
echo “可用的测试脚本:”
echo “1. ./basic-connectivity.sh # 基础连通性”
echo “2. ./functionality-test.sh # 功能测试”
echo “3. ./performance-test.sh # 性能测试”
echo “4. ./security-test.sh # 安全测试”
echo “5. ./comprehensive-test.sh # 综合测试”
echo “”
echo “测试说明: TESTING.md”
echo “测试目录: $(pwd)”


## 六、本次实践总结

### 6.1 迁移的方式总结

#### 6.1.1 迁移方法对比

| 迁移方法 | 适用场景 | 优点 | 缺点 | 推荐指数 |
|---------|---------|------|------|----------|
| **镜像迁移法** | 应用代码+环境迁移 | 简单快速,完整环境 | 不包含运行时数据 | ★★★★★ |
| **数据卷迁移法** | 持久化数据迁移 | 数据完整,独立迁移 | 需配合容器迁移 | ★★★★☆ |
| **容器导出导入法** | 完整容器迁移 | 完全一致,开箱即用 | 体积大,效率低 | ★★★☆☆ |
| **Docker Compose迁移** | 多容器应用迁移 | 配置化,易于管理 | 依赖环境一致性 | ★★★★★ |
| **注册中心迁移** | 生产环境迁移 | 标准化,可版本控制 | 需要配置仓库 | ★★★★☆ |

#### 6.1.2 迁移流程最佳实践

```mermaid
graph TD
    A[迁移规划] --> B[源服务器分析]
    B --> C[选择迁移方法]
    C --> D{迁移方式选择}
    
    D -->|简单应用| E[镜像迁移]
    D -->|数据库应用| F[数据卷+镜像迁移]
    D -->|复杂应用| G[Docker Compose迁移]
    
    E --> H[执行迁移]
    F --> H
    G --> H
    
    H --> I[目标服务器验证]
    I --> J[功能测试]
    J --> K[性能测试]
    K --> L[安全测试]
    L --> M[迁移完成]
6.1.3 迁移检查清单
# Docker容器迁移检查清单

## 迁移前准备
- [ ] 确认源服务器容器状态
- [ ] 备份重要数据
- [ ] 记录容器配置(端口、卷、环境变量)
- [ ] 检查网络配置
- [ ] 验证目标服务器环境

## 迁移执行
- [ ] 选择合适的迁移方法
- [ ] 执行数据备份
- [ ] 传输备份文件
- [ ] 在目标服务器恢复
- [ ] 验证数据完整性

## 迁移后验证
- [ ] 容器启动状态检查
- [ ] 服务连通性测试
- [ ] 功能完整性验证
- [ ] 性能基准测试
- [ ] 安全配置检查

## 监控和优化
- [ ] 设置监控告警
- [ ] 配置日志收集
- [ ] 优化资源配置
- [ ] 制定回滚方案

6.2 镜像的保存和导出区别

6.2.1 核心概念对比
# Docker save 和 export 的区别演示

# 1. docker save - 保存镜像(推荐用于迁移)
# 保存完整的镜像,包括所有层、标签、历史记录
docker save -o myimage.tar myimage:tag
# 特点:
# - 保存完整镜像
# - 包含元数据
# - 可以保存多个标签
# - 保持分层结构

# 2. docker export - 导出容器
# 导出容器的文件系统,不包含镜像历史
docker export -o mycontainer.tar mycontainer
# 特点:
# - 只导出文件系统
# - 不包含历史
# - 扁平化结构
# - 文件较小

# 3. docker commit - 创建新镜像
# 从容器创建新镜像
docker commit mycontainer newimage:tag
# 特点:
# - 创建新镜像
# - 包含容器变更
# - 保留分层结构
6.2.2 详细对比表
特性 docker save docker export docker commit
操作对象 镜像 容器 容器
输出内容 完整镜像(包括所有层) 容器文件系统快照 新镜像
元数据 保留(标签、历史、配置) 不保留 保留(可修改)
分层结构 保留 扁平化(单层) 保留(新增层)
文件大小 较大(包含所有层) 较小(仅当前状态) 中等(基础镜像+变更)
恢复方式 docker load docker import 自动创建
使用场景 镜像备份、迁移 容器快照、文件传输 保存容器状态
版本控制 支持(多层) 不支持 支持
迁移完整性
6.2.3 实际应用示例
#!/bin/bash
echo "=== 镜像保存和导出对比实验 ==="
echo ""

# 实验1: 使用docker save
echo "实验1: docker save保存镜像"
docker pull nginx:1.24-alpine
docker save -o nginx-image.tar nginx:1.24-alpine
image_size=$(du -h nginx-image.tar | cut -f1)
echo "  保存的镜像大小: $image_size"

# 检查保存的内容
echo "  镜像内容结构:"
tar -tf nginx-image.tar | head -5
echo "  总文件数: $(tar -tf nginx-image.tar | wc -l)"
echo ""

# 实验2: 使用docker export
echo "实验2: docker export导出容器"
docker run -d --name nginx-test nginx:1.24-alpine
sleep 2
docker export -o nginx-container.tar nginx-test
container_size=$(du -h nginx-container.tar | cut -f1)
echo "  导出的容器大小: $container_size"

# 检查导出的内容
echo "  容器内容结构:"
tar -tf nginx-container.tar | head -5
echo "  总文件数: $(tar -tf nginx-container.tar | wc -l)"
echo ""

# 实验3: 使用docker commit
echo "实验3: docker commit创建镜像"
# 在容器中做一些修改
docker exec nginx-test sh -c "echo 'Test File' > /usr/share/nginx/html/test.txt"
docker commit nginx-test nginx-modified:latest
docker images nginx-modified:latest --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

# 清理
docker rm -f nginx-test
rm -f nginx-image.tar nginx-container.tar

echo ""
echo "=== 实验结果总结 ==="
echo "1. docker save: 适合镜像迁移,保持完整结构"
echo "2. docker export: 适合容器快照,文件较小"
echo "3. docker commit: 适合保存容器状态"
6.2.4 选择建议
## 如何选择迁移方式?

### 场景1:开发环境迁移
**推荐**: `docker commit` + `docker save`
**理由**: 
- 保存开发过程中的所有变更
- 保持开发环境的完整状态
- 便于在其他机器恢复工作环境

### 场景2:生产环境迁移  
**推荐**: `docker save` + 数据卷迁移
**理由**:
- 确保镜像完整性
- 分离应用和数据,便于管理
- 支持版本回滚

### 场景3:紧急备份
**推荐**: `docker export`
**理由**:
- 快速备份当前状态
- 文件较小,传输快
- 适合临时应急

### 场景4:CI/CD流水线
**推荐**: 镜像仓库推送/拉取
**理由**:
- 标准化流程
- 版本控制
- 自动化部署

6.3 Docker迁移相关命令

6.3.1 核心命令速查表
# ======================
# 镜像操作命令
# ======================

# 查看镜像
docker images
docker image ls
docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.Size}}"

# 保存镜像到文件
docker save -o backup.tar image:tag
docker save image:tag | gzip > backup.tar.gz

# 从文件加载镜像
docker load -i backup.tar
docker load < backup.tar

# 导出容器到文件
docker export -o container.tar container_name

# 从文件导入容器
docker import container.tar new_image:tag
cat container.tar | docker import - new_image:tag

# 从容器创建镜像
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
docker commit --author "John Doe" --message "Updated config" mycontainer myimage:v2

# 删除镜像
docker rmi image:tag
docker image rm image:tag
docker image prune  # 删除悬空镜像

# ======================
# 容器操作命令
# ======================

# 查看容器
docker ps
docker ps -a  # 包括停止的容器
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker ps --filter "status=running"
docker ps -q  # 只显示ID

# 停止/启动容器
docker stop container_name
docker start container_name
docker restart container_name
docker pause container_name
docker unpause container_name

# 删除容器
docker rm container_name
docker rm -f container_name  # 强制删除运行中的容器
docker container prune  # 删除所有停止的容器

# 进入容器
docker exec -it container_name /bin/bash
docker exec -it container_name sh
docker exec container_name command

# 查看容器日志
docker logs container_name
docker logs -f container_name  # 实时日志
docker logs --tail 100 container_name
docker logs --since 1h container_name

# 查看容器信息
docker inspect container_name
docker inspect --format='{{.NetworkSettings.IPAddress}}' container_name
docker inspect --format='{{range .Mounts}}{{.Source}}:{{.Destination}} {{end}}' container_name

# ======================
# 数据卷操作命令
# ======================

# 查看数据卷
docker volume ls
docker volume inspect volume_name

# 创建数据卷
docker volume create volume_name
docker volume create --driver local --opt type=nfs --opt device=:/nfs volume_name

# 删除数据卷
docker volume rm volume_name
docker volume prune  # 删除未使用的数据卷

# 备份数据卷
docker run --rm -v source_volume:/source -v $(pwd):/backup ubuntu tar czf /backup/backup.tar.gz -C /source .

# 恢复数据卷
docker run --rm -v target_volume:/target -v $(pwd):/backup ubuntu tar xzf /backup/backup.tar.gz -C /target

# ======================
# 网络操作命令
# ======================

# 查看网络
docker network ls
docker network inspect network_name

# 创建网络
docker network create network_name
docker network create --driver bridge --subnet 172.25.0.0/16 network_name

# 连接容器到网络
docker network connect network_name container_name
docker network disconnect network_name container_name

# 删除网络
docker network rm network_name

# ======================
# Docker Compose命令
# ======================

# 启动服务
docker-compose up -d
docker-compose up --build -d  # 重新构建镜像

# 停止服务
docker-compose down
docker-compose down -v  # 同时删除数据卷

# 查看服务状态
docker-compose ps
docker-compose logs
docker-compose logs -f service_name

# 执行命令
docker-compose exec service_name command
docker-compose exec web bash

# 扩展服务
docker-compose up -d --scale web=3

# ======================
# 系统管理命令
# ======================

# 查看系统信息
docker system df
docker system df -v  # 详细信息
docker info

# 清理资源
docker system prune  # 清理所有未使用的资源
docker system prune -a  # 清理所有未使用的镜像
docker system prune --volumes  # 清理数据卷

# 查看资源使用
docker stats
docker stats --no-stream
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# ======================
# 迁移专用命令
# ======================

# 1. 完整迁移流程
# 备份镜像
docker save -o all-images.tar $(docker images -q)

# 备份数据卷
for volume in $(docker volume ls -q); do
    docker run --rm -v $volume:/data -v $(pwd):/backup alpine tar czf /backup/$volume.tar.gz -C /data .
done

# 2. 快速迁移脚本
# 保存所有容器为镜像
for container in $(docker ps -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    docker commit $container $name:migrated
done

# 3. 批量操作
# 停止所有容器
docker stop $(docker ps -q)

# 删除所有容器
docker rm -f $(docker ps -aq)

# 删除所有镜像
docker rmi -f $(docker images -q)
6.3.2 常用命令组合
#!/bin/bash
# Docker迁移常用命令组合

# 1. 备份所有运行中的容器
echo "1. 备份所有运行中的容器"
for container in $(docker ps -q); do
    container_name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    image_name="backup/${container_name}:$(date +%Y%m%d)"
    
    # 提交为镜像
    docker commit $container $image_name
    
    # 保存为文件
    safe_name=$(echo $image_name | sed 's/[\/:]/-/g')
    docker save -o "${safe_name}.tar" $image_name
    
    echo "已备份: $container_name -> ${safe_name}.tar"
done

# 2. 恢复备份的容器
echo ""
echo "2. 恢复备份的容器"
for tar_file in backup-*.tar; do
    if [ -f "$tar_file" ]; then
        # 加载镜像
        docker load -i $tar_file
        
        # 获取镜像名
        image_name=$(docker load -i $tar_file 2>&1 | grep "Loaded image:" | awk '{print $3}')
        
        # 运行容器(需要根据实际情况调整参数)
        container_name=$(echo $image_name | cut -d'/' -f2 | cut -d':' -f1)
        docker run -d --name "restored_${container_name}" $image_name
        
        echo "已恢复: $container_name"
    fi
done

# 3. 迁移数据卷
echo ""
echo "3. 迁移数据卷"

# 备份所有数据卷
mkdir -p volume_backups
for volume in $(docker volume ls -q); do
    echo "备份数据卷: $volume"
    
    docker run --rm \
        -v $volume:/source \
        -v $(pwd)/volume_backups:/backup \
        alpine tar czf /backup/${volume}.tar.gz -C /source .
done

# 4. 生成迁移报告
echo ""
echo "4. 生成迁移报告"
cat > migration-report.md <<EOF
# Docker迁移报告

## 迁移时间
$(date)

## 源服务器信息
- 主机名: $(hostname)
- Docker版本: $(docker --version)
- 镜像数量: $(docker images | wc -l)
- 容器数量: $(docker ps -a | wc -l)
- 数据卷数量: $(docker volume ls | wc -l)

## 迁移内容
### 备份的镜像
$(docker images --filter "reference=backup/*" --format "- {{.Repository}}:{{.Tag}} ({{.Size}})")

### 备份的数据卷
$(ls -lh volume_backups/*.tar.gz 2>/dev/null | awk '{print "- " $9 " (" $5 ")"}' || echo "无数据卷备份")

### 容器状态
\`\`\`
$(docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}")
\`\`\`

## 迁移步骤
1. 备份所有运行中的容器为镜像
2. 保存镜像为tar文件
3. 备份数据卷
4. 传输到目标服务器
5. 在目标服务器恢复

## 注意事项
- 检查容器端口映射
- 验证数据完整性
- 测试服务功能
- 监控系统资源

---
*报告生成完成*
EOF

echo "迁移报告已生成: migration-report.md"
6.3.3 故障排除命令
#!/bin/bash
# Docker迁移故障排除命令

echo "=== Docker迁移故障排除 ==="
echo ""

# 1. 检查Docker服务状态
echo "1. 检查Docker服务状态"
systemctl status docker | head -10
echo ""

# 2. 检查容器日志
echo "2. 检查容器日志(最后10行)"
for container in $(docker ps -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    echo "容器: $name"
    docker logs --tail 10 $container 2>&1 | grep -E "(ERROR|error|Error|FAIL|fail|exception)" || echo "  无错误日志"
    echo ""
done

# 3. 检查端口冲突
echo "3. 检查端口冲突"
netstat -tuln | grep -E ":80|:443|:3306|:3000" | while read line; do
    echo "端口占用: $line"
done
echo ""

# 4. 检查磁盘空间
echo "4. 检查磁盘空间"
df -h / /var/lib/docker 2>/dev/null
echo ""

# 5. 检查镜像完整性
echo "5. 检查镜像完整性"
for image in $(docker images -q); do
    if ! docker inspect $image >/dev/null 2>&1; then
        echo "损坏的镜像: $image"
    fi
done
echo ""

# 6. 检查网络连接
echo "6. 检查网络连接"
for container in $(docker ps -q); do
    name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
    echo "容器 $name 网络测试:"
    docker exec $container sh -c "ping -c 1 8.8.8.8 2>&1 | grep -E '(packet loss|unreachable)'" || echo "  网络正常"
    echo ""
done

# 7. 常见问题解决
echo "7. 常见问题解决命令"
cat <<'TROUBLESHOOTING'
# 问题1: 容器启动失败
docker logs [容器名/ID]          # 查看启动日志
docker inspect [容器名/ID]      # 查看详细配置

# 问题2: 端口被占用
netstat -tuln | grep :端口号    # 查看端口占用
lsof -i :端口号                 # 查看进程信息

# 问题3: 磁盘空间不足
docker system df               # 查看Docker磁盘使用
docker system prune           # 清理未使用的资源
docker image prune -a         # 删除所有未使用的镜像

# 问题4: 网络连接问题
docker network ls             # 查看网络列表
docker network inspect [网络名] # 查看网络详情
docker exec [容器名] ping [目标] # 测试容器网络

# 问题5: 数据卷权限问题
docker run -it --rm -v [卷名]:/data alpine ls -la /data  # 检查数据卷内容
docker run -it --rm -v [卷名]:/data alpine chown -R 1000:1000 /data  # 修改权限

# 问题6: 镜像拉取失败
docker pull [镜像名]           # 重新拉取
docker system prune -a        # 清理后重试
检查Docker配置文件: /etc/docker/daemon.json

# 问题7: 容器资源不足
docker stats                  # 查看资源使用
docker update --memory 512m [容器名]  # 调整内存限制
docker update --cpus 1.5 [容器名]     # 调整CPU限制
TROUBLESHOOTING
6.3.4 自动化迁移脚本
#!/bin/bash
# 自动化Docker迁移脚本
# 用法: ./auto-migrate.sh [源服务器] [目标服务器]

set -e

SOURCE_SERVER=${1:-"user@source-server"}
TARGET_SERVER=${2:-"user@target-server"}
BACKUP_DIR="/tmp/docker-migration-$(date +%Y%m%d-%H%M%S)"

echo "开始自动化Docker迁移"
echo "源服务器: $SOURCE_SERVER"
echo "目标服务器: $TARGET_SERVER"
echo "备份目录: $BACKUP_DIR"
echo ""

# 在源服务器执行备份
echo "=== 在源服务器备份 ==="
ssh $SOURCE_SERVER << EOF
    # 创建备份目录
    mkdir -p $BACKUP_DIR
    
    echo "1. 备份Docker镜像..."
    docker save -o $BACKUP_DIR/all-images.tar \$(docker images -q)
    
    echo "2. 备份数据卷..."
    mkdir -p $BACKUP_DIR/volumes
    for volume in \$(docker volume ls -q); do
        echo "  备份数据卷: \$volume"
        docker run --rm \
            -v \$volume:/source \
            -v $BACKUP_DIR/volumes:/backup \
            alpine tar czf /backup/\${volume}.tar.gz -C /source .
    done
    
    echo "3. 备份容器配置..."
    docker ps -a --format "{{.Names}}" > $BACKUP_DIR/containers.txt
    for container in \$(docker ps -a -q); do
        docker inspect \$container > $BACKUP_DIR/\${container}-inspect.json
    done
    
    echo "4. 生成备份报告..."
    cat > $BACKUP_DIR/backup-report.md <<REPORT
# Docker备份报告
## 备份时间
\$(date)

## 系统信息
- 主机名: \$(hostname)
- Docker版本: \$(docker --version)

## 备份内容
- 镜像: \$(docker images | wc -l) 个
- 容器: \$(docker ps -a | wc -l) 个
- 数据卷: \$(docker volume ls | wc -l) 个

## 文件列表
\$(ls -lh $BACKUP_DIR/)
REPORT
    
    echo "备份完成!备份目录: $BACKUP_DIR"
EOF

# 传输备份文件到目标服务器
echo ""
echo "=== 传输备份文件 ==="
scp -r $SOURCE_SERVER:$BACKUP_DIR $BACKUP_DIR
echo "文件已传输到本地: $BACKUP_DIR"

# 在目标服务器恢复
echo ""
echo "=== 在目标服务器恢复 ==="
ssh $TARGET_SERVER << EOF
    echo "1. 检查目标服务器环境..."
    if ! command -v docker &> /dev/null; then
        echo "Docker未安装,开始安装..."
        curl -fsSL https://get.docker.com -o get-docker.sh
        sh get-docker.sh
    fi
    
    echo "2. 恢复Docker镜像..."
    docker load -i $BACKUP_DIR/all-images.tar
    
    echo "3. 恢复数据卷..."
    for volume_file in $BACKUP_DIR/volumes/*.tar.gz; do
        if [ -f "\$volume_file" ]; then
            volume_name=\$(basename \$volume_file .tar.gz)
            echo "  恢复数据卷: \$volume_name"
            
            # 创建数据卷
            docker volume create \$volume_name
            
            # 恢复数据
            docker run --rm \
                -v \$volume_name:/target \
                -v $BACKUP_DIR/volumes:/backup \
                alpine tar xzf /backup/\${volume_name}.tar.gz -C /target
        fi
    done
    
    echo "4. 生成恢复报告..."
    cat > $BACKUP_DIR/restore-report.md <<REPORT
# Docker恢复报告
## 恢复时间
\$(date)

## 目标服务器信息
- 主机名: \$(hostname)
- Docker版本: \$(docker --version)

## 恢复内容
- 已加载镜像: \$(docker images | wc -l) 个
- 已恢复数据卷: \$(docker volume ls | wc -l) 个

## 下一步
需要根据容器配置手动启动容器:
\$(cat $BACKUP_DIR/containers.txt | while read container; do
    echo "- \$container"
done)
REPORT
    
    echo "恢复完成!请查看报告: $BACKUP_DIR/restore-report.md"
EOF

echo ""
echo "=== 自动化迁移完成 ==="
echo "迁移总结:"
echo "1. 备份文件保存在: $BACKUP_DIR"
echo "2. 需要在目标服务器手动启动容器"
echo "3. 建议进行功能测试验证迁移效果"
echo ""
echo "完成!"

这个完整的Docker迁移实践指南涵盖了从基础概念到高级实践的各个方面,包括详细的步骤说明、代码示例、故障排除方法和最佳实践建议。您可以根据实际需求调整和扩展这些内容。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐