Commit initial : réplication bidirectionnelle ZFS avec NFS HA
This commit is contained in:
commit
996b5c6c8e
4 changed files with 441 additions and 0 deletions
179
zfs-nfs-replica.sh
Normal file
179
zfs-nfs-replica.sh
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#!/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
|
||||
Loading…
Add table
Add a link
Reference in a new issue