mirror of
https://github.com/lephisto/crossover.git
synced 2025-12-06 12:19:20 +01:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cee976786 | ||
|
|
aadf7e656c |
54
README.md
54
README.md
@@ -11,7 +11,7 @@ ______
|
|||||||
| --| _| . |_ -|_ -| . | | | -_| _|
|
| --| _| . |_ -|_ -| . | | | -_| _|
|
||||||
|_____|_| |___|___|___|___|\_/|___|_|
|
|_____|_| |___|___|___|___|\_/|___|_|
|
||||||
|
|
||||||
Cross Pool (live) replication and near-live migration forProxmox VE
|
Cross Pool (live) replication and near-live migration for Proxmox VE
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
crossover <COMMAND> [ARGS] [OPTIONS]
|
crossover <COMMAND> [ARGS] [OPTIONS]
|
||||||
@@ -43,22 +43,32 @@ Report bugs to the Github repo at https://github.com/lephisto/crossover/
|
|||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
When working with hyperconverges Proxmox HA Clusters you sometimes need to get VMs migrated
|
When working with hyperconverged Proxmox HA Clusters you sometimes need to get VMs migrated to another cluster, or have a cold-standby copy of a VM ready to start there in case your main Datacenter goes boom. Crossover implements functionality that enables you to do the following:
|
||||||
to another cluster, or have a cold-standby copy of a VM ready to start there. Crossover implements
|
|
||||||
functions that enable you to do the following:
|
|
||||||
|
|
||||||
- Transfer a non-running VM to another Cluster
|
- Transfer a non-running VM to another Cluster
|
||||||
- Transfer a running VM to another Cluster
|
- Transfer a running VM to another Cluster
|
||||||
- Continuously update a previously tranferred VM in another Cluster with incemental snapshots
|
- Continuously update a previously tranferred VM in another Cluster with incemental snapshots
|
||||||
|
|
||||||
Backup And Restore Ceph for Proxmox VE with retention. This solution implements
|
|
||||||
a snapshotbackup for Ceph cluster exporting to specific directory. The mechanism using
|
|
||||||
Ceph snapshot, export and export differential. In backup export image and config
|
|
||||||
file VM/CT.
|
|
||||||
|
|
||||||
Currently this only works with Ceph based storage backends, since the incremental logic heavily
|
Currently this only works with Ceph based storage backends, since the incremental logic heavily
|
||||||
relies on Rados block device features.
|
relies on Rados block device features.
|
||||||
|
|
||||||
|
It'll work according this scheme:
|
||||||
|
|
||||||
|
```
|
||||||
|
.:::::::::. .:::::::::.
|
||||||
|
|Cluster-A| |Cluster-B|
|
||||||
|
| | | |
|
||||||
|
| _______ | rbd export-diff [..] | ssh pve04 | rbd import-diff [..] | _______ |
|
||||||
|
| pve01 -|-----------------------------------------------------------|->pve04 |
|
||||||
|
| _______ | | _______ |
|
||||||
|
| pve02 | | pve05 |
|
||||||
|
| _______ | | _______ |
|
||||||
|
| pve03 | | pve06 |
|
||||||
|
| _______ | | _______ |
|
||||||
|
| | | |
|
||||||
|
|:::::::::| |:::::::::|
|
||||||
|
```
|
||||||
|
|
||||||
## Main features
|
## Main features
|
||||||
|
|
||||||
* Currently only for KVM. I might add LXC support when I need to.
|
* Currently only for KVM. I might add LXC support when I need to.
|
||||||
@@ -89,6 +99,32 @@ Mirror VM to another Cluster:
|
|||||||
|
|
||||||
```
|
```
|
||||||
root@pve01:~/crossover# ./crossover mirror --vmid=100:10100 --destination=pve04 --pool=data2 --keeplocal=4 --keepremote=8 --overwrite --keep-dlock --online
|
root@pve01:~/crossover# ./crossover mirror --vmid=100:10100 --destination=pve04 --pool=data2 --keeplocal=4 --keepremote=8 --overwrite --keep-dlock --online
|
||||||
|
|
||||||
|
Start mirror 2022-10-21 18:09:36
|
||||||
|
Transmitting Config for VM 100 to desination 10100
|
||||||
|
update VM 100: -lock backup
|
||||||
|
update VM 10100: -lock backup
|
||||||
|
VM 100 - Issuing fsfreeze-freeze to 100 on pve01
|
||||||
|
2
|
||||||
|
VM 100 - Creating snapshot data/vm-100-disk-0@mirror-20221021180936
|
||||||
|
Creating snap: 100% complete...done.
|
||||||
|
VM 100 - Creating snapshot data/vm-100-disk-1@mirror-20221021180936
|
||||||
|
Creating snap: 100% complete...done.
|
||||||
|
VM 100 - Issuing fsfreeze-thaw to 100 on pve01
|
||||||
|
2
|
||||||
|
Exporting image: 100% complete...done.
|
||||||
|
Importing image diff: 100% complete...done.
|
||||||
|
Houskeeping localhost data vm-100-disk-0, keeping previous 4 Snapshots
|
||||||
|
Removing snap: 100% complete...done.
|
||||||
|
Houskeeping pve04 data2 vm-10100-disk-0, keeping previous 8 Snapshots
|
||||||
|
Exporting image: 100% complete...done.
|
||||||
|
Importing image diff: 100% complete...done.
|
||||||
|
Houskeeping localhost data vm-100-disk-1, keeping previous 4 Snapshots
|
||||||
|
Removing snap: 100% complete...done.
|
||||||
|
Houskeeping pve04 data2 vm-10100-disk-1, keeping previous 8 Snapshots
|
||||||
|
Unlocking source VM 100
|
||||||
|
root@pve01:~/crossover#
|
||||||
|
|
||||||
```
|
```
|
||||||
This example creates a mirror of VM 100 (in the source cluster) as VM 10100 (in the destination cluster) using the ceph pool "data2" for storing all attached disks. It will keep 4 Ceph snapshots prior the latest (in total 5) and 8 snapshots on the remote cluster. It will keep the VM on the target Cluster locked to avoid an accidental start (thus causing split brain issues), and will do it even if the source VM is running.
|
This example creates a mirror of VM 100 (in the source cluster) as VM 10100 (in the destination cluster) using the ceph pool "data2" for storing all attached disks. It will keep 4 Ceph snapshots prior the latest (in total 5) and 8 snapshots on the remote cluster. It will keep the VM on the target Cluster locked to avoid an accidental start (thus causing split brain issues), and will do it even if the source VM is running.
|
||||||
|
|
||||||
|
|||||||
30
crossover
30
crossover
@@ -5,7 +5,7 @@
|
|||||||
# Cross Pool Migration and incremental replication Tool for Proxmox VMs using Ceph.
|
# Cross Pool Migration and incremental replication Tool for Proxmox VMs using Ceph.
|
||||||
# Author: Bastian Mäuser <bma@netz.org>
|
# Author: Bastian Mäuser <bma@netz.org>
|
||||||
|
|
||||||
declare -r VERSION=0.2
|
declare -r VERSION=0.3
|
||||||
declare -r NAME=$(basename "$0")
|
declare -r NAME=$(basename "$0")
|
||||||
declare -r PROGNAME=${NAME%.*}
|
declare -r PROGNAME=${NAME%.*}
|
||||||
|
|
||||||
@@ -163,6 +163,13 @@ function exist_file(){
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lookupcephpool() {
|
||||||
|
pvehost=$1
|
||||||
|
pvepoolname=$2
|
||||||
|
res=$(ssh $pvehost cat /etc/pve/storage.cfg | sed -n "/rbd: $pvepoolname/,/^$/p" | grep pool | cut -d " " -f 2)
|
||||||
|
echo $res
|
||||||
|
}
|
||||||
|
|
||||||
function get_vm_ids(){
|
function get_vm_ids(){
|
||||||
local data=''
|
local data=''
|
||||||
local conf=''
|
local conf=''
|
||||||
@@ -325,11 +332,15 @@ function mirror() {
|
|||||||
fi
|
fi
|
||||||
log info "Transmitting Config for VM $vm_id to desination $dvmid"
|
log info "Transmitting Config for VM $vm_id to desination $dvmid"
|
||||||
rewriteconfig $PVE_NODES/"${pvnode[$vm_id]}"/$QEMU/"$vm_id".conf $opt_destination "$opt_pool" $PVE_NODES/"$opt_destination"/$QEMU/"$dvmid".conf "$dvmid"
|
rewriteconfig $PVE_NODES/"${pvnode[$vm_id]}"/$QEMU/"$vm_id".conf $opt_destination "$opt_pool" $PVE_NODES/"$opt_destination"/$QEMU/"$dvmid".conf "$dvmid"
|
||||||
|
map_vmids_to_dsthost "$opt_destination"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#Lock on source + destination
|
#Lock on source + destination
|
||||||
if [ $opt_lock -eq 1 ]; then
|
if [ $opt_lock -eq 1 ]; then
|
||||||
ssh root@"${pvnode[$vm_id]}" qm set "$vm_id" --lock backup
|
ssh root@"${pvnode[$vm_id]}" qm set "$vm_id" --lock backup
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $opt_lock -eq 1 ]; then
|
||||||
ssh root@"${dstpvnode[$dvmid]}" qm set "$dvmid" --lock backup
|
ssh root@"${dstpvnode[$dvmid]}" qm set "$dvmid" --lock backup
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -348,10 +359,13 @@ function mirror() {
|
|||||||
dst_image_spec=$(echo $src_image_spec | sed -r -e "s/([a-zA-Z0-9]+\/[a-zA-Z0-9]+\-)([0-9]+)(\-[a-zA-Z0-9]+\-[0-9]+)/\1$dvmid\3/")
|
dst_image_spec=$(echo $src_image_spec | sed -r -e "s/([a-zA-Z0-9]+\/[a-zA-Z0-9]+\-)([0-9]+)(\-[a-zA-Z0-9]+\-[0-9]+)/\1$dvmid\3/")
|
||||||
[ -z "$dst_image_spec" ] && continue
|
[ -z "$dst_image_spec" ] && continue
|
||||||
[[ $disk =~ $recephimg ]]
|
[[ $disk =~ $recephimg ]]
|
||||||
src_image_pool=${BASH_REMATCH[1]}
|
#src_image_pool=${BASH_REMATCH[1]}
|
||||||
|
src_image_pool=$(lookupcephpool "localhost" ${BASH_REMATCH[1]})
|
||||||
src_image_name=${BASH_REMATCH[2]}
|
src_image_name=${BASH_REMATCH[2]}
|
||||||
[[ $dst_image_spec =~ ^[a-zA-Z0-9]+\/(.*)$ ]]
|
[[ $dst_image_spec =~ ^[a-zA-Z0-9]+\/(.*)$ ]]
|
||||||
dst_image_name=${BASH_REMATCH[1]}
|
dst_image_name=${BASH_REMATCH[1]}
|
||||||
|
dst_image_pool=$(lookupcephpool $opt_destination $opt_pool)
|
||||||
|
echo "dst_image_pool: $dst_image_pool"
|
||||||
snapshot_name="@$opt_snapshot_prefix$timestamp"
|
snapshot_name="@$opt_snapshot_prefix$timestamp"
|
||||||
localsnapcount=$(rbd ls -l $src_image_pool | grep $src_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1|wc -l)
|
localsnapcount=$(rbd ls -l $src_image_pool | grep $src_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1|wc -l)
|
||||||
if [ $localsnapcount -ge 2 ]; then
|
if [ $localsnapcount -ge 2 ]; then
|
||||||
@@ -359,7 +373,7 @@ function mirror() {
|
|||||||
currentlocal=$(rbd ls -l $src_image_pool | grep $src_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1|tail -n 1)
|
currentlocal=$(rbd ls -l $src_image_pool | grep $src_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1|tail -n 1)
|
||||||
localts=$(rbd ls -l $src_image_pool | grep $src_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1 | sed -r -e 's/.*@mirror-(.*)/\1/')
|
localts=$(rbd ls -l $src_image_pool | grep $src_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1 | sed -r -e 's/.*@mirror-(.*)/\1/')
|
||||||
fi
|
fi
|
||||||
latestremote=$(ssh $opt_destination rbd ls -l $opt_pool | grep $dst_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1|tail -n 1)
|
latestremote=$(ssh $opt_destination rbd ls -l $dst_image_pool | grep $dst_image_name@$opt_snapshot_prefix | cut -d ' ' -f 1|tail -n 1)
|
||||||
if [ $latestremote ]; then
|
if [ $latestremote ]; then
|
||||||
[[ $latestremote =~ ^.*@$opt_snapshot_prefix([0-9]+)$ ]]
|
[[ $latestremote =~ ^.*@$opt_snapshot_prefix([0-9]+)$ ]]
|
||||||
latestremotets=${BASH_REMATCH[1]}
|
latestremotets=${BASH_REMATCH[1]}
|
||||||
@@ -370,24 +384,24 @@ function mirror() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
if [ -z $basets ]; then
|
if [ -z $basets ]; then
|
||||||
log debug "No matching Snapshot found on destination - Full Copy $src_image_pool/$src_image_name$snapshot_name to $opt_pool/$dst_image_name"
|
log debug "No matching Snapshot found on destination - Full Copy $src_image_pool/$src_image_name$snapshot_name to $dst_image_pool/$dst_image_name"
|
||||||
xmitjob="rbd export --rbd-concurrent-management-ops 8 $src_image_pool/$src_image_name$snapshot_name --no-progress -|pv -r|ssh $opt_destination rbd import --image-format 2 - $opt_pool/$dst_image_name"
|
xmitjob="rbd export --rbd-concurrent-management-ops 8 $src_image_pool/$src_image_name$snapshot_name --no-progress -|pv -r|ssh $opt_destination rbd import --image-format 2 - $dst_image_pool/$dst_image_name"
|
||||||
# create initial snapshot on destination
|
# create initial snapshot on destination
|
||||||
if ! do_run $xmitjob; then
|
if ! do_run $xmitjob; then
|
||||||
log error "Transmitting Image failed"
|
log error "Transmitting Image failed"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
cmd="ssh $opt_destination rbd snap create $opt_pool/$dst_image_name$snapshot_name"
|
cmd="ssh $opt_destination rbd snap create $dst_image_pool/$dst_image_name$snapshot_name"
|
||||||
do_run $cmd
|
do_run $cmd
|
||||||
else
|
else
|
||||||
log debug "Basecopy + snapshot on destination - let's just transfer the diff"
|
log debug "Basecopy + snapshot on destination - let's just transfer the diff"
|
||||||
xmitjob="rbd export-diff --from-snap $opt_snapshot_prefix$basets $src_image_pool/$currentlocal - | ssh $opt_destination rbd import-diff - $opt_pool/$dst_image_name"
|
xmitjob="rbd export-diff --from-snap $opt_snapshot_prefix$basets $src_image_pool/$currentlocal - | ssh $opt_destination rbd import-diff - $dst_image_pool/$dst_image_name"
|
||||||
if ! do_run $xmitjob; then
|
if ! do_run $xmitjob; then
|
||||||
log error "Transmitting Image failed"
|
log error "Transmitting Image failed"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
do_housekeeping "localhost" "$src_image_pool" "$src_image_name" $opt_keep_local
|
do_housekeeping "localhost" "$src_image_pool" "$src_image_name" $opt_keep_local
|
||||||
do_housekeeping "$opt_destination" "$opt_pool" "$dst_image_name" $opt_keep_remote
|
do_housekeeping "$opt_destination" "$dst_image_pool" "$dst_image_name" $opt_keep_remote
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ ! $opt_keepslock -eq 1 ]; then
|
if [ ! $opt_keepslock -eq 1 ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user