Obsah

Raspberry Pi 5 - domácí server

Dokumentovaná sestava se skládá z těchto komponent:

Napájecí zdroj je kvůli UPS desce, protože UPS deska dokáže přes jack konektor 2,5×5,5 mm dodat až 20W. To je maximum, které půjde do UPS desky dodat a zároveň pokud nebudu připojovat další náročné periferie, tak tento výkon utáhne NVMe disky.

Bootování z NVMe disku

Standardně Raspberry Pi 5 bootuje z microSD karty, ale je možné nakonfigurovat systém tak, aby bootoval přímo z NVMe SSD připojeného přes PCIe. Návod vychází z článku https://wiki.geekworm.com/NVMe_SSD_boot_with_the_Raspberry_Pi_5. Tento postup zahrnuje několik kroků:

1. Flashování NVMe SSD pomocí Raspberry Pi Imager

Pro instalaci operačního systému na NVMe SSD můžete použít nástroj Raspberry Pi Imager na Windows nebo macOS. K tomu budete potřebovat USB → NVMe adaptér.

Postup:

2. Aktivace PCIe konektoru

Ve výchozím nastavení je PCIe port deaktivovaný a je nutné jej povolit.

Otevřete konfigurační soubor:

sudo nano /boot/firmware/config.txt

Přidejte následující řádky na konec souboru:

# Povolení PCIe externího konektoru
dtparam=pciex1
 
# Alternativní zápis pro povolení NVMe portu
dtparam=nvme

Uložte změny a restartujte Raspberry Pi:

sudo reboot

3. Nastavení NVMe disku jako primárního bootovacího zařízení

Po povolení PCIe portu je třeba upravit pořadí bootování v bootloaderu.

Možnost 1: Použití raspi-config

Spusťte konfigurátor:

sudo raspi-config

Zvolte:

sudo reboot

Možnost 2: Ruční úprava bootovacího řetězce

Otevřete EEPROM konfiguraci:

sudo rpi-eeprom-config --edit

Najděte řádek `BOOT_ORDER` a upravte ho na:

BOOT_ORDER=0xf416

Uložte změny a restartujte Raspberry Pi.

UPS X728

UPS desku jsem kupoval ve verzi 2.5. Je důležité si uvědomit, že UPS X728 není určena k automatickému spuštění Raspberry Pi po obnovení napájení. Její hlavní funkcí je zajistit nepřerušený provoz během výpadku, dokud je baterie dostatečně nabitá.

Jakmile baterie klesne na kritickou úroveň, Raspberry Pi by mělo být řízeně vypnuto, aby se zabránilo náhlé ztrátě dat nebo poškození systému. UPS však Raspberry Pi znovu automaticky nenastartuje, i když je baterie plně nabitá.

Pokud po zapnutí tlačítka na UPS svítí červená LED dioda na Raspberry Pi, znamená to, že deska je pod napětím, ale Raspberry Pi se samo nespustilo. Skutečné spuštění a běh systému indikují jiné LED diody na Raspberry Pi.

Specifikace

Dokumentace:

Zprovoznění software

Tento návod popisuje postup pro zprovoznění softwaru UPS X728 a jeho správné nastavení.

Aktivace I2C podpory

Nejprve zapněte podporu I2C pomocí `raspi-config`:

sudo raspi-config

Poté nainstalujte potřebné balíčky:

apt-get install i2c-tools
apt install python3-smbus

Ověření připojení přes I2C:

root@propasiv-server:~# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- 36 -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         

#36 - adresa chipu pro měření úrovně nabití baterie #68 - adresa RTC chipu

Instalace X728 ovladačů a skriptů

Nainstalujte potřebné nástroje:

sudo apt install -y gpiod

Stáhněte ovladače:

git clone https://github.com/geekworm-com/x728-script

Nastavte práva pro skripty:

cd x728-script
chmod +x *.sh

Konfigurace tlačítka pro fyzické vypnutí

Přesuňte potřebné soubory na správná místa:

sudo cp -f ./xPWR.sh /usr/local/bin/
sudo cp -f x728-pwr.service /lib/systemd/system
sudo systemctl daemon-reload
sudo systemctl enable x728-pwr
sudo systemctl start x728-pwr

Tento postup umožní, aby fyzické tlačítko na UPS desce X728 fungovalo takto:

Tento postup nedělá nic jiného – pouze definuje chování tlačítka.

Softwarové vypnutí UPS (bez použití Linuxového `shutdown`)

Pro správné softwarové vypnutí je nutné nastavit vlastní alias:

1. Připravte skript `xSoft.sh`:

sudo cp -f ./xSoft.sh /usr/local/bin/

2. Vytvořte alias `x728off`, který bude bezpečně vypínat UPS:

echo "alias x728off='sudo /usr/local/bin/xSoft.sh 0 '''26''' '" >> ~/.bashrc

3. Aktivujte změny v `.bashrc`:

source ~/.bashrc

4. Nyní můžete bezpečně vypnout UPS příkazem:

x728off

Pozor: Nepoužívejte klasický Linuxový příkaz `shutdown`, jinak se UPS nevypne správně a může zůstat zapnutá.

Test bezpečného vypnutí

Softwarové bezpečné vypnutí:

x728off

Nepoužívejte příkaz `shutdown` v Linuxu, jinak se X728 nevypne správně.

Hardwarové bezpečné vypnutí

Pomocí fyzického tlačítka na UPS lze ověřit bezpečné vypnutí:

Tímto postupem zajistíte, že UPS X728 bude správně fungovat a umožní bezpečné vypnutí jak softwarově, tak fyzickým tlačítkem.

Přehled dostupných skriptů pro další práci s UPS

Každý skript demonstruje jinou funkcionalitu UPS X728:

Problém s knihovnou RPi.GPIO a řešení pomocí libgpiod

Starší ukázkové skripty používají knihovnu RPi.GPIO, která není podporována v jádru Linuxu 6.6.x. Pokud spustíte skript jako:

sudo python3 sample/x728-v2.x-plsd.py

může se objevit chyba kvůli nesprávnému gpiochip číslu.

Doporučuje se přejít na knihovnu libgpiod, která je oficiálně doporučena a lépe podporována. UPS X728 poskytuje jediný oficiální skript využívající libgpiod:

sudo python3 sample/x728-v2.x-plsd-gpiod.py

U ostatních skriptů, které stále používají RPi.GPIO, je doporučeno přepsání na libgpiod nebo jejich převedení na shell skripty.

Skript jsem si tedy přizpůsobil pro libgpiod:

#!/usr/bin/env python
 
import struct
import smbus
import time
import gpiod
import logging
 
 
# Log interval
LOG_INTERVAL = 300  # seconds
last_power_state = None # Stores last power state
last_log_time = 0 # Stores last log timestamp
 
 
# Logging configuration
logging.basicConfig(filename='/var/log/x728-battery.log', level=logging.INFO, format='%(asctime)s - %(message)s')
 
 
# GPIO chip and line definitions
#chipname = "gpiochip0"  # Use "gpiochip4" for Raspberry Pi 4/3
chipname = "gpiochip4"  # Use "gpiochip4" for Raspberry Pi 5
out_line_offset = 26  # Corresponds to GPIO26
pld_line_offset = 6  # Power loss detection pin
buzzer_line_offset = 20  # Buzzer control pin
 
# I2C address for battery monitoring
I2C_ADDR = 0x36
 
# Initialize I2C bus
bus = smbus.SMBus(1)
 
# Open GPIO chip
chip = gpiod.Chip(chipname)
 
# Get the output lines
out_line = chip.get_line(out_line_offset)
out_line.request(consumer="battery_monitor", type=gpiod.LINE_REQ_DIR_OUT)
 
buzzer_line = chip.get_line(buzzer_line_offset)
buzzer_line.request(consumer="power_monitor", type=gpiod.LINE_REQ_DIR_OUT)
 
# Get the input line for power loss detection
pld_line = chip.get_line(pld_line_offset)
pld_line.request(consumer="power_monitor", type=gpiod.LINE_REQ_EV_BOTH_EDGES)
 
def readVoltage(bus):
    """Reads battery voltage from I2C bus."""
    address = I2C_ADDR
    read = bus.read_word_data(address, 2)
    swapped = struct.unpack("<H", struct.pack(">H", read))[0]
    voltage = swapped * 1.25 / 1000 / 16
    return voltage
 
def readCapacity(bus):
    """Reads battery capacity from I2C bus."""
    address = I2C_ADDR
    read = bus.read_word_data(address, 4)
    swapped = struct.unpack("<H", struct.pack(">H", read))[0]
    capacity = swapped / 256
    if capacity > 100:
        capacity = 100
    return capacity
 
def print_power_status():
    """Monitors power status and logs changes."""
    global last_power_state, last_log_time
    state = pld_line.get_value()
    current_time = time.time()
 
    if state == 1:
        print("Power Supply A/C Lost") # First detection of power loss
        if last_power_state != 1:
            logging.warning("Power Supply A/C Lost")
        elif current_time - last_log_time > LOG_INTERVAL: # Subsequent logging at intervals
            logging.warning("Power Supply A/C Lost")
            last_log_time = current_time
 
        # Activate buzzer to indicate power loss
        for _ in range(3):  # Buzzer alarm cycle
            buzzer_line.set_value(1)
            time.sleep(0.1)
            buzzer_line.set_value(0)
            time.sleep(0.1)
    else:
        print("AC Power OK")
        if last_power_state != 0: # First detection of power restoration
            logging.info("AC Power OK")
        elif current_time - last_log_time > LOG_INTERVAL: # Subsequent logging at intervals
            logging.info("AC Power OK")
            last_log_time = current_time
        buzzer_line.set_value(0)
 
    last_power_state = state
    return state
 
 
try:
    while True:
        print("******************")
        voltage = readVoltage(bus)
        capacity = readCapacity(bus)
        print(f"Voltage: {voltage:.2f}V")
        print(f"Battery: {capacity:.0f}%")
 
        # Log battery voltage and capacity at intervals
        if time.time() - last_log_time > LOG_INTERVAL:
            logging.info(f"Voltage: {voltage:.2f}V, Battery: {capacity:.0f}%")
            last_log_time = time.time()
 
        # Monitor power loss detection
        power_status = print_power_status()
 
        if capacity == 100:
            print("Battery FULL")
 
        if capacity < 20:
            print("Battery Low")
 
        # This condition ensures shutdown only if the battery is below 99% and the power is disconnected.
        # This means that if the power is connected, the server will not shut down.
        # Everyone should evaluate whether this is safe and adjust the condition if necessary.
        if voltage < 3.00 and power_status == 1:
            print("Battery LOW!!!")
            logging.critical("Battery LOW! Shutdown in 10 seconds")
            print("Shutdown in 10 seconds")
            time.sleep(10)
            out_line.set_value(1)
            time.sleep(3)
            out_line.set_value(0)
 
 
        time.sleep(2)
 
except KeyboardInterrupt:
    print("Exiting...")
finally:
    chip.close()

Automatické spuštění po restartu

Pro zajištění automatického spuštění skriptu po restartu použijeme systemd service, což je spolehlivý způsob, jak zajistit, že skript poběží na pozadí.

Vytvoření systemd služby

Otevřete terminál a vytvořte soubor pro službu:

sudo nano /etc/systemd/system/x728-battery.service

Do souboru vložte následující obsah a uložte:

[Unit]
Description=X728 Battery Monitor
After=multi-user.target
 
[Service]
Type=simple
ExecStart=/usr/bin/python3 /etc/local-scripts/x728-script/sample/x728-v2.x-asd-gpiod.py
Restart=always
User=root
StandardOutput=append:/var/log/x728-battery.log
StandardError=append:/var/log/x728-battery.log
 
[Install]
WantedBy=multi-user.target
Aktivace služby při startu

Načtěte systemd a povolte službu při startu:

sudo systemctl daemon-reload
sudo systemctl enable x728-battery.service
Okamžité spuštění služby

Pro okamžité spuštění služby spusťte:

sudo systemctl start x728-battery.service
Kontrola stavu služby

Pro ověření, zda služba běží správně, použijte:

sudo systemctl status x728-battery.service

Použití logrotate pro rotaci logů na Raspberry Pi

Na Raspberry Pi mi aplikace vytvořila nový logový soubor `/var/log/x728-battery.log`. Abych zajistil jeho pravidelnou rotaci a zabránil nadměrnému růstu, rozhodl jsem se použít logrotate. Tento nástroj už byl v systému nainstalován.

Jak přidat pravidlo do logrotate

Nejprve jsem vytvořil nový konfigurační soubor v `/etc/logrotate.d/`:

sudo nano /etc/logrotate.d/x728-battery

Do souboru jsem přidal následující konfiguraci:

/var/log/x728-battery.log {                           
        weekly                                        
        rotate 4                                      
        compress                                      
        missingok                                     
        notifempty                                    
        create 644 root root                          
        postrotate                                    
                systemctl restart x728-battery.service
        endscript                                     
}                                                     

Bylo nutné přidat `postrotate` sekci, která restartuje službu `x728-battery.service`, protože jinak aplikace nepokračovala v logování do nového souboru.

Otestování konfigurace

Pro kontrolu, zda logrotate soubor správně rozpoznává, jsem spustil:

sudo logrotate -d /etc/logrotate.d/x728-battery

(Debug mód `-d` pouze zobrazí, co by se stalo, ale skutečně rotaci neprovede.)

Pokud konfigurace neobsahovala chyby, provedl jsem ruční rotaci:

sudo logrotate -f /etc/logrotate.d/x728-battery

Po této úpravě se logy správně rotují a starší soubory jsou automaticky komprimovány a spravovány.

Nastavení a čtení RTC času

Tento návod popisuje kompletní nastavení a použití RTC (Real Time Clock) na UPS X728. UPS využívá RTC modul pro udržení přesného času i po odpojení napájení. Tento postup byl testován na Raspberry Pi OS.

Povolení RTC modulu

1. Otevřete konfigurační soubor `/boot/config.txt` a přidejte na konec nebo pod sekci `[all]` tento řádek:

sudo nano /boot/config.txt

Přidejte:

dtoverlay=i2c-rtc,ds1307

Uložte změny stisknutím Ctrl+O, potvrďte Enter, a ukončete editor Ctrl+X.

2. Restartujte Raspberry Pi:

sudo reboot
Zakázání falešného hwclocku

Raspberry Pi OS obsahuje fake-hwclock, který simuluje RTC a může způsobovat problémy s reálným RTC modulem. Proto je nutné ho odstranit:

sudo apt-get -y remove fake-hwclock
sudo update-rc.d -f fake-hwclock remove
sudo systemctl disable fake-hwclock
Úprava udev pravidel

Pro správné fungování RTC je třeba upravit soubor `/lib/udev/hwclock-set` a zakomentovat řádky 7-12:

sudo nano /lib/udev/hwclock-set

Najděte následující část kódu:

#!/bin/sh
# Reset the System Clock to UTC if the hardware clock from which it
# was copied by the kernel was in localtime.

dev=$1

#if [ -e /run/systemd/system ] ; then
#    exit 0
#fi
#
#/sbin/hwclock --rtc=$dev --systz
#/sbin/hwclock --rtc=$dev --hctosys

Každý řádek označený `#` je zakomentován a nebude se vykonávat.

Uložte změny a ukončete editor.

Ověření a nastavení času

Po dokončení výše uvedených kroků restartujte Raspberry Pi:

sudo reboot

Po restartu můžete ověřit aktuální systémový čas:

date

Pokud je čas nesprávný, nastavte ho ručně:

date -s "5 MAR 2019 13:00:00"

Uložte nastavený čas do RTC:

sudo hwclock -w

Pro ověření času uloženého v RTC modulu použijte:

sudo hwclock -r

Poznámka: Ujistěte se, že jsou v UPS X728 vloženy baterie, aby se uložený čas zachoval i po vypnutí zařízení.

Po dokončení tohoto postupu bude při každém startu Raspberry Pi čas automaticky synchronizován z RTC modulu.

Možné problémy a jejich řešení

