mirror of
https://github.com/kevinveenbirkenbach/docker-volume-backup.git
synced 2025-09-09 19:57:11 +02:00
Compare commits
8 Commits
c4cbb290b3
...
main
Author | SHA1 | Date | |
---|---|---|---|
a538e537cb | |||
8f72d61300 | |||
c754083cec | |||
84d0fd6346 | |||
627187cecb | |||
978e153723 | |||
2bf2b0798e | |||
8196a0206b |
@@ -44,16 +44,14 @@ def get_machine_id():
|
|||||||
### GLOBAL CONFIGURATION ###
|
### GLOBAL CONFIGURATION ###
|
||||||
|
|
||||||
# Container names treated as special instances for database backups
|
# Container names treated as special instances for database backups
|
||||||
SPECIAL_INSTANCES = ['central-mariadb', 'central-postgres']
|
DATABASE_CONTAINERS = ['central-mariadb', 'central-postgres']
|
||||||
|
|
||||||
# Images which do not require container stop for file backups
|
# Images which do not require container stop for file backups
|
||||||
IMAGES_NO_STOP_REQUIRED = []
|
IMAGES_NO_STOP_REQUIRED = []
|
||||||
|
|
||||||
# Images to skip entirely
|
# Images to skip entirely
|
||||||
IMAGES_NO_BACKUP_REQUIRED = [
|
IMAGES_NO_BACKUP_REQUIRED = []
|
||||||
'redis',
|
|
||||||
'memcached'
|
|
||||||
]
|
|
||||||
# Compose dirs requiring hard restart
|
# Compose dirs requiring hard restart
|
||||||
DOCKER_COMPOSE_HARD_RESTART_REQUIRED = ['mailu']
|
DOCKER_COMPOSE_HARD_RESTART_REQUIRED = ['mailu']
|
||||||
|
|
||||||
@@ -70,7 +68,7 @@ VERSION_DIR = create_version_directory()
|
|||||||
|
|
||||||
def get_instance(container):
|
def get_instance(container):
|
||||||
"""Extract the database instance name based on container name."""
|
"""Extract the database instance name based on container name."""
|
||||||
if container in SPECIAL_INSTANCES:
|
if container in DATABASE_CONTAINERS:
|
||||||
instance_name = container
|
instance_name = container
|
||||||
else:
|
else:
|
||||||
instance_name = re.split("(_|-)(database|db|postgres)", container)[0]
|
instance_name = re.split("(_|-)(database|db|postgres)", container)[0]
|
||||||
@@ -224,15 +222,37 @@ def change_containers_status(containers, status):
|
|||||||
print(f"No containers to {status}.")
|
print(f"No containers to {status}.")
|
||||||
|
|
||||||
def is_image_whitelisted(container, images):
|
def is_image_whitelisted(container, images):
|
||||||
info = (container)[0]
|
"""
|
||||||
return any(img in info for img in images)
|
Return True if the container's image matches any of the whitelist patterns.
|
||||||
|
Also prints out the image name and the match result.
|
||||||
|
"""
|
||||||
|
# fetch the image (e.g. "nextcloud:23-fpm-alpine")
|
||||||
|
info = get_image_info(container)[0]
|
||||||
|
|
||||||
|
# check against each pattern
|
||||||
|
whitelisted = any(pattern in info for pattern in images)
|
||||||
|
|
||||||
|
# log the result
|
||||||
|
print(f"Container {container!r} → image {info!r} → whitelisted? {whitelisted}", flush=True)
|
||||||
|
|
||||||
|
return whitelisted
|
||||||
|
|
||||||
def is_container_stop_required(containers):
|
def is_container_stop_required(containers):
|
||||||
"""Check if any of the containers are using images that are not whitelisted."""
|
"""
|
||||||
return any(
|
Check if any of the containers are using images that are not whitelisted.
|
||||||
not is_image_whitelisted(c, IMAGES_NO_STOP_REQUIRED)
|
If so, print them out and return True; otherwise return False.
|
||||||
for c in containers
|
"""
|
||||||
)
|
# Find all containers whose image isn’t on the whitelist
|
||||||
|
not_whitelisted = [
|
||||||
|
c for c in containers
|
||||||
|
if not is_image_whitelisted(c, IMAGES_NO_STOP_REQUIRED)
|
||||||
|
]
|
||||||
|
|
||||||
|
if not_whitelisted:
|
||||||
|
print(f"Containers requiring stop because they are not whitelisted: {', '.join(not_whitelisted)}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def create_volume_directory(volume_name):
|
def create_volume_directory(volume_name):
|
||||||
"""Create necessary directories for backup."""
|
"""Create necessary directories for backup."""
|
||||||
@@ -311,7 +331,7 @@ def handle_docker_compose_services(parent_directory):
|
|||||||
print(f"No docker-compose.yml found in {dir_path}. Skipping.")
|
print(f"No docker-compose.yml found in {dir_path}. Skipping.")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global SPECIAL_INSTANCES, IMAGES_NO_STOP_REQUIRED
|
global DATABASE_CONTAINERS, IMAGES_NO_STOP_REQUIRED
|
||||||
parser = argparse.ArgumentParser(description='Backup Docker volumes.')
|
parser = argparse.ArgumentParser(description='Backup Docker volumes.')
|
||||||
parser.add_argument('--everything', action='store_true',
|
parser.add_argument('--everything', action='store_true',
|
||||||
help='Force file backup for all volumes and additional execute database dumps')
|
help='Force file backup for all volumes and additional execute database dumps')
|
||||||
@@ -320,21 +340,30 @@ def main():
|
|||||||
parser.add_argument('--compose-dir', type=str, required=True,
|
parser.add_argument('--compose-dir', type=str, required=True,
|
||||||
help='Path to the parent directory containing docker-compose setups')
|
help='Path to the parent directory containing docker-compose setups')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--special-instances',
|
'--database-containers',
|
||||||
nargs='+',
|
nargs='+',
|
||||||
default=SPECIAL_INSTANCES,
|
required=True,
|
||||||
help='List of container names treated as special instances for database backups'
|
help='List of container names treated as special instances for database backups'
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--images-no-stop-required',
|
'--images-no-stop-required',
|
||||||
nargs='+',
|
nargs='+',
|
||||||
|
required=True,
|
||||||
help='List of image names for which containers should not be stopped during file backup'
|
help='List of image names for which containers should not be stopped during file backup'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--images-no-backup-required',
|
||||||
|
nargs='+',
|
||||||
|
help='List of image names for which no backup should be performed (optional)'
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
SPECIAL_INSTANCES = args.special_instances
|
DATABASE_CONTAINERS = args.database_containers
|
||||||
IMAGES_NO_STOP_REQUIRED = args.images_no_stop_required
|
IMAGES_NO_STOP_REQUIRED = args.images_no_stop_required
|
||||||
|
if args.images_no_backup_required is not None:
|
||||||
|
global IMAGES_NO_BACKUP_REQUIRED
|
||||||
|
IMAGES_NO_BACKUP_REQUIRED = args.images_no_backup_required
|
||||||
|
|
||||||
print('Start volume backups...')
|
print('💾 Start volume backups...', flush=True)
|
||||||
volume_names = execute_shell_command("docker volume ls --format '{{.Name}}'")
|
volume_names = execute_shell_command("docker volume ls --format '{{.Name}}'")
|
||||||
for volume_name in volume_names:
|
for volume_name in volume_names:
|
||||||
print(f'Start backup routine for volume: {volume_name}')
|
print(f'Start backup routine for volume: {volume_name}')
|
||||||
|
Reference in New Issue
Block a user