Migration
Migration
Die Aufgabe ist folgende: Wir müssen ein Videoarchiv von einem teuren Rechenzentrum in ein günstigeres verlagern. Option eins: Die Festplatten per Post schicken. Allerdings handelt es sich um zehn alte Festplatten im RAID-0-Verbund. Wir müssten das System lokal auf den alten Festplatten wieder aufbauen und es dann auf die neuen übertragen. Option zwei: Die Daten über das Internet übertragen. Das klingt unwahrscheinlich, da es sich um ein Videoarchiv handelt, aber wir geben bekanntlich nicht so schnell auf!
1. Investitionen
Wir kaufen 10 vServer mit minutengenauer Abrechnung. Als Image-Installationsserver verwenden wir Debian (Debian-1200-*-minimal - Debian Bookworm oder Debian-1300-*-minimal - Trixie). Daraus ergibt sich: Server 0 ist die Quelle, Server 10 ist das Ziel, Server 1-9 sind Hilfsknoten.
2. Installation und Vorbereitungen Server 0
Sie benötigen ein Torrent-Erstellungsprogramm, den Torrent-Client selbst und rsync zum Kopieren der Metadaten. Server 0 sollte sich automatisch mit den Servern 1–10 verbinden, ohne dass ein Passwort eingegeben werden muss. Kopieren Sie den Schlüssel auf ALLE Server (1–10).
sudo -s
# Wenn Sie kein Root-Benutzer sind
apt update && apt install -y transmission-cli rsync
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
ssh-copy-id root@server1
ssh-copy-id root@server2
# ... und so weiter bis Server 10
# Prüfung
ssh root@server1 "echo OK"
# Wenn debian-minimal installiert ist, gehen wir sofort zum nächsten Punkt über.
# Wenn UFW verwendet wird
ufw allow 51413/tcp
ufw allow 51413/udp
# Wenn die Ports plötzlich über iptables geschlossen werden (dies ist bei Hetzner nicht der Fall, kann aber bei anderen Systemen im Installationsabbild enthalten sein)
iptables -L INPUT -v -n --line-numbers
# Wenn sie geschlossen sind, öffnen Sie sie.
iptables -A INPUT -p tcp --dport 51413 -j ACCEPT
iptables -A INPUT -p udp --dport 51413 -j ACCEPT
# Dies ist eine einfache Möglichkeit, sich keine Gedanken über Netzwerkschnittstellen machen zu müssen.
netfilter-persistent save
# Falls iptables-persistent nicht vorhanden ist, installieren Sie es.
apt install iptables-persistent
3. Installation und Vorbereitungen Server 1–9 (Worker/Peers)
Diese Server fungieren als Zwischenknoten. Sie laden gleichzeitig Dateien voneinander herunter und verteilen sie sofort weiter, wodurch die Last auf Server 0 reduziert wird.
sudo -s
# Wenn Sie kein Root-Benutzer sind
apt update && apt install -y transmission-cli rsync
# Wenn debian-minimal installiert ist, gehen wir sofort zum nächsten Punkt über.
# Wenn UFW verwendet wird
ufw allow 51413/tcp
ufw allow 51413/udp
# Wenn die Ports plötzlich über iptables geschlossen werden (dies ist bei Hetzner nicht der Fall, kann aber bei anderen Systemen im Installationsabbild enthalten sein)
iptables -L INPUT -v -n --line-numbers
# Wenn sie geschlossen sind, öffnen Sie sie.
iptables -A INPUT -p tcp --dport 51413 -j ACCEPT
iptables -A INPUT -p udp --dport 51413 -j ACCEPT
# Dies ist eine einfache Möglichkeit, sich keine Gedanken über Netzwerkschnittstellen machen zu müssen.
netfilter-persistent save
# Falls iptables-persistent nicht vorhanden ist, installieren Sie es.
apt install iptables-persistent
4. Installation und Vorbereitungen Server 10 (Endpunkt-/Zielserver)
Dieser Server ist das endgültige Ziel. Er sammelt alle Dateiteile im maximalen Multithreading-Modus aus dem gesamten Netzwerk der Worker.
sudo -s
# Wenn Sie kein Root-Benutzer sind
apt update && apt install -y transmission-cli rsync
# Wenn debian-minimal installiert ist, gehen wir sofort zum nächsten Punkt über.
ufw allow 51413/tcp && ufw allow 51413/udp
# Wenn die Ports plötzlich über iptables geschlossen werden (dies ist bei Hetzner nicht der Fall, kann aber bei anderen Systemen im Installationsabbild enthalten sein)
iptables -L INPUT -v -n --line-numbers
# Wenn sie geschlossen sind, öffnen Sie sie.
iptables -A INPUT -p tcp --dport 51413 -j ACCEPT
iptables -A INPUT -p udp --dport 51413 -j ACCEPT
# Dies ist eine einfache Möglichkeit, sich keine Gedanken über Netzwerkschnittstellen machen zu müssen.
netfilter-persistent save
# Falls iptables-persistent nicht vorhanden ist, installieren Sie es.
apt install iptables-persistent
5. Skript mit Protokollierung migration_script.sh (Server 0)
Wenn Sie das Skript ausführen, wird im Stammverzeichnis des Ordners /data/ (oder wo immer Sie es angeben) eine Datei namens migration_pipeline.log erstellt.
#!/bin/bash
set -e
# ==========================================
# CENTRAL CONFIGURATION
# ==========================================
# Verzeichnis mit Dateien auf Server 0
SOURCE_DIR="/data/oracle_migration_export_dir"
STAGE_DIR="/data/torrent_zone"
TORRENT_FILE="${STAGE_DIR}/migration.torrent"
REMOTE_USER="root"
REMOTE_META_DIR="/data/torrent_meta"
REMOTE_CONFIG_DIR="/data/torrent_config"
REMOTE_WORKER_DL_DIR="/data/torrent_download" # Server 1-9
REMOTE_ENDPOINT_DL_DIR="/data/final_target" # Server 10
# Allgemeine Protokolldatei auf Server 0
PIPELINE_LOG="/data/migration_pipeline.log"
WORKER_SERVERS=("server1" "server2" "server3" "server4" "server5" "server6" "server7" "server8" "server9")
ENDPOINT_SERVER="server10"
# Überwachungs-Intervall (in Sekunden)
POLL_INTERVAL=15
# ==========================================
# Zeitzählfunktion
format_duration() {
local seconds=$1
echo "$((seconds / 60)) min. $((seconds % 60)) sek."
}
# Es wird geprüft, ob das Quellverzeichnis existiert.
if [ ! -d "$SOURCE_DIR" ]; then
echo "[ERROR] Das Quellverzeichnis $SOURCE_DIR existiert nicht!" | tee -a "$PIPELINE_LOG"
exit 1
fi
# Wir erstellen eine Liste aller Dateien in einem Verzeichnis (nur Dateien, ohne Unterverzeichnisse).
IFS=$'\n' FILES=($(find "$SOURCE_DIR" -maxdepth 1 -type f))
unset IFS
TOTAL_FILES=${#FILES[@]}
if [ "$TOTAL_FILES" -eq 0 ]; then
echo "[INFO] Im Verzeichnis $SOURCE_DIR befinden sich keine Dateien, die übertragen werden müssen." | tee -a "$PIPELINE_LOG"
exit 0
fi
echo "=========================================================="
echo " Für die serielle Übertragung gefundene Dateien: $TOTAL_FILES"
echo " Das Ausführungsprotokoll wird in folgendes Verzeichnis geschrieben: $PIPELINE_LOG"
echo "=========================================================="
echo "=== START DER MIGRATIONSPIPELINE: $(date '+%Y-%m-%d %H:%M:%S') ===" >> "$PIPELINE_LOG"
CURRENT_INDEX=0
for CURRENT_FILE in "${FILES[@]}"; do
CURRENT_INDEX=$((CURRENT_INDEX + 1))
FILE_NAME=$(basename "$CURRENT_FILE")
# Korrigieren Sie die Startzeit für die aktuelle Datei.
START_TIME_SEC=$(date +%s)
START_DATE_STR=$(date '+%Y-%m-%d %H:%M:%S')
echo "----------------------------------------------------------"
echo " [Datei $CURRENT_INDEX von $TOTAL_FILES]: $FILE_NAME"
echo "----------------------------------------------------------"
echo "[$(date '+%H:%M:%S')] Die Verarbeitung der Datei $FILE_NAME wird gestartet..." >> "$PIPELINE_LOG"
# Die Torrent-Zeitzone auf Server 0 wird vor dem Herunterladen einer neuen Datei gelöscht.
rm -rf "$STAGE_DIR"
mkdir -p "$STAGE_DIR"
echo "[STAGE 1] Erstelle die .torrent Datei..."
transmission-create -p -o "$TORRENT_FILE" "$CURRENT_FILE"
echo "[STAGE 1] Starte Seeding auf Server 0..."
ORIGIN_BASE_DIR=$(dirname "$CURRENT_FILE")
nohup transmission-cli -g "$STAGE_DIR/.config" -w "$ORIGIN_BASE_DIR" "$TORRENT_FILE" > "${STAGE_DIR}/seeder.log" 2>&1 &
MASTER_SEED_PID=$!
echo "[STAGE 2] Verteile Metadaten an alle Server..."
ALL_REMOTES=("${WORKER_SERVERS[@]}" "$ENDPOINT_SERVER")
for SERVER in "${ALL_REMOTES[@]}"; do
ssh -o StrictHostKeyChecking=no "${REMOTE_USER}@${SERVER}" "mkdir -p ${REMOTE_META_DIR} ${REMOTE_CONFIG_DIR} ${REMOTE_WORKER_DL_DIR} ${REMOTE_ENDPOINT_DL_DIR}"
rsync -avz "$TORRENT_FILE" "${REMOTE_USER}@${SERVER}:${REMOTE_META_DIR}/"
done
echo "[STAGE 3] Initialisiere Prozesse auf Servern 1-9..."
for WORKER in "${WORKER_SERVERS[@]}"; do
ssh "${REMOTE_USER}@${WORKER}" "
nohup transmission-cli -g ${REMOTE_CONFIG_DIR} -w ${REMOTE_WORKER_DL_DIR} ${REMOTE_META_DIR}/migration.torrent > /var/log/torrent_worker.log 2>&1 &
"
done
echo "[STAGE 4] Initialisiere Endpoint auf Server 10..."
ssh "${REMOTE_USER}@${ENDPOINT_SERVER}" "rm -f /var/log/torrent_endpoint.log"
ssh "${REMOTE_USER}@${ENDPOINT_SERVER}" "
nohup transmission-cli -g ${REMOTE_CONFIG_DIR} -w ${REMOTE_ENDPOINT_DL_DIR} ${REMOTE_META_DIR}/migration.torrent > /var/log/torrent_endpoint.log 2>&1 &
"
echo "[STAGE 5] Überwachung der Übertragung..."
TRANSFER_SUCCESS=true
while true; do
if ! ssh "${REMOTE_USER}@${ENDPOINT_SERVER}" "pgrep -f 'transmission-cli.*migration.torrent'" > /dev/null 2>&1; then
echo ""
if ssh "${REMOTE_USER}@${ENDPOINT_SERVER}" "grep -q -E 'Progress: 100%|State: Seeding|Progress: 100.0%' /var/log/torrent_endpoint.log" > /dev/null 2>&1; then
echo "[SUCCESS] Die Datei $FILE_NAME wurde erfolgreich auf Server 10 heruntergeladen."
break
else
echo "[ERROR] Fehler beim Übertragen der Datei $FILE_NAME!"
TRANSFER_SUCCESS=false
break
fi
fi
PROGRESS=$(ssh "${REMOTE_USER}@${ENDPOINT_SERVER}" "grep -o 'Progress: [0-8kMGTPEZY]\{1,3\}[0-9.]*%' /var/log/torrent_endpoint.log | tail -n 1" || true)
if [ -z "$PROGRESS" ]; then
PROGRESS="Initialisiere..."
fi
echo -ne "[STATUS] Übertragung $FILE_NAME -> Server 10: ${PROGRESS}\r"
sleep "$POLL_INTERVAL"
done
# Die Verteilung der aktuellen Datei auf Server 0 wird beendet.
kill "$MASTER_SEED_PID" || true
# Berechnen die Zeitabläufe
END_TIME_SEC=$(date +%s)
DURATION_SEC=$((END_TIME_SEC - START_TIME_SEC))
DURATION_FORMATTED=$(format_duration "$DURATION_SEC")
if [ "$TRANSFER_SUCCESS" = true ]; then
# Bei Erfolg in das Protokoll schreiben
echo "Datei: $FILE_NAME | Start: $START_DATE_STR | Zeitaufwand: $DURATION_FORMATTED | Status: [SUCCESS]" >> "$PIPELINE_LOG"
else
# Protokollierung bei Absturz und Notfallbeendigung des Skripts
echo "Datei: $FILE_NAME | Startdatum: $START_DATE_STR | Verstrichene Zeit bis zum Fehler: $DURATION_FORMATTED | Status: [ERROR]" >> "$PIPELINE_LOG"
exit 1
fi
echo "----------------------------------------------------------"
echo " WORKER-AUFBAU: Entfernen einer Datei von den Servern 1-9"
echo "----------------------------------------------------------"
for WORKER in "${WORKER_SERVERS[@]}"; do
echo "[Bereinigung] Datei und Cache auf ${WORKER} werden gelöscht..."
ssh "${REMOTE_USER}@${WORKER}" "
pkill -f 'transmission-cli.*migration.torrent' || true
rm -f \"${REMOTE_WORKER_DL_DIR}/${FILE_NAME}\"
rm -rf ${REMOTE_WORKER_DL_DIR}/{*,.*} 2>/dev/null || true
rm -rf ${REMOTE_META_DIR}/* ${REMOTE_CONFIG_DIR}/*
"
done
# Beenden den Prozess und bereinigen die Metadaten auf Server 10 (die Datei selbst BLEIBT bestehen).
ssh "${REMOTE_USER}@${ENDPOINT_SERVER}" "
pkill -f 'transmission-cli.*migration.torrent' || true
rm -rf ${REMOTE_META_DIR}/* ${REMOTE_CONFIG_DIR}/*
"
echo "[INFO] Datei $FILE_NAME wurde auf den Servern 1-9 gelöscht. Speicherplatz ist verfügbar."
done
# Abschließende Bereinigung des temporären Torrent-Ordners auf Server 0
rm -rf "$STAGE_DIR"
echo "=== PIPELINE FERTIGGESTELLT: $(date '+%Y-%m-%d %H:%M:%S') ===" >> "$PIPELINE_LOG"
echo "=========================================================="
echo " END-TO-END PIPELINE ERFOLGREICH BEENDET!"
echo "=========================================================="
Das Skript ausführen:
- Es wird empfohlen, das Skript in einer Screen- oder tmux-Sitzung auszuführen, damit der Übertragungsprozess (der eine lange Zeit dauern kann) nicht unterbrochen wird, falls die Verbindung zu Server 0 versehentlich abbricht.
- tmux new -s migration
- ./migration_script.sh