Skip to content
Snippets Groups Projects
Commit 1aba2885 authored by nimrod's avatar nimrod
Browse files

Debian server role: Docker volume backup.

Script, Systemd service and timer to backup Docker volumes marked as
such.
parent 7830a162
No related branches found
No related tags found
No related merge requests found
#!/bin/sh
set -eu
# Cleanup of snapshots, added as trap so it's always executed even in case of a
# failure or ctrl+c.
cleanup () {
btrfs subvolume delete "$snapshot"
}
usage () {
echo "Usage: $(basename "$0") DESTINATION" >&2
exit 1
}
get_mountpoint () {
df --output=target "$1" | tail +2
}
get_snapshot () {
name="$1"
mountpoint="$(docker volume inspect --format '{{ .Mountpoint }}' "$name" )"
snapshot="$(dirname "$mountpoint")/snapshot)"
echo "$snapshot"
}
if [ "$#" -ne 1 ]
then
usage
elif [ ! -d "$1" ]
then
echo "Destinaton $1 not found or is not a directory." >&2
exit 1
elif [ "$(id -u)" -ne 0 ]
then
echo "This program must run as root." >&2
exit 1
elif ! command -v btrfs >/dev/null
then
echo 'btrfs-progs must be installed.' >&2
exit 1
elif ! docker info --format '{{ .ServerVersion }}' >/dev/null
then
echo 'Docker must be installed and running.' >&2
exit 1
elif ! df --type=btrfs /var/lib/docker/volumes >/dev/null
then
echo "Docker volumes are not on a btrfs filesystem." >&2
exit 1
elif ! df --type=btrfs "$1" >/dev/null
then
echo "Destination $1 is not on a btrfs filesystem." >&2
exit 1
elif [ "$(df --output=target /var/lib/docker/volumes)" != "$(df --output=target "$1")" ]
then
echo "The destination $1 is not on the same btrfs volume as the Docker volumes." >&2
exit 1
elif [ -z "$(docker volume ls --filter label=snapshot=true --format '{{ .Name }}')" ]
then
echo "No Docker volumes marked for backup, exiting." >&2
exit 1
fi
dest="$1"
root_volume="$(get_mountpoint /var/lib/docker/volumes)"
snapshot="$(mktemp --dry-run "--tmpdir=$root_volume")"
trap 'cleanup' INT QUIT EXIT TERM
btrfs subvolume snapshot "$root_volume" "$snapshot"
for name in $(docker volume ls --filter label=snapshot=true --format '{{ .Name }}')
do
echo "Backing up $name."
mountpoint="$(docker volume inspect --format '{{ .Mountpoint }}' "$name")"
src="$snapshot/${mountpoint#$root_volume/}"
# shellcheck disable=SC2115
[ ! -e "$dest/$name" ] || rm -rf "$dest/$name"
cp --archive --force --reflink=always "$src" "$dest/$name"
done
# vim: filetype=systemd
[Unit]
Description=Docker volume backup (dvb)
ConditionACPower=true
After=local-fs.target
[Service]
Type=exec
ExecStart=dvb /var/backups/docker-volumes
# vim: filetype=systemd
[Unit]
Description=Docker volume backup (dvb)
[Timer]
OnCalendar=weekly
RandomizedDelaySec=21600
[Install]
WantedBy=multi-user.target
......@@ -55,3 +55,35 @@
direction: in
interface: docker0
rule: allow
- name: Create the Docker volume backup destination directory
ansible.builtin.file:
group: backup
mode: 0o0750
owner: root
path: /var/backups/docker-volumes
state: directory
- name: Copy the Docker volume backup script
ansible.builtin.copy:
dest: /usr/local/bin/
mode: preserve
src: dvb
- name: Copy the Docker volume backup service and timer
loop:
- service
- timer
ansible.builtin.copy:
dest: /etc/systemd/system/dvb.{{ item }}
mode: 0o0644
src: dvb.{{ item }}
notify:
- Systemd daemon reload
- name: Enable the Docker volume backup timer
ansible.builtin.systemd:
enabled: true
name: dvb.timer
notify:
- Systemd daemon reload
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment