Skip to content

Instantly share code, notes, and snippets.

@firecat53
Created April 19, 2019 16:38
Show Gist options
  • Save firecat53/9fa40a73f18d5a297d39fb898b4337a7 to your computer and use it in GitHub Desktop.
Save firecat53/9fa40a73f18d5a297d39fb898b4337a7 to your computer and use it in GitHub Desktop.
Backup docker volumes
#!/usr/bin/env python3
"""Backs up and restores data-only volumes to/from host backup directory using
rsync. Only backup named volumes, which is arbitrarily set to ones having a name
length < 40 characters.
1. Create a Docker image containing rsync (named rsync)
https://github.com/firecat53/dockerfiles/tree/master/rsync
``docker build -t rsync .``
2. Run ./docker_backup.py -h
"""
import argparse
import platform
import docker
BACKUP_DIR = "/var/backups/{}/docker".format(platform.node())
def parse_arguments():
"""Parse command line options.
Returns: args
"""
arg_parse = argparse.ArgumentParser(description="Backup or restore named Docker volumes")
arg_parse.add_argument('mode', choices=['backup', 'restore'],
help="Choose backup or restore")
arg_parse.add_argument('volumes', nargs='*', default=[],
help="Zero or more volumes to backup or restore. "
"Default is all available (< 40 char name) for "
"backup and none for restore")
arg_parse.add_argument('--force', '-f', action='store_true', default=False,
help="Overwrite existing volume contents on "
"restore.")
arg_parse.add_argument('--exclude', nargs='*', default=[],
help="List of directories to exclude from backup")
return arg_parse.parse_args()
def run():
"""Main script entrypoint
"""
args = parse_arguments()
client = docker.DockerClient()
try:
client.images.get("rsync")
except docker.errors.ImageNotFound:
print("Rsync image not found. Build it and try again")
return
vols = client.volumes.list()
vols = [i for i in vols if len(i.name) < 40]
if args.exclude:
exclude = '--exclude "' + '" --exclude "'.join(args.exclude) + '"'
else:
exclude = ""
if args.mode == 'backup':
vols = [i for i in vols if i.name in args.volumes] \
if args.volumes else vols
cmd = "rsync -aAXq --delete {} /mnt/ /backup/".format(exclude)
if args.mode == 'restore':
vols = [i for i in vols if i.name in args.volumes] \
if args.volumes else []
cmd = "rsync -aAXq --delete {} /backup/ /mnt/".format(exclude)
if not vols:
print("No containers backed up or restored")
return
for vol in vols:
try:
client.volumes.get(vol.name)
except docker.errors.NotFound:
print("{} does not exist.".format(vol.name))
continue
vol_back = {"{}/{}".format(BACKUP_DIR, vol.name):
{'bind': '/backup', 'mode': 'rw'},
vol.name: {'bind': '/mnt', 'mode': 'rw'}}
client.containers.run(image='rsync',
remove=True,
command=cmd,
volumes=vol_back)
if __name__ == '__main__':
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment