魔方云scp、rsync冷迁移教程

前言

用魔方云,经常迁移进度100%了,然后就显示

执行失败 No URI available.

执行失败 error: 操作失败: 域不再运行

等等错误,让人实在无语,怎么办吧?只能自己写个迁移脚本了

下面的脚本,我做成了html网页,只要输入新的宿主机的ip、密码,kvm编号,就能自动执行迁移操作。

按照下面教程,会将魔方云后台的数据也改到新的宿主机上,就不用重新新建了。

图片[1]-魔方云scp、rsync冷迁移教程-朝晞小屋

当然了,不是scp那种低端的,用的rsync这种高端操作,说下为什么用rsync不用scp

特性rsync (您脚本用的)scp
传输机制智能同步,会检查文件差异简单复制,不管文件是否已存在
断点续传✅ 支持 (-p 参数)❌ 不支持
进度显示✅ 支持 (–progress)❌ 不支持
带宽控制✅ 支持✅ 支持
传输效率✅ 更高(只传输差异部分)❌ 较低(总是传输整个文件)
错误处理✅ 更好❌ 一般

懂了吧,rsync能显示进度,还能在主控和被控有网络波动时断点续传,就不会失败了

网页代码如下:(电脑创建个txt文档,复制填进去,保存,后缀改为.html,双击打开)

也可以直接顶部下载这个源码的文件

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>KVM虚拟机迁移脚本生成器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }
        
        .glass-container {
            background: rgba(255, 255, 255, 0.15);
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
            border-radius: 20px;
            border: 1px solid rgba(255, 255, 255, 0.2);
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            padding: 30px;
            width: 100%;
            max-width: 800px;
            color: white;
        }
        
        h1 {
            text-align: center;
            margin-bottom: 30px;
            font-size: 28px;
            text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
        }
        
        .form-group {
            margin-bottom: 20px;
        }
        
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 500;
        }
        
        .full-width-input {
            width: 100%;
            padding: 12px 15px;
            border-radius: 10px;
            border: 1px solid rgba(255, 255, 255, 0.3);
            background: rgba(255, 255, 255, 0.1);
            color: white;
            font-size: 16px;
            transition: all 0.3s ease;
        }
        
        .kvm-id-wrapper {
            position: relative;
            width: 100%;
        }
        
        .kvm-id-input {
            width: 100%;
            padding: 12px 15px 12px 70px;
            border-radius: 10px;
            border: 1px solid rgba(255, 255, 255, 0.3);
            background: rgba(255, 255, 255, 0.1);
            color: white;
            font-size: 16px;
            transition: all 0.3s ease;
        }
        
        .prefix-display {
            position: absolute;
            left: 1px;
            top: 1px;
            bottom: 1px;
            width: 60px;
            padding: 12px 15px;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 10px 0 0 10px;
            border-right: 1px solid rgba(255, 255, 255, 0.3);
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            pointer-events: none;
        }
        
        .full-width-input:focus, .kvm-id-input:focus {
            outline: none;
            border-color: rgba(255, 255, 255, 0.6);
            background: rgba(255, 255, 255, 0.15);
            box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2);
        }
        
        .full-width-input::placeholder, .kvm-id-input::placeholder {
            color: rgba(255, 255, 255, 0.6);
        }
        
        .script-container {
            margin-top: 30px;
            position: relative;
        }
        
        .script-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .script-header h2 {
            font-size: 20px;
        }
        
        .copy-btn {
            background: rgba(255, 255, 255, 0.2);
            border: 1px solid rgba(255, 255, 255, 0.3);
            color: white;
            padding: 8px 15px;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        
        .copy-btn:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: translateY(-2px);
        }
        
        .script-content {
            background: rgba(0, 0, 255, 0.2);
            border-radius: 10px;
            padding: 20px;
            font-family: 'Courier New', monospace;
            white-space: pre-wrap;
            word-break: break-all;
            max-height: 400px;
            overflow-y: auto;
            line-height: 1.5;
            border: 1px solid rgba(255, 255, 255, 0.2);
        }
        
        .notification {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.8);
            backdrop-filter: blur(10px);
            color: white;
            padding: 20px 30px;
            border-radius: 15px;
            box-shadow: 0 5px 25px rgba(0, 0, 0, 0.5);
            text-align: center;
            z-index: 1000;
            opacity: 0;
            transition: opacity 0.3s ease;
            max-width: 90%;
            width: 400px;
        }
        
        .notification.show {
            opacity: 1;
        }
        
        .notification h3 {
            margin-bottom: 10px;
            font-size: 22px;
            color: #4CAF50;
        }
        
        .notification-content {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 8px;
            padding: 15px;
            margin-top: 10px;
            text-align: left;
            max-height: 200px;
            overflow-y: auto;
            font-family: 'Courier New', monospace;
            font-size: 14px;
        }
        
        footer {
            margin-top: 20px;
            text-align: center;
            font-size: 14px;
            opacity: 0.7;
        }
        
        .info-text {
            font-size: 12px;
            opacity: 0.8;
            margin-top: 5px;
        }
        
        .prefix-selector {
            display: flex;
            gap: 10px;
            margin-bottom: 10px;
            flex-wrap: wrap;
        }
        
        .prefix-option {
            padding: 8px 15px;
            background: rgba(255, 255, 255, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.3);
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .prefix-option:hover {
            background: rgba(255, 255, 255, 0.2);
        }
        
        .prefix-option.active {
            background: rgba(255, 255, 255, 0.3);
            border-color: rgba(255, 255, 255, 0.6);
        }
        
        .status-info {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 8px;
            padding: 10px;
            margin-top: 10px;
            font-size: 14px;
        }
        
        .solution-options {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 8px;
            padding: 15px;
            margin-top: 10px;
        }
        
        .solution-option {
            margin-bottom: 10px;
            padding: 10px;
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .solution-option:hover {
            background: rgba(255, 255, 255, 0.2);
        }
        
        .solution-option.active {
            background: rgba(255, 255, 255, 0.3);
            border-left: 3px solid #4CAF50;
        }
        
        .transfer-options {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 8px;
            padding: 15px;
            margin-top: 10px;
        }
        
        .transfer-option {
            margin-bottom: 10px;
            padding: 10px;
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .transfer-option:hover {
            background: rgba(255, 255, 255, 0.2);
        }
        
        .transfer-option.active {
            background: rgba(255, 255, 255, 0.3);
            border-left: 3px solid #4CAF50;
        }
        
        .transfer-option input[type="radio"] {
            margin-right: 10px;
        }
        
        .advanced-options {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 8px;
            padding: 15px;
            margin-top: 10px;
        }
        
        .advanced-option {
            margin-bottom: 10px;
            padding: 10px;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div class="glass-container">
        <h1>KVM虚拟机迁移脚本生成器</h1>
        
        <div class="form-group">
            <label for="newHost">新宿主机IP地址</label>
            <input type="text" id="newHost" class="full-width-input" placeholder="例如: 192.168.1.100">
        </div>
        
        <div class="form-group">
            <label for="password">新宿主机Root密码</label>
            <input type="text" id="password" class="full-width-input" placeholder="请输入root密码">
        </div>
        
        <div class="form-group">
            <label>老宿主机KVM编号</label>
            <div class="prefix-selector">
                <div class="prefix-option active" data-prefix="kvm">kvm</div>
                <div class="prefix-option" data-prefix="vm">vm</div>
                <div class="prefix-option" data-prefix="host">host</div>
                <div class="prefix-option" data-prefix="">无前缀</div>
            </div>
            <div class="kvm-id-wrapper">
                <div class="prefix-display" id="oldPrefixDisplay">kvm</div>
                <input type="text" id="oldKvmId" class="kvm-id-input" placeholder="例如: 2682">
            </div>
            <div class="info-text">完整KVM编号: <span id="oldFullId">kvm2682</span></div>
        </div>
        
        <div class="form-group">
            <label>新宿主机KVM编号</label>
            <div class="prefix-selector">
                <div class="prefix-option active" data-prefix="kvm">kvm</div>
                <div class="prefix-option" data-prefix="vm">vm</div>
                <div class="prefix-option" data-prefix="host">host</div>
                <div class="prefix-option" data-prefix="">无前缀</div>
            </div>
            <div class="kvm-id-wrapper">
                <div class="prefix-display" id="newPrefixDisplay">kvm</div>
                <input type="text" id="newKvmId" class="kvm-id-input" placeholder="例如: 2682 (默认与老编号相同)">
            </div>
            <div class="info-text">完整KVM编号: <span id="newFullId">kvm2682</span></div>
        </div>
        
        <div class="transfer-options">
            <h3>传输选项</h3>
            <div class="transfer-option active" id="transferAll">
                <input type="radio" id="transferAllRadio" name="transferType" value="all" checked>
                <label for="transferAllRadio"><strong>传输所有文件(推荐)</strong><br>
                传输配置目录和磁盘目录的所有文件,包括备份文件</label>
            </div>
            <div class="transfer-option" id="transferSystemOnly">
                <input type="radio" id="transferSystemOnlyRadio" name="transferType" value="systemOnly">
                <label for="transferSystemOnlyRadio"><strong>仅传输系统磁盘</strong><br>
                只传输系统磁盘文件 (kvmXXXX-system.qcow2),不包含备份文件</label>
            </div>
        </div>
        
        <div class="advanced-options">
            <h3>高级传输选项</h3>
            <div class="advanced-option">
                <strong>完整断点续传支持</strong><br>
                配置文件和大文件都支持断点续传,SSH断开后重新执行可继续传输
            </div>
            <div class="advanced-option">
                <strong>文件校验优化</strong><br>
                使用文件大小和MD5校验,避免文件数量统计错误
            </div>
        </div>
        
        <div class="solution-options">
            <h3>选择解决方案:</h3>
            <div class="solution-option active" id="solution2">
                <strong>方案一:分离终端(推荐)</strong><br>
                使用 <code>-n</code> 选项,避免SSH占用终端
            </div>
            <div class="solution-option" id="solution1">
                <strong>方案二:后台执行</strong><br>
                在SSH命令后添加 <code>&</code>,让它在后台运行
            </div>
        </div>
        
        <div class="status-info">
            <strong>注意:</strong>优化的脚本会创建临时文件执行,确保变量正确传递,并在最后显示KVM编号信息。支持SSH断开后重新执行的断点续传。
        </div>
        
        <div class="script-container">
            <div class="script-header">
                <h2>生成的迁移脚本</h2>
                <button class="copy-btn" id="copyButton">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
                        <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
                    </svg>
                    复制脚本
                </button>
            </div>
            <div class="script-content" id="scriptOutput">
                # 请填写上方表单以生成脚本
            </div>
        </div>
        
        <footer>
            <p>注意:使用前请确保已安装 sshpass 工具 (yum install sshpass -y 或 apt install sshpass)</p>
        </footer>
    </div>
    
    <div class="notification" id="notification">
        <h3>复制成功</h3>
        <p>脚本内容已复制到剪贴板</p>
        <div class="notification-content" id="notificationContent"></div>
    </div>
 
    <script>
        // 获取DOM元素
        const newHostInput = document.getElementById('newHost');
        const passwordInput = document.getElementById('password');
        const oldKvmIdInput = document.getElementById('oldKvmId');
        const newKvmIdInput = document.getElementById('newKvmId');
        const oldPrefixDisplay = document.getElementById('oldPrefixDisplay');
        const newPrefixDisplay = document.getElementById('newPrefixDisplay');
        const oldFullIdSpan = document.getElementById('oldFullId');
        const newFullIdSpan = document.getElementById('newFullId');
        const scriptOutput = document.getElementById('scriptOutput');
        const copyButton = document.getElementById('copyButton');
        const notification = document.getElementById('notification');
        const notificationContent = document.getElementById('notificationContent');
        const solution1 = document.getElementById('solution1');
        const solution2 = document.getElementById('solution2');
        const transferAll = document.getElementById('transferAll');
        const transferSystemOnly = document.getElementById('transferSystemOnly');
        
        // 前缀选择和解决方案选择
        let oldPrefix = 'kvm';
        let newPrefix = 'kvm';
        let currentSolution = 'solution2'; // 默认选择方案一(分离终端)
        let currentTransferType = 'all'; // 默认传输所有文件
        
        // 初始化前缀选择器
        function initPrefixSelectors() {
            const prefixOptions = document.querySelectorAll('.prefix-option');
            prefixOptions.forEach(option => {
                option.addEventListener('click', function() {
                    const container = this.closest('.form-group');
                    const isOld = container.querySelector('#oldKvmId');
                    
                    // 移除所有active类
                    container.querySelectorAll('.prefix-option').forEach(opt => {
                        opt.classList.remove('active');
                    });
                    
                    // 添加active类到当前选项
                    this.classList.add('active');
                    
                    // 更新前缀
                    const prefix = this.getAttribute('data-prefix');
                    if (isOld) {
                        oldPrefix = prefix;
                        oldPrefixDisplay.textContent = prefix || '无';
                    } else {
                        newPrefix = prefix;
                        newPrefixDisplay.textContent = prefix || '无';
                    }
                    
                    updateFullIds();
                    generateScript();
                });
            });
        }
        
        // 初始化解决方案选择
        function initSolutionSelectors() {
            [solution1, solution2].forEach(solution => {
                solution.addEventListener('click', function() {
                    // 移除所有active类
                    solution1.classList.remove('active');
                    solution2.classList.remove('active');
                    
                    // 添加active类到当前选项
                    this.classList.add('active');
                    
                    // 更新当前解决方案
                    currentSolution = this.id;
                    
                    generateScript();
                });
            });
        }
        
        // 初始化传输选项选择
        function initTransferSelectors() {
            [transferAll, transferSystemOnly].forEach(option => {
                option.addEventListener('click', function() {
                    // 移除所有active类
                    transferAll.classList.remove('active');
                    transferSystemOnly.classList.remove('active');
                    
                    // 添加active类到当前选项
                    this.classList.add('active');
                    
                    // 更新传输类型
                    const radio = this.querySelector('input[type="radio"]');
                    radio.checked = true;
                    currentTransferType = radio.value;
                    
                    generateScript();
                });
            });
        }
        
        // 生成完整KVM ID
        function getFullKvmId(prefix, id) {
            return prefix + id;
        }
        
        // 更新显示的完整ID
        function updateFullIds() {
            const oldId = oldKvmIdInput.value.trim();
            const newId = newKvmIdInput.value.trim();
            
            oldFullIdSpan.textContent = getFullKvmId(oldPrefix, oldId);
            newFullIdSpan.textContent = getFullKvmId(newPrefix, newId || oldId);
        }
        
        // 生成脚本函数
        function generateScript() {
            const newHost = newHostInput.value.trim();
            const password = passwordInput.value.trim();
            const oldId = oldKvmIdInput.value.trim();
            let newId = newKvmIdInput.value.trim();
            
            // 计算完整的KVM ID
            const OLD_KVM_ID = getFullKvmId(oldPrefix, oldId);
            let NEW_KVM_ID = getFullKvmId(newPrefix, newId);
            
            // 如果新KVM编号为空,使用老的KVM编号(带新前缀)
            if (!newId) {
                NEW_KVM_ID = getFullKvmId(newPrefix, oldId);
            }
            
            if (!newHost || !password || !oldId) {
                scriptOutput.textContent = "# 请填写所有必填字段以生成脚本";
                return;
            }
            
            // 修复SSH配置问题
            const SSH_OPTIONS = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR';
            
            let sshCommand = '';
            if (currentSolution === 'solution1') {
                // 方案二:后台执行
                sshCommand = `sshpass -p "$PASSWORD" ssh ${SSH_OPTIONS} root@$NEW_HOST "echo SSH连接成功" &`;
            } else {
                // 方案一:分离终端(推荐)
                sshCommand = `sshpass -p "$PASSWORD" ssh ${SSH_OPTIONS} -n root@$NEW_HOST "echo SSH连接成功"`;
            }
            
            // 配置文件传输 - 使用rsync支持断点续传
            const configTransferCommand = `echo "传输配置文件(支持断点续传)..."
# 使用rsync传输配置文件,支持断点续传
sshpass -p "$PASSWORD" rsync -av --progress --human-readable --no-inc-recursive --bwlimit=0 --partial /home/kvm/$OLD_KVM_ID/ root@$NEW_HOST:/home/kvm/$NEW_KVM_ID/`;
            
            let diskTransferCommand = '';
            let verificationScript = '';
            
            if (currentTransferType === 'all') {
                // 传输所有磁盘文件 - 使用分片传输,移除--timeout
                diskTransferCommand = `echo "传输所有磁盘文件(包括备份文件,支持断点续传)..."
# 使用rsync进行分片传输,避免大文件传输降速
sshpass -p "$PASSWORD" rsync -av --progress --human-readable --no-inc-recursive --bwlimit=0 --partial /home/kvm/local/$OLD_KVM_ID/ root@$NEW_HOST:/home/kvm/local/$NEW_KVM_ID/`;
                
                verificationScript = `# 验证所有磁盘文件 - 使用文件大小校验
echo "验证所有磁盘文件传输..."
echo "正在计算源文件大小和校验和..."
SRC_TOTAL_SIZE=0
SRC_FILE_COUNT=0
# 计算源目录总大小和文件数
while IFS= read -r -d '' file; do
    if [ -f "$file" ]; then
        size=$(stat -c%s "$file")
        SRC_TOTAL_SIZE=$((SRC_TOTAL_SIZE + size))
        SRC_FILE_COUNT=$((SRC_FILE_COUNT + 1))
        echo "源文件: $(basename "$file") - $((size/1024/1024))MB"
    fi
done < <(find /home/kvm/local/$OLD_KVM_ID/ -type f -print0 2>/dev/null)
echo "正在计算目标文件大小和校验和..."
DST_TOTAL_SIZE=0
DST_FILE_COUNT=0
# 计算目标目录总大小和文件数
while IFS= read -r -d '' file; do
    if [ -n "$file" ]; then
        size=$(sshpass -p "$PASSWORD" ssh $SSH_OPTIONS -n root@$NEW_HOST "stat -c%s \\"$file\\" 2>/dev/null || echo 0")
        DST_TOTAL_SIZE=$((DST_TOTAL_SIZE + size))
        DST_FILE_COUNT=$((DST_FILE_COUNT + 1))
        echo "目标文件: $(basename "$file") - $((size/1024/1024))MB"
    fi
done < <(sshpass -p "$PASSWORD" ssh $SSH_OPTIONS -n root@$NEW_HOST "find /home/kvm/local/$NEW_KVM_ID/ -type f -print0 2>/dev/null")
echo "=========================================="
echo "文件数量: 源[$SRC_FILE_COUNT] -> 目标[$DST_FILE_COUNT]"
echo "总大小: 源[$((SRC_TOTAL_SIZE/1024/1024))MB] -> 目标[$((DST_TOTAL_SIZE/1024/1024))MB]"
if [ "$SRC_FILE_COUNT" -eq "$DST_FILE_COUNT" ] && [ "$SRC_TOTAL_SIZE" -eq "$DST_TOTAL_SIZE" ]; then
    echo "✅ 所有文件传输成功!大小和数量完全匹配"
else
    echo "❌ 文件数量或大小不匹配"
    echo "文件数量差异: $((DST_FILE_COUNT - SRC_FILE_COUNT))"
    echo "总大小差异: $(( (DST_TOTAL_SIZE - SRC_TOTAL_SIZE)/1024/1024 ))MB"
fi`;
            } else {
                // 仅传输系统磁盘 - 移除--timeout
                diskTransferCommand = `echo "仅传输系统磁盘文件(支持断点续传)..."
# 使用rsync进行分片传输,避免大文件传输降速
sshpass -p "$PASSWORD" rsync -av --progress --human-readable --no-inc-recursive --bwlimit=0 --partial /home/kvm/local/$OLD_KVM_ID/${OLD_KVM_ID}-system.qcow2 root@$NEW_HOST:/home/kvm/local/$NEW_KVM_ID/${NEW_KVM_ID}-system.qcow2`;
                
                verificationScript = `# 验证系统磁盘文件 - 使用文件大小校验
echo "验证系统磁盘文件传输..."
SYSTEM_DISK_SRC="/home/kvm/local/$OLD_KVM_ID/${OLD_KVM_ID}-system.qcow2"
SYSTEM_DISK_DST="/home/kvm/local/$NEW_KVM_ID/${NEW_KVM_ID}-system.qcow2"
if [ -f "$SYSTEM_DISK_SRC" ]; then
    SRC_SIZE=$(stat -c%s "$SYSTEM_DISK_SRC")
    DST_SIZE=$(sshpass -p "$PASSWORD" ssh $SSH_OPTIONS -n root@$NEW_HOST "stat -c%s \\"$SYSTEM_DISK_DST\\" 2>/dev/null || echo 0")
    
    echo "系统磁盘文件大小:"
    echo "  源: $((SRC_SIZE/1024/1024))MB"
    echo "  目标: $((DST_SIZE/1024/1024))MB"
    
    if [ "$SRC_SIZE" -eq "$DST_SIZE" ]; then
        echo "✅ 系统磁盘传输成功!文件大小完全匹配"
    else
        echo "❌ 系统磁盘大小不匹配"
        echo "大小差异: $(( (DST_SIZE - SRC_SIZE)/1024/1024 ))MB"
    fi
else
    echo "⚠️  源系统磁盘文件不存在: $SYSTEM_DISK_SRC"
fi`;
            }
            
            const script = `#!/bin/bash
# 创建迁移脚本文件
MIGRATION_SCRIPT="/tmp/kvm_migration_${OLD_KVM_ID}_to_${NEW_KVM_ID}.sh"
cat << 'EOF' > $MIGRATION_SCRIPT
#!/bin/bash
NEW_HOST="${newHost}"
PASSWORD="${password}"
OLD_KVM_ID="${OLD_KVM_ID}"
NEW_KVM_ID="${NEW_KVM_ID}"
# 修复SSH配置问题
SSH_OPTIONS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR"
echo "=== KVM虚拟机迁移开始 ==="
echo "源虚拟机: $OLD_KVM_ID"
echo "目标虚拟机: $NEW_KVM_ID"
echo "目标主机: $NEW_HOST"
echo "开始时间: $(date)"
echo "提示: 如果SSH断开,重新执行此脚本可断点续传"
echo "测试SSH连接..."
${sshCommand}
sleep 2
echo "创建目录结构..."
sshpass -p "$PASSWORD" ssh $SSH_OPTIONS -n root@$NEW_HOST "mkdir -p /home/kvm/$NEW_KVM_ID/"
sshpass -p "$PASSWORD" ssh $SSH_OPTIONS -n root@$NEW_HOST "mkdir -p /home/kvm/local/$NEW_KVM_ID/"
${configTransferCommand}
${diskTransferCommand}
echo "验证传输结果..."
# 验证配置文件
echo "验证配置文件传输..."
CONFIG_FILES_SRC=$(find /home/kvm/$OLD_KVM_ID/ -type f | wc -l)
CONFIG_FILES_DST=$(sshpass -p "$PASSWORD" ssh $SSH_OPTIONS -n root@$NEW_HOST "find /home/kvm/$NEW_KVM_ID/ -type f 2>/dev/null | wc -l")
echo "配置文件: 源[$CONFIG_FILES_SRC]个文件 -> 目标[$CONFIG_FILES_DST]个文件"
${verificationScript}
echo "=========================================="
echo "迁移完成时间: $(date)"
echo "✅ KVM虚拟机迁移完成!"
echo "📋 迁移信息:"
echo "   源KVM: $OLD_KVM_ID"
echo "   目标KVM: $NEW_KVM_ID"
echo "   目标主机: $NEW_HOST"
echo "=========================================="
EOF
# 设置执行权限
chmod +x $MIGRATION_SCRIPT
echo "迁移脚本已创建: $MIGRATION_SCRIPT"
echo "正在执行迁移脚本..."
bash $MIGRATION_SCRIPT`;
            
            scriptOutput.textContent = script;
        }
        
        // 复制脚本函数
        function copyScript() {
            const scriptText = scriptOutput.textContent;
            
            // 使用Clipboard API复制文本
            navigator.clipboard.writeText(scriptText).then(() => {
                // 显示通知
                notificationContent.textContent = scriptText;
                notification.classList.add('show');
                
                // 500毫秒后自动关闭通知
                setTimeout(() => {
                    notification.classList.remove('show');
                }, 500);
            }).catch(err => {
                console.error('复制失败: ', err);
                alert('复制失败,请手动选择并复制脚本内容');
            });
        }
        
        // 添加事件监听器
        newHostInput.addEventListener('input', generateScript);
        passwordInput.addEventListener('input', generateScript);
        oldKvmIdInput.addEventListener('input', function() {
            updateFullIds();
            generateScript();
        });
        newKvmIdInput.addEventListener('input', function() {
            updateFullIds();
            generateScript();
        });
        copyButton.addEventListener('click', copyScript);
        
        // 初始化
        initPrefixSelectors();
        initSolutionSelectors();
        initTransferSelectors();
        updateFullIds();
        generateScript();
        
        // 设置默认值
        oldKvmIdInput.placeholder = "2682";
        newKvmIdInput.placeholder = "2682 (默认与老编号相同)";
    </script>
</body>
</html>

使用说明:

1、kvm编号填写

kvm编号填写这个,新宿主机的非特殊情况不要写,会默认和老的一样

图片[2]-魔方云scp、rsync冷迁移教程-朝晞小屋

2、云服务器关机

要迁移的云服务器,需要关机后才行,也就是冷迁移

图片[3]-魔方云scp、rsync冷迁移教程-朝晞小屋

3、财务后台变更产品状态为已暂停

暂停的目的是,防止用户发现打不开服务器,就去重启什么的各种操作,影响迁移进度。

这里只修改状态就行了【禁止点暂停按钮】因为暂停状态的机子,魔方云不能迁移

图片[4]-魔方云scp、rsync冷迁移教程-朝晞小屋

4、生成命令

选择传输所有文件,这样会把有的用户生成备份也传输过去,不传输备份,第五步,将会真的全新开始,而不是瞬间100%。

图片[5]-魔方云scp、rsync冷迁移教程-朝晞小屋

选择方案二,方案一适合连上控制台操作,方案二适合ssh链接宿主机操作

图片[6]-魔方云scp、rsync冷迁移教程-朝晞小屋

5、进行一次常规迁移操作

迁移后,魔方云后台直接再次点冷迁移,也就是当作没有操作过,执行下常规迁移步骤,这时候,魔方云将自动检测到已有文件,会瞬间完成迁移,就能把魔方云的用户数据也变更了

图片[7]-魔方云scp、rsync冷迁移教程-朝晞小屋
文章版权声明 1、本网站名称:朝晞小屋
2、本站永久网址:https://www.zxiyun.com/
3、更多有趣网站:http://dh.zxiyun.com/
4、本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长QQ2604140139进行删除处理。
5、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
6、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
7、本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容