Tato sekce popisuje možné problémy při používání UPS X728 a jejich řešení. Informace čerpám z oficiální dokumentace výrobce: [Zdroj: Geekworm Wiki - X728 FAQ](https://wiki.geekworm.com/X728#FAQ)

Problém: Zobrazuje se hláška „This power supply is not capable of supplying 5A“ na Raspberry Pi 5

UPS X728/X729 má dostatečnou kapacitu pro napájení 5A, nicméně je potřeba provést následující nastavení:

Krok 1: Úprava konfigurace napájení (povinné)

Otevřete terminál v Raspberry Pi OS a spusťte příkaz:

sudo rpi-eeprom-config -e

Na konec souboru přidejte následující řádek:

PSU_MAX_CURRENT=5000

Uložte změny stisknutím Ctrl+O, potvrďte klávesou Enter, a ukončete editor stisknutím Ctrl+X.

Krok 2: Zvýšení proudového limitu USB (volitelné, pokud používáte USB zařízení s vyšší spotřebou)

Otevřete konfigurační soubor:

sudo nano /boot/firmware/config.txt

Přidejte tento řádek:

usb_max_current_enable=1

Uložte změny a zavřete editor stejným způsobem jako v předchozím kroku.

Restartujte Raspberry Pi, aby se změny projevily:

sudo reboot

Poznámka: Pokud používáte jiný operační systém (například Ubuntu), je nutné nejprve nahrát Raspberry Pi OS, provést výše uvedené úpravy a teprve poté znovu nahrát požadovaný operační systém.

Softwarové řízení nabíjení pomocí GPIO16

Od verze X728 v2.5 je možné softwarově řídit nabíjení baterie pomocí GPIO16. Tato funkce je určena pouze pro pokročilé uživatele, kteří mají zkušenosti s Linuxem. Pokud si nejste jisti, doporučuje se ponechat jumper „CHG Ctrl“ zkratovaný, což umožní automatické nabíjení při připojení napájecího adaptéru.

Možnosti nastavení „CHG Ctrl“ jumperu: - Jumper zkratovaný → Baterie se automaticky nabíjí, pokud je připojen napájecí adaptér. - Jumper otevřený → Nabíjení lze ovládat přes GPIO16:

  1. GPIO16 = HIGH → Nabíjení povoleno.
  2. GPIO16 = LOW → Nabíjení zakázáno.
Ovládání nabíjení pomocí GPIO16

Pro ruční ovládání nabíjení je nutné nejprve zjistit odpovídající GPIO pin:

GPIO=16
 
# Zjištění skutečného čísla GPIO pinu
GPIO=$(cat /sys/kernel/debug/gpio | grep "GPIO$GPIO" | awk -F'gpio-' '{print $2}' | awk -F' ' '{print $1}')
echo "$GPIO" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio$GPIO/direction

Povolení nabíjení:

echo "1" > /sys/class/gpio/gpio$GPIO/value

Zakázání nabíjení:

echo "0" > /sys/class/gpio/gpio$GPIO/value

Poznámka: Nakonec jsem tuto funkci nevyužil, ale uvádím ji pro kompletní dokumentaci, pokud by bylo v budoucnu potřeba řídit nabíjení softwarově.

Konfigurace softwarového RAIDu – zrcadlení

Při konfiguraci jsem vycházel z těchto návodů, přičemž ani jeden není dokonalý pro moji situaci a musel jsem si návody přízpůsobit. Uvádím je pro úplnost jako zdroj:

Adresace disků

Používám Suptronics X1005 2280 M.2 NVMe Dual Shield pro Raspberry Pi 5, který umožňuje připojení dvou disků.

Na desce jsou viditelně označeny dva sloty:

Při připojení jednoho disku do SSD1, systém jej rozpozná jako nvme0n1:

lsblk 
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1     259:0    0  1,8T  0 disk 
├─nvme0n1p1 259:1    0  512M  0 part /boot/firmware
└─nvme0n1p2 259:2    0  1,8T  0 part /

Po připojení druhého disku do SSD2 došlo k nečekanému přeskupení číslování disků. Systém nyní rozpoznává:

* Disk připojený v SSD2 jako nvme0n1 * Disk připojený v SSD1 jako nvme1n1

Výstup příkazu `lsblk` po připojení druhého disku:

lsblk 
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1     259:0    0  1,8T  0 disk 
nvme1n1     259:1    0  1,8T  0 disk 
├─nvme1n1p1 259:2    0  512M  0 part /boot/firmware
└─nvme1n1p2 259:3    0  1,8T  0 part /

Toto chování jsem nečekal, ale beru ho jako fakt a budu s ním dále pracovat obezřetně.

Abych si ušetřil problémy a následné zmatky, tak jsem raději disky prohodil. Tedy disk s operačním systémem jsem dal do slotu SSD2 a nový disk do slotu SSD1. Po změně zapojení vypadá výpis takto:

NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS   
nvme0n1     259:0    0  1,8T  0 disk               
├─nvme0n1p1 259:1    0  512M  0 part /boot/firmware
└─nvme0n1p2 259:2    0  1,8T  0 part /             
nvme1n1     259:3    0  1,8T  0 disk               

Vytvoření partition na novém disku před RAIDem

Před vytvořením softwarového RAIDu je doporučeno ručně vytvořit odpovídající partitiony na novém disku (nvme0n1), aby odpovídaly rozdělení stávajícího disku (nvme1n1). RAID se obvykle vytváří nad existujícími partitionami, nikoliv nad celým diskem (i když je to možné, ale méně běžné).

Jak vytvořit partitiony přesně podle původního disku?

K tomu použijeme příkaz sfdisk, který umožňuje zkopírovat tabulku oddílů ze nvme1n1 na nvme0n1.

Krok 1: Záloha stávající partition tabulky

Než cokoliv změníme, je vhodné uložit stávající partition tabulku, pokud by bylo potřeba ji obnovit:

sudo sfdisk -d /dev/nvme0n1 > partition_backup.txt

Tím se uloží rozložení oddílů do souboru partition_backup.txt, což umožní jejich případnou obnovu.

Krok 2: Zkopírování partition schématu na nový disk

Zkopíruj stejnou partition tabulku ze nvme1n1 na nvme0n1:

sudo sfdisk /dev/nvme1n1 < partition_backup.txt

Tento příkaz vytvoří na nvme1n1 stejnou strukturu jako na nvme0n1.

Krok 3: Ověření správnosti nového rozdělení disku

Po provedení předchozího příkazu je vhodné ověřit, zda jsou partitiony nyní identické:

lsblk
fdisk -l /dev/nvme1n1

Pokud se vše shoduje, můžeme pokračovat v nastavování RAIDu.

Konfigurace RAID 1 pomocí mdadm

Pro konfiguraci softwarového RAIDu pro zrcadlení (RAID 1) použijeme mdadm.

Nejprve nainstalujeme potřebný balíček:

sudo apt install mdadm

Vzhledem k tomu, že RAID nastavujeme na běžícím systému, vytvoříme RAID v degradovaném režimu, tedy pouze s jedním diskem. Druhý disk přidáme později.

Vytvoření RAID 1 v degradovaném režimu

Místo dvou disků v RAIDu vytvoříme pole pouze s novým diskem (nvme1n1p1 a nvme1n1p2). Druhý disk zatím nebude připojen (`missing`), což RAID umožňuje:

sudo mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/nvme1n1p1 missing --metadata=0.90
sudo mdadm --create --verbose /dev/md1 --level=1 --raid-devices=2 /dev/nvme1n1p2 missing

Vysvětlení:

Následujícím krokem je vytvoření souborového systému na nově vytvořeném RAIDu.

Vytvoření souborových systémů

Na nových RAID oddílech vytvoříme souborový systém:

sudo mkfs.vfat /dev/md0
sudo mkfs.ext4 /dev/md1

Nyní připojíme RAID a přeneseme systémové soubory.

Přenos systémových souborů na RAID

Nejprve připojíme bootovací oddíl RAID:

mkdir /mnt/raid_boot
mount /dev/md0 /mnt/raid_boot
rsync -axv /boot/firmware/ /mnt/raid_boot/

Poté připojíme hlavní oddíl a přeneseme systém:

mkdir /mnt/raid_root
mount /dev/md1 /mnt/raid_root
rsync -axv / /mnt/raid_root --exclude=/mnt --exclude=/proc --exclude=/sys --exclude=/dev --exclude=/run --exclude=/tmp

Úprava fstab pro použití RAIDu

Otevřeme soubor fstab na novém RAIDu:

nano /mnt/raid_root/etc/fstab

Najdeme řádky obsahující `/dev/nvme0n1p1` a `/dev/nvme0n1p2` a upravíme je:

/dev/md0  /boot/firmware vfat  defaults  0  2
/dev/md1  /              ext4  defaults,noatime,errors=remount-ro  0  1

Tím zajistíme, že systém při bootu použije RAID.

Aktualizace konfigurace mdadm

Zjistíme UUID RAID polí:

mdadm --detail --scan

Výstup bude podobný tomuto:

ARRAY /dev/md0 metadata=0.90 UUID=e11e72e6:cfe13794:c15f4f5c:b4c7de4f
ARRAY /dev/md1 metadata=1.2 name=server:1 UUID=6af6a5bd:b7fd9bcc:2cd91b19:bf09e0c3

Tento výstup zapíšeme na konec souboru:

nano /mnt/raid_root/etc/mdadm/mdadm.conf

Úprava cmdline.txt pro bootování z RAIDu

Otevřeme soubor bootovací konfigurace:

nano /mnt/raid_boot/cmdline.txt

Najdeme řádek obsahující:

root=PARTUUID=aa235387-02

A nahradíme jej:

root=/dev/md1 rootfstype=ext4 fsck.repair=yes rootwait rootdelay=10 cfg80211.ieee80211_regdom=CZ

Vysvětlení:

Přidání RAID modulů do initramfs

Aby jádro vědělo, že používáme RAID již při bootu, přidáme moduly do initramfs:

nano /mnt/raid_root/etc/initramfs-tools/modules

Přidáme tyto řádky:

raid1
md_mod
ext4

Aktualizujeme initramfs:

umount /mnt/raid_boot
mount /dev/md0 /mnt/raid_root/boot/firmware/
 
mkdir -p /mnt/raid_root/dev /mnt/raid_root/proc /mnt/raid_root/sys /mnt/raid_root/run
 
mount --bind /dev /mnt/raid_root/dev
mount --bind /proc /mnt/raid_root/proc
mount --bind /sys /mnt/raid_root/sys
mount --bind /run /mnt/raid_root/run
 
chroot /mnt/raid_root 
update-initramfs -u

Tím jsme zajistili, že RAID bude dostupný již při startu systému.

Nyní jsem mohl Raspberry restartovat. Důležitá věc, musel jsem primární disk - tedy disk s RAID přesunout do slotu SSD2 na desce. Dokud jsem to neudělal, boot se nepodařil, systém byl zmatený. Tento krok je klíčový - prohodit disky ve slotech.

Přidání disku do RAID

Po prohození disků ve slotech (disk s nastaveným RAIDem musí být ve slotu SSD2) a nabootování systému je možné přidat druhý disk zpět do RAID pole. Stejný postup se použije i v případě, že se nějaký disk odpojí a pole je degradované, tedy běží pouze s jedním diskem.

Klíčové pravidlo: Primární disk musí být ve slotu SSD2, aby systém správně nabootoval.

Stav RAID pole po nabootování

Z výpisu je vidět, že v RAID poli je zatím pouze jeden disk:

cat /proc/mdstat 

Výstup:

Personalities : [raid1] [linear] [raid0] [raid6] [raid5] [raid4] [raid10] 
md0 : active raid1 nvme0n1p1[0]
      524224 blocks [2/1] [U_]
      
md1 : active raid1 nvme0n1p2[0]
      1952854080 blocks super 1.2 [2/1] [U_]
      bitmap: 4/4 pages [64KB], 65536KB chunk

Nově připojený disk nvme1n1 zatím není součástí RAID pole.

Výpis připojených disků

Podíváme se na aktuální stav připojených disků:

lsblk

Výstup:

NAME        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
nvme0n1     259:0    0   1,8T  0 disk  
├─nvme0n1p1 259:1    0   512M  0 part  
│ └─md0       9:0    0 511,9M  0 raid1 /boot/firmware
└─nvme0n1p2 259:2    0   1,8T  0 part  
  └─md1       9:1    0   1,8T  0 raid1 /
nvme1n1     259:3    0   1,8T  0 disk  
├─nvme1n1p1 259:4    0   512M  0 part  
└─nvme1n1p2 259:5    0   1,8T  0 part  

Detaily RAID pole

Zkontrolujeme aktuální stav RAID pole:

mdadm --detail /dev/md0
/dev/md0:
           Version : 0.90
     Creation Time : Sat Feb 15 16:52:29 2025
        Raid Level : raid1
        Array Size : 524224 (511.94 MiB 536.81 MB)
     Used Dev Size : 524224 (511.94 MiB 536.81 MB)
      Raid Devices : 2
     Total Devices : 1
       Persistence : Superblock is persistent

       Update Time : Sat Feb 15 19:15:46 2025
             State : clean, degraded 
    Active Devices : 1
   Working Devices : 1
    Failed Devices : 0
     Spare Devices : 0

Consistency Policy : resync

    Number   Major   Minor   RaidDevice State
       0     259        1        0      active sync   /dev/nvme0n1p1
       -       0        0        1      removed

Stejným způsobem můžeme zkontrolovat i hlavní RAID oddíl:

mdadm --detail /dev/md1

Výstup ukazuje, že RAID pole je degradované – chybí druhý disk.

Přidání nového disku do RAID pole

Nyní přidáme nový disk nvme1n1 do RAID pole:

mdadm --add /dev/md0 /dev/nvme1n1p1
mdadm --add /dev/md1 /dev/nvme1n1p2

Po přidání zkontrolujeme stav RAIDu:

cat /proc/mdstat 

Výstup ukazuje, že RAID začíná synchronizaci:

Personalities : [raid1] [linear] [raid0] [raid6] [raid5] [raid4] [raid10] 
md0 : active raid1 nvme1n1p1[1] nvme0n1p1[0]
      524224 blocks [2/2] [UU]
      
md1 : active raid1 nvme1n1p2[2] nvme0n1p2[0]
      1952854080 blocks super 1.2 [2/1] [U_]
      [=>...................]  recovery =  5.3% (104572928/1952854080) finish=149.1min speed=206517K/sec
      bitmap: 4/4 pages [64KB], 65536KB chunk

Sledování průběhu synchronizace

Pro sledování průběhu synchronizace RAID pole můžeme použít:

watch -n 1 cat /proc/mdstat

RAID nyní probíhá synchronizace a disk bude plně zrcadlen po dokončení procesu.

Priorita disků po odpojení a opětovném připojení

Pokud dojde k odpojení jednoho disku a následně jej znovu připojíme, systém rozhodne, který disk bude v RAIDu upřednostněn, a to na základě několika faktorů.

Nezáleží na tom, že je disk ve slotu SSD2, protože po připojení obou disků systém automaticky vybere ten, který běžel jako poslední aktivní po rozpojení RAIDu.

Jak systém určuje, který disk bude upřednostněn?

Systém rozhoduje na základě poslední aktualizace metadat RAIDu. Každý disk v RAIDu obsahuje metadata (superblock), která uchovávají informace o změnách v poli. Klíčový parametr je tzv. Event Count, což je čítač změn.

RAID vybere disk s nejvyšším Event Count jako platný. Pokud má jeden disk vyšší Event Count než druhý, systém považuje tento disk za aktuální a použije jej jako primární.

Jak zjistit, který disk má vyšší Event Count?

Můžeš si ověřit, který disk má aktuálnější metadata pomocí příkazu:

mdadm --examine /dev/nvme0n1p2 /dev/nvme1n1p2

Výstup bude obsahovat řádky podobné tomuto:

/dev/nvme0n1p2:
           Events : 405

/dev/nvme1n1p2:
           Events : 392

Disk s vyšším číslem „Events“ je považován za aktuální. Pokud se Event Count neshoduje, mdadm automaticky označí starší disk jako neaktuální a vyřadí ho z RAID pole.

Co se stane při neaktuálním disku?

Pokud má jeden z disků nižší Event Count, systém jej při dalším spuštění považuje za zastaralý a označí jej jako neaktivní. RAID pak při připojení tohoto disku provede resynchronizaci dat.

Proto je důležité po výpadku disku **zkontrolovat stav RAID