#!/bin/bash # Description # Ce script effectue une sauvegarde de tous les dossiers dans /srv # Pour les dossiers correspondant à des conteneurs Docker actifs, # les conteneurs sont arrêtés pendant la sauvegarde puis redémarrés # Les sauvegardes sont conservées pendant 7 jours # Configuration SRV_DIR="/home/docker/srv" BACKUP_DEST="/home/docker/backup" RETENTION_DAYS=7 LOG_FILE="/home/docker/docker-backup.log" # Fonction de logging log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" } # Vérification de l'existence du répertoire de destination if [ ! -d "$BACKUP_DEST" ]; then log_message "Création du répertoire de destination $BACKUP_DEST" mkdir -p "$BACKUP_DEST" fi # Vérification de l'espace disque disponible (au moins 1GB) available_space=$(df "$BACKUP_DEST" | awk 'NR==2 {print $4}') if [ "$available_space" -lt 1048576 ]; then log_message "AVERTISSEMENT: Espace disque faible (moins de 1GB disponible)" fi log_message "Début de la sauvegarde Docker" # Compteurs pour les statistiques success_count=0 error_count=0 docker_stopped=() docker_errors=() # Fonction pour sauvegarder un répertoire backup_directory() { local source_dir="$1" local folder_name="$2" if [ ! -e "$source_dir" ]; then log_message "AVERTISSEMENT: $source_dir n'existe pas, ignoré" ((error_count++)) return 1 fi # Créer le dossier de destination pour ce 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 "Sauvegarde de $source_dir vers $folder_name/$filename" # Création de l'archive if tar -czf "$filepath" -C "$(dirname "$source_dir")" "$(basename "$source_dir")" 2>/dev/null; then # Création du fichier de checksum SHA-256 sha256sum "$filepath" > "$filepath.sha256" file_size=$(du -h "$filepath" | cut -f1) log_message "SUCCESS: $filename créé ($file_size) - Checksum généré" ((success_count++)) return 0 else log_message "ERREUR: Impossible de créer l'archive pour $source_dir" ((error_count++)) return 1 fi } # Fonction pour vérifier si Docker est installé is_docker_available() { command -v docker >/dev/null 2>&1 } # Fonction pour vérifier si un conteneur Docker existe et est en cours d'exécution 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 } # Fonction pour arrêter un conteneur Docker stop_container() { local container_name="$1" log_message "Arrêt du conteneur Docker: $container_name" if docker stop "$container_name" >/dev/null 2>&1; then log_message "Conteneur $container_name arrêté avec succès" docker_stopped+=("$container_name") return 0 else log_message "ERREUR: Impossible d'arrêter le conteneur $container_name" docker_errors+=("$container_name") return 1 fi } # Fonction pour redémarrer un conteneur Docker start_container() { local container_name="$1" log_message "Redémarrage du conteneur Docker: $container_name" if docker start "$container_name" >/dev/null 2>&1; then log_message "Conteneur $container_name redémarré avec succès" return 0 else log_message "ERREUR: Impossible de redémarrer le conteneur $container_name" return 1 fi } # Vérifier si Docker est disponible if ! is_docker_available; then log_message "AVERTISSEMENT: Docker n'est pas installé ou accessible" fi # Sauvegarde des répertoires dans /srv log_message "=== SAUVEGARDE DES SERVICES DOCKER ===" if [ -d "$SRV_DIR" ]; then # Parcourir tous les dossiers dans /srv for srv_folder in "$SRV_DIR"/*; do if [ -d "$srv_folder" ]; then folder_name=$(basename "$srv_folder") # Vérifier si un conteneur Docker avec ce nom existe et est en cours d'exécution if is_container_running "$folder_name"; then log_message "Conteneur Docker actif détecté: $folder_name" # Arrêter le conteneur if stop_container "$folder_name"; then # Attendre un peu pour s'assurer que le conteneur est complètement arrêté sleep 5 # Effectuer la sauvegarde backup_directory "$srv_folder" "$folder_name" # Redémarrer le conteneur start_container "$folder_name" else log_message "AVERTISSEMENT: Sauvegarde de $folder_name sans arrêter le conteneur (risque d'incohérence)" backup_directory "$srv_folder" "$folder_name" fi else # Pas de conteneur Docker correspondant, sauvegarde normale log_message "Service sans conteneur actif: $folder_name" backup_directory "$srv_folder" "$folder_name" fi fi done else log_message "ERREUR: Le répertoire $SRV_DIR n'existe pas" exit 1 fi # Vérification et génération des checksums pour toutes les archives log_message "=== VÉRIFICATION ET GÉNÉRATION DES CHECKSUMS ===" 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 "Génération du checksum manquant pour $(basename "$archive_file")" sha256sum "$archive_file" > "$checksum_file" ((archives_without_checksum++)) else ((archives_with_checksum++)) fi done # Compte final des archives 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 trouvées: $total_archives" if [ "$missing_checksums" -eq 0 ]; then log_message "SUCCESS: Toutes les archives ont un fichier checksum" else log_message "AVERTISSEMENT: $missing_checksums archives sans checksum" fi # Suppression des sauvegardes obsolètes log_message "=== NETTOYAGE DES ANCIENNES SAUVEGARDES ===" for service_dir in "$BACKUP_DEST"/*/; do if [ -d "$service_dir" ]; then service_name=$(basename "$service_dir") log_message "Suppression des sauvegardes de plus de $RETENTION_DAYS jours pour $service_name" # Chercher les fichiers obsolètes (archives et checksums séparément) 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 # Supprimer et logger les archives if [ -n "$deleted_files_tar" ]; then echo "$deleted_files_tar" | while read -r file; do log_message "Suppression: $service_name/$(basename "$file")" done find "$service_dir" -type f -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete 2>/dev/null fi # Supprimer et logger les checksums if [ -n "$deleted_files_sha" ]; then echo "$deleted_files_sha" | while read -r file; do log_message "Suppression: $service_name/$(basename "$file")" done find "$service_dir" -type f -name "*.sha256" -mtime +$RETENTION_DAYS -delete 2>/dev/null fi else log_message "Aucun fichier obsolète à supprimer pour $service_name" fi fi done # Redémarrage des conteneurs qui n'ont pas pu être redémarrés if [ ${#docker_errors[@]} -gt 0 ]; then log_message "=== TENTATIVE DE REDÉMARRAGE DES CONTENEURS EN ERREUR ===" for container in "${docker_errors[@]}"; do start_container "$container" done fi # Statistiques finales log_message "=== RÉSUMÉ DE LA SAUVEGARDE ===" log_message "Sauvegardes réussies: $success_count" log_message "Erreurs: $error_count" if [ ${#docker_stopped[@]} -gt 0 ]; then log_message "Conteneurs Docker gérés: ${docker_stopped[*]}" fi if [ ${#docker_errors[@]} -gt 0 ]; then log_message "Conteneurs Docker en erreur: ${docker_errors[*]}" fi log_message "Sauvegarde terminée" # Code de sortie basé sur les erreurs if [ $error_count -gt 0 ] || [ ${#docker_errors[@]} -gt 0 ]; then exit 1 else exit 0 fi