179 lines
5.3 KiB
Bash
179 lines
5.3 KiB
Bash
#!/bin/bash
|
|
#
|
|
# Script de réplication ZFS automatique pour NFS HA
|
|
# À déployer sur acemagician et elitedesk
|
|
#
|
|
# Ce script :
|
|
# - Vérifie 3 fois que le LXC nfs-server est actif localement
|
|
# - Détermine le nœud distant automatiquement
|
|
# - Réplique le dataset ZFS vers le nœud passif
|
|
# - Utilise un verrou pour éviter les réplications concurrentes
|
|
#
|
|
# Auteur : BENE Maël
|
|
# Version : 1.0
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
CTID=103
|
|
CONTAINER_NAME="nfs-server"
|
|
ZPOOL="zpool1" # Pool entier à répliquer (tous les datasets)
|
|
CHECK_DELAY=2 # Délai entre chaque vérification (secondes)
|
|
LOG_FACILITY="local0"
|
|
SSH_KEY="/root/.ssh/id_ed25519_zfs_replication"
|
|
|
|
# Fonction de logging
|
|
log() {
|
|
local level="$1"
|
|
shift
|
|
logger -t "zfs-nfs-replica" -p "${LOG_FACILITY}.${level}" "$@"
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $@" >&2
|
|
}
|
|
|
|
# Fonction de vérification du statut du LXC
|
|
check_lxc_running() {
|
|
local attempt="$1"
|
|
|
|
log "info" "Vérification #${attempt}/3 du statut du LXC ${CTID} (${CONTAINER_NAME})"
|
|
|
|
# Vérifier que le CT existe
|
|
if ! pct status "$CTID" &>/dev/null; then
|
|
log "warning" "Le conteneur ${CTID} n'existe pas sur ce nœud"
|
|
return 1
|
|
fi
|
|
|
|
# Vérifier le statut
|
|
local status
|
|
status=$(pct status "$CTID" 2>/dev/null | awk '{print $2}')
|
|
|
|
if [[ "$status" != "running" ]]; then
|
|
log "info" "Le conteneur ${CTID} n'est pas en cours d'exécution (statut: ${status})"
|
|
return 1
|
|
fi
|
|
|
|
# Vérification supplémentaire: le processus existe-t-il vraiment?
|
|
if ! pct exec "$CTID" -- test -f /proc/1/cmdline 2>/dev/null; then
|
|
log "warning" "Le conteneur ${CTID} semble running mais n'est pas responsive"
|
|
return 1
|
|
fi
|
|
|
|
log "info" "Vérification #${attempt}/3 réussie: LXC ${CTID} est actif"
|
|
return 0
|
|
}
|
|
|
|
# Triple vérification de sécurité
|
|
verify_lxc_is_active() {
|
|
local check_count=0
|
|
local success_count=0
|
|
|
|
for i in 1 2 3; do
|
|
check_count=$((check_count + 1))
|
|
|
|
if check_lxc_running "$i"; then
|
|
success_count=$((success_count + 1))
|
|
else
|
|
log "error" "Échec de la vérification #${i}/3"
|
|
return 1
|
|
fi
|
|
|
|
# Délai entre les vérifications (sauf après la dernière)
|
|
if [[ $i -lt 3 ]]; then
|
|
sleep "$CHECK_DELAY"
|
|
fi
|
|
done
|
|
|
|
if [[ $success_count -eq 3 ]]; then
|
|
log "info" "✓ Triple vérification réussie: le LXC ${CTID} est définitivement actif sur ce nœud"
|
|
return 0
|
|
else
|
|
log "error" "✗ Triple vérification échouée: ${success_count}/3 vérifications réussies"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Détermination du nœud local et distant
|
|
LOCAL_NODE=$(hostname)
|
|
log "info" "Démarrage du script sur le nœud: ${LOCAL_NODE}"
|
|
|
|
# Déterminer le nœud distant et son IP
|
|
case "$LOCAL_NODE" in
|
|
"acemagician")
|
|
REMOTE_NODE_NAME="elitedesk"
|
|
REMOTE_NODE_IP="192.168.100.20"
|
|
;;
|
|
"elitedesk")
|
|
REMOTE_NODE_NAME="acemagician"
|
|
REMOTE_NODE_IP="192.168.100.10"
|
|
;;
|
|
*)
|
|
log "error" "Nœud inconnu: ${LOCAL_NODE}. Ce script doit s'exécuter sur acemagician ou elitedesk."
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
log "info" "Nœud distant configuré: ${REMOTE_NODE_NAME} (${REMOTE_NODE_IP})"
|
|
|
|
# Triple vérification de sécurité
|
|
if ! verify_lxc_is_active; then
|
|
log "info" "Le LXC ${CTID} n'est pas actif sur ce nœud. Pas de réplication nécessaire."
|
|
exit 0
|
|
fi
|
|
|
|
# Vérification de l'existence du pool
|
|
if ! zpool list "$ZPOOL" &>/dev/null; then
|
|
log "error" "Le pool ${ZPOOL} n'existe pas sur ce nœud"
|
|
exit 1
|
|
fi
|
|
|
|
# Verrou pour éviter les réplications concurrentes
|
|
LOCKFILE="/var/run/zfs-replica-${ZPOOL}.lock"
|
|
LOCKFD=200
|
|
|
|
# Fonction de nettoyage
|
|
cleanup() {
|
|
if [[ -n "${LOCK_ACQUIRED:-}" ]]; then
|
|
log "info" "Libération du verrou"
|
|
flock -u $LOCKFD
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Tentative d'acquisition du verrou (non-bloquant)
|
|
eval "exec $LOCKFD>$LOCKFILE"
|
|
if ! flock -n $LOCKFD; then
|
|
log "info" "Une réplication est déjà en cours. Abandon."
|
|
exit 0
|
|
fi
|
|
LOCK_ACQUIRED=1
|
|
|
|
log "info" "Verrou acquis. Début de la réplication."
|
|
|
|
# Vérification de la connectivité SSH vers le nœud distant
|
|
if ! ssh -i "$SSH_KEY" -o ConnectTimeout=5 -o BatchMode=yes "root@${REMOTE_NODE_IP}" "echo OK" &>/dev/null; then
|
|
log "error" "Impossible de se connecter à ${REMOTE_NODE_NAME} (${REMOTE_NODE_IP}) via SSH"
|
|
exit 1
|
|
fi
|
|
|
|
log "info" "Connexion SSH vers ${REMOTE_NODE_NAME} (${REMOTE_NODE_IP}) vérifiée"
|
|
|
|
# Vérification que le pool existe sur le nœud distant
|
|
if ! ssh -i "$SSH_KEY" "root@${REMOTE_NODE_IP}" "zpool list ${ZPOOL}" &>/dev/null; then
|
|
log "error" "Le pool ${ZPOOL} n'existe pas sur ${REMOTE_NODE_NAME}"
|
|
exit 1
|
|
fi
|
|
|
|
# Réplication avec syncoid (récursive pour tous les datasets)
|
|
log "info" "Début de la réplication récursive: ${ZPOOL} → ${REMOTE_NODE_NAME} (${REMOTE_NODE_IP}):${ZPOOL}"
|
|
|
|
# Syncoid utilise les options SSH via la variable d'environnement SSH
|
|
export SSH="ssh -i ${SSH_KEY}"
|
|
|
|
if syncoid --recursive --no-sync-snap --quiet "$ZPOOL" "root@${REMOTE_NODE_IP}:$ZPOOL"; then
|
|
log "info" "✓ Réplication récursive réussie vers ${REMOTE_NODE_NAME} (${REMOTE_NODE_IP})"
|
|
log "info" " Tous les datasets de ${ZPOOL} ont été synchronisés"
|
|
exit 0
|
|
else
|
|
log "error" "✗ Échec de la réplication vers ${REMOTE_NODE_NAME} (${REMOTE_NODE_IP})"
|
|
exit 1
|
|
fi
|