Zuletzt aktiv 1 month ago

Full backup zip + all compose file ! Compose Docker + Report Html

Änderung 1d5d3ac3bb5269db4878d67803e4bbe8b1240a60

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