Table des matières

Hyperviseur Gargantua

Cet hyperviseur contient les machines virtuelles (KVM/QEMU) suivantes :

Orion : le serveur de sauvegarde n°1




Chez ilinux nous faisons les choses … simplement : la sauvegarde suit ce concept du KISS (Keep It Simple, Stupid).

Pour comprendre ce que suit je vous invite à consulter le schéma réseau de notre infrastructure.

En effet le positionnement du serveur de sauvegarde est primordial pour comprendre :



Voici un schéma synthétique des briques systèmes utilisées pour la sauvegarde (ici le LAN):

Le serveur de sauvegarde n°1 (Orion) peut donc accéder aux données de la production en mode pull (il va chercher les données à sauvegarder), c'est pour cela qu'il est :

Sans dévoiler d'information compromettante pour sa sécurité nous pouvons annoncer que l'accès ne se fait pas par identifiant, ni mot de passe : il faut détenir une clé de cryptage, connaître une phrase assez longue, et passer quelques portes …

Note : L'accès VPN dispose lui aussi d'un accès par clé avec en plus en mot de passe qui change toutes les 30 secondes …

Pour notre sauvegarde nous utilisons le logiciel libre BorgBackup.

Nous n'allons pas nous étaler sur les caractéristiques techniques de l'outil mais sachez seulement qu'il permet, entre autre :

Chez ilinux nous appliquons la stratégie de rétention des données suivante pour l'ensemble de nos serveurs:

Nous pouvons donc parfaitement effectuer un retour vers le passé, mais comme toute bonne chose il ne faut pas en abuser

… nom de Zeus !!!

Fonctionnement de la sauvegarde

Le serveur Orion est une machine virtuelle (pas un conteneur, SSHfs ne fonctionne pas avec un conteneur).

Il sauvegarde un à un tous les serveurs de notre infrastructure, mais comme il travaille en sauvegarde incrémentielle , il ne copie que ce qui a changé depuis la sauvegarde précédente.
Sur des serveurs où les données ne changent quasiment pas (80 % de nos serveurs) on arrive à un temps de sauvegarde (exemple ci-dessous : 1 minutes 13.38 secondes) qui se compte en quelques minutes et à des tailles de sauvegarde ridicules (hormis pour la toute première sauvegarde).

Les dépôts Borgbackup (les sauvegardes), un dépôt par serveur sauvegardé (physique, vm ou ct), sont placés sur un disque en RAID5 piloté par mdadm qui nous signale par courriel un dysfonctionnement.
De plus grâce à SMART la moindre erreur sur les disques dur physiques nous est aussi remontée instantanément par courriel.

RAID5 signifie que sur 3 disques dur, la perte d'un disque ne corrompt pas l'ensemble du disque.

La sauvegarde fonctionne simplement avec … 2 scripts écrits en BASH :-) :

  1. un script principal qui appelle des fonctions (on peut le lire comme du français),
  2. un script boite à outils dans lequel nous plaçons les fonctionnalités (fonctions) que nous réutilisons souvent (ce script est plus technique que le précédent !).

Voici un exemple de rapport de sauvegarde qui est envoyé aux administrateurs systèmes d'ilinux :

Note : A l'aide des filtres de courriel de Thunderbird il est facile de ne voir que les rapports critiques, ceux exigeants une action rapide.

>>>> sav_srv_ilinux.sh sur serveur : "websrv", executé le lundi 14 oct. 05:00:01
 
lundi 14 oct. 05:00:01 websrv sav_srv_ilinux.sh[2838]: [INFO] DEBUT-SCRIPT
lundi 14 oct. 05:00:01 websrv sav_srv_ilinux.sh[2838]: [INFO] Parametres : "websrv"
lundi 14 oct. 05:00:01 websrv sav_srv_ilinux.sh[2838]: [INFO] Serveur "websrv" en ligne
lundi 14 oct. 05:00:01 websrv sav_srv_ilinux.sh[2838]: [INFO] Serveur "websrv" : "stop" "nginx"
lundi 14 oct. 05:00:02 websrv sav_srv_ilinux.sh[2838]: [INFO] Export des bases "mysql" sous "/root/Prod/Sauvegardes/Bases"
lundi 14 oct. 05:00:02 websrv sav_srv_ilinux.sh[2838]: [INFO] Compression du fichier "/root/Prod/Sauvegardes/Bases/websrv-mysql---all-databases.sql" sous "/root/Prod/Sauvegardes/Bases"
lundi 14 oct. 05:00:03 websrv sav_srv_ilinux.sh[2838]: [INFO] Export des bases "postgresql" sous "/root/Prod/Sauvegardes/Bases"
lundi 14 oct. 05:00:04 websrv sav_srv_ilinux.sh[2838]: [INFO] Compression du fichier "/root/Prod/Sauvegardes/Bases/websrv-postgresql---full.sql" sous "/root/Prod/Sauvegardes/Bases"
lundi 14 oct. 05:00:04 websrv sav_srv_ilinux.sh[2838]: [INFO] Serveur "websrv" en ligne
lundi 14 oct. 05:00:04 websrv sav_srv_ilinux.sh[2838]: [INFO] Montage "/root/Prod/mnt/BorgBackup/websrv" de "websrv"
lundi 14 oct. 05:00:04 websrv sav_srv_ilinux.sh[2838]: [INFO] Sauvegarde de "/root/Prod/mnt/BorgBackup/websrv/etc /root/Prod/mnt/BorgBackup/websrv/root /root/Prod/mnt/BorgBackup/websrv/home /root/Prod/mnt/BorgBackup/websrv/var" en cours
------------------------------------------------------------------------------
Archive name: websrv-14_10_2019:00
Archive fingerprint: dae2ce16ahdfh4df9hd4h94dh4h9dd14fc6fcd6fe6ff8573378a55022f5dfd1
Time (start): Mon, 2019-10-14 05:00:05
Time (end):   Mon, 2019-10-14 05:01:18
Duration: 1 minutes 13.38 seconds
Number of files: 51726
------------------------------------------------------------------------------
                       Original size      Compressed size    Deduplicated size
This archive:                1.21 GB              1.21 GB             30.24 MB
All archives:               36.33 GB             36.33 GB              2.45 GB
 
                       Unique chunks         Total chunks
Chunk index:                   42252              1554816
------------------------------------------------------------------------------
 
lundi 14 oct. 05:01:22 websrv sav_srv_ilinux.sh[2838]: [INFO] Démontage de "/root/Prod/mnt/BorgBackup/websrv"
lundi 14 oct. 05:01:22 websrv sav_srv_ilinux.sh[2838]: [INFO] Serveur "websrv" en ligne
lundi 14 oct. 05:01:22 websrv sav_srv_ilinux.sh[2838]: [INFO] Serveur "websrv" : "start" "nginx"
 
 >>>> Statistiques du système de fichiers distant de "websrv" ...
 
Sys. de fichiers                       Taille Utilisé Dispo Uti% Monté sur
/dev/mapper/Raid1Data-vm--107--disk--0   270G    2,8G  253G   2% /
none                                     504k       0  504k   0% /dev
udev                                     8,4G       0  8,4G   0% /dev/tty
tmpfs                                    8,4G    4,1k  8,4G   1% /dev/shm
tmpfs                                    8,4G     18M  8,4G   1% /run
tmpfs                                    5,3M       0  5,3M   0% /run/lock
tmpfs                                    8,4G       0  8,4G   0% /sys/fs/cgroup
tmpfs                                    1,7G       0  1,7G   0% /run/user/1000
tmpfs                                    1,7G       0  1,7G   0% /run/user/0
 
 
 >>>> Statistiques du système de sauvegarde ...
 
websrv-31_07_2019:00                 Wed, 2019-07-31 05:00:02
websrv-31_08_2019:00                 Sat, 2019-08-31 05:00:02
websrv-08_09_2019:00                 Sun, 2019-09-08 05:00:03
websrv-15_09_2019:00                 Sun, 2019-09-15 05:00:03
websrv-22_09_2019:00                 Sun, 2019-09-22 05:00:03
websrv-29_09_2019:00                 Sun, 2019-09-29 05:00:03
websrv-30_09_2019:00                 Mon, 2019-09-30 05:00:02
websrv-01_10_2019:00                 Tue, 2019-10-01 05:00:02
websrv-02_10_2019:00                 Wed, 2019-10-02 05:00:02
websrv-03_10_2019:00                 Thu, 2019-10-03 05:00:03
websrv-04_10_2019:00                 Fri, 2019-10-04 05:00:02
websrv-05_10_2019:00                 Sat, 2019-10-05 05:00:02
websrv-06_10_2019:00                 Sun, 2019-10-06 05:00:03
websrv-06_10_2019:45                 Sun, 2019-10-06 16:45:04
websrv-06_10_2019:50                 Sun, 2019-10-06 16:50:36
websrv-06_10_2019:52                 Sun, 2019-10-06 16:52:29
websrv-06_10_2019:56                 Sun, 2019-10-06 16:56:27
websrv-06_10_2019:12                 Sun, 2019-10-06 17:12:17
websrv-06_10_2019:15                 Sun, 2019-10-06 17:15:59
websrv-06_10_2019:18                 Sun, 2019-10-06 17:18:03
websrv-06_10_2019:22                 Sun, 2019-10-06 20:22:09
websrv-06_10_2019:16                 Sun, 2019-10-06 21:16:29
websrv-07_10_2019:00                 Mon, 2019-10-07 05:00:08
websrv-08_10_2019:00                 Tue, 2019-10-08 05:00:05
websrv-09_10_2019:00                 Wed, 2019-10-09 05:00:05
websrv-10_10_2019:00                 Thu, 2019-10-10 05:00:05
websrv-11_10_2019:00                 Fri, 2019-10-11 05:00:05
websrv-12_10_2019:00                 Sat, 2019-10-12 05:00:05
websrv-13_10_2019:00                 Sun, 2019-10-13 05:00:05
websrv-14_10_2019:00                 Mon, 2019-10-14 05:00:05
 
lundi 14 oct. 05:01:23 websrv sav_srv_ilinux.sh[2838]: [INFO] FIN-SCRIPT
lundi 14 oct. 05:01:23 websrv sav_srv_ilinux.sh[2838]: [INFO] Courriel de notification

Le script principal

Tout est commenté … ;-)

