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

Recreate the backup script with shell, AWK, reg and skopeo.

A few reasons. First of all, it doesn't require a running Docker daemon
instead outputting directly to a file (faster and saves space). Also,
the restore script will probably use skopeo so this the codebase is more
uniform. Without the Docker daemon it can run with lower privileges.
Lastly, it should work without setting the really high timeout that bugs
me a little.
parent 7c0fdbeb
No related branches found
No related tags found
No related merge requests found
Pipeline #2394 passed
......@@ -43,11 +43,10 @@ backup:
- >-
docker run
--volume /var/backups/registry:/var/backups/registry
--volume /run/docker.sock:/run/docker.sock
--user nobody
--rm
registry.shore.co.il/registry-backup
backup registry.shore.co.il /var/backups/registry
after_script:
- docker image prune -f
retry:
max: 2
timeout: 3h
*
!backup
!restore
FROM docker.io/library/alpine:3.14
# hadolint ignore=DL3018
RUN apk add --update --no-cache docker-py
RUN echo 'https://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories && \
echo 'https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories && \
apk add --update --no-cache \
skopeo \
reg \
;
COPY --chown=root:root backup /usr/local/bin/backup
COPY --chown=root:root restore /usr/local/bin/restore
#!/usr/bin/env python3
"""Backup a container image registry."""
import argparse
import pathlib
import sys
import docker
import requests
def get_images(registry):
"""Return a list of images from the registry."""
return requests.get(f"https://{registry}/v2/_catalog").json()[
"repositories"
]
def get_image_tags(registry, image):
"""Return a list of tags for an image in the registry."""
return requests.get(f"https://{registry}/v2/{image}/tags/list").json()[
"tags"
]
def backup(docker_client, registry, image, tag, dest):
"""Backup an image tag from the registry to a file in the destination
directory."""
full_name = f"{registry}/{image}:{tag}"
print(f"Backing up {full_name}.", file=sys.stderr)
try:
docker_client.images.get(full_name)
image_existed = True
except docker.errors.ImageNotFound:
image_existed = False
docker_image = docker_client.images.pull(full_name)
with open(dest / f"{tag}.tar", "wb") as tarball:
for chunk in docker_image.save():
tarball.write(chunk)
if not image_existed:
docker_client.images.remove(full_name)
def backup_registry(registry, dest):
"""Backup the images in the registry to the destination."""
docker_client = docker.from_env(timeout=600)
docker_client.ping()
for image in get_images(registry):
(dest / image).mkdir(exist_ok=True)
for tag in get_image_tags(registry, image):
backup(docker_client, registry, image, tag, dest / image)
if __name__ == "__main__":
arg_parser = argparse.ArgumentParser(description=__doc__)
arg_parser.add_argument("registry", help="FQDN of the registry to backup.")
arg_parser.add_argument(
"destination", help="Location to store the images.", type=pathlib.Path
)
args = arg_parser.parse_args()
destination = args.destination.expanduser()
if not destination.exists() or not destination.is_dir():
arg_parser.error(
"Backup destination doesn't exists or isn't a directory."
)
try:
destination.touch()
except IsADirectoryError:
arg_parser.error("Can't write to the backup destination.")
try:
backup_registry(args.registry, destination)
except Exception as exception:
arg_parser.error(str(exception))
sys.exit()
#!/bin/sh
set -eu
usage() {
echo "$0: REGISTRY_DOMAIN BACKUP_DEST"
}
if [ "${1:-}" = -h ] || [ "${1:-}" = --help ]
then
usage
exit 0
fi
if [ "$#" -ne 2 ]
then
usage
exit 1
fi
registry="$1"
dest="$2"
mkdir -p "$dest"
reg ls "$registry" | \
sed 's/,//g' | \
awk -v "registry=$registry" -v "dest=$dest" '
BEGIN {
exitcode = 0
}
NR>2 {
system("mkdir -p " dest "/" $1)
for (i=2; i<=NF; i++) {
image_url = registry "/" $1 ":" $(i)
image_file = dest "/" $1 "/" $(i) ".tar"
printf "Saving %s to %s.\n", image_url, image_file
system("rm " image_file)
if (system("skopeo copy docker://" image_url " docker-archive://" image_file) == 0)
printf "Backup of %s was successful.\n", image_url
else {
exitcode = 1
printf "Backup of %s failed, continuing with other images.\n", image_url
}
}
}
END {
exit retruncode
}
'
#!/bin/sh
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment