forked from coop-cloud/backup-bot-two
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			fix-quotes
			...
			backup_vol
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e360c3d8f8 | |||
| 50b317c12d | |||
| 9cb3a469f3 | |||
| 24d2c0e85b | 
							
								
								
									
										16
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								.drone.yml
									
									
									
									
									
								
							@ -7,22 +7,6 @@ steps:
 | 
				
			|||||||
    commands:
 | 
					    commands:
 | 
				
			||||||
      - shellcheck backup.sh
 | 
					      - shellcheck backup.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: publish image
 | 
					 | 
				
			||||||
    image: plugins/docker
 | 
					 | 
				
			||||||
    settings:
 | 
					 | 
				
			||||||
      auto_tag: true
 | 
					 | 
				
			||||||
      username: thecoopcloud
 | 
					 | 
				
			||||||
      password:
 | 
					 | 
				
			||||||
        from_secret: thecoopcloud_password
 | 
					 | 
				
			||||||
      repo: thecoopcloud/backup-bot-two
 | 
					 | 
				
			||||||
      tags: latest
 | 
					 | 
				
			||||||
    depends_on:
 | 
					 | 
				
			||||||
      - run shellcheck
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        exclude:
 | 
					 | 
				
			||||||
          - pull_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
trigger:
 | 
					trigger:
 | 
				
			||||||
  branch:
 | 
					  branch:
 | 
				
			||||||
    - main
 | 
					    - main
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Dockerfile
									
									
									
									
									
								
							@ -1,13 +0,0 @@
 | 
				
			|||||||
FROM docker:24.0.2-dind
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RUN apk add --upgrade --no-cache \
 | 
					 | 
				
			||||||
  bash \
 | 
					 | 
				
			||||||
  curl \
 | 
					 | 
				
			||||||
  jq \
 | 
					 | 
				
			||||||
  restic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
COPY backup.sh /usr/bin/backup.sh
 | 
					 | 
				
			||||||
COPY setup-cron.sh /usr/bin/setup-cron.sh
 | 
					 | 
				
			||||||
RUN chmod +x /usr/bin/backup.sh /usr/bin/setup-cron.sh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ENTRYPOINT [ "/usr/bin/setup-cron.sh" ]
 | 
					 | 
				
			||||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@ -4,7 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
_This Time, It's Easily Configurable_
 | 
					_This Time, It's Easily Configurable_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Automatically take backups from running Docker Swarm services into a volume.
 | 
					Automatically take backups from all volumes of running Docker Swarm services and runs pre- and post commands.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Background
 | 
					## Background
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,15 +49,13 @@ services:
 | 
				
			|||||||
  db:
 | 
					  db:
 | 
				
			||||||
    deploy:
 | 
					    deploy:
 | 
				
			||||||
      labels:
 | 
					      labels:
 | 
				
			||||||
        backupbot.backup: "true"
 | 
					        backupbot.backup: ${BACKUP:-"true"} 
 | 
				
			||||||
        backupbot.backup.pre-hook: 'mysqldump -u root -p"$(cat /run/secrets/db_root_password)" -f /tmp/dump/dump.db'
 | 
					        backupbot.backup.pre-hook: 'mysqldump -u root -p"$(cat /run/secrets/db_root_password)" -f /volume_path/dump.db'
 | 
				
			||||||
        backupbot.backup.post-hook: "rm -rf /tmp/dump/dump.db"
 | 
					        backupbot.backup.post-hook: "rm -rf /volume_path/dump.db"
 | 
				
			||||||
        backupbot.backup.path: "/tmp/dump/,/etc/foo/"
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `backupbot.backup` -- set to `true` to back up this service (REQUIRED)
 | 
					- `backupbot.backup` -- set to `true` to back up this service (REQUIRED)
 | 
				
			||||||
- `backupbot.backup.path` -- comma separated list of file paths within the service to copy (REQUIRED)
 | 
					- `backupbot.backup.pre-hook` -- command to run before copying files (optional), save all dumps into the volumes
 | 
				
			||||||
- `backupbot.backup.pre-hook` -- command to run before copying files (optional)
 | 
					 | 
				
			||||||
- `backupbot.backup.post-hook` -- command to run after copying files (optional)
 | 
					- `backupbot.backup.post-hook` -- command to run after copying files (optional)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
As in the above example, you can reference Docker Secrets, e.g. for looking up database passwords, by reading the files in `/run/secrets` directly.
 | 
					As in the above example, you can reference Docker Secrets, e.g. for looking up database passwords, by reading the files in `/run/secrets` directly.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								abra.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								abra.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					export ENTRYPOINT_VERSION=v1
 | 
				
			||||||
 | 
					export BACKUP_VERSION=v1
 | 
				
			||||||
							
								
								
									
										49
									
								
								backup.sh
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								backup.sh
									
									
									
									
									
								
							@ -1,12 +1,14 @@
 | 
				
			|||||||
#!/bin/bash
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
server_name="${SERVER_NAME:?SERVER_NAME not set}"
 | 
					server_name="${SERVER_NAME:?SERVER_NAME not set}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