Ce script sauvegarde un serveur (type web/base de données) avec les services actifs suivants :


  1. On envoie des informations initiales dans les journaux et le courriel (nom du script, date/heure, serveur sauvegardé, paramètres de lancement),
  2. On commence par stopper le service d'entrée web du serveur : NGINX (on stoppe ce service, cela permettra d'avoir une sauvegarde consistante à tous les niveaux : système de fichiers, bases commitées),
  3. On procède à un export sur le serveur distant de toutes les bases de données (mises à jour figées) présentes sur le serveur,
  4. On procède à la sauvegarde des répertoires stratégiques de ce serveur et par conséquent des exports fait ci-dessus des bases de données,
  5. On redémarre le service web : NGINX … à ce stade l'exploitation peut reprendre,
  6. On récolte les statistiques du système de fichiers et de l'historique des sauvegardes (borgbackup) de ce serveur,
  7. On envoie le courriel de notification;
  8. On a fini (avec des erreurs ou pas !)


A VOIR : Amélioration possible, faire un cliché instantané du système de fichiers avant ordre de dump/sauvegarde, le but étant de n'avoir aucune indisponibilité du service durant la sauvegarde. Par contre il faudra impérativement être sure que le cliché instantané est libéré.
https://docs.ceph.com/docs/mimic/rbd/rbd-snapshot/

#!/bin/bash
###############################################################################
#                                                                             #
# PGM     : Script de sauvegarde serveur (distant) web & databases            #
#                                                                             #
# AUTEUR  : Stephane  (iLinux)                                                #
# VERSION : 1.0.0                                                             #
#                                                                             #
# DATE    : 03/06/2019                                                        #
#                                                                             #
# PARAM.  : IN  < SERVEUR à sauvegarder                                             #
#           OUT > logger + mail                                               #
#                                                                             #
###############################################################################
 
# EN PREMIER LIEU
# On inclus les fonctions et variables communes ( le script boite à outils)
. ~/Prod/Scripts/fonctions_variables.sh
 
# Variables de travail ...
# ... LOCALES
 
# ... DISTANTES
# N/A
 
# Au commencement il n'y a jamais d'erreur ... dans un script
G_ERREUR=0
 
# Serveur à sauvegarder :
SERVEUR=$1
 
# Surcharge var. fichier log (en attendant serveur mail !)
#G_FICHIER_LOG="/root/Prod/Journaux/Sauvegardes/$SERVEUR.log"
 
# Script actuellement en cours d'execution
echo "SCRIPT    : $G_APPLI"
echo "" >> $G_FICHIER_LOG
 
echo ">>>> $G_APPLI sur serveur : \"$SERVEUR\", executé le $G_JOUR $G_DATE">> $G_FICHIER_LOG
echo "" >> $G_FICHIER_LOG
 
 
# 1 seul paramétre attendu
#if [ $# = 1 ] || [ -z "$1"]
#then
#       G_ERREUR='Donnez 1 nom de serveur svp ...'
#       # On sort du script avec $G_ERREUR
#       g_log_message "ERROR" $G_MSG_FIN_SCRIPT
#       exit $G_ERREUR
#fi
 
# DEBUT DU SCRIPT
g_log_message "INFO" $G_MSG_DEBUT_SCRIPT
g_log_message "INFO" "Parametres : \"$*\""
 
# Préparation sauvegarde
#
# On stoppe le serveur web distant ...
gestionServiceServeur $SERVEUR "stop" "nginx"
 
# On fait les "dump" Mysql et PostgreSQL des bases sur le serveur distant sous "/root/Prod/Sauvegardes/Bases" ...
# Rappel : sauvegardeBasesServeur server dbType dbDumpDir
sauvegardeBasesServeur  $SERVEUR "mysql" "/root/Prod/Sauvegardes/Bases"
sauvegardeBasesServeur  $SERVEUR "postgresql" "/root/Prod/Sauvegardes/Bases"
 
# On lance la sauvegarde du système de fichiers ... (localement via le montage sshFS)
depotBorgBackup="/Sauvegardes/Prod/$SERVEUR"
mountDist="/root/Prod/mnt/BorgBackup/$SERVEUR"
repertoiresAsauvegarder="$mountDist/etc $mountDist/root $mountDist/home $mountDist/var"
sauvegardeBorgBackupServeur $SERVEUR "$depotBorgBackup" "$repertoiresAsauvegarder"
 
# On relance le serveur Web distant
gestionServiceServeur $SERVEUR "start" "nginx"
 
# On audit le système de fichier distant (volumétrie etc)
statutSFServeur $SERVEUR
 
# On audit le système de sauvegarde
statutBorgBackup "$depotBorgBackup"
 
# On sort du script avec $G_ERREUR
g_log_message "INFO" $G_MSG_FIN_SCRIPT
exit $G_ERREUR
 
 
# FIN DU SCRIPT

Le script boite à outils

Là aussi le code est abondamment commenté ;-)
Vous noterez que nous passons 80% du temps à tester les codes retours, dans le but d'avoir une sauvegarde la plus fiable et la plus transparente possible (via envoi de courriels journalier clairs et concis, un par serveur)

#!/bin/bash
###############################################################################
#                                                                             #
# PGM     : Script de fonctions et variables réutilisables                    #
#                                                                             #
# AUTEUR  : Stephane                                                          #
# VERSION : 1.0.0                                                             #
#                                                                             #                                                                                                       
# DATE    : 02/06/2019                                                        #                                                                                                       
#                                                                             #                                                                                                       
# PARAM.  : IN  < N/A                                                         #                                                                                                       
#           OUT > N/A                                                         #                                                                                                       
#                                                                             #                                                                                                       
###############################################################################                                                                                                       
 
# Variables de travail ...                                                                                                                                                            
# ... LOCALES                                                                                                                                                                         
 
# ... DISTANTES                                                                                                                                                                                              
# N/A                                                                                                                                                                                                        
 
# Chemin complet des outils utilises A GARDER POUR PORTABILITE MULTI-PLATEFORME etc ...                                                                                                                      
 
# Ce fichier, amene a evoluer, contient en ensemble de fonctions et variables                                                                                                                                
# globales courament utilisees.                                                                                                                                                                              
 
########################### NOMENCLATURE (rajouter ici vos régles de nommage)                                                                                                                                
#                                                                                                                                                                                                            
# Variable GLOBALE (doit etre reutilisee dans plusieurs script)                                                                                                                                              
# Elle est prefixee d'un "G_" comme "G_lobale"                                                                                                                                                                                                                                                                                
# G_<nom_variable>                                                                                                                                                                                                                                                                                                            
 
# Variable LOCALE (utilisable dans un script particulier)                                                                                                                                                                                                                                                                     
# a Elle n'est pas prefixee, et n'a donc pas lieu d'etre dans ce fichier                                                                                                                                                                                                                                                      
# ormis leur présence dans les fonctions                                                                                                                                                                                                                                                                                      
# <nom_variable>                                                                                                                                                                                                                                                                                                              
 
# IDEM pour les fonctions : g_<nom_fonction>                                                                                                                                                                                                                                                                                  
 
# Par defaut quand nous definissons un chemin (PATH) nous ne mettrons jamais de "/"                                                                                                                                                                                                                                           
# en fin de chemin (cf. code ci-dessous)                                                                                                                                                                                                                                                                                      
 
# Ce fichier contient une section GLOBALE et une section APPLI (specifique a certains scripts)                                                                                                                                                                                                                                
 
###############################################################################                                                                                                                                                                                                                                               
#                                                                             #                                                                                                                                                                                                                                               
#                                    GLOBALE                                  #                                                                                                                                                                                                                                               
#                                                                             #                                                                                                                                                                                                                                               
###############################################################################                                                                                                                                                                                                                                               
 
#                                                                                                                                                                                                                                                                                                                             
 
####### VARIABLES GLOBALES #####################################################                                                                                                                                                                                                                                              
 
# ELLES SONT OBLIGATOIRES POUR TOUT SCRIPT ECRIT 
export LANG=fr_FR.UTF-8 # NE PAS MODIFIER => sinon les jours ci-dessous seront en anglais.
# MERCI
G_DATE=`date +%d\ %b\ %T`
G_JOUR=`date "+%A"`
#G_NOM_MACHINE=`hostname`
G_NOM_MACHINE=$1
G_APPLI=`basename $0`
G_PID_APPLI=$$
 
G_FICHIER_LOG="/root/Prod/Journaux/Sauvegardes/$G_APPLI.log" 
G_COURRIEL_TXT="/root/Prod/Journaux/Sauvegardes/$G_APPLI-COURRIEL.txt"
 
G_CONTACTS_MAIL="yourMonitoringMail@domain.tld"
G_SUJET_MAIL="$G_APPLI sur $G_NOM_MACHINE"
 
# Message de debut et fin de script
G_MSG_DEBUT_SCRIPT="DEBUT-SCRIPT"
G_MSG_FIN_SCRIPT="FIN-SCRIPT"
 
# Il faudra mettre G_ERREUR=1 en cas de soucis et
# bien sure ne jamais remettre a "0" cette variable !!!
 
# Chemin complet des outils utilises A GARDER POUR PORTABILITE MULTI-PLATEFORME etc ...
SSH="/usr/bin/ssh"
#TAR="/bin/tar"
MKDIR="/bin/mkdir"
#LN="/bin/ln"
#CHOWN="/bin/chown"
#CHMOD="/bin/chmod"
#CP="/bin/cp"
#USERMOD="/usr/sbin/usermod"
LOGGER="/usr/bin/logger"
#MAIL="/usr/bin/mail"
MSMTP="/usr/bin/msmtp"
RM="/bin/rm"
CAT="/bin/cat"
DU="/usr/bin/du"
DF="/bin/df"
TEE="/usr/bin/tee"
SYSTEMCTL='/bin/systemctl'
SLEEP='/bin/sleep'
SYNC='/bin/sync'
UMOUNT='/bin/umount'
MOUNT='/bin/mount'
GREP='/bin/grep'
GZIP='/bin/gzip'
HOSTNAME='/bin/hostname'
SMARTCTL='/usr/sbin/smartctl'
CEPH='/usr/bin/ceph'
WHICH='/usr/bin/which'
# -> NE PAS ENLEVER MERCI.
 
 
# Fonction qui permet de loger les messages d'erreurs, d'informations, de warning etc ...
# Param IN      $1 = TYPE du Message, gravité( INFO ou ERROR)voir doc. facility syslog
#                               $2 = MESSAGE a loger
# ex: g_log_message "INFO" "script arrêté"
 
function g_log_message ()
{
 
        local TYPE=$1
        local MESSAGE=$2
 
        #Mise à jour de la date et de l'heure
        G_DATE=`date +%d\ %b\ %T`
        G_JOUR=`date "+%A"`
 
        # On prépare le formatage du type d'erreur pour le systeme SYSLOG (utilisation des priorités de logger : les "facilities" de syslog)
        case $TYPE in
                INFO)           LOG_FACILITY=local3.info
                                ;;
                ERROR)          LOG_FACILITY=local3.error
                                ;;
                WARNING)        LOG_FACILITY=local3.warning
                                ;;
                # AUTRE_TYPE) LOG_FACILITY=prefix.type
                #       ;;
                *)              #TODO codage type inconnu : on ne fait rien
                                ;;
        esac
 
        # Mise en forme des informations pour affichage :
        local INFOS_APPLI="$G_NOM_MACHINE $G_APPLI[$G_PID_APPLI]:"
 
        # ... sur sortie standard
        echo $G_JOUR $G_DATE $INFOS_APPLI [$TYPE] $MESSAGE
 
        # ... dans le fichier log
        echo $G_JOUR $G_DATE $INFOS_APPLI [$TYPE] $MESSAGE >> $G_FICHIER_LOG
 
        # ... dans les log du système d'exploitation
        local DETAIL_APPLI="$G_APPLI[$G_PID_APPLI]"
        $LOGGER -t $DETAIL_APPLI -p $LOG_FACILITY $MESSAGE
 
        # Si c'est la fin du script on envoie un mail avec le fichier de log complet
        if [ "$MESSAGE" == "$G_MSG_FIN_SCRIPT" ]
        then
                if [ $G_ERREUR -eq 0 ]
                then
                        G_SUJET_MAIL="SUCCES : $G_SUJET_MAIL"
                else
                        G_SUJET_MAIL="ECHEC : $G_SUJET_MAIL"
                fi
 
                # On log la tache en cours ...
                echo $G_JOUR $G_DATE $INFOS_APPLI [$TYPE] "Courriel de notification" >> $G_FICHIER_LOG
                # Puis on prépare le fichier du courriel ($G_COURRIEL_TXT) et on lui ajoute le contenu du log ($G_FICHIER_LOG)
                echo -e "Subject: "$G_SUJET_MAIL"\r\n""Content-Type: text/plain; charset=UTF-8""\r\n" > $G_COURRIEL_TXT
                $CAT $G_FICHIER_LOG >> $G_COURRIEL_TXT
 
                # On envoie le courriel
                $CAT $G_COURRIEL_TXT | $MSMTP $G_CONTACTS_MAIL
 
                # ... et on les supprime (option -v disparue)
                $RM -f $G_FICHIER_LOG
                $RM -f $G_COURRIEL_TXT
        fi
}
 
function statutSF ()
{
        # Statistiques diverses 
        echo "" >> $G_FICHIER_LOG
        echo " >>>> Statistiques du système de fichiers ..." >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
        $DF -Hl >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
 
}
 
function statutSFServeur ()
{
        local server=$1
 
        # Statistiques diverses
        echo "" >> $G_FICHIER_LOG
        echo " >>>> Statistiques du système de fichiers distant de \""$server\"" ..." >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
        local   command="$DF -Hl"
        $SSH $server $command >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
}
 
function statusSmartServeur ()
{
        local server=$1
        shift # On enlève le premier arguments ($SERVEUR), pour traiter les devices (/dev/sdx)
        while [ $# -ge 1 ]; do #on sort après avoir traiter tous les arguments $#=0) => LOOP pas top a virer !!! ASAP
                # Récupération état SMART du périphérique passé en paramètre ...
                echo "" >> $G_FICHIER_LOG
                echo " >>>> Etat SMART du périphérique \""$1\"" de \""$server\"" ..." >> $G_FICHIER_LOG
                echo "" >> $G_FICHIER_LOG
                local   command="$SMARTCTL -H -i $1"
                $SSH $server $command >> $G_FICHIER_LOG
                echo "" >> $G_FICHIER_LOG
                shift
        done
}
 
function statusCephServeur ()
{
        local server=$1
 
        # Récupération état SMART du périphérique passé en paramètre ...
        echo "" >> $G_FICHIER_LOG
        echo " >>>> Etat du système de fichiers distribué CEPH de \""$server\"" ..." >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
        local   command="$CEPH health"
        $SSH $server $command >> $G_FICHIER_LOG
        local   command2="$CEPH df"
        $SSH $server $command2 >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
}
 
function statusRaidServeur ()
{
        local server=$1
 
        # Récupération état système RAID ...
        echo "" >> $G_FICHIER_LOG
        echo " >>>> Etat du système RAID de \""$server\"" ..." >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
        local   command="$CAT /proc/mdstat"
        $SSH $server $command >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
}
 
function statutBorgBackup ()
{
        local borgBackupRepository=$1
 
        # Chemin complet des outils utilises A GARDER POUR PORTABILITÉ MULTI-PLATEFORME etc ...
        local BORG='/usr/bin/borg'
 
        echo "" >> $G_FICHIER_LOG
        echo " >>>> Statistiques du système de sauvegarde ..." >> $G_FICHIER_LOG
        $BORG list $borgBackupRepository >> $G_FICHIER_LOG
        echo "" >> $G_FICHIER_LOG
 
}
 
 
function serveurJoignable ()
{
         local server=$1
 
         $SSH $server $HOSTNAME > /dev/null
         ret=$?
         if [ $ret -ne 0 ];then # ... erreur  pour récupérer le nom d'hote du serveur distant 
                G_ERREUR=$ret
                g_log_message "ERROR" "Serveur \"$server\" injoignable, ssh: \"$G_ERREUR\""
                return $G_ERREUR
         else
                g_log_message "INFO" "Serveur \"$server\" en ligne"
                return 0
         fi
}
 
function gestionService ()
{
        local action=$1
        local service=$2
        $SYSTEMCTL $action $service
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur à l'arrêt/démarrage du service
                G_ERREUR=$ret
                g_log_message "ERROR" "$action \"$service\" impossible \"$G_ERREUR\""
                return $G_ERREUR
        else
                g_log_message "INFO" "$action \"$service\""
                return 0
        fi
}
 
function gestionServiceServeur ()
{
        local server=$1
        local action=$2
        local service=$3
 
        # Serveur en ligne ?
        serveurJoignable $server
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur serveur injoignable, on sort avec le code erreur.
                return $ret
        fi
 
        local   command="$SYSTEMCTL $action $service"
        $SSH $server $command > /dev/null
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur à l'arrêt/démarrage du service
                G_ERREUR=$ret
                g_log_message "ERROR" "Serveur \"$server\" : \"$action\" \"$service\"  impossible \"$G_ERREUR\""
                return $G_ERREUR
        else
                g_log_message "INFO" "Serveur \"$server\" : \"$action\" \"$service\""
                return 0
        fi
}
 
 
function demontage ()
{
        local   pointMontage=$1
        $UMOUNT $pointMontage
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur au démmontage
                G_ERREUR=$ret
                g_log_message "ERROR" "Impossible de démonter \"$pointMontage\" erreur \"$G_ERREUR\""
                return $G_ERREUR
        else
                g_log_message "INFO" "Démontage de \"$pointMontage\""
                return 0
        fi
}
 
function montageHdd ()
{
        local   pointMontage=$1
        $MOUNT $pointMontage
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur au montage
                G_ERREUR=$ret
                g_log_message "ERROR" "Impossible de monter \"$pointMontage\" erreur \"$G_ERREUR\""
                return $G_ERREUR
        else
                g_log_message "INFO" "Montage de \"$pointMontage\""
                return 0
        fi
}
 
function montageSshFS ()
{
        local SSHFS='/usr/bin/sshfs'
        #local SSHFS='/usr/bin/sshfs -oCiphers=arcfour'
 
        local server=$1
        local pointMontage=$2
 
        $SSHFS $server:/ $pointMontage &>> $G_FICHIER_LOG
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur au montage
                G_ERREUR=$ret
                g_log_message "ERROR" "Impossible de monter \"$pointMontage\" de \"$server\" erreur \"$G_ERREUR\""
                return $G_ERREUR
        else
                g_log_message "INFO" "Montage \"$pointMontage\"  de \"$server\""
                return 0
        fi
}
 
 
#function sauvegardeBase () # SI utilisé : CODE à RELIRE !!!
#{
#
#}
 
 
function sauvegardeBasesServeur ()
{
 
        local server=$1 #serveur distant
        local dbType=$2 #mysql ou postgresql
        local dbDumpDir=$3 #répertoire ou sera placé le dump sur le $server distant
 
        # Chemin complet des outils utilises A GARDER POUR PORTABILITÉ MULTI-PLATEFORME etc ...
        local MYSQLDUMP='/usr/bin/mysqldump'
        local POSTGRESQLDUMP='/usr/bin/pg_dump'
        local POSTGRESQLDUMPALL='/usr/bin/pg_dumpall'
        local SYNC='/bin/sync'
 
        local dbUser=''
        local dbName=''
        local dbPwd=''
 
        # Preparation env. dump sur serveur distant  
        # Note : Il n'y a théoriquement plus de transaction sur la base de données car les services susceptibles de provoquer des update etc sont arrêtés, les FS SYNC
        local command="$SSH $server [ -d $dbDumpDir]"
        if [ command ];then # Si le repertoire ou sera déposé le dump n'est pas présent  ...
                local   command="$MKDIR -p $dbDumpDir" # ... alors nous le créons.
                $SSH $server $command > /dev/null
                ret=$?
                if [ $ret -ne 0 ];then # ... erreur à la création du répertoire contenant le dump des bases de données
                        G_ERREUR=$ret
                        g_log_message "ERROR" "Impossible d'acceder au repertoire de dump \"$dbDumpDir\" erreur \"$G_ERREUR\""
                        return $G_ERREUR
                fi
        fi
 
        case $dbType in
 
                mysql)
 
                        # On test si MySQL est présent ... sinon on sort
                        command="$WHICH mysql"
                        $SSH $server $command > /dev/null
                        ret=$? # On stocke le code retour de la commande
                        if [ $ret -ne 0 ];then # ... erreur
                                g_log_message "WARNING" "\"$dbType\" non disponible"
                                return 0
                        fi
 
                        # ... mysql présent , on continue !
                        dbName='--all-databases' # Toutes les bases (option Mysql, donc ne pas changer SVP!!!!!)
                        dbUser='admin'
                        dbPwd='xxxxxxxxxxxxxxxxxx'
                        # Préparation de la commande pour MySql
                        local  command="$MYSQLDUMP -u$dbUser -p$dbPwd -h localhost  --single-transaction --opt $dbName  > $dbDumpDir/$server-$dbType-$dbName.sql"
                        ;;
 
                postgresql)
 
                        # On test si PostgreSQL est présent ... sinon on sort
                        command="$WHICH psql"
                        $SSH $server $command > /dev/null
                        ret=$? # On stocke le code retour de la commande
                        if [ $ret -ne 0 ];then # ... erreur
                                g_log_message "WARNING" "\"$dbType\" non disponible"
                                return 0
                        fi
                        # ... postgresql présent , on continue !
                        dbName='--full' # Toutes les bases (nommage ilinux)
                        dbUser='postgresUser' # fonctionne avec le fichier /root/.pgpass, en chmod 0600, et un password à postgres  (psql : \password)
 
                        # Préparation de la commande pour PostgreSQL
                        local  command="$POSTGRESQLDUMPALL -U $dbUser -h localhost > $dbDumpDir/$server-$dbType-$dbName.sql"
                        ;;
 
                *)      # Base de donnée inconnue : ERREUR
                        G_ERREUR=1
                        g_log_message "ERROR" "Impossible de sauvegarder le type de base \"$dbType\""
                        return $G_ERREUR
                        ;;
        esac
 
        # On lance l'export des bases de données :
        $SSH $server $command > /dev/null
        ret=$? # On stocke le code retour de la commande
 
        # on  teste le resultat de l'export des bases de données :
        if [ $ret -ne 0 ];then # ... erreur durant le dump(export)
                G_ERREUR=$ret
                g_log_message "ERROR" "Export impossible des bases \"$dbType\" erreur \"$G_ERREUR\""
                return $G_ERREUR
        else
                g_log_message "INFO" "Export des bases \"$dbType\" sous \"$dbDumpDir\""
        fi
 
        # Compression de l'export des bases de données, avec SYNC du systéme de fichiers préalable :
        command="$SYNC ; $GZIP -f $dbDumpDir/$server-$dbType-$dbName.sql"
        $SSH $server $command > /dev/null
        ret=$? # On test le resultat de la compression ...
        if [ $ret -ne 0 ];then # ... erreur durant la compression de l'export
                G_ERREUR=$ret
                g_log_message "ERROR" "Compression impossible du fichier \"$dbDumpDir/$server-$dbType-$dbName.sql\" erreur \"$G_ERREUR\""
                return $G_ERREUR
        else
                g_log_message "INFO" "Compression du fichier \"$dbDumpDir/$server-$dbType-$dbName.sql\" sous \"$dbDumpDir\""
                return 0
        fi
 
        # On synchronise les I/O (entrée sorties du système de fichiers en vue de la sauvegarde BorgBackup)
        $SYNC
}
 
 
 
