Add script and documentation to expamd raid6.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
*.swp
|
||||
*.log
|
||||
|
425
raid6-expand-interactive.sh
Executable file
425
raid6-expand-interactive.sh
Executable file
@ -0,0 +1,425 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =========================================
|
||||
# RAID- und FS-Erweiterung (mdadm/Ext4/XFS)
|
||||
# Interaktiv, Dry-Run-, Debug- und Yes-Option
|
||||
# Mit Zeitstempeln, Logfile & farbigen Headern
|
||||
# =========================================
|
||||
|
||||
# ---- Globale Defaults ----
|
||||
DRYRUN=0
|
||||
DEBUG=0
|
||||
ASSUME_YES=0
|
||||
FS_TYPE="ext4" # wird in prompt_inputs gesetzt; hier nur Default
|
||||
LOG_FILE="./raid6-expand-$(date +%F-%H%M%S).log"
|
||||
|
||||
# ---- Zeit & Logging ----
|
||||
ts() { date '+%F %T'; }
|
||||
strip_ansi() { sed -r 's/\x1B\[[0-9;]*[A-Za-z]//g'; }
|
||||
|
||||
log_plain() {
|
||||
# Schreibt *ohne* ANSI-Farben ins Log mit Zeitstempel
|
||||
{ printf "[%s] %s\n" "$(ts)" "$(printf "%s" "$*" | strip_ansi)"; } >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# ---- Farben/Logging-Ausgaben ----
|
||||
cecho() { # $1=colorcode, $2=prefix, $3=msg...
|
||||
local c="$1"; shift
|
||||
local pfx="$1"; shift
|
||||
printf "\e[%sm[%s] %s\e[0m\n" "$c" "$(ts)" "$pfx $*"
|
||||
log_plain "$pfx $*"
|
||||
}
|
||||
|
||||
info() { cecho "36" "INFO " "$@"; } # cyan
|
||||
warn() { cecho "33" "WARN " "$@"; } # gelb
|
||||
ok() { cecho "32" "OK " "$@"; } # grün
|
||||
err() { cecho "31" "ERROR" "$@"; } # rot
|
||||
note() { cecho "35" "NOTE " "$@"; } # magenta
|
||||
|
||||
header() { # Abschnittsüberschrift
|
||||
local line="============================================================"
|
||||
printf "\e[36m[%s] %s\e[0m\n" "$(ts)" "$line"
|
||||
printf "\e[36m[%s] == %s ==\e[0m\n" "$(ts)" "$*"
|
||||
printf "\e[36m[%s] %s\e[0m\n" "$(ts)" "$line"
|
||||
log_plain "$line"
|
||||
log_plain "== $* =="
|
||||
log_plain "$line"
|
||||
}
|
||||
|
||||
# ---- Usage ----
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
|
||||
raid6-expand-interactive.sh — Vergrößert RAID-Mitglieds-Partitionen und das Array/FS
|
||||
|
||||
Nutzung:
|
||||
./raid6-expand-interactive.sh [Optionen]
|
||||
|
||||
Optionen:
|
||||
-n, --dry-run Keine Änderungen; loggt nur, was passieren würde.
|
||||
(Unterdrückt Rückfragen pro Partition; aktiviert --debug)
|
||||
-d, --debug Zusätzliche Diagnosen (lsblk/blkid/sfdisk-Auszüge).
|
||||
-y, --yes Bestätigt alle Rückfragen automatisch (produktiv praktisch).
|
||||
-L, --log <pfad> Schreibe Log nach <pfad> (Default: ./raid6-expand-YYYY-MM-DD-HHMMSS.log)
|
||||
-h, --help Diese Hilfe anzeigen.
|
||||
|
||||
Ablauf (produktiv):
|
||||
1) Interaktive Eingaben (RAID-Device, Disks, Partitionsnummer, FS, ggf. Mountpoint)
|
||||
2) Status-Checks & Partitionstabellen-Backups
|
||||
3) Pro Platte: Partition bis maximal vergrößern (Start & Typ bleiben gleich)
|
||||
4) Nach jeder Platte: mdadm --grow --size=max + auf Resync warten
|
||||
5) Am Ende: Dateisystem vergrößern (resize2fs oder xfs_growfs)
|
||||
|
||||
Voraussetzungen:
|
||||
mdadm, sfdisk, lsblk, blkid, awk, partprobe, udevadm
|
||||
(xfs_growfs nur, wenn FS_TYPE=xfs)
|
||||
|
||||
USAGE
|
||||
}
|
||||
|
||||
# ---- Helfer ----
|
||||
need_bin() { command -v "$1" >/dev/null 2>&1 || { err "Fehlt: $1"; exit 1; }; }
|
||||
|
||||
run() {
|
||||
# Zeigt und loggt den Befehl; führt ihn in produktiv aus
|
||||
printf "[%s] + %s\n" "$(ts)" "$*" | tee -a "$LOG_FILE" >/dev/null
|
||||
if [[ "$DRYRUN" -eq 0 ]]; then
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
confirm() {
|
||||
# Auto-yes im Dry-Run oder wenn -y/--yes gesetzt ist, sonst interaktiv fragen
|
||||
local prompt="$1"
|
||||
if [[ "$DRYRUN" -eq 1 || "$ASSUME_YES" -eq 1 ]]; then
|
||||
note "(auto) $prompt: yes"
|
||||
return 0
|
||||
fi
|
||||
read -r -p "$prompt [yes/NO] " a
|
||||
if [[ "${a:-}" == "yes" ]]; then
|
||||
log_plain "$prompt: yes"
|
||||
return 0
|
||||
fi
|
||||
log_plain "$prompt: NO"
|
||||
return 1
|
||||
}
|
||||
|
||||
confirm_summary() {
|
||||
# Zusammenfassung IMMER bestätigen (auch im Dry-Run), außer -y/--yes
|
||||
local prompt="$1"
|
||||
if [[ "$ASSUME_YES" -eq 1 ]]; then
|
||||
note "(auto) $prompt: yes"
|
||||
return 0
|
||||
fi
|
||||
read -r -p "$prompt [yes/NO] " a
|
||||
if [[ "${a:-}" == "yes" ]]; then
|
||||
log_plain "$prompt: yes"
|
||||
return 0
|
||||
fi
|
||||
log_plain "$prompt: NO"
|
||||
return 1
|
||||
}
|
||||
|
||||
check_clean() {
|
||||
local md="$1"
|
||||
if ! grep -q "$(basename "$md")" /proc/mdstat; then
|
||||
err "$md nicht in /proc/mdstat gefunden."
|
||||
exit 1
|
||||
fi
|
||||
if ! mdadm --detail "$md" | grep -q "State : clean"; then
|
||||
warn "$md ist nicht 'clean'. Details:"
|
||||
mdadm --detail "$md" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||
confirm "Trotzdem fortfahren?" || exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
wait_resync() {
|
||||
local md="$1"
|
||||
info "Warte auf Abschluss von Initialisierung/Resync auf $md ..."
|
||||
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||
note "(Dry-Run) Würde auf Resync/Initialisierung warten und /proc/mdstat beobachten."
|
||||
return 0
|
||||
fi
|
||||
while :; do
|
||||
local blk
|
||||
blk=$(grep -E "^\s*$(basename "$md")" /proc/mdstat || true)
|
||||
if [[ -z "$blk" ]]; then
|
||||
err "$md nicht in /proc/mdstat gefunden!"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -Eiq "(resync|reshape|recovery|check)" /proc/mdstat; then
|
||||
break
|
||||
fi
|
||||
printf "[%s] %s\n" "$(ts)" "$blk" | tee -a "$LOG_FILE" >/dev/null
|
||||
sleep 20
|
||||
done
|
||||
ok "Resync/Initialisierung abgeschlossen."
|
||||
}
|
||||
|
||||
backup_pt() {
|
||||
local disk="$1"
|
||||
local bkup="backup-$(basename "$disk")-$(date +%F-%H%M%S).sfdisk"
|
||||
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||
note "(Dry-Run) Würde Partitionstabelle sichern: $bkup"
|
||||
else
|
||||
sfdisk -d "$disk" > "$bkup"
|
||||
ok "Partitionstabelle gesichert: $bkup"
|
||||
fi
|
||||
if [[ "$DEBUG" -eq 1 ]]; then
|
||||
header "DEBUG: sfdisk -d $disk"
|
||||
sfdisk -d "$disk" | tee -a "$LOG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---- Start & Typ via lsblk/blkid (robuster als sfdisk-parsing) ----
|
||||
get_part_start() {
|
||||
local part="$1"
|
||||
local start
|
||||
start="$(lsblk -no START "$part" 2>/dev/null | head -n1 | tr -d '[:space:]')"
|
||||
[[ -n "$start" ]] || { err "Konnte START für $part nicht ermitteln (lsblk)."; exit 1; }
|
||||
printf "%s" "$start"
|
||||
}
|
||||
|
||||
get_part_type() {
|
||||
local part="$1"
|
||||
local ptype
|
||||
ptype="$(blkid -s PART_ENTRY_TYPE -o value "$part" 2>/dev/null | head -n1 | tr -d '[:space:]' || true)"
|
||||
if [[ -z "$ptype" ]]; then
|
||||
ptype="$(lsblk -no PARTTYPE "$part" 2>/dev/null | head -n1 | tr -d '[:space:]' || true)"
|
||||
fi
|
||||
if [[ -z "$ptype" ]]; then
|
||||
local line
|
||||
line="$(sfdisk -d "${part%[0-9]*}" | awk -v p="$part" '$1==p {print; exit}')"
|
||||
ptype="$(awk -F',' '{
|
||||
for(i=1;i<=NF;i++){
|
||||
gsub(/^ +| +$/,"",$i);
|
||||
if($i ~ /^type=/){sub(/^type=/,"",$i); print $i}
|
||||
}
|
||||
}' <<< "$line" | tr -d '[:space:]')"
|
||||
fi
|
||||
[[ -n "$ptype" ]] || { err "Konnte Partitions-Typ für $part nicht ermitteln."; exit 1; }
|
||||
printf "%s" "$ptype"
|
||||
}
|
||||
|
||||
expand_partition() {
|
||||
local disk="$1" pn="$2"
|
||||
local part="${disk}${pn}"
|
||||
[[ -b "$part" ]] || { err "Partition $part existiert nicht."; exit 1; }
|
||||
|
||||
if [[ "$DEBUG" -eq 1 ]]; then
|
||||
header "DEBUG: lsblk/blkid für $part"
|
||||
lsblk -no NAME,PATH,TYPE,SIZE,START,PARTTYPE "$part" | tee -a "$LOG_FILE"
|
||||
blkid "$part" | tee -a "$LOG_FILE" || true
|
||||
fi
|
||||
|
||||
local pstart ptype
|
||||
pstart="$(get_part_start "$part")"
|
||||
ptype="$(get_part_type "$part")"
|
||||
|
||||
warn "Geplante Änderung auf $part:"
|
||||
note " Startsektor bleibt: $pstart"
|
||||
note " Größe: bis maximal verfügbar"
|
||||
note " Typ bleibt: $ptype"
|
||||
|
||||
if [[ "$DRYRUN" -eq 1 || "$ASSUME_YES" -eq 1 ]]; then
|
||||
note "(Dry-Run) Würde ${part} erweitern: Start=${pstart}, Typ=${ptype}, bis maximal verfügbar."
|
||||
else
|
||||
confirm "Jetzt ${part} erweitern?" || { warn "Übersprungen."; return; }
|
||||
fi
|
||||
|
||||
printf "[%s] + %s\n" "$(ts)" "printf ',,type=${ptype}\n' | sfdisk --no-reread --force -N ${pn} ${disk}" | tee -a "$LOG_FILE" >/dev/null
|
||||
if [[ "$DRYRUN" -eq 0 ]]; then
|
||||
printf ",,type=%s\n" "$ptype" | sfdisk --no-reread --force -N "$pn" "$disk"
|
||||
run partprobe "$disk" || true
|
||||
run udevadm settle || true
|
||||
ok "Partition $part wurde bis ans Ende erweitert."
|
||||
else
|
||||
note "(Dry-Run) Würde Kernel neu einlesen: partprobe $disk ; udevadm settle"
|
||||
fi
|
||||
}
|
||||
|
||||
grow_md_size() {
|
||||
local md="$1"
|
||||
run mdadm --grow "$md" --size=max || { :; }
|
||||
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||
note "(Dry-Run) Würde mdadm-Grow ausführen."
|
||||
fi
|
||||
}
|
||||
|
||||
final_fs_grow() {
|
||||
local md="$1" fs="$2" mp="${3:-}"
|
||||
case "$fs" in
|
||||
ext4)
|
||||
run resize2fs "$md" || { :; }
|
||||
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||
note "(Dry-Run) Würde resize2fs ausführen."
|
||||
fi
|
||||
;;
|
||||
xfs)
|
||||
[[ -n "$mp" ]] || { err "Für XFS muss ein Mountpoint angegeben werden."; exit 1; }
|
||||
if ! mount | grep -qE "^$md on $mp "; then
|
||||
warn "$md scheint nicht auf $mp gemountet zu sein (laut 'mount')."
|
||||
mount | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||
confirm "Trotzdem mit xfs_growfs $mp fortfahren?" || exit 1
|
||||
fi
|
||||
run xfs_growfs "$mp" || { :; }
|
||||
if [[ "$DRYRUN" -eq 1 ]]; then
|
||||
note "(Dry-Run) Würde xfs_growfs ausführen."
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
err "Unbekanntes Dateisystem: $fs (erwartet: ext4 oder xfs)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
ok "Dateisystem-Schritt abgeschlossen."
|
||||
}
|
||||
|
||||
# ---- Interaktive Eingabe ----
|
||||
prompt_inputs() {
|
||||
echo
|
||||
header "Interaktiver Modus: Eingaben sammeln"
|
||||
[[ "$DRYRUN" -eq 1 ]] && note "Dry-Run aktiviert: Es werden KEINE Änderungen durchgeführt."
|
||||
[[ "$DEBUG" -eq 1 ]] && note "Debug aktiviert: Zeige zusätzliche Diagnosen."
|
||||
[[ "$ASSUME_YES" -eq 1 ]] && note "--yes aktiviert: Bestätige alle Rückfragen automatisch."
|
||||
note "Logfile: $LOG_FILE"
|
||||
|
||||
read -r -p "RAID-Device (z.B. /dev/md4): " MDDEV
|
||||
[[ -n "${MDDEV:-}" ]] || { err "RAID-Device darf nicht leer sein."; exit 1; }
|
||||
[[ -b "$MDDEV" ]] || { err "$MDDEV ist kein Block-Device."; exit 1; }
|
||||
|
||||
echo "Physikalische Geräte (ohne Partitionsnummer), leerzeichengetrennt (z.B. /dev/sda /dev/sdb /dev/sdc /dev/sdd):"
|
||||
read -r -a DISKS
|
||||
[[ "${#DISKS[@]}" -ge 2 ]] || { err "Mindestens zwei Geräte angeben."; exit 1; }
|
||||
for d in "${DISKS[@]}"; do
|
||||
[[ -b "$d" ]] || { err "$d ist kein Block-Device."; exit 1; }
|
||||
done
|
||||
|
||||
read -r -p "Partitionsnummer der RAID-Partition (Standard 1): " PARTNUM
|
||||
PARTNUM="${PARTNUM:-1}"
|
||||
[[ "$PARTNUM" =~ ^[0-9]+$ ]] || { err "Partitionsnummer muss numerisch sein."; exit 1; }
|
||||
|
||||
read -r -p "Dateisystem auf $MDDEV [ext4|xfs] (Standard ext4): " FS_TYPE
|
||||
FS_TYPE="${FS_TYPE:-ext4}"
|
||||
FS_TYPE="$(echo "$FS_TYPE" | tr '[:upper:]' '[:lower:]')"
|
||||
if [[ "$FS_TYPE" != "ext4" && "$FS_TYPE" != "xfs" ]]; then
|
||||
err "Ungültiges Dateisystem: $FS_TYPE (erlaubt: ext4, xfs)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MOUNTPOINT=""
|
||||
if [[ "$FS_TYPE" == "xfs" ]]; then
|
||||
read -r -p "Mountpoint des XFS-Dateisystems (z.B. /mnt/raid): " MOUNTPOINT
|
||||
[[ -n "$MOUNTPOINT" ]] || { err "Mountpoint für XFS erforderlich."; exit 1; }
|
||||
fi
|
||||
|
||||
echo
|
||||
header "ZUSAMMENFASSUNG"
|
||||
note "Dry-Run : $([[ "$DRYRUN" -eq 1 ]] && echo "JA" || echo "NEIN")"
|
||||
note "Debug : $([[ "$DEBUG" -eq 1 ]] && echo "JA" || echo "NEIN")"
|
||||
note "Auto-Yes : $([[ "$ASSUME_YES" -eq 1 ]] && echo "JA" || echo "NEIN")"
|
||||
note "RAID-Device: $MDDEV"
|
||||
note "Disks : ${DISKS[*]}"
|
||||
note "Part.-Nr. : $PARTNUM"
|
||||
note "Filesystem : $FS_TYPE"
|
||||
[[ "$FS_TYPE" == "xfs" ]] && note "Mountpoint : $MOUNTPOINT"
|
||||
echo
|
||||
|
||||
confirm_summary "Eingaben korrekt? Fortfahren?" || { warn "Abgebrochen."; exit 0; }
|
||||
}
|
||||
|
||||
# ---- Argumente parsen ----
|
||||
parse_args() {
|
||||
|
||||
echo
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-n|--dry-run) DRYRUN=1; DEBUG=1; shift ;;
|
||||
-d|--debug) DEBUG=1; shift ;;
|
||||
-y|--yes) ASSUME_YES=1; shift ;;
|
||||
-L|--log)
|
||||
shift || true
|
||||
[[ $# -gt 0 ]] || { err "--log erfordert einen Pfad"; exit 1; }
|
||||
LOG_FILE="$1"; shift
|
||||
;;
|
||||
-h|--help) usage; echo; exit 0 ;;
|
||||
*) err "Unbekannte Option: $1"; usage; echo; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Logfile anlegen & Kopf schreiben
|
||||
touch "$LOG_FILE" 2>/dev/null || { err "Kann Logfile nicht schreiben: $LOG_FILE"; exit 1; }
|
||||
header "Start: raid6-expand-interactive | Log: $LOG_FILE"
|
||||
}
|
||||
|
||||
# ---- Main ----
|
||||
main() {
|
||||
echo
|
||||
|
||||
need_bin mdadm
|
||||
need_bin sfdisk
|
||||
need_bin lsblk
|
||||
need_bin blkid
|
||||
need_bin awk
|
||||
need_bin partprobe
|
||||
need_bin udevadm
|
||||
|
||||
prompt_inputs
|
||||
[[ "$FS_TYPE" == "xfs" ]] && need_bin xfs_growfs || true
|
||||
|
||||
for d in "${DISKS[@]}"; do
|
||||
local p="${d}${PARTNUM}"
|
||||
[[ -b "$p" ]] || { err "Erwartete Partition $p existiert nicht."; exit 1; }
|
||||
done
|
||||
|
||||
header "RAID-Status vor Start"
|
||||
mdadm --detail "$MDDEV" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||
echo
|
||||
|
||||
check_clean "$MDDEV"
|
||||
confirm "Letzte Chance vor Änderungen. Weiter?" || exit 0
|
||||
echo
|
||||
|
||||
for d in "${DISKS[@]}"; do
|
||||
local p="${d}${PARTNUM}"
|
||||
|
||||
header "Bearbeite $d / $p"
|
||||
if ! mdadm --detail "$MDDEV" | grep -q " active sync .*$(basename "$p")"; then
|
||||
warn "$p scheint nicht als active sync im $MDDEV gelistet zu sein."
|
||||
mdadm --detail "$MDDEV" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||
confirm "Trotzdem mit $p fortfahren?" || { echo; continue; }
|
||||
fi
|
||||
|
||||
backup_pt "$d"
|
||||
expand_partition "$d" "$PARTNUM"
|
||||
grow_md_size "$MDDEV"
|
||||
wait_resync "$MDDEV"
|
||||
ok "Fertig mit $p."
|
||||
echo
|
||||
done
|
||||
|
||||
header "Final: Dateisystem vergrößern"
|
||||
final_fs_grow "$MDDEV" "$FS_TYPE" "${MOUNTPOINT:-}"
|
||||
echo
|
||||
|
||||
header "Abschluss"
|
||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||
echo
|
||||
mdadm --detail "$MDDEV" | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||
echo
|
||||
df -h | sed 's/^/ /' | tee -a "$LOG_FILE"
|
||||
|
||||
echo
|
||||
header "Ende"
|
||||
echo
|
||||
}
|
||||
|
||||
# ---- Einstieg ----
|
||||
parse_args "$@"
|
||||
main "$@"
|
||||
|
||||
# (Ende)
|
||||
echo
|
||||
|
140
raid6-expand.md
Normal file
140
raid6-expand.md
Normal file
@ -0,0 +1,140 @@
|
||||
Perfekt — dann schreibe ich dir jetzt einen **minimal-Risiko-Plan**, damit du dein RAID6 (`/dev/md4`) mit Ext4 sauber und ohne Datenverlust auf die volle Größe bringst.
|
||||
|
||||
Ich gehe davon aus, dass:
|
||||
|
||||
* `/dev/sda1`, `/dev/sdb1`, `/dev/sdc1`, `/dev/sdd1` **die RAID-Mitglieder** sind.
|
||||
* Jede Platte 20 TB groß ist, die Partitionen aktuell nur 10 TB.
|
||||
* Das RAID ist **gesund** (`cat /proc/mdstat` zeigt „clean“).
|
||||
|
||||
---
|
||||
|
||||
## **Schritt-für-Schritt Anleitung – Minimal Risk**
|
||||
|
||||
### **1. Status checken**
|
||||
|
||||
Bevor wir irgendwas anfassen, prüfen wir den Status:
|
||||
|
||||
```bash
|
||||
cat /proc/mdstat
|
||||
mdadm --detail /dev/md4
|
||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
|
||||
```
|
||||
|
||||
Es muss **clean** sein, und die Partitionen (`sdX1`) müssen ungefähr 10 TB groß sein.
|
||||
|
||||
---
|
||||
|
||||
### **2. Backup der Partitionstabelle**
|
||||
|
||||
Falls wir einen falschen Startsektor erwischen, ist das RAID im Eimer.
|
||||
Deshalb sichern wir die Partitionstabellen **aller** Platten:
|
||||
|
||||
```bash
|
||||
sfdisk -d /dev/sda > sda.part
|
||||
sfdisk -d /dev/sdb > sdb.part
|
||||
sfdisk -d /dev/sdc > sdc.part
|
||||
sfdisk -d /dev/sdd > sdd.part
|
||||
```
|
||||
|
||||
Damit kannst du im Notfall zurückschreiben:
|
||||
|
||||
```bash
|
||||
sfdisk /dev/sda < sda.part
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **3. Eine Platte vergrößern (nacheinander!)**
|
||||
|
||||
Wir machen das **Platte für Platte**, damit immer 3 von 4 Platten voll intakt bleiben.
|
||||
Beispiel für `/dev/sda`:
|
||||
|
||||
1. **Startsektor merken**:
|
||||
|
||||
```bash
|
||||
parted /dev/sda unit s print
|
||||
```
|
||||
|
||||
Notiere die **Start-Sektor-Zahl** der Partition 1 (z. B. `2048s`).
|
||||
|
||||
2. **Partition löschen und neu anlegen** (nur Metadaten, keine Daten!):
|
||||
|
||||
```bash
|
||||
parted /dev/sda
|
||||
(parted) unit s
|
||||
(parted) rm 1
|
||||
(parted) mkpart primary 2048s 100%
|
||||
(parted) quit
|
||||
```
|
||||
|
||||
3. **Kernel über neue Partition informieren**:
|
||||
|
||||
```bash
|
||||
partprobe /dev/sda
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **4. RAID Resync starten**
|
||||
|
||||
Nach dem Anpassen der ersten Platte muss mdadm den neuen Bereich initialisieren:
|
||||
|
||||
```bash
|
||||
mdadm --grow /dev/md4 --size=max
|
||||
```
|
||||
|
||||
⚠️ Das kann mehrere Stunden/Tage dauern.
|
||||
Fortschritt beobachten:
|
||||
|
||||
```bash
|
||||
watch cat /proc/mdstat
|
||||
```
|
||||
|
||||
Warte, bis der Status wieder **\[UUUU] clean** ist.
|
||||
|
||||
---
|
||||
|
||||
### **5. Nächste Platten**
|
||||
|
||||
Wiederhole **Schritt 3** für `/dev/sdb`, dann `/dev/sdc`, dann `/dev/sdd`.
|
||||
Nach jeder Platte läuft ein **Teil-Resync** – auch hier **immer abwarten**, bis es fertig ist, bevor du die nächste Platte anfässt.
|
||||
|
||||
---
|
||||
|
||||
### **6. Dateisystem vergrößern**
|
||||
|
||||
Wenn alle Platten angepasst und der RAID-Resync komplett abgeschlossen ist:
|
||||
|
||||
```bash
|
||||
resize2fs /dev/md4
|
||||
```
|
||||
|
||||
Das Ext4-Dateisystem wird dann den gesamten neuen Platz nutzen.
|
||||
|
||||
---
|
||||
|
||||
### **7. Kontrolle**
|
||||
|
||||
```bash
|
||||
df -h /mountpunkt
|
||||
lsblk
|
||||
mdadm --detail /dev/md4
|
||||
```
|
||||
|
||||
Jetzt solltest du die volle Kapazität sehen.
|
||||
|
||||
---
|
||||
|
||||
## **Wichtige Sicherheitsregeln**
|
||||
|
||||
* Immer nur **eine Platte gleichzeitig** verändern.
|
||||
* Niemals den **Startsektor** ändern!
|
||||
* Während Resyncs am besten keine großen Schreiblasten.
|
||||
* Falls irgendetwas nicht clean ist → erst reparieren, dann weitermachen.
|
||||
|
||||
---
|
||||
|
||||
Wenn du magst, kann ich dir **ein fertiges Shell-Skript** bauen, das dir pro Platte die Startsektoren automatisch liest, die Partition neu anlegt und sicherstellt, dass du dich nicht vertippst.
|
||||
Das minimiert Tippfehler und verhindert den Super-GAU.
|
||||
|
||||
Willst du, dass ich dir so ein Skript direkt schreibe?
|
145
raid6-expand.txt
Normal file
145
raid6-expand.txt
Normal file
@ -0,0 +1,145 @@
|
||||
============================================================
|
||||
Anleitung: RAID-6 Array erweitern (mdadm, GPT, ext4/xfs)
|
||||
============================================================
|
||||
|
||||
Diese Anleitung beschreibt die Erweiterung eines bestehenden
|
||||
RAID-6-Arrays, nachdem die einzelnen RAID-Mitgliedsplatten physisch
|
||||
durch größere Platten ersetzt wurden und ein Resync durchgeführt wurde.
|
||||
|
||||
ACHTUNG:
|
||||
- Nur durchführen, wenn RAID in "clean"-State ist.
|
||||
- Backup wichtiger Daten wird dringend empfohlen.
|
||||
- Root-Rechte erforderlich.
|
||||
- Beispielangaben (Gerätenamen) anpassen!
|
||||
|
||||
------------------------------------------------------------
|
||||
1) Vorbereitung & Kontrolle
|
||||
------------------------------------------------------------
|
||||
1.1 Prüfen, dass alle neuen, größeren Platten erkannt werden:
|
||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
|
||||
|
||||
1.2 RAID-Status prüfen:
|
||||
cat /proc/mdstat
|
||||
mdadm --detail /dev/mdX
|
||||
|
||||
1.3 Sicherstellen, dass der Status "clean" ist.
|
||||
Falls "resync" oder "recovery" läuft → warten, bis fertig.
|
||||
|
||||
------------------------------------------------------------
|
||||
2) Partitionstabellen sichern
|
||||
------------------------------------------------------------
|
||||
Für jedes RAID-Mitgliedsgerät:
|
||||
sfdisk -d /dev/sdX > backup-sdX-YYYYMMDD.sfdisk
|
||||
|
||||
------------------------------------------------------------
|
||||
3) Partitionen auf Mitgliedsplatten vergrößern
|
||||
------------------------------------------------------------
|
||||
Für jede Platte:
|
||||
- Startsektor unverändert lassen!
|
||||
- Partition bis zum maximal verfügbaren Platz vergrößern.
|
||||
- GPT-Partitionstyp unverändert lassen.
|
||||
|
||||
- TYP 'A19D880F-05FC-4D3B-A006-743F0F84911E' für Linux-RAID
|
||||
|
||||
|
||||
1. Vergrößern auf maximal verfügbaren Platz mit sfdisk:
|
||||
|
||||
- GPT-UUID ermitteln z.bsp. für partition 1 von /dev/sda1:
|
||||
|
||||
lsblk -no PARTTYPE "/dev/sda1"
|
||||
|
||||
- Partition 1 con /dev/sdX vergrößern
|
||||
|
||||
sfdisk --no-reread --force -N 1 /dev/sdX <<EOF
|
||||
,,type=A19D880F-05FC-4D3B-A006-743F0F84911E
|
||||
EOF
|
||||
|
||||
--no-reread: Verhindert, dass der Kernel die Partitionstabelle sofort neu einliest.
|
||||
--force: Erzwingt Änderungen auch dann, wenn sfdisk normalerweise abbrechen würde,
|
||||
z. B. weil die Partition in Benutzung ist.
|
||||
N 1: Bearbeitet nur die angegebene Partitionsnummer – hier also Partition 1.
|
||||
|
||||
Eingabe-Redirect <<EOF ... EOF: Damit wird der eigentliche neue Partitions-Eintrag
|
||||
an sfdisk per Here-Document übergeben.
|
||||
Startsektor, Größe, Typ
|
||||
|
||||
- Startsektor: Wenn leer (""), bleibt der bisherige Startsektor erhalten.
|
||||
- Größe: Wenn leer (""), nimmt sfdisk den maximal verfügbaren Platz bis zum Plattenende.
|
||||
- Typ: Hier type=<GPT-UUID>
|
||||
|
||||
oder
|
||||
|
||||
1. Vergrößern auf maximal verfügbaren Platz mit parted:
|
||||
|
||||
parted /dev/sdX
|
||||
(parted) print
|
||||
(parted) resizepart 1 100%
|
||||
(parted) quit
|
||||
|
||||
oder in einer Zeile:
|
||||
|
||||
parted -s /dev/sdX resizepart 1 100%
|
||||
|
||||
|
||||
|
||||
2. Danach Partitionstabelle neu einlesen:
|
||||
partprobe /dev/sdX
|
||||
udevadm settle
|
||||
|
||||
|
||||
Bemerkung 'parted' im Vergleich zu sfdisk:
|
||||
|
||||
- sfdisk ist "roh", arbeitet auf Sektorebene - super für Skripte, aber etwas sperrig für Menschen.
|
||||
|
||||
- parted versteht Größenangaben wie 100%, 10TiB, etc. und ist interaktiv angenehmer. resize verändert
|
||||
nur die Größe, nicht den Typcode - der Linux-Typ bleibt also bestehen.
|
||||
|
||||
- Für ein Skript wäre sfdisk robuster, weil es keine Rückfragen stellt und 100 % skriptfähig ist.
|
||||
|
||||
------------------------------------------------------------
|
||||
4) RAID-Array vergrößern
|
||||
------------------------------------------------------------
|
||||
Nachdem alle Partitionen angepasst sind:
|
||||
mdadm --grow /dev/mdX --size=max
|
||||
|
||||
Anschließend auf Abschluss des internen Resync warten:
|
||||
watch cat /proc/mdstat
|
||||
|
||||
------------------------------------------------------------
|
||||
5) Dateisystem vergrößern
|
||||
------------------------------------------------------------
|
||||
Je nach Dateisystem:
|
||||
|
||||
- ext4:
|
||||
resize2fs /dev/mdX
|
||||
|
||||
- xfs (muss gemountet sein):
|
||||
xfs_growfs <Mountpoint>
|
||||
|
||||
------------------------------------------------------------
|
||||
6) Abschließende Kontrolle
|
||||
------------------------------------------------------------
|
||||
- Blockgeräte prüfen:
|
||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
|
||||
|
||||
- RAID-Details:
|
||||
mdadm --detail /dev/mdX
|
||||
|
||||
- Dateisystemgröße:
|
||||
df -h <Mountpoint>
|
||||
|
||||
------------------------------------------------------------
|
||||
Hinweise
|
||||
------------------------------------------------------------
|
||||
- GPT-Typcode ermitteln - z.bsp. mit
|
||||
|
||||
lsblk -no PARTTYPE /dev/sdX1
|
||||
|
||||
für Linux RAID Partition (bei dir):
|
||||
A19D880F-05FC-4D3B-A006-743F0F84911E
|
||||
|
||||
- Wenn nur einzelne Platten ersetzt/geändert werden:
|
||||
Schrittweise vorgehen: Platte ersetzen - RAID resync - nächste Platte.
|
||||
|
||||
- Bei Einsatz von LUKS oder LVM: Zusätzliche Schritte für LUKS-Resize
|
||||
bzw. LVM PV Resize nach dem RAID-Resize durchführen.
|
Reference in New Issue
Block a user