Backup & Restore
The backup and restore is a procedure for disaster-recovery scenario: it can be used to save the data of an installed module and restore it to a different node or cluster.
Design
The backup engine is restic which runs inside a container along with rclone used to inspect the backup repository contents.
Backups are saved inside backup repositories, remote network-accessible spaces where data are stored. No local storage backup is possible (eg. USB disks). A backup repository can contain multiple backup instances, each module instance has its own sub-directory to avoid conflicts.
The system implements the common logic for backup inside the agent with module-backup command.
Each module can implement module-dump-state and module-cleanup-state to prepare/cleanup the data that has to be included in the backup.
The state/environment file is always included inside the backup, as it is required by the cluster/restore-module action.
The restore is implemented using a restore-module action inside the module agent, each module can extend it to implement specific restore steps.
The basic 10restore step actually runs the Restic restore procedure in a temporary container.
All backups are scheduled by systemd timers. Given a backup with id 1, it is possible to retrieve the time status with:
- rootless containers, eg.
dokuwiki1, executed bydokuwiki1user:systemctl --user status backup1.timer - rootfull containers, eg.
samba1, executed byrootuser:systemctl status backup1-samba1.timer
Include and exclude files
Whenever possible, containers should always use volumes to avoid SELinux issues during backup an restore.
Includes can be added to the state-include.conf file saved inside AGENT_INSTALL_DIR/etc/.
In the source tree, the file should be placed under <module>/imageroot/etc/state-include.conf.
On installed modules, the file will appear on different paths:
- rootless containers, eg.
dokuwiki1, full path will be/home/dokuwiki1/.config/etc/state-include.conf - rootfull containers, eg.
samba1, full path will be/var/lib/nethserver/samba1/etc/state-include.conf
Lines are interpreted as path patterns. Only patterns referring to
volumes and the agent state/ directory are considered.
Lines starting with state/ refer to AGENT_STATE_DIR contents. Eg. to
include mykey.dump under the AGENT_STATE_DIR add
state/mykey.dump
Lines starting with volumes/ will be mapped to a volume name. Eg. to
include the whole dokuwiki-data volume add
volumes/dokuwiki-data
Internally, volumes will be mapped as:
-
<volume_name>(1-1) for rootless containers; eg. for moduledokuwiki1, line prefixvolumes/dokuwiki-datamaps to volume namedokuwiki-data -
<module_id>-<volume_name>for rootfull containers; eg. for modulesamba1, line prefixvolumes/ datamaps to volume namesamba1-data
Volumes listed in state-include.conf are automatically mounted (and
created if necessary) by the basic 10restore step of the
restore-module action.
Excludes can be added to state-exclude.conf file saved inside the AGENT_INSTALL_DIR.
For a complete explanation of the patterns, like wildcard characters, see the official Restic documentation to include and exclude files. Note that include and exclude patterns have a slight different syntax.
Save and restore Redis keys
Dump key and state
To save a Redis key, you should:
- dump the key inside the
module-dump-statecommand - include the dump inside the backup
Given a module named mymodule, create the file mymodule/imageroot/bin/module-dump-state inside the module source tree:
#!/bin/bash
redis-dump module/mymodule1/mykey > mykey.dump
Make sure also module-dump-state is executable:
chmod a+x mymodule/imageroot/bin/module-dump-state
Then, add the key dump path to mymodule/imageroot/etc/state-include.conf:
state/mykey.dump
Cleanup state
As best practice, the dump should be removed when the backup has completed.
Given a module named mymodule, create the file mymodule/imageroot/bin/module-cleanup-state inside the module source tree:
#!/bin/bash
rm -f mykey.dump
Make sure also module-cleanup-state is executable:
chmod a+x mymodule/imageroot/bin/module-cleanup-state
Restore key
To restore a Redis key, you should add a step inside the restore-module action, after index 10.
Given a module named mymodule, create a file named mymodule/imageroot/actions/restore-module/20loadkey inside the module source tree:
#!/bin/bash
redis-restore mymodule1/mykey < mykey.dump
Execute a backup
Before executing a backup, you must create a backup repository. The flow to execute a backup will be something like:
- create a backup repository and retrieve its UUID
- retrieve the UUID of the repository
- configure a backup using the repository as target
- retrieve the backup ID
- execute the backup
- Create the repository:
api-cli run add-backup-repository --data '{"name":"BackBlaze repo1","url":"b2:backupns8","parameters":{"b2_account_id":"xxxxxxxxxxxxxxxxxxxxxxxxx","b2_account_key":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"},"provider":"backblaze","password":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}' - The output will be something like, please note the
idfield:{"password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "id": "48ce000a-79b7-5fe6-8558-177fd70c27b4"} - Create a new daily backup named
mybackupwith a retention of 3 snapshots (3 days) which includesdokuwiki1andsamba1instances:api-cli run add-backup --data '{"repository":"48ce000a-79b7-5fe6-8558-177fd70c27b4","schedule":"daily","retention":3,"instances":["dokuwiki1","samba1"],"enabled":true, "name":"mybackup"}' - The output will the id of the backup:
1 - Run the backup with id
1:api-cli run run-backup --data '{"id":1}'
For debugging purposes, you can also launch systemd units:
- rootless container, eg.
dokuwiki1:ssh dokuwiki1@localhost "systemctl --user start backup1.service" - rootfull container, eg.
samba1: systemctl start backup1-samba1.service
To remove the backup use:
api-cli run remove-backup --data '{"id":1}'
To remove the backup repository:
api-cli run remove-backup-repository --data '{"id":"c7a9cfea-303c-5104-8ab7-39ac9f9842bd"}'
Execute a restore
Before executing a restore, the backup repository should already be configured.
Restore the dokuwiki1 instance at node 1 from repository 48ce000a-79b7-5fe6-8558-177fd70c27b4:
api-cli run cluster/restore-module --data '{"node":1, "repository":"48ce000a-79b7-5fe6-8558-177fd70c27b4", "path":"dokuwiki/dokuwiki1@3792c7db-9450-4bd3-84a3-034cd0087839","snapshot":""}'
Cluster configuration backup
The cluster/download-cluster-backup API returns a random URL path where an encrypted
archive is available for download.
curl -O http://127.0.0.1:9311/backup/$(api-cli run cluster/download-cluster-backup | jq -r .path)
If the previous command is successful a file dump.json.gz.gpg is created
in the current directory.
The file with .gpg extension is encrypted with the sha256sum of the admin’s
password. To decrypt it run a command like this:
echo -n "${ADMIN_PASSWORD:?}" | sha256sum | awk '{print $1}' | tr -d $'\n' | \
gpg --batch -d --passphrase-file /dev/stdin --pinentry-mode loopback -o dump.json.gz dump.json.gz.gpg
The restore procedure can be started from the UI of a new NS8 installation: upload the file and specify the password from the UI.