Last active 1 month ago

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

Revision acde2cd6553d06eb86f6bd4556e88224b0bb5478

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