######### FONCTION ######################################################
#
# Sauvegarde une liste de répertoire vers une destination avec BorgBackup
# Param IN      $1 = Serveur à sauvegarder
#               $2 = dépot BorgBackup à utiliser
#               $3 = Répertoires à sauvegarder
#
# Ex. appel : sauvegardeBorgBackup $SERVER "$depotBorgBackup" "$repertoiresAsauvegarder"
#
# Note la politique de rétention de données est : --keep-within=15d --keep-weekly=4 --keep-monthly=12 --keep-yearly=5
#                                               - 5 ans,
#                                               - 12 mois,
#                                               - 4 semaines,
#                                               - 15 jours consécutifs,
 
function sauvegardeBorgBackupServeur () # sauvegarde serveur distant avec sshFS
{
        local server=$1
        local borgBackupRepo=$2
        local dirsToBackup=$3
        local borgMntPoint="/root/Prod/mnt/BorgBackup/$server" # Point de montage du système de fichiers du serveur distant (/, racine)
 
        # Chemin complet des outils utilises A GARDER POUR PORTABILITÉ MULTI-PLATEFORME etc ...
        local BORG='/usr/bin/borg'
        local SYNC='/bin/sync'
 
        # Serveur en ligne ?
        serveurJoignable $server
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur serveur injoignable, on sort avec le  code erreur.
                return $ret
        fi
 
        # Verification du répertoire du point de montage pour sshFS
        if [ ! -d $borgMntPoint ];then # Si le repertoire pour le pt de montage de sshFS n'est pas présent  ...
                $MKDIR -p $borgMntPoint # ... alors nous le créons.
                ret=$?
                if [ $ret -ne 0 ];then # ... erreur à la création du repertoire (sshFS)
                        G_ERREUR=$ret
                        g_log_message "ERROR" "Impossible de creer le repertoire pour sshFS \"$borgMntPoint\" erreur \"$G_ERREUR\""
                        return $G_ERREUR
                fi
        fi
 
        # Montage du système de fichier / (root/racine)  distant sur $borgMntPoint (répertoire temporaire pour la sauvegarde)
        montageSshFS $server $borgMntPoint
        ret=$?
        if [ $ret -ne 0 ];then # ... erreur pour le montage du systéme de fichiers distant, on sort avec le code erreur.
                return $ret
        fi
 
        # Verification du dépot BorgBackup $borgBackupRepo 
        if [ ! -d $borgBackupRepo ];then # Si le dépôt BorgBackup n'est pas présent  ...
                $BORG init --encryption=none $borgBackupRepo # ... alors nous le créons, sans cryptage (CPU eater...).
                ret=$?
                if [ $ret -ne 0 ];then # ... erreur à la création du dépôt BorgBackup
                        G_ERREUR=$ret
                        g_log_message "ERROR" "Impossible d'initialiser le dépôt BorgBackup \"$borgBackupRepo\" erreur \"$G_ERREUR\""
 
                        # ARRET de la sauvegarde (on synchronise les i/O et demonte le système de ficher racine distant)
                        $SYNC #synchro des IO disques (pour laisser un FS distant propre)
                        demontage $borgMntPoint
                        return $G_ERREUR
                fi
        fi
 
        # On lance la sauvegarde ...
        g_log_message "INFO" "Sauvegarde de \"$dirsToBackup\" en cours"
 
        # ... on corrige le bug
        export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
        $BORG create -v --stats $borgBackupRepo::$server-{now:%d_%m_%Y:%M} $dirsToBackup &>> $G_FICHIER_LOG
 
        # On vérifie que la sauvegarde s'est bien déroulée ...
        ret=$?
        if [ $ret -eq 2 ];then # ... erreur durant la sauvegarde, on sort en erreur.
                G_ERREUR=$ret
                g_log_message "ERROR" "Borg backup erreur \"$G_ERREUR\""
 
                # ARRET de la sauvegarde (on synchronise les i/O et demonte le système de ficher racine distant)
                $SYNC #synchro des IO disques
                demontage $borgMntPoint
                return $G_ERREUR
 
        else
 
                #Rétention, 15 jours, 4 semaines, 1 par mois, sur 5 ans ...
                $BORG prune --error --keep-within=15d --keep-weekly=4 --keep-monthly=12 --keep-yearly=5 $borgBackupRepo &>> $G_FICHIER_LOG
                # On vérifie que la rétention a bien fonctionné ... (nettoyage des anciennes sauvegardes)
                ret=$?
                if [ $ret -eq 2 ];then # ... erreur durant la rétention, on sort en erreur.
                        G_ERREUR=$ret
                        g_log_message "ERROR" "Borg prune erreur \"$G_ERREUR\""
 
                        # ARRET de la sauvegarde (on synchronise les i/O et demonte le système de ficher racine distant)
                        $SYNC #synchro des IO disques
                        demontage $borgMntPoint
                        return $G_ERREUR
 
                fi
        fi
 
        # Fin de la sauvegarde (on synchronise les i/O et demonte le système de ficher racine distant)
        $SYNC #synchro des IO disques
        demontage $borgMntPoint
}
 
