Files
scripts-admin-debian/backup_and_restore/sauvegarde_docker/sauvegarde_docker.sh

271 lines
8.9 KiB
Bash
Executable File

#!/bin/bash
# Description
# This script performs a backup of all folders in /srv
# For folders corresponding to active Docker containers,
# containers are stopped during backup then restarted
# Containers listed in EXCLUDED_CONTAINERS are ignored
# Backups are kept for 7 days
# Configuration
SRV_DIR="/srv"
BACKUP_DEST="/root/backup/docker"
RETENTION_DAYS=7
LOG_FILE="/root/backup/docker/docker-backup.log"
# List of containers to exclude from backup (space separated)
# Example: EXCLUDED_CONTAINERS="traefik mysql-prod redis-cache"
EXCLUDED_CONTAINERS=""
# Logging function
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Check if destination directory exists
if [ ! -d "$BACKUP_DEST" ]; then
log_message "Creating destination directory $BACKUP_DEST"
mkdir -p "$BACKUP_DEST"
fi
# Check available disk space (at least 1GB)
available_space=$(df "$BACKUP_DEST" | awk 'NR==2 {print $4}')
if [ "$available_space" -lt 1048576 ]; then
log_message "WARNING: Low disk space (less than 1GB available)"
fi
log_message "Starting Docker backup"
# Counters for statistics
success_count=0
error_count=0
docker_stopped=()
docker_errors=()
# Function to backup a directory
backup_directory() {
local source_dir="$1"
local folder_name="$2"
if [ ! -e "$source_dir" ]; then
log_message "WARNING: $source_dir does not exist, skipped"
((error_count++))
return 1
fi
# Create destination folder for this service
local dest_folder="$BACKUP_DEST/$folder_name"
mkdir -p "$dest_folder"
local filename="${folder_name}_$(date +%Y-%m-%d_%H-%M-%S).tar.gz"
local filepath="$dest_folder/$filename"
log_message "Backing up $source_dir to $folder_name/$filename"
# Create the archive
if tar -czf "$filepath" -C "$(dirname "$source_dir")" "$(basename "$source_dir")" 2>/dev/null; then
# Create SHA-256 checksum file
sha256sum "$filepath" > "$filepath.sha256"
file_size=$(du -h "$filepath" | cut -f1)
log_message "SUCCESS: $filename created ($file_size) - Checksum generated"
((success_count++))
return 0
else
log_message "ERROR: Unable to create archive for $source_dir"
((error_count++))
return 1
fi
}
# Function to check if Docker is installed
is_docker_available() {
command -v docker >/dev/null 2>&1
}
# Function to check if a container is excluded
is_container_excluded() {
local container_name="$1"
if [ -n "$EXCLUDED_CONTAINERS" ]; then
for excluded in $EXCLUDED_CONTAINERS; do
if [ "$container_name" = "$excluded" ]; then
return 0
fi
done
fi
return 1
}
# Function to check if a Docker container exists and is running
is_container_running() {
local container_name="$1"
if is_docker_available; then
docker ps --format "table {{.Names}}" | grep -q "^${container_name}$" 2>/dev/null
else
return 1
fi
}
# Function to stop a Docker container
stop_container() {
local container_name="$1"
log_message "Stopping Docker container: $container_name"
if docker stop "$container_name" >/dev/null 2>&1; then
log_message "Container $container_name stopped successfully"
docker_stopped+=("$container_name")
return 0
else
log_message "ERROR: Unable to stop container $container_name"
docker_errors+=("$container_name")
return 1
fi
}
# Function to start a Docker container
start_container() {
local container_name="$1"
log_message "Starting Docker container: $container_name"
if docker start "$container_name" >/dev/null 2>&1; then
log_message "Container $container_name started successfully"
return 0
else
log_message "ERROR: Unable to start container $container_name"
return 1
fi
}
# Check if Docker is available
if ! is_docker_available; then
log_message "WARNING: Docker is not installed or accessible"
fi
# Backup directories in /srv
log_message "=== DOCKER SERVICES BACKUP ==="
if [ -d "$SRV_DIR" ]; then
# Loop through all folders in /srv
for srv_folder in "$SRV_DIR"/*; do
if [ -d "$srv_folder" ]; then
folder_name=$(basename "$srv_folder")
# Check if the container is in the exclusion list
if is_container_excluded "$folder_name"; then
log_message "EXCLUSION: Container $folder_name skipped (in exclusion list)"
continue
fi
# Check if a Docker container with this name exists and is running
if is_container_running "$folder_name"; then
log_message "Active Docker container detected: $folder_name"
# Stop the container
if stop_container "$folder_name"; then
# Wait a bit to ensure the container is completely stopped
sleep 5
# Perform the backup
backup_directory "$srv_folder" "$folder_name"
# Restart the container
start_container "$folder_name"
else
log_message "WARNING: Backing up $folder_name without stopping container (risk of inconsistency)"
backup_directory "$srv_folder" "$folder_name"
fi
else
# No corresponding Docker container, normal backup
log_message "Service without active container: $folder_name"
backup_directory "$srv_folder" "$folder_name"
fi
fi
done
else
log_message "ERROR: Directory $SRV_DIR does not exist"
exit 1
fi
# Verification and generation of checksums for all archives
log_message "=== CHECKSUM VERIFICATION AND GENERATION ==="
archives_without_checksum=0
archives_with_checksum=0
find "$BACKUP_DEST" -name "*.tar.gz" -type f | while read -r archive_file; do
checksum_file="${archive_file}.sha256"
if [ ! -f "$checksum_file" ]; then
log_message "Generating missing checksum for $(basename "$archive_file")"
sha256sum "$archive_file" > "$checksum_file"
((archives_without_checksum++))
else
((archives_with_checksum++))
fi
done
# Final archive count
total_archives=$(find "$BACKUP_DEST" -name "*.tar.gz" -type f | wc -l)
missing_checksums=$(find "$BACKUP_DEST" -name "*.tar.gz" -type f | while read -r archive; do [ ! -f "${archive}.sha256" ] && echo "$archive"; done | wc -l)
log_message "Archives found: $total_archives"
if [ "$missing_checksums" -eq 0 ]; then
log_message "SUCCESS: All archives have a checksum file"
else
log_message "WARNING: $missing_checksums archives without checksum"
fi
# Cleanup of old backups
log_message "=== OLD BACKUPS CLEANUP ==="
for service_dir in "$BACKUP_DEST"/*/; do
if [ -d "$service_dir" ]; then
service_name=$(basename "$service_dir")
log_message "Removing backups older than $RETENTION_DAYS days for $service_name"
# Find obsolete files (archives and checksums separately)
deleted_files_tar=$(find "$service_dir" -type f -name "*.tar.gz" -mtime +$RETENTION_DAYS -print 2>/dev/null)
deleted_files_sha=$(find "$service_dir" -type f -name "*.sha256" -mtime +$RETENTION_DAYS -print 2>/dev/null)
if [ -n "$deleted_files_tar" ] || [ -n "$deleted_files_sha" ]; then
# Remove and log archives
if [ -n "$deleted_files_tar" ]; then
echo "$deleted_files_tar" | while read -r file; do
log_message "Removing: $service_name/$(basename "$file")"
done
find "$service_dir" -type f -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete 2>/dev/null
fi
# Remove and log checksums
if [ -n "$deleted_files_sha" ]; then
echo "$deleted_files_sha" | while read -r file; do
log_message "Removing: $service_name/$(basename "$file")"
done
find "$service_dir" -type f -name "*.sha256" -mtime +$RETENTION_DAYS -delete 2>/dev/null
fi
else
log_message "No obsolete files to remove for $service_name"
fi
fi
done
# Restart containers that failed to restart
if [ ${#docker_errors[@]} -gt 0 ]; then
log_message "=== RETRY STARTING CONTAINERS IN ERROR ==="
for container in "${docker_errors[@]}"; do
start_container "$container"
done
fi
# Final statistics
log_message "=== BACKUP SUMMARY ==="
log_message "Successful backups: $success_count"
log_message "Errors: $error_count"
if [ ${#docker_stopped[@]} -gt 0 ]; then
log_message "Docker containers managed: ${docker_stopped[*]}"
fi
if [ ${#docker_errors[@]} -gt 0 ]; then
log_message "Docker containers in error: ${docker_errors[*]}"
fi
log_message "Backup completed"
# Exit code based on errors
if [ $error_count -gt 0 ] || [ ${#docker_errors[@]} -gt 0 ]; then
exit 1
else
exit 0
fi