Остання активність 1 month ago

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

Версія 19c1d4995c9bf926009bf36a192f2c9e1adc40c6

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