Aller au contenu

La recherche est disponible uniquement en mode production. Essayez de construire puis de prévisualiser votre site pour tester la recherche localement.

Externalisation hors ligne des backups Proxmox

Pour des raisons de sécurité, les sauvegardes doivent être externalisées sur des disques externes. L’objectif est double :

  • garder une copie hors ligne, physiquement isolée du site ;
  • pouvoir remonter l’infrastructure rapidement en cas d’incident majeur.
ContrainteDétail
VolumétriePlus de 20 To de sauvegardes
CloisonnementPlusieurs réseaux à sauvegarder séparément
DisponibilitéLe disque ne doit être monté que sur une plage horaire définie

Les sauvegardes sont découpées en deux namespaces sur le serveur Proxmox Backup Server (PBS) :

  1. Reseau A
  2. Reseau B

Chaque namespace est externalisé sur son propre disque de 24 To. Deux disques ont donc été achetés — ce qui représente un budget conséquent en 2026.

  • Un serveur Proxmox Backup Server opérationnel.
  • Un ou plusieurs disques externes dimensionnés pour la volumétrie à externaliser.
  • Un accès root en SSH sur le PBS.

PBS est basé sur Debian : la plupart des outils sont déjà présents, mais gdisk et les outils du système de fichiers choisi doivent généralement être installés.

Fenêtre de terminal
apt update
apt install -y gdisk btrfs-progs
PaquetFournitUtilisé pour
gdiskgdiskCréer la table de partition GPT
btrfs-progsmkfs.btrfs, btrfsFormater et étiqueter le disque
util-linuxlsblk, mount, umount, findmnt, mountpoint, blkidIdentifier et monter le disque (déjà installé)
bsdutilsloggerJournaliser le script d’automatisation (déjà installé)
udevudevadmAttendre la détection du disque (déjà installé)
cron/etc/crontabPlanifier le montage et le démontage (déjà installé)
proxmox-backup-serverproxmox-backup-managerPiloter les datastores (déjà installé)
  1. Identifier le nouveau disque

    Insérer le disque dans le serveur PBS, puis lister les périphériques bloc :

    Fenêtre de terminal
    lsblk
    Fenêtre de terminal
    NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
    sda 8:0 0 45G 0 disk
    ├─sda1 8:1 0 1G 0 part /boot
    └─sda2 8:2 0 44G 0 part
    ├─rl_heal-root 253:0 0 40,5G 0 lvm /
    └─rl_heal-swap 253:1 0 3,5G 0 lvm
    sdb 8:16 0 200G 0 disk

    Dans cet exemple, le nouveau disque est sdb. Adapter toutes les commandes suivantes à ce nom.

  2. Créer la table de partition

    Fenêtre de terminal
    gdisk /dev/sdb

    Enchaîner les commandes interactives suivantes :

    ToucheAction
    dSupprimer les partitions existantes (à répéter s’il y en a plusieurs)
    oCréer une nouvelle table de partition GPT
    nCréer une nouvelle partition — valider toutes les valeurs par défaut avec Entrée
    wÉcrire les modifications sur le disque et quitter
  3. Formater la partition

    Fenêtre de terminal
    mkfs.btrfs /dev/sdb1
  4. Étiqueter le disque

    L’étiquette (label) permet de retrouver le disque quel que soit le nom attribué par le noyau (sdb, sdc…). C’est indispensable pour la rotation, et fortement recommandé même avec un seul disque.

    Fenêtre de terminal
    btrfs filesystem label /dev/sdb1 offline-disk-1

    Vérifier :

    Fenêtre de terminal
    blkid -L offline-disk-1

Un datastore PBS a besoin d’une arborescence spécifique (.chunks, .lock…) sur le disque. Cette arborescence est créée automatiquement à la création du datastore dans l’interface.

La marche à suivre diffère selon qu’il s’agit du premier disque ou d’un disque de rotation.

  1. Créer le point de montage et monter le disque

    Fenêtre de terminal
    mkdir -p /mnt/datastore/OFFLINE
    mount /dev/sdb1 /mnt/datastore/OFFLINE/
  2. Créer le datastore définitif dans l’interface PBS

    Dans l’interface web : Datastore → Add Datastore, en pointant sur /mnt/datastore/OFFLINE.

    Création du datastore dans l'interface Proxmox Backup Server

    Le disque est prêt : passer directement à l’Étape 3.

Étape 3 — Automatiser le montage et le démontage

Section intitulée « Étape 3 — Automatiser le montage et le démontage »

Le disque ne doit rester monté que pendant la fenêtre de sauvegarde. Deux tâches planifiées sont nécessaires :

gantt
    title Cycle quotidien du disque d'externalisation
    dateFormat YYYY-MM-DD HH:mm
    axisFormat %Hh
    tickInterval 3hour

    section Disque externe
    Démonté — débranchable          :crit,      2026-01-01 08:00, 9h
    Monté — sur /mnt/datastore      :active,    2026-01-01 17:00, 15h

    section Sauvegardes PBS
    Datastore en maintenance        :crit,      2026-01-01 08:00, 9h
    Fenêtre de sauvegarde           :active,    2026-01-01 17:00, 15h
  • 17:00 — montage. Le script détecte le disque branché par son label, le monte, puis lève le mode maintenance du datastore. Les sauvegardes peuvent démarrer.
  • 08:00 — démontage. Le datastore passe en maintenance, PBS termine ses tâches en cours, puis le disque est démonté. Il peut alors être débranché et emporté hors site.

Le démontage est simple : une seule ligne dans la crontab suffit.

Fenêtre de terminal
vim /etc/crontab
/etc/crontab
0 8 * * * root proxmox-backup-manager datastore update OFFLINE --maintenance-mode offline && sleep 180 && umount /mnt/datastore/OFFLINE

Il n’existe pas de solution idéale pour le montage. Les règles udev ont été testées mais se sont révélées trop instables. Le script ci-dessous est la solution retenue : il cherche le disque par son label, vérifie son système de fichiers, le monte et lève le mode maintenance du datastore.

  1. Déposer le script sur le serveur

    Fenêtre de terminal
    vim /root/auto-mount-backup-disk.sh
    chmod +x /root/auto-mount-backup-disk.sh
    /root/auto-mount-backup-disk.sh
    #!/bin/bash
    set -uo pipefail
    ################################ CONFIGURATION ###############################
    # Zone -> labels candidats, par ordre de préférence
    declare -A LABELS=(
    [OFFLINE]="offline-disk-1 offline-disk-2 offline-disk-3"
    120 collapsed lines
    )
    # Zone -> point de montage (peut différer du nom du datastore)
    declare -A MOUNTPOINT=(
    [OFFLINE]="/mnt/datastore/OFFLINE"
    )
    # Zone -> datastore PBS correspondant
    declare -A DATASTORE=(
    [OFFLINE]="OFFLINE"
    )
    MOUNTOPTS="rw,relatime,space_cache=v2,max_inline=0,nosuid,nodev,noexec"
    WAIT_SECS=30 # attente max de l'apparition d'un label (remplace le sleep fixe)
    SET_MAINTENANCE_IF_ABSENT=1 # 1 = passe le datastore en maintenance quand le disque est absent
    MAINTENANCE_MSG="Disque externe non branché"
    ################################################################################
    TAG="auto-mount-backup-disk"
    rc=0
    log() { printf '[%s] %s\n' "$(date '+%F %T')" "$*"; logger -t "$TAG" -- "$*" 2>/dev/null; }
    warn() { printf '[%s] ATTENTION: %s\n' "$(date '+%F %T')" "$*" >&2; logger -t "$TAG" -p user.warning -- "$*" 2>/dev/null; }
    find_device() {
    local zone="$1" label dev deadline=$(( SECONDS + WAIT_SECS ))
    while :; do
    for label in ${LABELS[$zone]}; do
    dev="$(blkid -L "$label" 2>/dev/null)"
    if [ -n "$dev" ]; then
    printf '%s\t%s\n' "$label" "$dev"
    return 0
    fi
    done
    (( SECONDS >= deadline )) && return 1
    sleep 2
    done
    }
    pbs_maintenance_off() {
    local ds="$1"
    proxmox-backup-manager datastore update "$ds" --delete maintenance-mode \
    && log "Datastore $ds : maintenance-mode retiré." \
    || { warn "Datastore $ds : échec du retrait du maintenance-mode."; rc=1; }
    }
    pbs_maintenance_on() {
    local ds="$1"
    [ "$SET_MAINTENANCE_IF_ABSENT" -eq 1 ] || return 0
    proxmox-backup-manager datastore update "$ds" \
    --maintenance-mode "type=offline,message=$MAINTENANCE_MSG" \
    && log "Datastore $ds : passé en maintenance (offline)." \
    || warn "Datastore $ds : échec du passage en maintenance."
    }
    process_zone() {
    local zone="$1"
    local mp="${MOUNTPOINT[$zone]}" ds="${DATASTORE[$zone]}"
    local found label dev fstype src
    # Déjà monté ? on vérifie juste et on lève la maintenance
    if mountpoint -q "$mp"; then
    src="$(findmnt -no SOURCE "$mp")"
    log "[$zone] $mp déjà monté ($src)."
    pbs_maintenance_off "$ds"
    return 0
    fi
    if ! found="$(find_device "$zone")"; then
    warn "[$zone] aucun disque trouvé parmi : ${LABELS[$zone]}"
    pbs_maintenance_on "$ds"
    rc=1
    return 1
    fi
    label="${found%%$'\t'*}"
    dev="${found##*$'\t'}"
    fstype="$(blkid -o value -s TYPE "$dev" 2>/dev/null)"
    if [ "$fstype" != "btrfs" ]; then
    warn "[$zone] $label ($dev) n'est pas en btrfs (type=$fstype) — montage annulé."
    pbs_maintenance_on "$ds"
    rc=1
    return 1
    fi
    # Le disque est-il déjà monté ailleurs ?
    if findmnt -no TARGET --source "$dev" >/dev/null 2>&1; then
    warn "[$zone] $dev est déjà monté sur $(findmnt -no TARGET --source "$dev" | paste -sd' ')."
    rc=1
    return 1
    fi
    mkdir -p "$mp"
    log "[$zone] montage de $label ($dev) sur $mp..."
    if mount -t btrfs -o "$MOUNTOPTS" "$dev" "$mp"; then
    log "[$zone] montage OK."
    pbs_maintenance_off "$ds"
    else
    warn "[$zone] échec du montage de $dev sur $mp."
    pbs_maintenance_on "$ds"
    rc=1
    return 1
    fi
    }
    # --- main ---------------------------------------------------------------------
    if [ "$(id -u)" -ne 0 ]; then
    warn "Ce script doit être exécuté en root."
    exit 1
    fi
    command -v udevadm >/dev/null && udevadm settle --timeout=10 >/dev/null 2>&1
    zones=( "$@" )
    [ ${#zones[@]} -eq 0 ] && zones=( "${!LABELS[@]}" )
    for zone in "${zones[@]}"; do
    if [ -z "${LABELS[$zone]:-}" ]; then
    warn "Zone inconnue : $zone (zones définies : ${!LABELS[*]})"
    rc=1
    continue
    fi
    process_zone "$zone"
    done
    exit $rc
  2. Adapter la configuration

    Seul le bloc CONFIGURATION en haut du script est à modifier :

    VariableRôle
    LABELSLabels des disques acceptés pour la zone, par ordre de préférence (rotation)
    MOUNTPOINTPoint de montage de la zone
    DATASTORENom du datastore PBS correspondant
    MOUNTOPTSOptions de montage
    WAIT_SECSDurée max d’attente de l’apparition d’un label
    SET_MAINTENANCE_IF_ABSENT1 = bascule le datastore en maintenance si aucun disque n’est trouvé

    Pour gérer plusieurs réseaux, ajouter une entrée par zone dans les trois tableaux :

    Fenêtre de terminal
    declare -A LABELS=(
    [RESEAU_A]="reseau-a-disk-1 reseau-a-disk-2"
    [RESEAU_B]="reseau-b-disk-1 reseau-b-disk-2"
    )
    declare -A MOUNTPOINT=(
    [RESEAU_A]="/mnt/datastore/RESEAU_A"
    [RESEAU_B]="/mnt/datastore/RESEAU_B"
    )
    declare -A DATASTORE=(
    [RESEAU_A]="RESEAU_A"
    [RESEAU_B]="RESEAU_B"
    )
  3. Tester le script à la main

    Fenêtre de terminal
    /root/auto-mount-backup-disk.sh
    findmnt /mnt/datastore/OFFLINE

    Il est aussi possible de ne traiter qu’une zone :

    Fenêtre de terminal
    /root/auto-mount-backup-disk.sh RESEAU_A
  4. Planifier l’exécution

    Fenêtre de terminal
    vim /etc/crontab
    /etc/crontab
    0 17 * * * root /root/auto-mount-backup-disk.sh
  • Répertoire/mnt/datastore/
    • RépertoireOFFLINE/ point de montage du disque externe
      • …
  • Répertoire/root/
    • auto-mount-backup-disk.sh script de montage automatique
  • Répertoire/etc/
    • crontab planification montage (17h) / démontage (8h)
    • Répertoireproxmox-backup/
      • datastore.cfg configuration des datastores PBS
HeureActionDéclencheur
08:00Datastore en maintenance puis démontagecron
17:00Détection du disque, montage, sortie de maintenancecron + auto-mount-backup-disk.sh
SymptômePiste
aucun disque trouvé parmi : …Le disque n’est pas branché, ou son label ne correspond pas — vérifier avec blkid -L <label>
n'est pas en btrfs (type=…)Le disque n’a pas été formaté, ou l’a été dans un autre système de fichiers
est déjà monté sur …Démonter le disque avant de relancer le script : umount <point_de_montage>
Le datastore reste en maintenanceLever le mode manuellement : proxmox-backup-manager datastore update OFFLINE --delete maintenance-mode
umount: target is busyUne tâche PBS tourne encore — augmenter le sleep de la crontab de démontage

Les messages du script sont également envoyés au journal système :

Fenêtre de terminal
journalctl -t auto-mount-backup-disk -n 50