Práce v prostředí ramdisku

Z DCEwiki
(přesměrováno z ramdisk)
Skočit na navigaci Skočit na vyhledávání


Zavaděč

Zavaděč je standalone binární aplikace, která se stará o zavedení binárního jádra operačního systému do paměti počítače a jeho spuštění. Tato aplikace musí být schopna rozpoznat blokové zařízení a přečíst souborový systém na kterém má operační systém své jádro uložen.


O spuštění zavaděče se stará BIOS (resp. UEFI) počítače:

BIOS
BIOS sahá na první sektor blokového zařízení (tzv. MBR - Master Boot Record), kde se pokouší najít informaci o tom, odkud a kolik bajtů dat má natáhnout do paměti – pokud ji najde, načte data z této oblasti do paměti a předá další řízení načtenému kódu.
UEFI
Má svoje soubory uloženy na diskovém oddíle se souborovým systémem FAT a v podstatě se dá říct, že jde o předinstalovaný zavaděč, který má do sebe zadrátovaný ověřovací mechanismus – secure boot – který před zavedením kódu zkontroluje, zda-li byl podepsán "tím správným klíčem".

UEFI si vynutil Microsoft, protože jedině secureboot je schopen zajistit aby se uživateli nenatáhnul do paměti infikovaný kernel. Pochopitelně pro linuxové uživatele představovalo zavedení UEFI opruz – obzvláště tam, kde nebylo možné secureboot vypnout. Aby secureboot při zavádění akceptoval vlastní kernel tak by musel být podepsán certifikátem od Microsoftu.

  • Dnes většina strojů umožňuje UEFI boot vypnout a použít tzv. Legacy Boot, kter funguje podobně jako při zavádění přes BIOS. V takovém případě, pokud chcete používat diskless, musíte použít tzv. Legacy PXE Boot
  • U slušných UEFI lze secureboot vypnout. Pak, pokud vaše UEFI podporuje u síťové karty PXE můžete pro zavádění disklessu využít UEFI PXE Boot
  • Pro ta ostatní UEFI dnes existuje několik způsobů jak ho obejít, většinou jsou založeny na tom, že secureboot natáhne podepsaný zavaděč.

Nejrozšířenější linuxový zavaděč pro zavádění z lokálních blokových zařízení je v současné době GRUB2, který má oproti staršímu zavaděči LILO výhodu především v tom, že jeho zaváděcí proces je velmi podobný tomu, jak funguje zavádění linuxového systému.

Upozornění Součástí načteného a spuštěného binárního kódu musí být vždy ovladače, které umožní další pokračování zaváděcího procesu. Bez ohledu na to, jde-li o zavaděč, nebo jádro operačního systému. Pokud některý z nich chybí, zavádění selže.
Kód natahovaný do paměti BIOSem, musí být vždy uložen na začátku disku, v prostoru mezi tabulkou rozdělení disku a začátkem prvního diskového oddílu. To v případě MS-DOS tabulky představovalo původně 62 datových bloků (cca 32 kB). Do tohoto prostoru se musel vejít veškerý kód zavaděče.

Více místa bylo mezi začátkem diskového oddílu a začátkem souborového systému (cca 66 kB). Toho využívala první verze zavaděče GRUB, která si na začátek disku uložila pouze malou část kódu, která si pak místo BIOSu natáhla do paměti vlastní kód zavaděče, uložený na začátku některého z diskových oddílů.

Tento koncept však přestal být s rostoucím množstvím existujícího hardware neudržitelný, protože nebylo možné do tak malého prostoru vtěsnat všechny potřebné ovladače. GRUB2, který nahradil původní verzi, používá podobný modulární koncept, jako linuxový kernel.

Na začátku disku je uloženo jádro s ovladači pro zpřístupnění adresáře s moduly a pak už lze natahovat moduly podle potřeby buďto manuálně z prostředí integrovaného shellu, nebo prostřednictvím konfiguračního souboru grub.cfg (GRUB legacy používal menu.lst).

Integrovaný shell, který umožňuje v průběhu zaváděcího procesu opravit případnou chybu v konfiguračním souboru je jednou z největších výhod zavaděče GRUB.


Upozornění Pokud se používá pro zavádění BIOS je třeba hlídat aby:
  1. sahal při zavádění na správné zařízení
  2. použité disky ve stroji neměly v MBR nějaké zbytkové záznamy, které by mohly zmást BIOS
  3. byla po aktualizaci zavaděče aktualizována také část, která je uložena na blokovém zařízení, odkud ji načítá BIOS

Chybí-li ovladač v jádře zavaděče, potřebný pro zpřístupnění souborového systému se zbývajícímu moduly, pak nelze problém vyřešit jinak, než pomocí zavedení prostřednictvím jiného zavaděče a nebo opravou záznamu na blokovém zařízení přes chroot z prostředí linuxového live CD.

Zavádění po síti

Zavádění po síti má oproti lokálnímu zavádění především tu výhodu, že můžeme operativně měnit zaváděné soubory, aniž by bylo nutné cokoliv instalovat na lokální stanici či server. Využívá se při tom tzv. Preboot Execution Environment – PXE, což je funkcionalita firmware síťové karty. Podmínkou je ale funkční a spolehlivá síť.

Funguje to tak že PXE pošle do sítě dotaz, na který mu odpoví DHCP server a spolu s konfigurací sítě mu zároveň cestku k binárnímu souboru zavaděče, který pak BIOS (UEFI) natáhne do paměti.

Pochopitelně musí jít o zavaděč, který má rovněž podporu PXE. Bez toho by bylo možné zavést systém pouze z lokálního disku. Použitelné jsou v současné době tyto tři:

PXELINUX
Má podporu pro zavádění jak z Legacy PXE (pxelinux.0), tak UEFI PXE (syslinux.efi). Výhodné při tom je, že oba typy zavaděče pracují se stejnými konfiguračními soubory. Problém je ale při zavádění přes UEFI, protože nemá (psáno v září 2019) podporu pro skriptování a neumí vypnínat stroje přes ACPI.
GRUB2
Soubor grubnetx64.efi zavedený přes UEFI PXE umí vypínat stroje přes ACPI. Protože má GRUB2 podporu skriptování, lze používat jeden konfigurační soubor a při tom mít zároveň všechny výhody, které má jeho lokální instalace. Bohužel má i svoje nectnosti – výstup z konzole je při zavádění přesměrován kamsi a pokud během zavádění jádra skončíte v ramdisku máte smůlu, protože se na konzoli – přes kterou by bylo možné problém opravit – nedostanete.
iPXE
Kombinuje výhody PXELINUXU a GRUBU. A v případě disklessových laboratoří lze brát jako výhodu i fakt, že u něj nelze proces zavádění přerušit a jádru předhodit jiné (nežádoucí) paramery.

Zavedení jádra

Zavaděč si lze představit jako jednoúčelový operační systém, který má za úkol:

  1. rozbalit jádro operačního systému do paměti počítače..
  2. spustit ho..
  3. ..a přitom mu předat případné parametry.

Jádro je spustitelný binární soubor, který poběží - na rozdíl od zavaděče - po celý čas běhu operačního systému.

Zavaděč jádro z úložiště přebírá ve formě samorozbalovacího archívu, jehož jméno podle obvyklého úzu začíná řetězcem vmlinuz. Toto pojmenování je závazné pouze do té míry, aby ho pod ním zavaděč v úložišti našel.

Jádro se rozbalí do paměti a od zavaděče převezme případné konfigurační volby. Pak se pokusí identifikovat a připojit zařízení, na kterém má být uložen operační systém a z něj spustit spouštěč dalších procesů - init. Aby to bylo možné, musí mít k dispozici všechny potřebné ovladače. Aby se jádro obešlo bez ramdisku, musí mít v sobě tyto ovladače zakompilované na tvrdo.

U distribucí, které si kompilují jádro na míru podle aktuální konfigurace stroje, s tím obvykle není problém. Ovšem u distribucí, které mají mít univerzální použití, by optimální konfigurace takového jádra mohla představovat neřešitelný problém. Proto se u nich využívá při zavádění tzv. RAM disk.

Monolitické jádro
Nepotřebuje ramdisk protože je kompilované na míru a obsahuje pouze potřebné ovladače. Má výhodu v tom, že může být lépe optimalizované a tím pádem i o něco výkonnější.
Modulární jádro
Nemusí využívat ramdisk, pokud má potřebné ovladače k dispozici. Výhoda ramdisku je však v tom, že mu stačí pouze základní ovladače potřebné pro rozbalení a připojení ramdisku. Ostatní ovladače jsou uloženy ve formě jaderných modulů v ramdisku a do jádra se zavádějí pouze v případě že jsou zatřebí. Takové jádro je tedy mnohem více flexibilní z hlediska diverzity hardware.


Prostředí RAM disku

Technicky vzato je to další kus operační paměti (odtud RAM disk), do kterého si jádro vybalí z komprimovaného cpio archívu minimalistické linuxové prostředí, se kterým pracuje stejně, jako by šlo o normální systémový disk. Tzn. že si ho připojí, a pak z něj spustí binární spouštěč procesů busybox, kterému předhodí hlavní shellový skript init, který poté postupně natahuje a spouští další skripty.

Z hlediska zavádění systému je klíčové, že skript init obsahuje podmínky, které umožňují proces zavádění cíleně přerušit.

K takovému přerušení zaváděcího procesu obvykle dojde, pokud některý ze spouštěných skriptů vrátí chybu.

Prostředí a nástroje v ramdisku, umožňují zjistit příčinu chyby a případně provést její opravu. Někdy ale může být v okamžiku selhání příliš pozdě na to zjišťovat kde je problém, proto je dobré vědět:

  1. Jak cíleně přerušit zaváděcí proces v určité fázi zavádění
  2. Jak konfigurovat síťová rozhraní v prostředí ramdisku
  3. Jak rozbalit nebo vypsat obsah ramdisku
  4. Jak zabalit upravený obsah ramdisku
  5. Jak korektně do ramdisku přidat chybějící jaderné moduly a další užitečné nástroje
  6. Jak přidat do ramdisku vlastní skripty a tím ovlivnit proces zavedení systému
  7. Jak restartovat stroj z prostředí ramdisku
  8. Jakým způsobem v ramdisku řešit problémy se zaváděním systému

Klíčové skripty

V rámci naší disklessové infrastruktury se za posledních 15 let vyskytlo několik modifikací distribučních verzí skriptů, proto následující tabulky uvádí kontrolní součty, které umožňují vyhledání konkrétní verze skriptu init pomocí následujícího příkazu:

# find ./ -type f -name 'init' -exec md5sum '{}' \; -exec ls -al '{}' \; | grep <MD5SUM>

init

Hlavní skript spouštěný jádrem po rozbalení ramdisku

MD5SUM Velikost kB Datum vytvoření
102f86435e01d930d739e75a9be7c6fc 6428 2011-05-11 distribuční skript (squeeze 6.0.5)
c651762e1dba95073c21194a719be270 7057 2015-03-01 distribuční skript (jessie 8.3)
8193a4d31e802b0bf7fa00b96b6f1bc7 6202 2018-07-18 distribuční skript (buster)
266cf6f69434699cd267f3bf262fbba8 6351 2019-08-21
875eeb7c90341a91ef50a1343031d3be 6336 2020-01-18
9b0cd0efd99138e771725a1754e477d7 6277 2020-09-11 distribuční (bullseye/sid)
47ab816fb90d758c62c8b9129a24f333 6301 2021-01-14 distribuční skript (bullseye 11.7)
cecb7af519da107bdd01365b55160331 6556 2022-04-10 distribuční skript (bookworm/sid) (aktivně se používá)

scripts/functions

Pomocný skript, který obsahuje funkce volané ostatními skripty:

maybe_break
Přeruší zavádění pokud předaný řetězec odpovídá obsahu proměnné break=, předané jádru při jeho spuštění
run_scripts
Načítá a spouští další skripty z adresáře předaného coby parametr. Např. fáze top začíná zpracováním skriptů z adresáře scripts/init-top

Pro diskless bylo nutné vytvořit upravenou verzi (od 21.8.2019) protože distribuční kód má ve funkci panic() kód, který v případě, že se vyskytne chyba jádra (kernel panic) a nemá žádnou hodnotu pro parametr panic= udělá halt, nikoliv reboot. Což je u disklessu, který má své systémové soubory sdílené po síti stav nežádoucí, protože buď zůstane viset nedostupný v prostředí ramdisku, nebo udělá halt a zůstane vypnutý. Což vede opět k tomu, že zůstane nedostupný – pokud není připojený ethernetem s podporou probuzení přes „magic packet”.

MD5SUM Velikost kB Datum vytvoření
79edcc22d4ca90b48f90e57aa5e0a60d 8143 2011-05-11 distribuční skript (squeeze 6.0.5)
d0e5601f769bb18d46bc833f8b00b072 9122 2015-04-12 distribuční skript (jessie 8.3)
24be99540ea22348611150811a575e78 9581 2018-07-25 distribuční skript (buster)
d7e8d4883588255e0219c0928d76f54b 9831 2019-08-21 upravený skript, odvozený od verze bullseye/sid
33b9c89850730e911bb3e34b2fbbb986 10403 2020-09-11 distribuční skript (bullseye/sid)
a742d64f285c460f28cc1ad3f3e321ae 11152 2021-01-14 distribuční skript (bullseye 11.7)
d15457b1a4bc6bfd895f9bde06b423d5 12169 2022-04-10 distribuční skript (bookworm/sid)

skripts/local

Spouští skripty které mají připravit zavedení systému z lokálního blokového zařízení.

skripts/nfs

Volá skripty, které mají zajistit zavedení systému z adresáře připojeného přes NFS. Standardní distribuční skript ovšem obsahuje několik funkcí, které komplikují sestavení sendviče. Konkrétně je o funkce:

nfs_mount_root_impl
nfs_mount_root
nfs_mount_fs_impl
nfs_mount_fs

Proto nelze distribuční skript pro sestavení ramdisku použít. Upravenou verzi stačí umístit do adresáře /etc/initramfs-tools/scripts/ na stroji kde se generuje ramdisk pro diskless s overlayem.

MD5SUM Velikost kB Datum vytvoření
3d09fc9676c55eba9834e3b60d968f3b 1778 2010-08-25 distribuční skript (squeeze 6.0.5)
32ad4b2abb6cf9b4f3b102669b9c0462 3058 2015-03-01 distribuční skript (jessie 8.3)
b469c0d91b27ed85f3c805874017f975 3050 2016-02-20 (24.10.2018) distribuční skript (buster)
17a71cf22247962bdde71d6993c8379d 5561 2019-09-07 vývojová verze pro diskless, která ještě obsahovala výše uvedené funkce
d242cf4bc681fb8877004103285acd8f 3143 2019-09-10 finální verze pro diskless, která se stále aktivně používá
c5d60b2a64c57df7937dc0d228f03e85 3093 2021-01-14 distribuční skript (bullseye)
b0f02ad90c2b79a682ada17d81cc749c 3103 2022-04-10 distribuční skript (bookworm)

Přerušení zaváděcího procesu

Zaváděcí proces postupně prochází následující fáze:

top
Parametr top vyvolá přerušení zaváděcího procesu před spuštěním prvního skriptu. Jediná operace, která se realizuje dřív, je načtení obsahu paměti z disku - pokud byl systém při předchozím spuštění uspán na disk.
modules
Parametrem modules se přeruší zavádění předtím, než init zavolá funkci load_modules, která zavádí do jádra další moduly, které nemusí být bezprostředně vázané na konkrétní hardware, ale mohou být kupř. nezbytné pro další zavádění systému.
premount
K přerušení dojde v okamžiku, než se spustí proces přípravy na to, aby ho bylo možné připojit zařízení na kterém je uložen systém.
mount
K přerušení dojde před připojením zařízení na kterém je uložen systém.
bottom
Zaváděcí proces je přerušen po připojení zařízení. Kdy jsou už aplikovány skripty pro tuto fázi
init
Poslední přerušení těsně před tím než dojde k přepnutí do finálního systému, ze kterého se pak spustí /sbin/init

Přerušit ho lze tím, že jádru předá při zavádění volba break:

vmlinuz... break ...

Že jsme se ocitli v prostředí ramdisku se pozná tak, že se místo obvyklého přihlášení rovnou objeví příkazový řádek:

(initramfs)

Jaké proměnné byly předány při spuštění jádra si lze vypsat (pokud je součástí ramdisku busybox) příkazem env. Pokud byl předán pouze parametr break bez bližšího upřesnění fáze, nastaví init jako výchozí hodnotu premount. Má to tedy stejný efekt, jako kdybychom jádru předali toto:

vmlinuz... break=premount ...

Zaváděcí proces bude přerušen ve fázi premount. Odesláním příkazu exit opustíte příkazový řádek ramdisku a zaváděcí proces bude dále pokračovat.

Upozornění Po spuštění init skriptu již nelze měnit obsah proměnných předaných na příkazové řádce jádru. Tudíž není možné postupovat od fáze k fázi tím, že by se postupně měnila hodnota proměnné break.

Jak debugovat skripty v ramdisku

Pokud máte spouštění v některé fázi přerušeno, můžete skripty debugovat s využitím funkcí ramdisku. Na začátek testovaného spriptu přidejte následující kód:

. /scripts/functions

Tím do vašeho skriptu načtete standardní funkce ramdisku. Pokud chcete v některém míste jeho běh přerušit, přidejte na příslušné místo:

panic "Info text"

Při vykonávání skriptu dojde k přerušení a otevření nové instance konzole. Že jste na nově otevřeném shellu můžete poznat podle chybové hlášky:

sh: cant't access tty: job control turned off

Vyskočíte z ní příkazem exit, stejným způsobem jako když pokračujete ve zpracování výchozího init skriptu.

Chcete-li zpracování skriptu přerušit v konkrétním místě a dodatečně zjistit kde, umístěte ve skriptu za příkaz exit číselným kód. Co se vrátilo vypíšete přes:

echo $?

Více viz manuál pro dash.

busybox

Je binární aplikace, která má v sobě zakompilovanou sadu potřebných nástrojů pro práci v ramdisku, pro které ve spuštěném systému existují plnotučné verze.

Pokud si vylistujete obsah adresáře /bin v ramdisku, zjistíte že obsahuje řadu souborů – ve skutečnosti je většina z nich pouhým hardlinkem na busybox, což zjistíte při pozornějším pohledu na výpis:

(initramfs) ls -al /bin/
…
-rwxr-xr-x  247 0        0           707288 Apr  1 05:17 ash
-rwxr-xr-x  247 0        0           707288 Apr  1 05:17 awk
-rwxr-xr-x  247 0        0           707288 Apr  1 05:17 basename
-rwxr-xr-x    1 0        0          1168776 Apr 18 04:12 bash
…

Číslo 247 signalizuje, že soubory 'basename', 'awk' nebo 'ash' jsou ve skutečnosti jména hardlinků, pro jeden a ten samý soubor – busybox. Tyto hardlinky nástroj initramfs-tools vygeneruje jen pokud do ramdisku nepřidáte jejich plnotučnou verzi. Ta pochopitelně dostane přednost. Jako kupř. 'bash' – pro je u něj číslo 1. Ten toho umí pochopitelně mnohem víc 'ash', který je součástí busyboxu, ale taky zabírá mnohem víc místa.

Důležité je mít na paměti, že nástroje z busyboxu jsou očesané na funkční minimum, takže některé věci přes ně není možné realizovat. Na druhou stranu busybox můžete použít v situaci, kdy vám specializovaný nástroj chybí. Např. pokud potřebujete stáhnout soubor přes TFTP a nemáte nainstalovaného tftp klienta.

~# busybox tftp -g -r turtle.conf 192.168.210.5
Poznámka Příkaz tftp z busyboxu můžete využívat také k odeslání upravených spouštěcích skriptů z ramdisku na DHCP server:
tftp -p -l soubor_k_odeslani -r cilovy_soubor <adresa TFTP serveru>

Pamatuje ale na to, že přes TFTP půjde odeslat soubor pouze do adresáře kam lze zapisovat, případně do souboru, který na TFTP severu již existuje a je do něj možné zapisovat.

Co všechno vaše verze busyboxu podporuje se dozvíte pokud mu předáte parametr --help:

~# busybox --help

Mimo jiné obsahuje také interpreter ash, který zpracovává skript init. Ten má podobné funkcionality jako bash, ale v některých detailech se může lišit. Více se dozvíte na stránce https://busybox.net/downloads/BusyBox.html

Konfigurace sítě

K nastavení sítě v prostředí ramdisku lze použít nástroj ipconfig (součást balíku klibc-utils), který používají skripty z initramfs-tools nebo utilitu ip z balíku iproute2.

ipconfig

Upozornění Pozor ipconfig je binární nástroj, který umožňuje staticky nebo s využitím DHCP[1] nastavit pouze IPv4 adresu!

Tento nástroj volají shellové funkce, které používá u bezdiskového systému skript /scripts/nfs během fáze mount. Nastavuje jím síťová rozhraní podle parametrů předaných jádru těsně předtím, než se pokusí o namountování systémového disku publikovaného přes NFS server.

Konfigurace pro rozhraní, přes které má být NFS připojeno, se předává jádru prefixem "ip=" nebo "nfsaddrs=". Z hlediska použití mezi nimi není rozdíl, neboť v případě "nfsaddrs=" jde pouze o relikt. Výsledek je v obou případech stejný.

Jaký je aktuální obsah proměnných ramdisku si lze ověřit příkazem env. Nastavení sítě pak můžeme provést předáním proměnné utilitě ipconfig.

(initramfs) ipconfig ${ip}

Pokud chceme použít jiné nastavení, než je obsahem této proměnné, můžeme ho vypsat manuálně. Syntaxe je stejná, jako když se předává parametrem "ip=" jádru při zavádění.

(initramfs) ipconfig -t 2 147.32.87.200:147.32.87.255:147.32.87.129:255.255.255.128:diskless:eth0:none
IP-Config: eth0 hardware address be:be:be:01:71:12 mtu 1500
IP-Config: eth0 guessed broadcast address 147.32,87,255
IP-Config: eth0 complete (from 147.32.87.255):
address: 147.32.87.200     broadcast: 147.32.87.255    netmask: 255.255.255.128
gateway: 147.32.87.200     dns0     :   0.0.0.0        dns1
host   : diskless
rootserver: 147.32.87.255 rootpath:
filename  :

Zda-li došlo k nastavení síťového rozhraní můžeme zjistit příkazy ifconfig nebo ip

147.32.87.200 - IPv4 adresa stroje
147.32.87.255 - IPv4 adresa NFS serveru (broadcast)
147.32.87.129 - gateway
255.255.255.128 - maska
diskless - hostname stroje
eth0 - síťové rozhraní
none - použitá metoda
Poznámka Chybí-li nastavení pro lo zařízení, lze provést konfiguraci takto:
(initramfs) ipconfig 127.0.0.1:::::lo:none

Nastavení IPv4 adresy síťového rozhraní s využitím DHCP

(initramfs) ipconfig -t 2 -c dhcp eth0

Nastavení IPv4 adres na více síťových rozhraní současně

(initramfs) ipconfig -c any eth0 eth1 192.168.1.1:::::eth2:none
 

ip

Protože ipconfig umí pracovat pouze s IPv4 je nutné pro nastavení IPv6 adresy použít utilitu ip (součást balíku iproute2)

(initramfs) ip route add 2001:718:2:1612::/64 dev eth0
(initramfs) ip route add fe80::/64 dev eth0
(initramfs) ip route add default via fe80::7272:cfff:fe1b:d243 dev eth0
(initramfs) ip -6 route

Tuto utilitu by pochopitelně bylo možné použít také u diskless strojů. Vyžadovalo by to však manuální úpravu skriptů v ramdisku. Nicméně pro úplnost uvádím také použití této utility pro nastavení IPv4 adresy

(initramfs) ip address add 147.32.87.216/25 dev eth0
(initramfs) ip link set eth0 up
(initramfs) ip route add default via 147.32.87.129 dev eth0

Připojení může selhat také kvůli některé z následujících příčin:

  • v jádře i ramdisku může chybět ovladač síťového rozhraní - v takovém případě je ho třeba přidat do souboru /etc/initramfs-tools/modules a poté aktualizovat ramdisk
  • konfigurované síťové rozhraní také může být omylem připojeno do subnetu, který nemá přístup na vzdálené úložiště
  • také může být úříčina v konfiguraci vzdáleného úložiště, kdy pro IP adresu klientské stanice nemusí být povolen přístup
  • chyba se může také vyskytnout v konfiguraci pro síťové zařízení, nebo v nastavení pro připojení vzdáleného zařízení (NFS, iSCSI, NBD,...).

NFS

Pokud se používá systémový adresář sdílený z NFS serveru, stará se v ramdisku o jeho připojení skript /scripts/nfs, který je součástí instalačního balíčku initramfs-tools. V systému ho však nenajdete mezi skripty v adresáři /etc/initramfs-tools, nýbrž v /usr/share/initramfs-tools/scripts.

Poznámka U běžných blokových zařízení jádro před jejich připojením kontroluje integritu souborového systému. Většina z klasických souborových systémů vyžaduje aby během kontroly zařízení nebylo pokud možno připojené, nebo když už, tak na něj nebyl povolen zápis - proto se jádru předává také parametr ro který zajistí aby bylo v ramdisku zařízení připojeno jen pro čtení.

U připojení přes NFS se žádná kontrola neprovádí, takže je tento parametr většinou zbytečný.

Skript /scripts/nfs volá binární utilitu nfsmount, které obsah proměnné nfsroot, která se jádru předává ještě před začátkem zavádění jako parametr.

nfsroot=<IP adresa NFS serveru>:<publikovaný adresář>[,<další parametry NFS připojení>]

Pokud se vyskytne nějaký problém při mountování NFS adresáře, zůstane jádro "viset" na opakovaných pokusech o jeho připojení. Ve skriptu je nastaveno 180 pokusů o připojení se sekundovou prodlevou, takže zhruba po třech minutách, pokud se připojení nezdaří zkusí jádro připojit adresář /tftpboot/%s a pokud ani to neprojde, zůstanete v prostředí ramdisku.

Příčiny selhání mohou být různé:

  • chybná adresu NFS serveru
  • chyba v cestě ke vzdálenému adresáři
  • špatně nastavená přístupová práva na NFS serveru
Upozornění Utilita nfsmount umí komunikovat pouze jako NFSv3, protože v prostředí ramdisku neběží démoni potřební pro autorizaci a šifrovanou komunikaci!

Aby bylo možné zjistit co je špatně, lze jádru předat parametr - nfsrootdebug - který zajistí, že se během zavádění do logu umístěného v /tmp/initramfs zapíší informace, které umožní zjistit co se dělo a na jejich základě odhalit chybu.

Poznámka U Debianu který postupně přechází na systemd se můžete setkat s tím, že se během bezdiskového zavádění systém opakovaně pokouší namountovat systémový disk z NFS. Na straně NFS serveru je vidět že se klient opakovaně připojuje a odpojuje. Nakonec ale vše naběhne jak má. Může za to pravděpodobně démon /lib/systemd-udevd, že původní test kterým se ve skriptu /scripts/nfs kontrolovalo zda-li je vzdálený adresář připojen přestal fungovat.

V repozitáři intramfs-tools již je od 3.června 2014 oprava. Distribuční verze balíku initramfs-tools 0.116 už je opravena:

diff --git a/scripts/nfs b/scripts/nfs
index 6fa0c43..967e67f 100644
--- a/scripts/nfs
+++ b/scripts/nfs
@@ -66,7 +66,8 @@ mountroot()
 
 	# loop until nfsmount succeeds
 	do_nfsmount
-	while [ ${retry_nr} -lt ${delay} ] && [ ! -e ${rootmnt}${init} ]; do
+	while [ ${retry_nr} -lt ${delay} ] \
+		&& ! chroot "${rootmnt}" test -x "${init}" ; do
 		[ "$quiet" != "y" ] && log_begin_msg "Retrying nfs mount"
 		/bin/sleep 1
 		do_nfsmount

Řízení zaváděcího procesu v ramdisku

Skript init, který je v kořeni ramdisku, se generuje na základě konfigurace v souboru /etc/initramfs-tools/initramfs.conf při sestavení souboru initrd. Ostatní skripty, které má ramdisk v adresáři /scripts, se při jeho sestavení, či aktualizaci kopírují z adresáře /etc/initramfs-tools/scripts.

top

Během této fáze jádro provádí inicializaci existujících fyzických zařízení. Ty se přitom ohlašují svým specifickým kódem, na jehož základě jádro provádí identifikaci a natažení příslušného ovladače.

Protože k zařízení může existovat více ovladačů, které pochopitelně nelze používat současně, je součástí ramdisku adresář /etc/modprobe.d, s konfiguračními soubory k jaderným modulům, ve kterých může být použití konfliktních modulů zakázáno.

Může se však vyskytnout situace, kdy některý z automaticky natahovaných modulů obsahuje chybu, která vede ke hroucení jádra. Pro takové případy akceptuje jádro seznam zakázaných modulů předaný zavaděčem prostřednictvím volby blacklist

vmlinuz... blacklist="xfs jfs ceph" ...

I když pak může být do určité míry systém omezen, můžeme jej dostat do stavu kdy se dá problém vyřešit.

Poznámka Tabulka, podle které jádro provádí tuto identifikaci se generuje před(!) sestavením, či aktualizací ramdisku příkazem depmod

modules

Seznam těchto modulů může být uložen rovnou v ramdisku - soubor /conf/modules, ale také předán jádru prostřednictvím zavaděče

vmlinuz... modules="aufs reisefs" ...

premount

Kromě jaderných modulů a skriptů, které řídí zaváděcí proces, obsahuje ramdisk také nástroje a utility, co umožňují zavést systém ze sdíleného NFS adresáře, sestavit RAID pole, nahodit LVM skupinu, používat kryptovaný souborový systém, opravit poškozený souborový systém aj.

Poznámka Protože v tomto bodě vzniká při zavádění nečastěji problém, je parametr #premount výchozím pro volbu break, je-li uvedena bez parametru.

mount

Cestu k zařízení, na kterém má být uložen zaváděný systém, jádru předává zavaděč jako parametr volby root.

vmlinuz... root=/dev/sda1 ...

Skript init hodnotu /dev/sda1 uloží do proměnné ${ROOT}, která se před přepnutím do finálního systému zruší. V prostředí ramdisku si lze vypsat přes echo.

Během předchozí fáze mělo být zařízení připraveno a nyní se ho init pokusí připojit. Pokud se mu to nedaří, umožňuje přerušení ve fázi #mount zjistit proč.

Nejprve je třeba zkontrolovat obsah proměnné ${ROOT}. Jeví-li se v pořádku, je třeba zjistit, zda-li příslušné zařízení skutečně existuje.

Pokud ne, tak to může znamenat že..

  • jádru chybí potřebný ovladač (po kompilaci modulu nebyl aplikován depmod a zaktualizován příslušný ramdisk)
  • nebo došlo k přejmenování zařízení (kupř. po přidání dalšího blokového zařízení do počítače, či při změně pořadí diskových oddílů)
  • nebo z nějakého důvodu nedošlo k sestavení příslušného blokového zařízení (v případě RAID pole či LVM skupiny)
Poznámka Některé zavaděče, jako např. GRUB, umožňují parametr volby root změnit ještě před spuštěním zaváděcího procesu, ale jsou situace, kdy to udělat nelze a je třeba úpravu provést v ramdisku.
Upozornění Jednou z příčin, proč se nepodaří zařízení připojit, může být poškozený souborový systém. Proto musí být součástí ramdisku také nástroje, které ho umožňují opravit.

Pokud v něm chybí, pak to znamená, že je během sestavení ramdisku neměl systém k dispozici.

V takovém případě je třeba nejprve souborový systém opravit jiným způsobem. A pak přes chroot provést aktualizaci ramdisku

bottom

Většinu souborových systémů nelze opravit, pokud se s nimi pracuje. Systém uložený v ramdisku je tak ideálním prostředím pro takové operaci, protože

init

Na závěr přepne do finálního systému a spustí z něj /sbin/init

Pro úspěšné zavedení systému je tak nutné odstavit služby, které by se při startu pokoušely o rekonfiguraci síťového rozhraní přes které bude probíhat komunikace se vzdáleným úložištěm. V takovém případě by se totiž mohlo stát, že by stroj zůstal "viset" během zaváděcího procesu v ramdisku, bez možnosti přerušení zaváděcího procesu.


Úpravy ramdisku

Pro práci s ramdiskem jsou určeny nástroje z balíku initramfs-tools. Ale lze si poradit i bez nástrojů z tohoto balíku - pomocí běžně dostupných systémových utilit.

Bezdiskový systém

Upozornění Pokud chceme provozovat linuxový systém jako bezdiskový, tak musíme zajistit, aby měl stroj v okamžiku, kdy se pokusí o připojení zařízení kde má uložen systémem, funkční síťové připojení.

Výpis obsahu ramdisku

Pro výpis obsahu ramdisku lze použít nástroj lsinitramfs. Pokud ale není k dispozici, stačí vědět, že ramdisk je v podstatě pouze zkomprimovaný cpio archív

zcat /boot/initrd.img-3.2.0-2-686-pae | cpio -t

Rozbalení ramdisku

Pokud si chceme důkladněji prozkoumat obsah ramdisku, tak si ho můžeme vybalit do samostatného adresáře

zcat /boot/initrd.img-3.7.7 | cpio -i

U novějších verzí jádra se nepoužívá ke kompresi gzip, ale Zstandard. V takovém případě je potřeba použít k rozbalení

zstd --decompress --stdout /boot/initrd.img-6.6.13-amd64 | cpio -i

Zabalení ramdisku

Po případných úpravách ho pak zase můžeme zabalit - pro jistotu do nového souboru

find . | cpio -o -H newc | gzip > /boot/initrd.img-3.7.7-upraveny

Korektní sestavení ramdisku u Debianu

Výše uvedené postupy je dobré znát, ale pro korektní sestavení ramdisku je rozumnější používat nástroj update-initramfs z balíku initramfs-tools. Ten totiž zajistí - na základě konfigurace - aby sestavený ramdisk obsahoval vše potřebné i po každé aktualizaci, bez toho, že by bylo nutné pokaždé vytvářet či upravovat prostředí ramdisku ručně.

Ostatně stejný nástroj se volá i při instalaci nového jádra.

Konfigurační soubory a skripty spojené se sestavením ramdisku, se v systému vyskytují na třech místech:

  1. Adresáře v /etc/initramfs-tools jsou určeny pro uživatelskou konfiguraci
  2. Do adresáře /usr/share/initramfs-tools umísťují své skripty instalační balíčky
  3. A do adresáře /var/lib/initramfs-tools se ukládá kontrolní součet (realizovaný přes shasum ) ramdisku sestaveného pomocí initramfs-tools


kmod - se stará o načítání jaderných modulů v ramdisku

  1. načte shellový skript /hook-functions (používá funkce zde uvedené)
  2. zavolá funkci copy_exec, (která udělá co?)
  3. zkopíruje nástroje modprobe a rmmod
  4. se stará o include jaderných modulů.

udev -

Struktura konfiguračních adresářů pro sestavení ramdisku

/conf.d
/hooks 
/scripts
	/init-bottom
	/init-top
	… 	
initramfs.conf
modules
update-initramfs.conf
initramfs.conf
Je konfigurační soubor, který použije mkinitramfs při sestavení ramdisku a nakopíruje do adresáře /conf (ramdisku). V něm je určeno, zda-li se má použít komprese, kolik procent dostupné paměti se má vyčlenit pro tmpfs, do kterého se vybalí obsah ramdisku, atp.
update-initramfs.conf
Je konfigurační soubor pro nástroj update-initramfs, který se volá automaticky kupř. při aktualizaci linuxového jádra. V něm lze nastavit, jestli se má původní ramdisk odzálohovat, nebo se má rovnou přepsat.
modules
Obsahuje seznam modulů, které mají začleněny do ramdisku. By default se totiž do ramdisku zahrnou jen ty moduly, které jsou zavedeny v jádře během sestavení ramdisku. Na to je třeba dát velký pozor! Protože pokud sestavíte nový ramdisk bez ovladače pro souborový FS na kterém bude systém s ostatními moduly jádra, tak se vám spouštění systému nepodaří opravit, pokud nebudete mít ještě jiné jádro s ramdiskem, které ho mít bude.
/hooks
Adresář s uživatelskými skripty, které se spouští při sestavení ramdisku. S jejich pomocí lze do něj přidat další aplikace a moduly. A pomocné funkce se postarají o to, aby byly splněny případné závislosti. Potřebné moduly v takovém případě nutné přidávat do souboru modules.
/scripts
Obsahuje podadresáře, ve kterých jsou skripty, které spouští výchozí init skript v různých fázích zaváděcího procesu.
Poznámka Všechny skripty, ať již v adresáři /hooks, nebo v podadresářích adresáře /scripts musí být spustitelné. Pokud na to zapomenete, sestavení neprojde podle vašich představ a příslušné soubory či moduly budou v ramdisku chybět!
Upozornění Od jádra verze >= 3.5 je modul nfs rozdělen na více modulů

Reboot z prostředí ramdisku

echo b > /proc/sysrq-trigger

Vypnutí z prostředí ramdisku:

echo o > /proc/sysrq-trigger

Monitorování a řešení problémů během zaváděcího procesu

Zaváděcí parametry jádra

S jakými parametry bylo linuxové jádro zavedeno, se dozvíte z obsahu souboru /proc/cmdline

Upozornění Obsah tohoto souboru může číst každý, proto by se při zavádění jádra neměly používat parametry, které by mohly vést ke kompromitaci systému, resp. jeho zneužití.

Debug ramdisku

vmlinuz... debug ...

Lokální přesměrování konzolového výstupu

vmlinuz... console=ttyS0 ...

Síťové přesměrování konzolového výstupu

Pokud má jádro zakompilovaný modul netconsole, je možné tento výstup odesílat po síti a zachytávat na vzdáleném stroji.

Po jeho zavedení se vytvoří zařízení /dev/kmsg, se kterým pracuje aplikace syslog, ovšem journald, který používá systemd načítá fata také z procesu /proc/ksmg. Rozdíl není jen v cestě.
  • Obsah /proc/kmsg je pouze read-only náhled jaderného zásobníku, který může číst pouze root.
  • Kdežto znakové zařízení /dev/kmsg umožňuje zapisovat i vlastní zprávy.


Zachytávání na straně cílového stroje

Než spustíte stroj, měla by na cílovém stroji na zvoleném portu naslouchat nějaká aplikace, která bude přijaté zprávy vypisovat na konzoli.

Pokud je to pouze nárazová záležitost, vystačíme si s utilitou nc (netcat)

nc -u -l -p 6666

Takto spuštěná začne naslouchat na UDP portu č. 6666 a zprávy začne vypisovat jakmile začnou chodit. Praktičtější ovšem je přijaté zprávy zapisovat paralelně do souboru, abychom si je mohli dodatečně prozkoumat.

nc -u -l -p 6666 -s 192.168.1.1 2>&1 | tee cesta/k/souboru/192_168_136_220.log

Zavedení a nastavení přes parametry jádra

Pokud známe předem hodnoty všech parametrů, můžeme je nastavit rovnou přes parametry jádra. Zprávy se začnou odesílat ihned po nahození síťového rozhraní:

vmlinuz... netconsole=6665@[IP stanice]/[síťové zařízení],6666@[IP vzdálené stanice]/[MAC síťového rozhraní vzdálené stanice]

U disklessové infrastruktury, kde se všechny stroje spouští přes jeden univerzální záznam zavaděče GRUB, jehož možnosti skriptování jsou omezené, to takto udělat nelze. Stroje běží v rámci různých subnetů, takže IPv4 a MAC adresa rozhraní cílového stroje není stejná. A navíc může mít každý stroj jiný název síťového zařízení přes které bude komunikovat.

Naštěstí lze odesílání logovacích zpráv zapnout i dodatečně z prostředí ramdisku, manuálně, nebo skriptem.

Aktivace vzdáleného logování v prostředí ramdisku

Jádro musí být zkompilováno s podporou následujících voleb:

  • CONFIGFS_FS
  • NETCONSOLE
  • NETCONSOLE_DYNAMIC
modprobe configfs
1, Modul configfs umožňuje manipulaci s objekty jádra z userspace prostředí ramdisku. Obvykle se mountuje na adresář /sys/kernel/config, ale může být namountován i na jiný přípojný bod (např. na adresář /config.
umount /sys/kernel/config 2> /dev/null
mount -t configfs none /sys/kernel/config  
2, Jakmile máme přístup k objektům jádra, můžeme, po zavedení modulu netconsole založit a nakonfigurovat nový objekt, dejme tomu s názvem hostname. Těch objektů může být nakonfigurováno více, takže by mělo být možné v případě potřeby distribuovat log na více cílových strojů
modprobe netconsole
mkdir  /sys/kernel/config/netconsole/hostname  
3, Po vytvoření adresáře hostname</hostname> se objeví "soubory", které obsahují výchozí konfigurační hodnoty, které bude potřeba, před zapnutím přepsat:
dev_name : eth0
enabled : 0
extended : 0
local_ip : 0.0.0.0
local_mac: ff:ff:ff:ff:ff:ff
local_port: 6665
remote_ip : 0.0.0.0
remote_mac: ff:ff:ff:ff:ff:ff
remote_port: 6665


echo "xx:xx:xx:xx:xx:xx" > /sys/kernel/config/netconsole/hostname/remote_mac  
echo 192.168.1.1 > /sys/kernel/config/netconsole/hostname/remote_ip  
echo 64001 >| /sys/kernel/config/netconsole/hostname/remote_port  
echo 192.168.1.2 > /sys/kernel/config/netconsole/hostname/local_ip  
echo 64001 > /sys/kernel/config/netconsole/hostname/local_port  
echo eth1 > /sys/kernel/config/netconsole/hostname/dev_name  
echo 1 > /sys/kernel/config/netconsole/hostname/enabled  
dmesg -n 8