This is an old revision of the document!


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 (Yocto Linux, 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.:

$ sudo debootstrap --arch="arm64" --include="vim bash curl" \
        --variant=minbase "bookworm" "/root/mydebian/" "http://ftp.de.debian.org/debian"

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…

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:

$ 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

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.listgă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ă):

$ tar czf mydebian.tar.gz -C /root/mydebian

În final, dorim să vedem cât ocupă rootfs-ul nostru:

$ du -hs /root/mydebian

Vom trece să instalăm + rulăm sistemul pe un Raspberry PI 4 fizic imediat ce pornim compilarea unui sistem Buildroot ;)

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).

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 35GB și atașați-l proiectului VirtualBox / VMWare ca dispozitiv de stocare suplimentar (din setări, add 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.

TODO

3. Instalarea rootfs-ului în imaginea RPI 4

0. Descărcați de aici imaginea rpi-empty.img.tar.xz.

1. Trebuie să montăm imaginea și să copiem noul RootFs pe cea de a doua partiție (este creată deja!):

  • Dezarhivați fișierul imaginii rpi-empty; ar trebui să obțineți o imagine de 2GB;
  • Verificați că există ambele partiții: parted rpi-empty.img print;
  • Conectați imaginea la un dispozitiv de tip bloc folosind losetup /dev/loop0 rpi-empty.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):
    tar xf <ARCHIVE_FILE> -C <DEST_MOUNTPOINT>
    # inspectați calea dezarhivată:
    ls -l <DEST_MOUNTPOINT>
  • 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 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.

  • 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ă).

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 laboratorul 4.

Resurse

si/laboratoare/08.1701628752.txt.gz · Last modified: 2023/12/03 20:39 by florin.stancu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0