Backup-Home-docker.tar.gz.sh
· 14 KiB · Bash
Sin formato
#!/bin/bash
#############################
# CONFIG
#############################
# Backup complet des data docker , casaos et portainer
# avec liste de tous les compose files des containers classés par répertoire.
# Répertoire contenant tes containers
SOURCE_DIR="/home/docker"
# Répertoire où stocker les sauvegardes
BACKUP_DIR="/home/backup/docker"
# Nombre max de backups à conserver (dossiers datés)
MAX_BACKUPS=3
# Date interne (pour logs / JSON)
DATE_RAW="$(date +'%Y%m%d_%H%M%S')"
# Date pour le dossier de backup (format humain demandé)
DATE_DIR="$(date +'%Y.%m.%d_%H-%M-%S')"
# Dossier de ce backup
RUN_DIR="$BACKUP_DIR/$DATE_DIR"
# Fichiers TAR dans le dossier de ce backup
BACKUP_FILE="$RUN_DIR/docker_backup.tar.gz"
CASAOS_FULL_TAR="$RUN_DIR/casaos_full_backup.tar.gz"
# Dossier où l'on met les compose / configs
CONFIG_DIR="$RUN_DIR"
# Dossiers à exclure (chemins relatifs à $SOURCE_DIR pour le tar Docker)
EXCLUDE_DIRS=(
"docker_var_lib"
"1_Backups"
"casaos_data/*"
"nginx_proxy/data/logs/*"
"yt-dl/video/*"
)
# LOGS
LOG_FILE="/var/log/docker_backup.log"
#############################
# COULEURS
#############################
NC="\e[0m"
GREEN="\e[32m"
YELLOW="\e[33m"
CYAN="\e[36m"
RED="\e[31m"
#############################
# FONCTIONS
#############################
log_message() {
local msg="$1"
local now
now="$(date +'%Y-%m-%d %H:%M:%S')"
echo "$now - $msg" >> "$LOG_FILE"
echo -e "$now - $msg"
}
format_time() {
local t=$1
((h=t/3600))
((m=(t%3600)/60))
((s=t%60))
printf "%02d:%02d:%02d\n" $h $m $s
}
check_pigz() {
if ! command -v pigz >/dev/null 2>&1; then
echo -e "${YELLOW}pigz n'est pas installé. Voulez-vous l’installer ? (y/n)${NC}"
read rep
if [ "$rep" = "y" ]; then
sudo apt update && sudo apt install -y pigz || {
echo -e "${RED}Échec de l’installation de pigz. Abandon.${NC}"
exit 1
}
else
echo -e "${RED}pigz est requis pour ce script. Abandon.${NC}"
exit 1
fi
fi
}
rotate_backups() {
log_message "Rotation des anciens backups (conserver les $MAX_BACKUPS plus récents)..."
# On ne garde que les dossiers datés dans BACKUP_DIR
mapfile -t backups_dirs < <(ls -1td "$BACKUP_DIR"/*/ 2>/dev/null || true)
if [ "${#backups_dirs[@]}" -gt "$MAX_BACKUPS" ]; then
for d in "${backups_dirs[@]:$MAX_BACKUPS}"; do
log_message "Suppression ancien backup (dossier) : $d"
rm -rf "$d"
done
fi
}
get_size() {
du -sh "$1" 2>/dev/null | awk '{print $1}'
}
#############################
# DÉBUT SCRIPT
#############################
SECONDS=0
check_pigz
mkdir -p "$RUN_DIR"
touch "$LOG_FILE"
echo -e "${CYAN}=== Backup Docker + Configs (Portainer / CasaOS) ===${NC}"
log_message "Début sauvegarde Docker + configs Portainer & CasaOS..."
log_message "Dossier de backup : $RUN_DIR"
##############################################
# 1) Sauvegarde TAR Docker via pigz + exclusions
##############################################
log_message "Création de l’archive Docker : $BACKUP_FILE"
EXCLUDE_ARGS=(
"--exclude=*.log"
"--exclude=.composer"
"--exclude=.aptitude"
"--exclude=.cache"
"--exclude=.cmake"
"--exclude=.yarn"
"--exclude=.w3m"
"--exclude=.pip"
"--exclude=.pm2"
"--exclude=.pm"
"--exclude=.bundle"
"--exclude=.gem"
"--exclude=.cpan"
"--exclude=.cpanm"
"--exclude=.git"
"--exclude=.local"
"--exclude=.npm"
"--exclude=.nvm"
"--exclude=.rvm"
"--exclude=node_modules"
"--exclude=lost+found"
)
for d in "${EXCLUDE_DIRS[@]}"; do
EXCLUDE_ARGS+=( "--exclude=$d" )
done
tar -cf "$BACKUP_FILE" -I pigz \
--directory="$SOURCE_DIR" \
"${EXCLUDE_ARGS[@]}" \
. 2>> "$LOG_FILE"
if [ $? -ne 0 ]; then
log_message "ERREUR lors de la création du tar Docker."
echo -e "${RED}Erreur lors de la création de l’archive Docker.${NC}"
exit 1
fi
##############################################
# 2) Extraction des docker-compose
# - CasaOS: RUN_DIR/casaos/<container>/
# - Docker: RUN_DIR/docker/<container>/
##############################################
log_message "Extraction des docker-compose CasaOS..."
if [ -d "$SOURCE_DIR/casaos_data" ]; then
find "$SOURCE_DIR/casaos_data" -maxdepth 3 -type f -name "docker-compose.y*ml" | while read file; do
container_name=$(basename "$(dirname "$file")")
dest_dir="$CONFIG_DIR/casaos/$container_name"
mkdir -p "$dest_dir"
cp "$file" "$dest_dir/"
log_message " [CasaOS] → Compose trouvé : $file"
done
fi
log_message "Extraction des docker-compose Docker (hors casaos_data & dossiers exclus)..."
find "$SOURCE_DIR" -mindepth 2 -maxdepth 2 -type f -name "docker-compose.y*ml" \
! -path "$SOURCE_DIR/casaos_data/*" \
! -path "$SOURCE_DIR/docker_var_lib/*" \
2>/dev/null | while read file; do
container_name=$(basename "$(dirname "$file")")
dest_dir="$CONFIG_DIR/Compose/$container_name"
mkdir -p "$dest_dir"
cp "$file" "$dest_dir/"
log_message " [Docker] → Compose trouvé : $file"
done
##############################################
# 3) Config Portainer (full)
##############################################
PORTAINER_CONFIG_DIR="$CONFIG_DIR/portainer"
PORTAINER_FILES=0
if docker volume inspect portainer_data >/dev/null 2>&1; then
log_message "Sauvegarde config Portainer..."
mkdir -p "$PORTAINER_CONFIG_DIR"
cp -r /var/lib/docker/volumes/portainer_data/_data/* "$PORTAINER_CONFIG_DIR/" 2>>"$LOG_FILE"
PORTAINER_FILES=$(find "$PORTAINER_CONFIG_DIR" -type f 2>/dev/null | wc -l)
else
PORTAINER_CONFIG_DIR=""
fi
##############################################
# 4) Config CasaOS (full + tar.gz)
##############################################
CASAOS_CONFIG_DIR="$CONFIG_DIR/casaos/full_config"
if [ -d "/var/lib/casaos" ]; then
log_message "Sauvegarde config CasaOS (full vers $CASAOS_CONFIG_DIR)..."
mkdir -p "$CASAOS_CONFIG_DIR"
cp -r /var/lib/casaos/* "$CASAOS_CONFIG_DIR/" 2>>"$LOG_FILE"
fi
# Création du second tar.gz CasaOS FULL (config + volumes casaos_data)
if [ -d "/var/lib/casaos" ] || [ -d "$SOURCE_DIR/casaos_data" ]; then
CASAOS_TAR_SOURCES=()
[ -d "/var/lib/casaos" ] && CASAOS_TAR_SOURCES+=( "/var/lib/casaos" )
[ -d "$SOURCE_DIR/casaos_data" ] && CASAOS_TAR_SOURCES+=( "$SOURCE_DIR/casaos_data" )
if [ "${#CASAOS_TAR_SOURCES[@]}" -gt 0 ]; then
log_message "Création de l’archive CASAOS FULL : $CASAOS_FULL_TAR"
tar -cf "$CASAOS_FULL_TAR" -I pigz "${CASAOS_TAR_SOURCES[@]}" 2>>"$LOG_FILE"
if [ $? -ne 0 ]; then
log_message "ERREUR lors de la création du tar CasaOS."
echo -e "${RED}Erreur lors de la création de l’archive CasaOS.${NC}"
fi
fi
fi
##############################################
# 5) Comptages finaux (sur le backup courant)
##############################################
# Compose dans le backup (docker + CasaOS compose seulement)
CASAOS_COMPOSE_BACKUP=$(find "$CONFIG_DIR/casaos" -maxdepth 3 -type f -name "docker-compose.y*ml" ! -path "$CASAOS_CONFIG_DIR/*" 2>/dev/null | wc -l)
DOCKER_COMPOSE_BACKUP=$(find "$CONFIG_DIR/docker" -type f -name "docker-compose.y*ml" 2>/dev/null | wc -l)
# Compose dans le dossier source (uniquement /home/docker/<container>/docker-compose.*)
SOURCE_COMPOSE_COUNT=$(find "$SOURCE_DIR" -mindepth 2 -maxdepth 2 -type f -name "docker-compose.y*ml" \
! -path "$SOURCE_DIR/casaos_data/*" \
! -path "$SOURCE_DIR/docker_var_lib/*" \
2>/dev/null | wc -l)
# Containers Docker "valides"
CONTAINERS_DOCKER=$(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d \
! -path "$SOURCE_DIR/casaos_data" \
! -path "$SOURCE_DIR/docker_var_lib" \
! -path "$SOURCE_DIR/portainer" 2>/dev/null | wc -l)
# Rotation des anciens backups (par dossiers)
rotate_backups
# Nombre total de sets de backup (dossiers datés)
TOTAL_BACKUP_SETS=$(ls -1d "$BACKUP_DIR"/*/ 2>/dev/null | wc -l)
# Oldest / newest backup set
mapfile -t CURRENT_SETS < <(ls -1d "$BACKUP_DIR"/*/ 2>/dev/null | sort)
OLDEST_DIR="${CURRENT_SETS[0]}"
NEWEST_DIR="${CURRENT_SETS[${#CURRENT_SETS[@]}-1]}"
DURATION="$(format_time $SECONDS)"
# Tailles des backups courants
TAR_SIZE=$(get_size "$BACKUP_FILE")
CONFIG_SIZE=$(get_size "$CONFIG_DIR")
DOCKER_CONFIG_SIZE=$(get_size "$CONFIG_DIR/docker")
# CasaOS full config size (copie dans RUN_DIR/casaos/full_config)
if [ -d "$CASAOS_CONFIG_DIR" ]; then
CASAOS_CONFIG_SIZE=$(get_size "$CASAOS_CONFIG_DIR")
else
CASAOS_CONFIG_SIZE=""
fi
# CasaOS FULL TAR size
if [ -f "$CASAOS_FULL_TAR" ]; then
CASAOS_FULL_TAR_SIZE=$(get_size "$CASAOS_FULL_TAR")
else
CASAOS_FULL_TAR_SIZE=""
fi
# Portainer full config size
if [ -n "$PORTAINER_CONFIG_DIR" ] && [ -d "$PORTAINER_CONFIG_DIR" ]; then
PORTAINER_CONFIG_SIZE=$(get_size "$PORTAINER_CONFIG_DIR")
else
PORTAINER_CONFIG_SIZE=""
fi
##############################################
# 6) RÉSUMÉS
##############################################
echo -e "${GREEN}===== BACKUP TERMINÉ =====${NC}"
echo -e "${CYAN}Dossier backup courant : ${YELLOW}$RUN_DIR${NC}"
##############################################
# LISTE DES DOSSIERS DOCKER PAR TAILLE (ASCII)
##############################################
echo -e "\n${CYAN}----- LISTE/TAILLE DES RÉPERTOIRES DOCKER -----${NC}"
DOCKER_DIRS=$(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d \
! -path "$SOURCE_DIR/casaos_data" \
! -path "$SOURCE_DIR/docker_var_lib" \
! -path "$SOURCE_DIR/portainer" 2>/dev/null)
if [ -z "$DOCKER_DIRS" ]; then
echo -e "${YELLOW}(Aucun dossier Docker trouvé)${NC}"
else
echo -e "┌──────────────────────────┬─────────┬────────────────────────────────────────┐"
printf "│ %-24s │ %-7s │ %-38s │\n" "Container" "Taille" "Chemin"
echo -e "├──────────────────────────┼─────────┼────────────────────────────────────────┤"
du -sh $DOCKER_DIRS 2>/dev/null | sort -hr | while read -r size path; do
name=$(basename "$path")
printf "│ %-24s │ %-7s │ %-38s │\n" "$name" "$size" "$path"
done
echo -e "└──────────────────────────┴─────────┴────────────────────────────────────────┘"
fi
#################################
# DOCKER
#################################
echo -e "${CYAN}\n----- DOCKER -----${NC}"
echo -e "${CYAN}Containers Docker détectés : ${GREEN}$CONTAINERS_DOCKER${NC}"
echo -e "${CYAN}Compose source (Docker) : ${GREEN}$SOURCE_COMPOSE_COUNT${NC}"
echo -e "${CYAN}Compose backup (Docker) : ${GREEN}$DOCKER_COMPOSE_BACKUP${NC}"
echo -e "${CYAN}Dossier compose Docker : ${YELLOW}$CONFIG_DIR/docker${NC}"
echo -e "${CYAN}Taille compose Docker : ${GREEN}$DOCKER_CONFIG_SIZE${NC}"
#################################
# CASAOS (si présent)
#################################
if [ -d "$CONFIG_DIR/casaos" ] || [ -f "$CASAOS_FULL_TAR" ]; then
echo -e "${CYAN}\n----- CASAOS -----${NC}"
echo -e "${CYAN}Compose CasaOS backup : ${GREEN}$CASAOS_COMPOSE_BACKUP${NC}"
echo -e "${CYAN}Dossier CasaOS backup : ${YELLOW}$CONFIG_DIR/casaos${NC}"
[ -n "$CASAOS_CONFIG_SIZE" ] && echo -e "${CYAN}Taille config CasaOS (full) : ${GREEN}$CASAOS_CONFIG_SIZE${NC}"
if [ -f "$CASAOS_FULL_TAR" ]; then
echo -e "${CYAN}Tar CasaOS FULL : ${YELLOW}$CASAOS_FULL_TAR${NC}"
echo -e "${CYAN}Taille tar CasaOS FULL : ${GREEN}$CASAOS_FULL_TAR_SIZE${NC}"
fi
fi
#################################
# PORTAINER (si présent)
#################################
if [ -n "$PORTAINER_CONFIG_DIR" ] && [ -d "$PORTAINER_CONFIG_DIR" ]; then
echo -e "${CYAN}\n----- PORTAINER -----${NC}"
echo -e "${CYAN}Fichiers config Portainer : ${GREEN}$PORTAINER_FILES${NC}"
echo -e "${CYAN}Dossier Portainer backup : ${YELLOW}$PORTAINER_CONFIG_DIR${NC}"
[ -n "$PORTAINER_CONFIG_SIZE" ] && echo -e "${CYAN}Taille Portainer (full) : ${GREEN}$PORTAINER_CONFIG_SIZE${NC}"
fi
#################################
# GLOBAL
#################################
echo -e "${CYAN}\n----- GLOBAL -----${NC}"
echo -e "${CYAN}Nombre de sets de backup : ${GREEN}$TOTAL_BACKUP_SETS${NC}"
echo -e "${CYAN}Oldest backup dir : ${YELLOW}${OLDEST_DIR:-N/A}${NC}"
echo -e "${CYAN}Newest backup dir : ${YELLOW}${NEWEST_DIR:-N/A}${NC}"
echo -e "${CYAN}Taille archive Docker tar.gz : ${GREEN}$TAR_SIZE${NC}"
[ -f "$CASAOS_FULL_TAR" ] && echo -e "${CYAN}Taille archive CasaOS tar.gz : ${GREEN}$CASAOS_FULL_TAR_SIZE${NC}"
echo -e "${CYAN}Taille dossier backup courant: ${GREEN}$CONFIG_SIZE${NC}"
echo -e "${CYAN}Archive Docker courante : ${YELLOW}$BACKUP_FILE${NC}"
[ -f "$CASAOS_FULL_TAR" ] && echo -e "${CYAN}Archive CasaOS courante : ${YELLOW}$CASAOS_FULL_TAR${NC}"
echo -e "${CYAN}Durée totale : ${GREEN}$DURATION${NC}"
#################################
# JSON (optionnel : ./script.sh --json)
#################################
if [ "$1" = "--json" ]; then
echo ""
echo "{"
echo " \"date_raw\": \"$DATE_RAW\","
echo " \"date_dir\": \"$DATE_DIR\","
echo " \"run_dir\": \"$RUN_DIR\","
echo " \"docker_backup_file\": \"$BACKUP_FILE\","
echo " \"docker_backup_size\": \"$TAR_SIZE\","
echo " \"casaos_full_backup_file\": \"${CASAOS_FULL_TAR:-}\","
echo " \"casaos_full_backup_size\": \"${CASAOS_FULL_TAR_SIZE:-}\","
echo " \"docker\": {"
echo " \"containers\": $CONTAINERS_DOCKER,"
echo " \"compose_source\": $SOURCE_COMPOSE_COUNT,"
echo " \"compose_backup\": $DOCKER_COMPOSE_BACKUP"
echo " },"
echo " \"casaos\": {"
echo " \"compose_backup\": $CASAOS_COMPOSE_BACKUP"
echo " },"
echo " \"portainer\": {"
echo " \"files\": $PORTAINER_FILES"
echo " },"
echo " \"backup_sets\": $TOTAL_BACKUP_SETS,"
echo " \"oldest_backup_dir\": \"${OLDEST_DIR:-}\","
echo " \"newest_backup_dir\": \"${NEWEST_DIR:-}\","
echo " \"duration\": \"$DURATION\""
echo "}"
fi
| 1 | #!/bin/bash |
| 2 | |
| 3 | ############################# |
| 4 | # CONFIG |
| 5 | ############################# |
| 6 | |
| 7 | # Backup complet des data docker , casaos et portainer |
| 8 | # avec liste de tous les compose files des containers classés par répertoire. |
| 9 | |
| 10 | |
| 11 | # Répertoire contenant tes containers |
| 12 | SOURCE_DIR="/home/docker" |
| 13 | |
| 14 | # Répertoire où stocker les sauvegardes |
| 15 | BACKUP_DIR="/home/backup/docker" |
| 16 | |
| 17 | # Nombre max de backups à conserver (dossiers datés) |
| 18 | MAX_BACKUPS=3 |
| 19 | |
| 20 | # Date interne (pour logs / JSON) |
| 21 | DATE_RAW="$(date +'%Y%m%d_%H%M%S')" |
| 22 | # Date pour le dossier de backup (format humain demandé) |
| 23 | DATE_DIR="$(date +'%Y.%m.%d_%H-%M-%S')" |
| 24 | |
| 25 | # Dossier de ce backup |
| 26 | RUN_DIR="$BACKUP_DIR/$DATE_DIR" |
| 27 | |
| 28 | # Fichiers TAR dans le dossier de ce backup |
| 29 | BACKUP_FILE="$RUN_DIR/docker_backup.tar.gz" |
| 30 | CASAOS_FULL_TAR="$RUN_DIR/casaos_full_backup.tar.gz" |
| 31 | |
| 32 | # Dossier où l'on met les compose / configs |
| 33 | CONFIG_DIR="$RUN_DIR" |
| 34 | |
| 35 | # Dossiers à exclure (chemins relatifs à $SOURCE_DIR pour le tar Docker) |
| 36 | EXCLUDE_DIRS=( |
| 37 | "docker_var_lib" |
| 38 | "1_Backups" |
| 39 | "casaos_data/*" |
| 40 | "nginx_proxy/data/logs/*" |
| 41 | "yt-dl/video/*" |
| 42 | ) |
| 43 | |
| 44 | # LOGS |
| 45 | LOG_FILE="/var/log/docker_backup.log" |
| 46 | |
| 47 | ############################# |
| 48 | # COULEURS |
| 49 | ############################# |
| 50 | NC="\e[0m" |
| 51 | GREEN="\e[32m" |
| 52 | YELLOW="\e[33m" |
| 53 | CYAN="\e[36m" |
| 54 | RED="\e[31m" |
| 55 | |
| 56 | ############################# |
| 57 | # FONCTIONS |
| 58 | ############################# |
| 59 | |
| 60 | log_message() { |
| 61 | local msg="$1" |
| 62 | local now |
| 63 | now="$(date +'%Y-%m-%d %H:%M:%S')" |
| 64 | echo "$now - $msg" >> "$LOG_FILE" |
| 65 | echo -e "$now - $msg" |
| 66 | } |
| 67 | |
| 68 | format_time() { |
| 69 | local t=$1 |
| 70 | ((h=t/3600)) |
| 71 | ((m=(t%3600)/60)) |
| 72 | ((s=t%60)) |
| 73 | printf "%02d:%02d:%02d\n" $h $m $s |
| 74 | } |
| 75 | |
| 76 | check_pigz() { |
| 77 | if ! command -v pigz >/dev/null 2>&1; then |
| 78 | echo -e "${YELLOW}pigz n'est pas installé. Voulez-vous l’installer ? (y/n)${NC}" |
| 79 | read rep |
| 80 | if [ "$rep" = "y" ]; then |
| 81 | sudo apt update && sudo apt install -y pigz || { |
| 82 | echo -e "${RED}Échec de l’installation de pigz. Abandon.${NC}" |
| 83 | exit 1 |
| 84 | } |
| 85 | else |
| 86 | echo -e "${RED}pigz est requis pour ce script. Abandon.${NC}" |
| 87 | exit 1 |
| 88 | fi |
| 89 | fi |
| 90 | } |
| 91 | |
| 92 | rotate_backups() { |
| 93 | log_message "Rotation des anciens backups (conserver les $MAX_BACKUPS plus récents)..." |
| 94 | |
| 95 | # On ne garde que les dossiers datés dans BACKUP_DIR |
| 96 | mapfile -t backups_dirs < <(ls -1td "$BACKUP_DIR"/*/ 2>/dev/null || true) |
| 97 | |
| 98 | if [ "${#backups_dirs[@]}" -gt "$MAX_BACKUPS" ]; then |
| 99 | for d in "${backups_dirs[@]:$MAX_BACKUPS}"; do |
| 100 | log_message "Suppression ancien backup (dossier) : $d" |
| 101 | rm -rf "$d" |
| 102 | done |
| 103 | fi |
| 104 | } |
| 105 | |
| 106 | get_size() { |
| 107 | du -sh "$1" 2>/dev/null | awk '{print $1}' |
| 108 | } |
| 109 | |
| 110 | ############################# |
| 111 | # DÉBUT SCRIPT |
| 112 | ############################# |
| 113 | |
| 114 | SECONDS=0 |
| 115 | |
| 116 | check_pigz |
| 117 | |
| 118 | mkdir -p "$RUN_DIR" |
| 119 | touch "$LOG_FILE" |
| 120 | |
| 121 | echo -e "${CYAN}=== Backup Docker + Configs (Portainer / CasaOS) ===${NC}" |
| 122 | log_message "Début sauvegarde Docker + configs Portainer & CasaOS..." |
| 123 | log_message "Dossier de backup : $RUN_DIR" |
| 124 | |
| 125 | ############################################## |
| 126 | # 1) Sauvegarde TAR Docker via pigz + exclusions |
| 127 | ############################################## |
| 128 | log_message "Création de l’archive Docker : $BACKUP_FILE" |
| 129 | |
| 130 | EXCLUDE_ARGS=( |
| 131 | "--exclude=*.log" |
| 132 | "--exclude=.composer" |
| 133 | "--exclude=.aptitude" |
| 134 | "--exclude=.cache" |
| 135 | "--exclude=.cmake" |
| 136 | "--exclude=.yarn" |
| 137 | "--exclude=.w3m" |
| 138 | "--exclude=.pip" |
| 139 | "--exclude=.pm2" |
| 140 | "--exclude=.pm" |
| 141 | "--exclude=.bundle" |
| 142 | "--exclude=.gem" |
| 143 | "--exclude=.cpan" |
| 144 | "--exclude=.cpanm" |
| 145 | "--exclude=.git" |
| 146 | "--exclude=.local" |
| 147 | "--exclude=.npm" |
| 148 | "--exclude=.nvm" |
| 149 | "--exclude=.rvm" |
| 150 | "--exclude=node_modules" |
| 151 | "--exclude=lost+found" |
| 152 | ) |
| 153 | |
| 154 | for d in "${EXCLUDE_DIRS[@]}"; do |
| 155 | EXCLUDE_ARGS+=( "--exclude=$d" ) |
| 156 | done |
| 157 | |
| 158 | tar -cf "$BACKUP_FILE" -I pigz \ |
| 159 | --directory="$SOURCE_DIR" \ |
| 160 | "${EXCLUDE_ARGS[@]}" \ |
| 161 | . 2>> "$LOG_FILE" |
| 162 | |
| 163 | if [ $? -ne 0 ]; then |
| 164 | log_message "ERREUR lors de la création du tar Docker." |
| 165 | echo -e "${RED}Erreur lors de la création de l’archive Docker.${NC}" |
| 166 | exit 1 |
| 167 | fi |
| 168 | |
| 169 | ############################################## |
| 170 | # 2) Extraction des docker-compose |
| 171 | # - CasaOS: RUN_DIR/casaos/<container>/ |
| 172 | # - Docker: RUN_DIR/docker/<container>/ |
| 173 | ############################################## |
| 174 | |
| 175 | log_message "Extraction des docker-compose CasaOS..." |
| 176 | if [ -d "$SOURCE_DIR/casaos_data" ]; then |
| 177 | find "$SOURCE_DIR/casaos_data" -maxdepth 3 -type f -name "docker-compose.y*ml" | while read file; do |
| 178 | container_name=$(basename "$(dirname "$file")") |
| 179 | dest_dir="$CONFIG_DIR/casaos/$container_name" |
| 180 | mkdir -p "$dest_dir" |
| 181 | cp "$file" "$dest_dir/" |
| 182 | log_message " [CasaOS] → Compose trouvé : $file" |
| 183 | done |
| 184 | fi |
| 185 | |
| 186 | log_message "Extraction des docker-compose Docker (hors casaos_data & dossiers exclus)..." |
| 187 | find "$SOURCE_DIR" -mindepth 2 -maxdepth 2 -type f -name "docker-compose.y*ml" \ |
| 188 | ! -path "$SOURCE_DIR/casaos_data/*" \ |
| 189 | ! -path "$SOURCE_DIR/docker_var_lib/*" \ |
| 190 | 2>/dev/null | while read file; do |
| 191 | container_name=$(basename "$(dirname "$file")") |
| 192 | dest_dir="$CONFIG_DIR/Compose/$container_name" |
| 193 | mkdir -p "$dest_dir" |
| 194 | cp "$file" "$dest_dir/" |
| 195 | log_message " [Docker] → Compose trouvé : $file" |
| 196 | done |
| 197 | |
| 198 | ############################################## |
| 199 | # 3) Config Portainer (full) |
| 200 | ############################################## |
| 201 | PORTAINER_CONFIG_DIR="$CONFIG_DIR/portainer" |
| 202 | PORTAINER_FILES=0 |
| 203 | |
| 204 | if docker volume inspect portainer_data >/dev/null 2>&1; then |
| 205 | log_message "Sauvegarde config Portainer..." |
| 206 | mkdir -p "$PORTAINER_CONFIG_DIR" |
| 207 | cp -r /var/lib/docker/volumes/portainer_data/_data/* "$PORTAINER_CONFIG_DIR/" 2>>"$LOG_FILE" |
| 208 | PORTAINER_FILES=$(find "$PORTAINER_CONFIG_DIR" -type f 2>/dev/null | wc -l) |
| 209 | else |
| 210 | PORTAINER_CONFIG_DIR="" |
| 211 | fi |
| 212 | |
| 213 | ############################################## |
| 214 | # 4) Config CasaOS (full + tar.gz) |
| 215 | ############################################## |
| 216 | CASAOS_CONFIG_DIR="$CONFIG_DIR/casaos/full_config" |
| 217 | |
| 218 | if [ -d "/var/lib/casaos" ]; then |
| 219 | log_message "Sauvegarde config CasaOS (full vers $CASAOS_CONFIG_DIR)..." |
| 220 | mkdir -p "$CASAOS_CONFIG_DIR" |
| 221 | cp -r /var/lib/casaos/* "$CASAOS_CONFIG_DIR/" 2>>"$LOG_FILE" |
| 222 | fi |
| 223 | |
| 224 | # Création du second tar.gz CasaOS FULL (config + volumes casaos_data) |
| 225 | if [ -d "/var/lib/casaos" ] || [ -d "$SOURCE_DIR/casaos_data" ]; then |
| 226 | CASAOS_TAR_SOURCES=() |
| 227 | [ -d "/var/lib/casaos" ] && CASAOS_TAR_SOURCES+=( "/var/lib/casaos" ) |
| 228 | [ -d "$SOURCE_DIR/casaos_data" ] && CASAOS_TAR_SOURCES+=( "$SOURCE_DIR/casaos_data" ) |
| 229 | |
| 230 | if [ "${#CASAOS_TAR_SOURCES[@]}" -gt 0 ]; then |
| 231 | log_message "Création de l’archive CASAOS FULL : $CASAOS_FULL_TAR" |
| 232 | tar -cf "$CASAOS_FULL_TAR" -I pigz "${CASAOS_TAR_SOURCES[@]}" 2>>"$LOG_FILE" |
| 233 | if [ $? -ne 0 ]; then |
| 234 | log_message "ERREUR lors de la création du tar CasaOS." |
| 235 | echo -e "${RED}Erreur lors de la création de l’archive CasaOS.${NC}" |
| 236 | fi |
| 237 | fi |
| 238 | fi |
| 239 | |
| 240 | ############################################## |
| 241 | # 5) Comptages finaux (sur le backup courant) |
| 242 | ############################################## |
| 243 | |
| 244 | # Compose dans le backup (docker + CasaOS compose seulement) |
| 245 | CASAOS_COMPOSE_BACKUP=$(find "$CONFIG_DIR/casaos" -maxdepth 3 -type f -name "docker-compose.y*ml" ! -path "$CASAOS_CONFIG_DIR/*" 2>/dev/null | wc -l) |
| 246 | DOCKER_COMPOSE_BACKUP=$(find "$CONFIG_DIR/docker" -type f -name "docker-compose.y*ml" 2>/dev/null | wc -l) |
| 247 | |
| 248 | # Compose dans le dossier source (uniquement /home/docker/<container>/docker-compose.*) |
| 249 | SOURCE_COMPOSE_COUNT=$(find "$SOURCE_DIR" -mindepth 2 -maxdepth 2 -type f -name "docker-compose.y*ml" \ |
| 250 | ! -path "$SOURCE_DIR/casaos_data/*" \ |
| 251 | ! -path "$SOURCE_DIR/docker_var_lib/*" \ |
| 252 | 2>/dev/null | wc -l) |
| 253 | |
| 254 | # Containers Docker "valides" |
| 255 | CONTAINERS_DOCKER=$(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d \ |
| 256 | ! -path "$SOURCE_DIR/casaos_data" \ |
| 257 | ! -path "$SOURCE_DIR/docker_var_lib" \ |
| 258 | ! -path "$SOURCE_DIR/portainer" 2>/dev/null | wc -l) |
| 259 | |
| 260 | # Rotation des anciens backups (par dossiers) |
| 261 | rotate_backups |
| 262 | |
| 263 | # Nombre total de sets de backup (dossiers datés) |
| 264 | TOTAL_BACKUP_SETS=$(ls -1d "$BACKUP_DIR"/*/ 2>/dev/null | wc -l) |
| 265 | |
| 266 | # Oldest / newest backup set |
| 267 | mapfile -t CURRENT_SETS < <(ls -1d "$BACKUP_DIR"/*/ 2>/dev/null | sort) |
| 268 | OLDEST_DIR="${CURRENT_SETS[0]}" |
| 269 | NEWEST_DIR="${CURRENT_SETS[${#CURRENT_SETS[@]}-1]}" |
| 270 | |
| 271 | DURATION="$(format_time $SECONDS)" |
| 272 | |
| 273 | # Tailles des backups courants |
| 274 | TAR_SIZE=$(get_size "$BACKUP_FILE") |
| 275 | CONFIG_SIZE=$(get_size "$CONFIG_DIR") |
| 276 | DOCKER_CONFIG_SIZE=$(get_size "$CONFIG_DIR/docker") |
| 277 | |
| 278 | # CasaOS full config size (copie dans RUN_DIR/casaos/full_config) |
| 279 | if [ -d "$CASAOS_CONFIG_DIR" ]; then |
| 280 | CASAOS_CONFIG_SIZE=$(get_size "$CASAOS_CONFIG_DIR") |
| 281 | else |
| 282 | CASAOS_CONFIG_SIZE="" |
| 283 | fi |
| 284 | |
| 285 | # CasaOS FULL TAR size |
| 286 | if [ -f "$CASAOS_FULL_TAR" ]; then |
| 287 | CASAOS_FULL_TAR_SIZE=$(get_size "$CASAOS_FULL_TAR") |
| 288 | else |
| 289 | CASAOS_FULL_TAR_SIZE="" |
| 290 | fi |
| 291 | |
| 292 | # Portainer full config size |
| 293 | if [ -n "$PORTAINER_CONFIG_DIR" ] && [ -d "$PORTAINER_CONFIG_DIR" ]; then |
| 294 | PORTAINER_CONFIG_SIZE=$(get_size "$PORTAINER_CONFIG_DIR") |
| 295 | else |
| 296 | PORTAINER_CONFIG_SIZE="" |
| 297 | fi |
| 298 | |
| 299 | ############################################## |
| 300 | # 6) RÉSUMÉS |
| 301 | ############################################## |
| 302 | |
| 303 | echo -e "${GREEN}===== BACKUP TERMINÉ =====${NC}" |
| 304 | echo -e "${CYAN}Dossier backup courant : ${YELLOW}$RUN_DIR${NC}" |
| 305 | |
| 306 | ############################################## |
| 307 | # LISTE DES DOSSIERS DOCKER PAR TAILLE (ASCII) |
| 308 | ############################################## |
| 309 | |
| 310 | echo -e "\n${CYAN}----- LISTE/TAILLE DES RÉPERTOIRES DOCKER -----${NC}" |
| 311 | |
| 312 | DOCKER_DIRS=$(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d \ |
| 313 | ! -path "$SOURCE_DIR/casaos_data" \ |
| 314 | ! -path "$SOURCE_DIR/docker_var_lib" \ |
| 315 | ! -path "$SOURCE_DIR/portainer" 2>/dev/null) |
| 316 | |
| 317 | if [ -z "$DOCKER_DIRS" ]; then |
| 318 | echo -e "${YELLOW}(Aucun dossier Docker trouvé)${NC}" |
| 319 | else |
| 320 | echo -e "┌──────────────────────────┬─────────┬────────────────────────────────────────┐" |
| 321 | printf "│ %-24s │ %-7s │ %-38s │\n" "Container" "Taille" "Chemin" |
| 322 | echo -e "├──────────────────────────┼─────────┼────────────────────────────────────────┤" |
| 323 | du -sh $DOCKER_DIRS 2>/dev/null | sort -hr | while read -r size path; do |
| 324 | name=$(basename "$path") |
| 325 | printf "│ %-24s │ %-7s │ %-38s │\n" "$name" "$size" "$path" |
| 326 | done |
| 327 | echo -e "└──────────────────────────┴─────────┴────────────────────────────────────────┘" |
| 328 | fi |
| 329 | |
| 330 | ################################# |
| 331 | # DOCKER |
| 332 | ################################# |
| 333 | echo -e "${CYAN}\n----- DOCKER -----${NC}" |
| 334 | echo -e "${CYAN}Containers Docker détectés : ${GREEN}$CONTAINERS_DOCKER${NC}" |
| 335 | echo -e "${CYAN}Compose source (Docker) : ${GREEN}$SOURCE_COMPOSE_COUNT${NC}" |
| 336 | echo -e "${CYAN}Compose backup (Docker) : ${GREEN}$DOCKER_COMPOSE_BACKUP${NC}" |
| 337 | echo -e "${CYAN}Dossier compose Docker : ${YELLOW}$CONFIG_DIR/docker${NC}" |
| 338 | echo -e "${CYAN}Taille compose Docker : ${GREEN}$DOCKER_CONFIG_SIZE${NC}" |
| 339 | |
| 340 | ################################# |
| 341 | # CASAOS (si présent) |
| 342 | ################################# |
| 343 | if [ -d "$CONFIG_DIR/casaos" ] || [ -f "$CASAOS_FULL_TAR" ]; then |
| 344 | echo -e "${CYAN}\n----- CASAOS -----${NC}" |
| 345 | echo -e "${CYAN}Compose CasaOS backup : ${GREEN}$CASAOS_COMPOSE_BACKUP${NC}" |
| 346 | echo -e "${CYAN}Dossier CasaOS backup : ${YELLOW}$CONFIG_DIR/casaos${NC}" |
| 347 | [ -n "$CASAOS_CONFIG_SIZE" ] && echo -e "${CYAN}Taille config CasaOS (full) : ${GREEN}$CASAOS_CONFIG_SIZE${NC}" |
| 348 | if [ -f "$CASAOS_FULL_TAR" ]; then |
| 349 | echo -e "${CYAN}Tar CasaOS FULL : ${YELLOW}$CASAOS_FULL_TAR${NC}" |
| 350 | echo -e "${CYAN}Taille tar CasaOS FULL : ${GREEN}$CASAOS_FULL_TAR_SIZE${NC}" |
| 351 | fi |
| 352 | fi |
| 353 | |
| 354 | ################################# |
| 355 | # PORTAINER (si présent) |
| 356 | ################################# |
| 357 | if [ -n "$PORTAINER_CONFIG_DIR" ] && [ -d "$PORTAINER_CONFIG_DIR" ]; then |
| 358 | echo -e "${CYAN}\n----- PORTAINER -----${NC}" |
| 359 | echo -e "${CYAN}Fichiers config Portainer : ${GREEN}$PORTAINER_FILES${NC}" |
| 360 | echo -e "${CYAN}Dossier Portainer backup : ${YELLOW}$PORTAINER_CONFIG_DIR${NC}" |
| 361 | [ -n "$PORTAINER_CONFIG_SIZE" ] && echo -e "${CYAN}Taille Portainer (full) : ${GREEN}$PORTAINER_CONFIG_SIZE${NC}" |
| 362 | fi |
| 363 | |
| 364 | ################################# |
| 365 | # GLOBAL |
| 366 | ################################# |
| 367 | echo -e "${CYAN}\n----- GLOBAL -----${NC}" |
| 368 | echo -e "${CYAN}Nombre de sets de backup : ${GREEN}$TOTAL_BACKUP_SETS${NC}" |
| 369 | echo -e "${CYAN}Oldest backup dir : ${YELLOW}${OLDEST_DIR:-N/A}${NC}" |
| 370 | echo -e "${CYAN}Newest backup dir : ${YELLOW}${NEWEST_DIR:-N/A}${NC}" |
| 371 | echo -e "${CYAN}Taille archive Docker tar.gz : ${GREEN}$TAR_SIZE${NC}" |
| 372 | [ -f "$CASAOS_FULL_TAR" ] && echo -e "${CYAN}Taille archive CasaOS tar.gz : ${GREEN}$CASAOS_FULL_TAR_SIZE${NC}" |
| 373 | echo -e "${CYAN}Taille dossier backup courant: ${GREEN}$CONFIG_SIZE${NC}" |
| 374 | echo -e "${CYAN}Archive Docker courante : ${YELLOW}$BACKUP_FILE${NC}" |
| 375 | [ -f "$CASAOS_FULL_TAR" ] && echo -e "${CYAN}Archive CasaOS courante : ${YELLOW}$CASAOS_FULL_TAR${NC}" |
| 376 | echo -e "${CYAN}Durée totale : ${GREEN}$DURATION${NC}" |
| 377 | |
| 378 | ################################# |
| 379 | # JSON (optionnel : ./script.sh --json) |
| 380 | ################################# |
| 381 | if [ "$1" = "--json" ]; then |
| 382 | echo "" |
| 383 | echo "{" |
| 384 | echo " \"date_raw\": \"$DATE_RAW\"," |
| 385 | echo " \"date_dir\": \"$DATE_DIR\"," |
| 386 | echo " \"run_dir\": \"$RUN_DIR\"," |
| 387 | echo " \"docker_backup_file\": \"$BACKUP_FILE\"," |
| 388 | echo " \"docker_backup_size\": \"$TAR_SIZE\"," |
| 389 | echo " \"casaos_full_backup_file\": \"${CASAOS_FULL_TAR:-}\"," |
| 390 | echo " \"casaos_full_backup_size\": \"${CASAOS_FULL_TAR_SIZE:-}\"," |
| 391 | echo " \"docker\": {" |
| 392 | echo " \"containers\": $CONTAINERS_DOCKER," |
| 393 | echo " \"compose_source\": $SOURCE_COMPOSE_COUNT," |
| 394 | echo " \"compose_backup\": $DOCKER_COMPOSE_BACKUP" |
| 395 | echo " }," |
| 396 | echo " \"casaos\": {" |
| 397 | echo " \"compose_backup\": $CASAOS_COMPOSE_BACKUP" |
| 398 | echo " }," |
| 399 | echo " \"portainer\": {" |
| 400 | echo " \"files\": $PORTAINER_FILES" |
| 401 | echo " }," |
| 402 | echo " \"backup_sets\": $TOTAL_BACKUP_SETS," |
| 403 | echo " \"oldest_backup_dir\": \"${OLDEST_DIR:-}\"," |
| 404 | echo " \"newest_backup_dir\": \"${NEWEST_DIR:-}\"," |
| 405 | echo " \"duration\": \"$DURATION\"" |
| 406 | echo "}" |
| 407 | fi |
| 408 |