前言
用魔方云,经常迁移进度100%了,然后就显示
执行失败 No URI available.
执行失败 error: 操作失败: 域不再运行
等等错误,让人实在无语,怎么办吧?只能自己写个迁移脚本了
下面的脚本,我做成了html网页,只要输入新的宿主机的ip、密码,kvm编号,就能自动执行迁移操作。
按照下面教程,会将魔方云后台的数据也改到新的宿主机上,就不用重新新建了。
![图片[1]-魔方云scp、rsync冷迁移教程-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/20251127221811956-81d3ed782aca47a1b0f94cbc21bfacdf-d8q1yh.png)
当然了,不是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冷迁移教程-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/20251127222629419-d5f05afcfa294768aa6404f94aaf0f13-2PV813.png)
2、云服务器关机
要迁移的云服务器,需要关机后才行,也就是冷迁移
![图片[3]-魔方云scp、rsync冷迁移教程-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/20251127223527804-image-FTZhR2.png)
3、财务后台变更产品状态为已暂停
暂停的目的是,防止用户发现打不开服务器,就去重启什么的各种操作,影响迁移进度。
这里只修改状态就行了【禁止点暂停按钮】因为暂停状态的机子,魔方云不能迁移
![图片[4]-魔方云scp、rsync冷迁移教程-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/20251127223642395-image-8VhUyJ.png)
4、生成命令
选择传输所有文件,这样会把有的用户生成备份也传输过去,不传输备份,第五步,将会真的全新开始,而不是瞬间100%。
![图片[5]-魔方云scp、rsync冷迁移教程-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/20251127223712850-image-joIz7e.png)
选择方案二,方案一适合连上控制台操作,方案二适合ssh链接宿主机操作
![图片[6]-魔方云scp、rsync冷迁移教程-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/20251127223743484-image-iHjjkw.png)
5、进行一次常规迁移操作
迁移后,魔方云后台直接再次点冷迁移,也就是当作没有操作过,执行下常规迁移步骤,这时候,魔方云将自动检测到已有文件,会瞬间完成迁移,就能把魔方云的用户数据也变更了
![图片[7]-魔方云scp、rsync冷迁移教程-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/20251127223940647-image-hsjwcx.png)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END








![如何通过mt管理器去除软件内置广告呢? 进来我告诉你 [教程]-朝晞小屋](https://image.baidu.com/search/down?url=http://cdn.u1.huluxia.com/g4/M01/14/C8/rBAAdmKW5RyAQd2zAACK4fnSwOU618.png)










![战神引擎传奇手游【1.76青龙复古[白猪3.1]】2025整理特色服务端+情怀复古+复刻三端【站长亲测】-朝晞小屋](https://aliyun.zxiyun.com/images/2025/11/64541-战神引擎传奇手游【1-76青龙复古白猪3-1】2025整理特色-7K8Zyh-800x450.jpg)


暂无评论内容