restic_password_file="${RESTIC_PASSWORD_FILE:?RESTIC_PASSWORD_FILE not set}"
 | 
					restic_password_file="${RESTIC_PASSWORD_FILE:?RESTIC_PASSWORD_FILE not set}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
restic_host="${RESTIC_HOST:?RESTIC_HOST not set}"
 | 
					restic_host="${RESTIC_HOST:?RESTIC_HOST not set}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
backup_path="${BACKUP_DEST:?BACKUP_DEST not set}"
 | 
					backup_paths=()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# shellcheck disable=SC2153
 | 
					# shellcheck disable=SC2153
 | 
				
			||||||
ssh_key_file="${SSH_KEY_FILE}"
 | 
					ssh_key_file="${SSH_KEY_FILE}"
 | 
				
			||||||
@ -71,8 +73,8 @@ else
 | 
				
			|||||||
	mapfile -t services < <(docker service ls --format '{{ .Name }}')
 | 
						mapfile -t services < <(docker service ls --format '{{ .Name }}')
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					post_commands=()
 | 
				
			||||||
if [[ \ $*\  != *\ --skip-backup\ * ]]; then
 | 
					if [[ \ $*\  != *\ --skip-backup\ * ]]; then
 | 
				
			||||||
	rm -rf "${backup_path}"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for service in "${services[@]}"; do
 | 
						for service in "${services[@]}"; do
 | 
				
			||||||
		echo "service: $service"
 | 
							echo "service: $service"
 | 
				
			||||||
@ -80,36 +82,21 @@ if [[ \ $*\  != *\ --skip-backup\ * ]]; then
 | 
				
			|||||||
		if echo "$details" | jq -r '.["backupbot.backup"]' | grep -q 'true'; then
 | 
							if echo "$details" | jq -r '.["backupbot.backup"]' | grep -q 'true'; then
 | 
				
			||||||
			pre=$(echo "$details" | jq -r '.["backupbot.backup.pre-hook"]')
 | 
								pre=$(echo "$details" | jq -r '.["backupbot.backup.pre-hook"]')
 | 
				
			||||||
			post=$(echo "$details" | jq -r '.["backupbot.backup.post-hook"]')
 | 
								post=$(echo "$details" | jq -r '.["backupbot.backup.post-hook"]')
 | 
				
			||||||
			path=$(echo "$details" | jq -r '.["backupbot.backup.path"]')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if [ "$path" = "null" ]; then
 | 
					 | 
				
			||||||
				echo "ERROR: missing 'path' for $service"
 | 
					 | 
				
			||||||
				continue # or maybe exit?
 | 
					 | 
				
			||||||
			fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			container=$(docker container ls -f "name=$service" --format '{{ .ID }}')
 | 
								container=$(docker container ls -f "name=$service" --format '{{ .ID }}')
 | 
				
			||||||
 | 
								stack_name=$(echo "$details" | jq -r '.["com.docker.stack.namespace"]')
 | 
				
			||||||
			echo "backing up $service"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if [ "$pre" != "null" ]; then
 | 
								if [ "$pre" != "null" ]; then
 | 
				
			||||||
				# run the precommand
 | 
									# run the precommand
 | 
				
			||||||
				# shellcheck disable=SC2086
 | 
									echo "executing precommand $pre in container $container"
 | 
				
			||||||
				docker exec "$container" sh -c "$pre"
 | 
									docker exec "$container" sh -c "$pre"
 | 
				
			||||||
			fi
 | 
								fi
 | 
				
			||||||
 | 
					 | 
				
			||||||
			# run the backup
 | 
					 | 
				
			||||||
			for p in ${path//,/ }; do
 | 
					 | 
				
			||||||
				# creates the parent folder, so `docker cp` has reliable behaviour no matter if $p ends with `/` or `/.`
 | 
					 | 
				
			||||||
				dir=$backup_path/$service/$(dirname "$p")
 | 
					 | 
				
			||||||
				test -d "$dir" || mkdir -p "$dir"
 | 
					 | 
				
			||||||
				docker cp -a "$container:$p" "$dir/$(basename "$p")"
 | 
					 | 
				
			||||||
			done
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if [ "$post" != "null" ]; then
 | 
								if [ "$post" != "null" ]; then
 | 
				
			||||||
				# run the postcommand
 | 
									# append post command
 | 
				
			||||||
				# shellcheck disable=SC2086
 | 
									post_commands+=("docker exec $container sh -c \"$post\"")
 | 
				
			||||||
				docker exec "$container" sh -c "$post"
 | 
					 | 
				
			||||||
			fi
 | 
								fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								# add volume paths to backup path
 | 
				
			||||||
 | 
								backup_paths+=(/var/lib/docker/volumes/"${stack_name}"_*)
 | 
				
			||||||
		fi
 | 
							fi
 | 
				
			||||||
	done
 | 
						done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -121,10 +108,12 @@ if [[ \ $*\  != *\ --skip-backup\ * ]]; then
 | 
				
			|||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [[ \ $*\  != *\ --skip-upload\ * ]]; then
 | 
					if [[ \ $*\  != *\ --skip-upload\ * ]]; then
 | 
				
			||||||
	_restic backup --host "$server_name" --tag coop-cloud "$backup_path"
 | 
						echo "${backup_paths[@]}"
 | 
				
			||||||
 | 
						_restic backup --host "$server_name" --tag coop-cloud "${backup_paths[@]}"
 | 
				
			||||||
	if [ "$REMOVE_BACKUP_VOLUME_AFTER_UPLOAD" -eq 1 ]; then
 | 
					 | 
				
			||||||
		echo "Cleaning up ${backup_path}"
 | 
					 | 
				
			||||||
		rm -rf "${backup_path}"
 | 
					 | 
				
			||||||
	fi
 | 
					 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# run post commands
 | 
				
			||||||
 | 
					for post in "${post_commands[@]}"; do
 | 
				
			||||||
 | 
						echo "executing postcommand $post"
 | 
				
			||||||
 | 
						eval "$post"
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										29
									
								
								compose.yml
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								compose.yml
									
									
									
									
									
								
							@ -2,11 +2,10 @@
 | 
				
			|||||||
version: "3.8"
 | 
					version: "3.8"
 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  app:
 | 
					  app:
 | 
				
			||||||
    image: thecoopcloud/backup-bot-two:latest
 | 
					    image: docker:24.0.2-dind
 | 
				
			||||||
#    build: .
 | 
					 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - "/var/run/docker.sock:/var/run/docker.sock"
 | 
					      - "/var/run/docker.sock:/var/run/docker.sock"
 | 
				
			||||||
      - "backups:/backups"
 | 
					      - "/var/lib/docker/volumes/:/var/lib/docker/volumes/:ro"
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      - CRON_SCHEDULE
 | 
					      - CRON_SCHEDULE
 | 
				
			||||||
      - RESTIC_REPO
 | 
					      - RESTIC_REPO
 | 
				
			||||||
@ -19,17 +18,25 @@ services:
 | 
				
			|||||||
      - restic_password
 | 
					      - restic_password
 | 
				
			||||||
    deploy:
 | 
					    deploy:
 | 
				
			||||||
      labels:
 | 
					      labels:
 | 
				
			||||||
        - "traefik.enable=true"
 | 
					 | 
				
			||||||
        - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=8008"
 | 
					 | 
				
			||||||
        - "traefik.http.routers.${STACK_NAME}.rule="
 | 
					 | 
				
			||||||
        - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure"
 | 
					 | 
				
			||||||
        - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}"
 | 
					 | 
				
			||||||
        - coop-cloud.${STACK_NAME}.version=0.1.0+latest
 | 
					        - coop-cloud.${STACK_NAME}.version=0.1.0+latest
 | 
				
			||||||
 | 
					    configs:
 | 
				
			||||||
volumes:
 | 
					      - source: entrypoint
 | 
				
			||||||
  backups:
 | 
					        target: /entrypoint.sh
 | 
				
			||||||
 | 
					        mode: 0555
 | 
				
			||||||
 | 
					      - source: backup
 | 
				
			||||||
 | 
					        target: /backup.sh
 | 
				
			||||||
 | 
					        mode: 0555
 | 
				
			||||||
 | 
					    entrypoint: ['/entrypoint.sh']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
secrets:
 | 
					secrets:
 | 
				
			||||||
  restic_password:
 | 
					  restic_password:
 | 
				
			||||||
    external: true
 | 
					    external: true
 | 
				
			||||||
    name: ${STACK_NAME}_restic_password_${SECRET_RESTIC_PASSWORD_VERSION}
 | 
					    name: ${STACK_NAME}_restic_password_${SECRET_RESTIC_PASSWORD_VERSION}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					configs:
 | 
				
			||||||
 | 
					  entrypoint:
 | 
				
			||||||
 | 
					    name: ${STACK_NAME}_entrypoint_${ENTRYPOINT_VERSION}
 | 
				
			||||||
 | 
					    file: entrypoint.sh
 | 
				
			||||||
 | 
					  backup:
 | 
				
			||||||
 | 
					    name: ${STACK_NAME}_backup_${BACKUP_VERSION}
 | 
				
			||||||
 | 
					    file: backup.sh
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,12 @@
 | 
				
			|||||||
#!/bin/bash
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
set -o pipefail
 | 
					
 | 
				
			||||||
 | 
					apk add --upgrade --no-cache bash curl jq restic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cron_schedule="${CRON_SCHEDULE:?CRON_SCHEDULE not set}"
 | 
					cron_schedule="${CRON_SCHEDULE:?CRON_SCHEDULE not set}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "$cron_schedule /usr/bin/backup.sh" | crontab -
 | 
					echo "$cron_schedule /backup.sh" | crontab -
 | 
				
			||||||
crontab -l
 | 
					crontab -l
 | 
				
			||||||
 | 
					
 | 
				
			||||||
crond -f -d8 -L /dev/stdout
 | 
					crond -f -d8 -L /dev/stdout
 | 
				
			||||||
		Reference in New Issue
	
	Block a user