From a726bdecd45ebfd755ab6e69032305ac98c84a85 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Fri, 2 Apr 2021 13:59:47 +0200 Subject: [PATCH] add initial source --- .circleci/config.yml | 39 +++++++++++++++++++ Dockerfile | 19 +++++++++ Makefile | 3 ++ NOTICE | 22 +++++++++++ README.md | 5 ++- src/backup.sh | 93 ++++++++++++++++++++++++++++++++++++++++++++ src/entrypoint.sh | 35 +++++++++++++++++ 7 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 .circleci/config.yml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 NOTICE create mode 100644 src/backup.sh create mode 100644 src/entrypoint.sh diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..dacf9dd --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,39 @@ +version: 2.1 + +jobs: + build: + docker: + - image: cimg/base:2020.06 + working_directory: ~/docker-volume-backup + steps: + - checkout + - setup_remote_docker + - run: + name: Build + command: make build + - run: + name: Check if image needs to be pushed + command: | + if [[ -z "$CIRCLE_TAG" ]]; then + echo "Not a git tag, nothing to do ..." + circleci-agent step halt + fi + - docker/install-docker-credential-helper + - docker/configure-docker-credentials-store + - run: + name: Push to Docker Hub + command: | + echo "$DOCKER_ACCESSTOKEN" | docker login --username offen --password-stdin + docker tag offen/docker-volume-backup:local offen/docker-volume-backup:$CIRCLE_TAG + docker tag offen/docker-volume-backup:local offen/docker-volume-backup:latest + docker push offen/docker-volume-backup:$CIRCLE_TAG + docker push offen/docker-volume-backup:latest + +workflows: + version: 2 + deploy: + jobs: + - build + +orbs: + docker: circleci/docker@1.0.1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..df255ef --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +# Copyright 2021 - Offen Authors +# SPDX-License-Identifier: MIT + +FROM alpine:3.13 + +WORKDIR /root + +RUN apk add --update ca-certificates docker openrc gnupg +RUN rc-update add docker boot + +RUN wget https://dl.min.io/client/mc/release/linux-amd64/mc && \ + chmod +x mc && \ + mv mc /usr/bin/mc + +COPY src/backup.sh src/entrypoint.sh /root/ +RUN chmod +x backup.sh && mv backup.sh /usr/bin/backup \ + && chmod +x entrypoint.sh + +ENTRYPOINT ["/root/entrypoint.sh"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e94db70 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +.PHONY: build +build: + @docker build -t offen/docker-volume-backup:latest . diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..37b7c92 --- /dev/null +++ b/NOTICE @@ -0,0 +1,22 @@ +Copyright 2021 Offen Authors + +This project contains software that is Copyright (c) 2018 Futurice +Licensed under the Terms of the MIT License: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index f521a3f..9acfce4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # docker-volume-backup -Backup Docker volumes to any S3 compatible storage + +Backup Docker volumes to any S3 compatible storage. + +The `offen/docker-volume-backup` Docker image can be used as a sidecar container to an existing Docker setup. It handles recurring backups of Docker volumes to any S3 compatible storage and rotates away old backups if configured. diff --git a/src/backup.sh b/src/backup.sh new file mode 100644 index 0000000..83d319e --- /dev/null +++ b/src/backup.sh @@ -0,0 +1,93 @@ +#!/bin/sh + +# Copyright 2021 - Offen Authors +# SPDX-License-Identifier: MPL-2.0 + +# Portions of this file are taken from github.com/futurice/docker-volume-backup +# See NOTICE for information about authors and licensing. + +source env.sh + +function info { + echo -e "\n[INFO] $1\n" +} + +info "Backup starting" +DOCKER_SOCK="/var/run/docker.sock" + +if [ -S "$DOCKER_SOCK" ]; then + TEMPFILE="$(mktemp)" + docker ps \ + --format "{{.ID}}" \ + --filter "label=offen.stop-during-backup=true" \ + > "$TEMPFILE" + CONTAINERS_TO_STOP="$(cat $TEMPFILE | tr '\n' ' ')" + CONTAINERS_TO_STOP_TOTAL="$(cat $TEMPFILE | wc -l)" + CONTAINERS_TOTAL="$(docker ps --format "{{.ID}}" | wc -l)" + rm "$TEMPFILE" + echo "$CONTAINERS_TOTAL containers running on host in total" + echo "$CONTAINERS_TO_STOP_TOTAL containers marked to be stopped during backup" +else + CONTAINERS_TO_STOP_TOTAL="0" + CONTAINERS_TOTAL="0" + echo "Cannot access \"$DOCKER_SOCK\", won't look for containers to stop" +fi + +if [ "$CONTAINERS_TO_STOP_TOTAL" != "0" ]; then + info "Stopping containers" + docker stop $CONTAINERS_TO_STOP +fi + +info "Creating backup" +BACKUP_FILENAME="$(date +"${BACKUP_FILENAME:-backup-%Y-%m-%dT%H-%M-%S.tar.gz}")" +tar -czvf "$BACKUP_FILENAME" $BACKUP_SOURCES # allow the var to expand, in case we have multiple sources + +if [ ! -z "$GPG_PASSPHRASE" ]; then + info "Encrypting backup" + gpg --symmetric --cipher-algo aes256 --batch --passphrase "$GPG_PASSPHRASE" \ + -o "${BACKUP_FILENAME}.gpg" $BACKUP_FILENAME + rm $BACKUP_FILENAME + BACKUP_FILENAME="${BACKUP_FILENAME}.gpg" +fi + +if [ "$CONTAINERS_TO_STOP_TOTAL" != "0" ]; then + info "Starting containers back up" + docker start $CONTAINERS_TO_STOP +fi + +if [ ! -z "$AWS_S3_BUCKET_NAME" ]; then + info "Uploading backup to remote storage" + echo "Will upload to bucket \"$AWS_S3_BUCKET_NAME\"" + mc cp "$BACKUP_FILENAME" "backup-target/$AWS_S3_BUCKET_NAME" + echo "Upload finished" +fi + +if [ -f "$BACKUP_FILENAME" ]; then + info "Cleaning up" + rm -vf "$BACKUP_FILENAME" +fi + +info "Backup finished" +echo "Will wait for next scheduled backup" + +if [ ! -z "$BACKUP_RETENTION_DAYS" ]; then + info "Pruning old backups" + bucket=$AWS_S3_BUCKET_NAME + + rule_applies_to=$(mc rm --fake --recursive -force --older-than "${BACKUP_RETENTION_DAYS}d" "backup-target/$bucket" | wc -l) + if [ "$rule_applies_to" == "0" ]; then + echo "No backups found that match the configured retention period. Doing nothing." + exit 0 + fi + + available=$(mc ls "backup-target/$bucket" | wc -l) + + if [ "$rule_applies_to" == "$available" ]; then + echo "Using a retention of $BACKUP_RETENTION_DAYS days would prune all currently existing backups, will not continue." + echo "If this is what you want, please remove files manually instead of using this script." + exit 1 + fi + + mc rm --recursive -force --older-than "${BACKUP_RETENTION_DAYS}d" "backup-target/$bucket" + echo "Successfully pruned all backups older than ${BACKUP_RETENTION_DAYS} days" +fi diff --git a/src/entrypoint.sh b/src/entrypoint.sh new file mode 100644 index 0000000..34e12be --- /dev/null +++ b/src/entrypoint.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# Copyright 2021 - Offen Authors +# SPDX-License-Identifier: MPL-2.0 + +# Portions of this file are taken from github.com/futurice/docker-volume-backup +# See NOTICE for information about authors and licensing. + +set -e + +# Write cronjob env to file, fill in sensible defaults, and read them back in +cat < env.sh +BACKUP_SOURCES="${BACKUP_SOURCES:-/backup}" +BACKUP_CRON_EXPRESSION="${BACKUP_CRON_EXPRESSION:-@daily}" +BACKUP_FILENAME=${BACKUP_FILENAME:-"backup-%Y-%m-%dT%H-%M-%S.tar.gz"} + +BACKUP_RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-}" + +AWS_S3_BUCKET_NAME="${AWS_S3_BUCKET_NAME:-}" +AWS_ENDPOINT="${AWS_ENDPOINT:-s3.amazonaws.com}" + +GPG_PASSPHRASE="${GPG_PASSPHRASE:-}" +EOF +chmod a+x env.sh +source env.sh + +mc alias set backup-target "https://$AWS_ENDPOINT" "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" + +# Add our cron entry, and direct stdout & stderr to Docker commands stdout +echo "Installing cron.d entry with expression $BACKUP_CRON_EXPRESSION" +echo "$BACKUP_CRON_EXPRESSION backup 2>&1" | crontab - + +# Let cron take the wheel +echo "Starting cron in foreground" +crond -f -l 8