Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Samuel Lauren
container-backup
Commits
66a383ad
Commit
66a383ad
authored
Oct 05, 2016
by
Samuel Laurén
Browse files
Initial commit
parents
Changes
3
Hide whitespace changes
Inline
Side-by-side
README.md
0 → 100644
View file @
66a383ad
# Backup Docker Containers :floppy_disk:
This repository contains a Bash script for backing up Docker containers along
with their associated data volumes to a Git repository. The backups can be
automatically signed and encrypted to one or more recipients using GPG.
container-backup.sh [-znh] [-s KEY] [-e KEY]... DIR CONTAINER
Export CONTAINERs to DIR. Containers are stopped before they are exported.
This is done in the order they are specified in the command line. Afterwards,
the containers are restarted in the reverse order.
-s KEY Sign backups with KEY
-e KEY Encrypt backups for KEY (can appear multiple times)
-m MANAGER Use MANAGER for container management
-z Compress backups
-n Do not push
-h Display this help
## Example Invocation
# container-backup.sh -z -n backup-dir web-server database-server
## About External Service Managers
Since containers are often managed by an external service manager such as
`systemd`
, the script supports handing off container management commands to
those. This can be done using the
`-m`
option. Example manager can be found in
`systemctl-wrap.sh`
.
container-backup.sh
0 → 100755
View file @
66a383ad
#!/usr/bin/env bash
# Export containers and their associated mount points into a git repository.
set
-euo
pipefail
declare
-a
DEPS
=(
docker gpg jq
tar
git
gzip sha1sum
)
declare
-a
STOPPED
=()
function
export-container
{
# $1: container
echo
"Exporting container
\"
${
1
}
\"
"
docker save
"
$(
get-image
"
$c
"
)
"
| compress | shield
>
"
${
STORE
}
/
${
1
}
/
${
1
}
.tar
$(
suffix
)
"
# Probably not necessary but nice to have anyway
docker inspect
"
$c
"
| compress | shield
>
"
${
STORE
}
/
${
1
}
/
${
1
}
-properties.json
$(
suffix
)
"
}
function
get-image
{
# $1: container
docker inspect
"
$1
"
|
\
jq
-re
'.[] | .Image'
}
function
get-mounts
{
# $1: container
docker inspect
"
$1
"
|
\
jq
-r
'.[] | .Mounts | .[] | .Source,.Name'
}
function
save-mount
{
# $1: container -> $2: source -> $3: name
echo
"Saving mount content for
\"
${
name
}
\"
"
tar
-cp
-C
"
$2
"
.
| compress | shield
>
"
${
STORE
}
/
${
1
}
/
${
3
}
.tar
$(
suffix
)
"
}
function
backup
{
local source
name
for
c
in
"
${
CONTAINERS
[@]
}
"
;
do
export-container
"
$c
"
get-mounts
"
$c
"
|
{
while
true
;
do
read source
||
break
read
name
||
break
if
[[
"
$name
"
=
"null"
]]
;
then
read
-d
" "
name
<<<
"
$(
sha1sum
-b
<<<
"
$source
"
)
"
fi
save-mount
"
$c
"
"
$source
"
"
$name
"
done
}
done
}
function
upload
{
# Not exactly the best way to store big binary blobs...
echo
"Adding changes"
pushd
"
$STORE
"
git add
.
git commit
-m
"Backup
$(
date
)
"
if
[[
"
$PUSH
"
-eq
1
]]
;
then
echo
"Uploading"
git push
fi
popd
}
function
prepare
{
[[
-d
"
$STORE
"
&&
-d
"
$STORE
/.git"
]]
\
||
error
"
\"
$STORE
\"
is not a repository"
for
c
in
"
${
CONTAINERS
[@]
}
"
;
do
mkdir
-p
"
$STORE
/
$c
"
done
}
function
stop-containers
{
echo
"Stopping containers:
${
CONTAINERS
[@]
}
"
for
c
in
"
${
CONTAINERS
[@]
}
"
;
do
status
=
"
$(
docker inspect
"
$c
"
| jq
-re
'.[] | .State | .Running'
)
"
if
[[
"
$status
"
=
"true"
]]
;
then
STOPPED+
=(
"
$c
"
)
fi
done
$MANAGER
stop
"
${
STOPPED
[@]
}
"
}
function
shield
{
# FIXME: Proper quoting
local
args
=
""
if
[[
-n
"
$SIGNER
"
]]
;
then
args
=
"
$args
--local-user
$SIGNER
--sign"
fi
if
[[
-n
"
${
RECIPIENTS
+1
}
"
]]
;
then
args
=
"
$args
--trust-model=always --encrypt
$(
printf
"%s"
"
${
RECIPIENTS
[@]/#/ --recipient
}
"
)
"
fi
if
[[
-n
"
$args
"
]]
;
then
gpg
$args
else
tee
fi
}
function
compress
{
[[
"
$COMPRESS
"
-eq
1
]]
&&
gzip
-9
||
tee
}
function
suffix
{
if
[[
"
$COMPRESS
"
-eq
1
]]
;
then
echo
-n
".gz"
fi
if
[[
-n
"
${
SIGNER
}
"
||
-n
"
${
RECIPIENTS
+1
}
"
]]
;
then
echo
-n
".gpg"
fi
}
function
resume-containers
{
if
[[
-n
"
${
STOPPED
+1
}
"
]]
;
then
local
-a
reversed
=()
for
((
i
=
${#
STOPPED
[@]
}
-1
;
i>
=
0
;
i--
))
;
do
reversed+
=(
"
${
STOPPED
[i]
}
"
)
done
echo
"Restarting stopped containers:
${
STOPPED
[@]
}
"
$MANAGER
start
"
${
reversed
[@]
}
"
fi
}
trap
resume-containers EXIT
function
check-deps
{
for
d
in
"
${
DEPS
[@]
}
"
;
do
hash
"
$d
"
2>/dev/null
||
error
"
$d
is required"
done
}
function
error
{
echo
"Error:
$1
"
>
&2
exit
1
}
# ------------------------------------------------------------------------------
DOC
=
"
$(
cat
<<
EOF
$0
[-znh] [-s KEY] [-e KEY]... DIR CONTAINER
Export CONTAINERs to DIR. Containers are stopped before they are exported.
This is done in the order they are specified in the command line. Afterwards,
the containers are restarted in the reverse order.
-s KEY Sign backups with KEY
-e KEY Encrypt backups for KEY (can appear multiple times)
-m MANAGER Use MANAGER for container management
-z Compress backups
-n Do not push
-h Display this help
EOF
)
"
[[
"
$EUID
"
-eq
0
]]
||
error
"
$0
requires root"
declare
SIGNER
=
""
declare
MANAGER
=
"docker"
declare
-a
RECIPIENTS
=()
declare
-i
COMPRESS
=
0
PUSH
=
1
while
getopts
":s:e:m:znh"
opt
;
do
case
"
$opt
"
in
's'
)
if
[[
"
$OPTARG
"
!=
-
*
]]
;
then
SIGNER
=
"
$OPTARG
"
else
error
"-s requires an argument"
fi
;;
'e'
)
if
[[
"
$OPTARG
"
!=
-
*
]]
;
then
RECIPIENTS+
=(
"
$OPTARG
"
)
else
error
"-e requires an argument"
fi
;;
'm'
)
if
[[
"
$OPTARG
"
!=
-
*
]]
;
then
MANAGER
=
"
$OPTARG
"
else
error
"-m requires an argument"
fi
;;
'z'
)
COMPRESS
=
1
;;
'n'
)
PUSH
=
0
;;
'h'
)
echo
"
$DOC
"
exit
;;
'?'
)
error
"Invalid option: -
$OPTARG
"
;;
esac
done
shift
"
$((
OPTIND-1
))
"
if
[[
"$#"
-lt
2
]]
;
then
echo
"
$DOC
"
exit
1
else
declare
-r
STORE
=
"
$(
realpath
"
$1
"
)
"
;
shift
declare
-ra
CONTAINERS
=(
"
$@
"
)
fi
check-deps
stop-containers
prepare
backup
upload
systemctl-wrap.sh
0 → 100755
View file @
66a383ad
#!/usr/bin/env bash
set
-euo
pipefail
function
error
{
echo
"Error:
$1
"
>
&2
exit
1
}
[[
"
$EUID
"
-eq
0
]]
||
error
"
$0
requires root"
declare
-r
COMMAND
=
"
$1
"
;
shift
case
"
$COMMAND
"
in
"start"
)
for
c
in
"
$@
"
;
do
systemctl start
"
${
c
}
_docker"
done
;;
"stop"
)
for
c
in
"
$@
"
;
do
systemctl stop
"
${
c
}
_docker"
done
;;
esac
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment