Skip to content

Instantly share code, notes, and snippets.

@usrflo
Last active April 23, 2023 21:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save usrflo/f4f62e886490b2efed2a1503aed3e228 to your computer and use it in GitHub Desktop.
Save usrflo/f4f62e886490b2efed2a1503aed3e228 to your computer and use it in GitHub Desktop.
lxc copy --refresh workaround: efficient incremental ZFS snapshot sync with send/receive
LXCBIN=/snap/bin/lxc
LXDBIN=/snap/bin/lxd
ZFSBIN=/sbin/zfs
SYNCSNAPNAME=bsync
LASTSYNCSNAPNAME=bsync-last
ZFSROOT=tank/containers/
MOUNTROOT=/var/snap/lxd/common/lxd/storage-pools/
# This cronjob is configured on the target server:
# - srclxdremotename: name of the lxd remote that contains the container to be synced
# - srclxdcontainername: name of the remote lxd container to be synced
# - locallxdcontainertargetname: local target container name to sync to
# - root@srcserver: ssh root login to the remote lxd server
# lxc-sync jobs
25 12,18 * * * /opt/lxc-sync/lxc-sync.sh -o srclxdremotename -r srclxdcontainername -l locallxdcontainertargetname -c "ssh -p2222 -oStrictHostKeyChecking=no root@srcserver"
#!/bin/bash
# This script is meant to run on the sync target server that syncs remote container snapshots to local;
# remote and local containers may be running; a remote container is snapshotted and this
# snapshot will be restored on the local receiving container.
#
# IMPORTANT:
# - in the local lxc configuration all remotes need to be configured (see `lxc remote list`)
# - ssh access to the remote/source servers needs to be available to execute remote zfs send
#
# Florian Sager, sager@agitos.de, 2021-02-23
set -e
# set -x
DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
PROG=${0##*/}
# load local system specific sync config
. "$DIR/lxc-sync-config"
# cron sample:
# 45 12,18 * * * /root/tools/lxc-sync.sh -o host01 -r mycontainer -l mycontainer-standby -c "ssh -p2222 -oStrictHostKeyChecking=no root@host01.dom.tld"
function usage {
cat <<EOF
usage: $PROG [options]
$PROG will sync a ZFS based LXD container from a remote origin server to a local target container.
Options:
-h print this help message
-o remote origin host according to the name in 'lxc remote list', e.g. "host01"
-r remote origin container name, e.g. "mycontainer"
-l local target container in LXD syntax, e.g. "mycontainer-standby"
-c ssh command to connect to origin server, e.g. "ssh -p2222 -oStrictHostKeyChecking=no root@host01.dom.tld"
EOF
}
function localtargetexists {
local CONTNAME=$1
$LXCBIN info $CONTNAME > /dev/null
RETVAL=$?
return $([ "$RETVAL" == 0 ] && echo 1 || echo 0)
}
function initsync {
local LOCALCONTNAME=$1
local LXCREMOTE=$2
local REMOTECONTNAME=$3
local SSHCMD=$4
$LXCBIN snapshot $LXCREMOTE:$REMOTECONTNAME $SYNCSNAPNAME
$LXCBIN copy $LXCREMOTE:$REMOTECONTNAME/$SYNCSNAPNAME $LOCALCONTNAME
$LXCBIN snapshot $LOCALCONTNAME $SYNCSNAPNAME
$ZFSBIN destroy -r $ZFSROOT$LOCALCONTNAME
$ZFSBIN create -o mountpoint=$MOUNTROOT$ZFSROOT$LOCALCONTNAME $ZFSROOT$LOCALCONTNAME
$SSHCMD $ZFSBIN send -c -L $ZFSROOT$REMOTECONTNAME@snapshot-$SYNCSNAPNAME | $ZFSBIN receive -Fu $ZFSROOT$LOCALCONTNAME
}
function incrementalsync {
local LOCALCONTNAME=$1
local LXCREMOTE=$2
local REMOTECONTNAME=$3
local SSHCMD=$4
set +e
$LXCBIN delete $LXCREMOTE:$LOCALCONTNAME/$LASTSYNCSNAPNAME
set -e
$LXCBIN rename $LXCREMOTE:$REMOTECONTNAME/$SYNCSNAPNAME $LXCREMOTE:$REMOTECONTNAME/$LASTSYNCSNAPNAME
$LXCBIN snapshot $LXCREMOTE:$REMOTECONTNAME $SYNCSNAPNAME
$ZFSBIN rename $ZFSROOT$LOCALCONTNAME@snapshot-$SYNCSNAPNAME $ZFSROOT$LOCALCONTNAME@snapshot-$LASTSYNCSNAPNAME
$SSHCMD $ZFSBIN send -c -L -i $ZFSROOT$REMOTECONTNAME@snapshot-$LASTSYNCSNAPNAME $ZFSROOT$REMOTECONTNAME@snapshot-$SYNCSNAPNAME | $ZFSBIN receive -Fu $ZFSROOT$LOCALCONTNAME
$ZFSBIN destroy $ZFSROOT$LOCALCONTNAME@snapshot-$LASTSYNCSNAPNAME
$LXCBIN restore $LOCALCONTNAME $SYNCSNAPNAME
$LXDBIN sql global "UPDATE instances_snapshots SET creation_date=datetime('now') WHERE instance_id=(SELECT id FROM instances WHERE name='$LOCALCONTNAME') AND name='$SYNCSNAPNAME';" > /dev/null
}
if [ "$#" == "0" ]; then
usage;
exit 1;
fi
# read arguments
while getopts o:r:l:c:h option; do
case "${option}" in
o) LXCREMOTE=${OPTARG};;
r) REMOTECONTNAME=${OPTARG};;
l) LOCALCONTNAME=${OPTARG};;
c) SSHCMD=${OPTARG};;
h) usage; exit 1;;
esac
done
if [ -z "$LXCREMOTE" ] ; then
echo 'Missing parameter -o' >&2
exit 3;
fi
if [ -z "$REMOTECONTNAME" ] ; then
echo 'Missing parameter -r' >&2
exit 4;
fi
if [ -z "$LOCALCONTNAME" ] ; then
echo 'Missing parameter -l' >&2
exit 5;
fi
if [ -z "$SSHCMD" ] ; then
echo 'Missing parameter -c' >&2
exit 6;
fi
set +e
localtargetexists $LOCALCONTNAME
LOCALCONTEXISTS=$?
set -e
if [ $LOCALCONTEXISTS == 0 ]; then
initsync "$LOCALCONTNAME" "$LXCREMOTE" "$REMOTECONTNAME" "$SSHCMD"
else
incrementalsync "$LOCALCONTNAME" "$LXCREMOTE" "$REMOTECONTNAME" "$SSHCMD"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment