This shows you the differences between two versions of the page.
|
si:laboratoare:08 [2023/12/03 22:16] florin.stancu |
— (current) | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Laboratorul 08. RootFS Bootstrapping ====== | ||
| - | |||
| - | ===== Crearea unei distribuții Linux ===== | ||
| - | |||
| - | Până acum am folosit Root Filesystem de-a gata pre-instalat. | ||
| - | |||
| - | La acest laborator, dorim să învățăm procesul de generare a unui sistem de bază și modalitățile de personalizare a acestuia, proces numit **rootfs bootstrapping**. | ||
| - | |||
| - | Avem 2 variante: | ||
| - | * plecarea de la o distribuție existentă și alegerea pachetelor pe care să le includem + configurații specifice; | ||
| - | * compilarea (manuală sau automată) a tuturor componentelor unui astfel de sistem; | ||
| - | |||
| - | Prima soluție, deși mult mai facilă (atât rapidă, cât și ușor de înțeles), nu ne permite o optimizare a spațiului utilizat (sisteme bazate pe Debian ocupă de ordinul ''~1GB'') și nici fine-tuningul ulterior pentru îmbunătățirea performanțelor în cazul rulării pe anumite dispozitive (e.g., ARMv8 cu extensii NEON pentru SIMD). | ||
| - | |||
| - | Pe de altă parte, dacă am dori să facem asta de la zero, ar trebui să plecăm de la un toolchain (pe care, de cele mai multe ori, trebuie să-l compilăm tot noi), pe care îl vom folosi să compilăm din cod sursă toate componentele necesare într-un sistem Linux: biblioteci standard (LibC), sistem de init (SystemV / SystemD / OpenRC etc.), suita de programe de bază Unix (e.g., ''mount'', ''cat'', ''ls'', ''cp'', ''sh'', ''awk'' și multe altele!), device manager (''udev'') precum și multe alte servicii (e.g., de networking) și aplicații necesare scopului nostru final (e.g., Python, X11 + QT Toolkit pentru interfețe grafice etc.). | ||
| - | |||
| - | Din fericire, există suite software ([[https://www.yoctoproject.org/|Yocto Linux]], [[https://buildroot.org/|Buildroot]]) care pot automatiza repetabil acest proces. Însă un dezavantaj tot rămâne: timpul de compilare este de ordinul orelor, iar spațiul pe disk necesar între ''20GB -- 50GB''. | ||
| - | |||
| - | |||
| - | ===== Exerciții ===== | ||
| - | |||
| - | Pentru acest laborator, vom încerca ambele abordări! | ||
| - | |||
| - | ==== 1. Crearea de distribuții bazate pe Debian (debootstrap) ==== | ||
| - | |||
| - | Pentru a genera distribuții personalizate bazate pe Debian (și derivate, e.g., Ubuntu), se poate folosi utilitarul ''debootstrap''. | ||
| - | |||
| - | Acesta descarcă automat din repository-uri pachetele ''.dpkg'', pe care le instalează într-un prefix (director) precizat de utilizator, e.g.: | ||
| - | <code shell> | ||
| - | $ sudo debootstrap --arch="arm64" --include="vim bash curl" \ | ||
| - | --variant=minbase "bookworm" "/root/mydebian/" "http://ftp.de.debian.org/debian" | ||
| - | </code> | ||
| - | |||
| - | <note tip> | ||
| - | Aproape toate distribuțiile de Linux au câte un utilitar (de multe ori, chiar package managerul distribuției) care permite bootstrappingul RootFS-urilor din pachete (tot așa sunt generate și imaginile ISO live!). | ||
| - | Spre exemplu, Arch Linux are ''pacstrap'', Alpine Linux are ''apk.static'' and so on... | ||
| - | </note> | ||
| - | |||
| - | De menționat ar fi că instalarea unui pachet are loc în 2 etape: | ||
| - | * decompresia arhivelor și extragerea fișierelor în directorul destinație; | ||
| - | * rularea unor scripturi post-install pentru crearea configurațiilor implicite / upgrade fișiere / integrarea cu alte aplicații / instalarea serviciilor etc.; | ||
| - | |||
| - | Acest aspect devine important când încercăm să generăm un rootfs pentru o arhitectură străină celei gazdă (cross-bootstrapping)! | ||
| - | Pentru a realiza acest lucru, va trebui să distingem cele două etape: decompresia va putea avea loc direct pe host (avem utilitarele necesare instalate cu scriptul de bootstrapping), pe când etapa de execuție a programelor va trebui să fie virtualizată cumva (chroot + ''qemu-user-static'' FTW). | ||
| - | |||
| - | Astfel, pentru a realiza ''debootstrap'' de pe o arhitectură Intel/AMD ''x86_64'' pentru o țintă AArch64 (ARM 64-bit), vom avea de realizat următoarele: | ||
| - | <code shell> | ||
| - | $ sudo debootstrap --foreign --arch="arm64" ... /root/mydebian ... # restul de argumente normale | ||
| - | # apoi copiem emulatorul în noua rădăcină (va fi rulat de binfmt_misc ca în Lab 04) | ||
| - | $ sudo cp -af "$(which qemu-aarch64-static)" /root/mydebian/usr/bin/ | ||
| - | # Acum va trebui să rulăm stagiul 2 al debootstrap, însă într-un container / chroot! | ||
| - | # Folosim systemd-nspawn pentru a rula binarul de second-stage instalat pe rootfs de la primul: | ||
| - | $ sudo systemd-nspawn -as-pid2 --resolv-conf=copy-host -D /root/mydebian \ | ||
| - | /debootstrap/debootstrap --second-stage --verbose | ||
| - | </code> | ||
| - | |||
| - | Tehnica cu systemd-nspawn + qemu-user-static (și binfmt_misc) este utilă pentru a intra într-un chroot pe rootfs-ul nostru pentru a-l personaliza (putem chiar și instala programe noi sau configura servicii etc.). Însă va trebui configurat gestionarul de pachete, ''apt'': de adăugat mirror-uri la repo-urile de bază în ''/etc/apt/sources.list'' -- [[https://wiki.debian.org/SourcesList#Example_sources.list|găsiți exemplu aici]]. | ||
| - | |||
| - | **Atenție:** momentan, rootfs-ul NU ARE parolă la contul ''root'' (și nu are nici un alt cont) și nu vă veți putea autentifica din niciun //tty//! | ||
| - | Se recomandă accesarea unui chroot (rulați ''/usr/bin/bash'' ca ultim argument al lui ''systemd-nspawn'') și setarea uneia prin ''passwd''. | ||
| - | |||
| - | O dată generat rootfs-ul, acesta trebuie copiat (atenție să nu se piardă permisiunile și atributele unor fișiere speciale -- mai ales symlink-urile) pe o partiție și bootat! Se recomandă arhivarea acestuia folosind utilitarul ''tar'' (acesta salvând automat metadatele necesare în arhivă):<code shell> | ||
| - | $ tar czf mydebian.tar.gz -C /root/mydebian | ||
| - | </code> | ||
| - | |||
| - | În final, dorim să vedem cât ocupă rootfs-ul nostru: | ||
| - | <code shell> | ||
| - | $ du -hs /root/mydebian | ||
| - | </code> | ||
| - | |||
| - | <note tip> | ||
| - | Vom trece să instalăm + rulăm sistemul pe un Raspberry PI 4 fizic imediat ce pornim compilarea unui sistem Buildroot ;) | ||
| - | </note> | ||
| - | |||
| - | ==== 2. Compilarea programelor de bază (buildroot) ==== | ||
| - | |||
| - | Mai departe, descriem procesul de utilizare al unui utilitar destul de popular (cel mai important fiind ușurința de învățare față de Yocto Linux). | ||
| - | |||
| - | <note warning> | ||
| - | Din păcate, vom avea nevoie de aproximativ ''~20-30GB'' de spațiu disponibil (în mașina virtuală). | ||
| - | |||
| - | Cel mai simplu este să creați un Virtual HDD nou de ''30GB'' (**nu îi pre-alocați** spațiul, deoarece nu va ajunge să ocupe atât azi -- poate la temă :P ) și atașați-l proiectului VirtualBox / VMWare ca dispozitiv de stocare suplimentar (din setări / storage, add [hard] disk pe ambele hipervizoare). | ||
| - | Apoi va trebui formatat + montat din VM: ''mkdir /media/big && mkfs.ext4 /dev/sd<X> && mount /dev/sd<X> /media/big''. | ||
| - | O idee bună ar mai fă să creați director de lucru pentru ''student'': ''mkdir /media/big/work && chown student:student /media/big/work -R''. | ||
| - | |||
| - | De asemenea, tot acum ar fi momentul să alocați memorie RAM mai multă (3-6GB, dacă aveți de unde) aproape câte nuclee aveți procesorului virtualizat (să se paralelizeze procesul). | ||
| - | Și să conectați laptop-ul la 230V ;) | ||
| - | </note> | ||
| - | |||
| - | Spre deosebire de ''debootstrap'', care avea nevoie de drepturi de ''root'', aceste utilitare folosesc principiul de ''fake root'' (metadatele sistemului de fișiere sunt virtualizate într-o bază de date externă kernelului), fiind utilizabile (uneori obligatoriu) și de către conturi non-superuser. Aproape toți pașii va trebui să-i urmați autentificați ca ''student''! | ||
| - | |||
| - | Primul pas, ar fi, desigur descărcarea lui ''buildroot''. Alegeți o versiune de la [[https://buildroot.org/downloads/]] și descărcați-o + dezarhivați-o (sub contul student și pe HDD-ul mare!)<code shell> | ||
| - | $ wget https://buildroot.org/downloads/buildroot-2023.08.tar.xz | ||
| - | $ tar xf ... | ||
| - | $ cd buildroot-* | ||
| - | </code> | ||
| - | |||
| - | Utilitarul Buildroot folosește mecanismul Kconfig (la fel ca U-Boot și kernel-ul Linux), deci: ''make menuconfig''. | ||
| - | Navigați prin meniuri și schimbați următoarele opțiuni: | ||
| - | |||
| - | * ''Target Options'' => ''Target Architecture'' => ''AArch64 (little endian)''; | ||
| - | * ''Target Options'' => ''Target Architecture Variant'' => ''cortex-A53'' (pentru compatibilitate sporită cu mai multe modele Raspberry PI + Qemu); | ||
| - | * ''Target Options'' => ''Floating point strategy'' => ''VFPv4'' (la fel, compatibilitate sporită); | ||
| - | * ''Toolchain'' => ''Enable compatibility shims ...''; | ||
| - | * ''System Configuration'' => ''Root password'' => ''student'' -- pentru a ne putea autentifica în shell! | ||
| - | * ''Filesystem images'' => ''Compression method'' => ''gzip'' -- opțional, imaginea ocupă extrem de puțin oricum. | ||
| - | |||
| - | Apoi rulați ''make''! Procesul poate dura între 10 minute și 1h, în funcție de numărul de nuclee, frecvența lor (ce poate fi scăzută dacă sunteți pe baterie) și memoria RAM disponibilă. | ||
| - | |||
| - | <note tip> | ||
| - | Distribuția de bază Buildroot include doar utilitarele ce vin cu [[https://busybox.net/|busybox]]. | ||
| - | Aveți, însă, o mulțime de pachete de aplicații pe care le puteți bifa la secțiunea ''Target packages''! | ||
| - | |||
| - | Însă acest lucru va duce la o mărire drastică a timpului de compilare + consum de spațiu pe disk (în funcție de ce / câte pachete + câte dependințe au)! | ||
| - | </note> | ||
| - | |||
| - | **Nu așteptați! lăsați-l să ruleze și treceți la task-ul următor ;)** | ||
| - | |||
| - | La final, să inspectați sistemul de fișiere rezultat, îl găsiți la calea ''<buildroot-dir>/output/images/''. Cât ocupă? | ||
| - | |||
| - | ==== 3. Instalarea rootfs-ului în imaginea RPI 4 ==== | ||
| - | |||
| - | **0.** Descărcați [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts/releases|de aici imaginea rpi-full.img.tar.xz]]. | ||
| - | |||
| - | **1.** Trebuie să montăm imaginea și să copiem noul RootFs pe cea de a doua partiție (pe care o vom formata): | ||
| - | |||
| - | * Dezarhivați fișierul imaginii ''rpi-full.img''; ar trebui să obțineți imaginea de ''2GB''; | ||
| - | * Verificați că există ambele partiții: ''parted rpi-full.img print''; | ||
| - | * Conectați imaginea la un dispozitiv de tip bloc folosind ''losetup /dev/loop0 rpi-full.img'' (verificați cu ''losetup -L'' ce aveți conectat dacă întâmpinați probleme); | ||
| - | * Formatați sistemul de fișiere a celei de-a doua partiții în ext4: ''mkfs.ext4 -L RPI_ROOTFS /dev/loop0p2''; | ||
| - | * Montați noua partiție goală ''ext4'' (puteți folosi ''/mnt'' ca mountpoint) și copiați conținutul arhivei rootfs generate anterior (ori cea de Debian, ori cea obținută prin Buildroot): <code shell> | ||
| - | tar xf <ARCHIVE_FILE> -C <DEST_MOUNTPOINT> | ||
| - | # inspectați calea dezarhivată: | ||
| - | ls -l <DEST_MOUNTPOINT> | ||
| - | </code> | ||
| - | * Demontați partiția ext4 și deconectați dispozitivul ''loop'' folosite! | ||
| - | |||
| - | **2.** Porniți Raspberry PI-ul în u-boot, apoi intrați în modul ''ums mmc X'' (vedeți [[:si:laboratoare/05|laboratorul 5]]); folosiți ori ''dd'', ori un utilitar de Raw Disk Imager pentru Windows (recomandat: Raspberry PI Imager) ca la laboratoarele anterioare pentru a scrie noua imagine pe RPI. | ||
| - | |||
| - | <note important> | ||
| - | * **Atenție**: Va trebui, mai întâi, să aduceți imaginea pe Windows (folosiți ''scp'' cu ''username@<ip_sursa>:/cale/catre/fisier.img'' ca prim argument, iar, ca destinație, o cale de pe PC-ul fizic, e.g., ''C:\Users\..etc'')! | ||
| - | * **Notă pentru VirtualBox**: va trebui să folosiți argumentul ''-P 2023'' și ''student@localhost:<cale>'' ca sursă. | ||
| - | * **Notă pentru cei cu gazda pe Linux**: dacă utilizați ''dd'', va trebui să rulați comanda ''sync'' și să așteptați să se termine! Asta e important deoarece kernelul modern cache-uiește în memoria RAM și scrierea se termină, aproape instant (însă rămâne să se copieze pe SD în background, această operațiune fiind mult mai lentă). | ||
| - | </note> | ||
| - | |||
| - | 3. Reporniți Raspberry PI-ul. Lăsați-l să booteze normal, ar trebui să pornească kernel-ul implicit cu rootfs-ul vostru! | ||
| - | |||
| - | * Inspectați-l! Cât ocupă pe disk (''df'')? | ||
| - | * Dacă vă printează mesajul cu ''waiting for device /dev/mmcblk0p2'' și nu se termină procesul, verificați dacă partiția a doua există din u-boot (''part list mmc 0''). | ||
| - | |||
| - | ==== 4. [BONUS] Rulați imaginea voastră în QEmu (system) ==== | ||
| - | |||
| - | Observăm că procesul de testare a unei imagini embedded consumă ceva timp (mai ales la scrierea pe cardul SD). | ||
| - | Dorim să optimizăm! | ||
| - | |||
| - | Rulați imaginea obținută anterior (va trebui să exrageți kernel-ul din imagine!) prin ''qemu-system-aarch64''. | ||
| - | Re-vedeți instrucțiunile din [[:si:laboratoare/04|laboratorul 4]]. | ||
| - | |||
| - | ===== Resurse ===== | ||
| - | |||
| - | * [[https://github.com/cs-pub-ro/SI-rpi-debian-scripts|Cod sursă scripturi compilare bootloader / kernel / generare rootfs]] | ||
| - | * [[https://buildroot.org/downloads/manual/manual.html#_getting_started|Buildroot Manual - Getting started]] | ||
| - | |||