function sauvegardeBorgBackup () # Sauvegarde locale
{
        local server=$1
        local borgBackupRepo=$2
        local dirsToBackup=$3
 
        # Chemin complet des outils utilises A GARDER POUR PORTABILITÉ MULTI-PLATEFORME etc ...
        local BORG='/usr/bin/borg'
        local SYNC='/bin/sync'
 
        # Verification du dépot BorgBackup $borgBackupRepo 
        if [ ! -d $borgBackupRepo ];then # Si le dépôt BorgBackup n'est pas présent  ...
                $BORG init --encryption=none $borgBackupRepo # ... alors nous le créons, sans cryptage.
                ret=$?
                if [ $ret -ne 0 ];then # ... erreur à la création du dépôt BorgBackup
                        G_ERREUR=$ret
                        g_log_message "ERROR" "Impossible d'initialiser le dépôt BorgBackup \"$borgBackupRepo\" erreur \"$G_ERREUR\""
 
                        # ARRET de la sauvegarde
                        $SYNC #synchro des IO disques
                        return $G_ERREUR
                fi
        fi
 
        # On lance la sauvegarde ...
        g_log_message "INFO" "Sauvegarde de \"$dirsToBackup\" en cours"
 
        # ... on corrige le bug
        export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
        $BORG create -v --stats $borgBackupRepo::$server-{now:%d_%m_%Y:%M} $dirsToBackup &>> $G_FICHIER_LOG
 
        # On vérifie que la sauvegarde s'est bien déroulée ...
        ret=$?
        if [ $ret -eq 2 ];then # ... erreur durant la sauvegarde, on sort en erreur.
                G_ERREUR=$ret
                g_log_message "ERROR" "Borg backup erreur \"$G_ERREUR\""
 
                # ARRET de la sauvegarde
                $SYNC #synchro des IO disques
                return $G_ERREUR
 
        else
                #Rétention, 10 jours, 4 semaines, 1 par mois, sur 5 ans ...
                $BORG prune --error --keep-within=15d --keep-weekly=4 --keep-monthly=12 --keep-yearly=5 $borgBackupRepo &>> $G_FICHIER_LOG
                # On vérifie que la rétention a bien fonctionné ... (nettoyage des anciennes sauvegardes)
                ret=$?
                if [ $ret -eq 2 ];then # ... erreur durant la rétention, on sort en erreur.
                        G_ERREUR=$ret
                        g_log_message "ERROR" "Borg prune erreur \"$G_ERREUR\""
 
                        # ARRET de la sauvegarde (on synchronise les i/O et demonte le système de ficher racine distant)
                        $SYNC #synchro des IO disques
                        demontage $borgMntPoint
                        return $G_ERREUR
 
                fi
        fi
 
        # Fin de la sauvegarde (on synchronise les i/O)
        $SYNC #synchro des IO disques
}

Ensuite chaque serveur est sauvegardé via la crontab … ( si certains connaissent un bon ordonnanceur sur GNU/Linux je suis preneur ! … à l'époque je jouais avec Ortro)

20 0 * * * /root/Prod/Scripts/Sauvegardes/backup_srv_std.sh serveur_001 > /dev/null

Code iLinux: nos codes sources sont également disponibles dans notre forge logiciel Gitea (Git).

TODO : Installation et paramétrage du serveur

Ensuite ces sauvegardes sont externalisées chaque jour sur un autre site, sur un autre serveur : rigel.