Hai mai sperimentato con codice o utilità di sistema in Linux in modo da non dover temere per il sistema di base e non cancellare tutto in caso di errore nel codice che deve essere eseguito con privilegi di root?
E che ne dici di dover testare o avviare un intero cluster di microservizi su una sola macchina? Cento o anche mille?
Con le macchine virtuali gestite da un hypervisor, queste esigenze possono essere soddisfatte, ma a quale prezzo? Ad esempio, un contenitore in LXD basato sulla distribuzione Alpine Linux consuma minimamente solo 7.60MB di RAM, e dove la partizione radice dopo l'avvio occupa 9.5MB! Che ne pensi, Elon Musk? Ti consiglio di dare un'occhiata alle funzionalità di base di LXD — un sistema di contenitori in Linux
Dopo aver chiarito cosa sono in generale i contenitori LXD, andiamo oltre e consideriamo cosa succederebbe se ci fosse una piattaforma-macchina dove potremmo eseguire codice in modo sicuro per l'host, generare grafici, collegare dinamicamente (interattivamente) widget UI con il nostro codice, e integrare il codice con testo e formattazione... Qualcosa come un blog interattivo? Wow... Voglio! Voglio! 🙂
Dai un'occhiata qui sotto dove lanceremo in un contenitore JupyterLab — la nuova generazione dell'interfaccia utente al posto del superato Jupyter Notebook, e installeremo moduli Python come NumPy, Pandas, Matplotlib, IPyWidgets che permetteranno di fare tutto ciò che abbiamo menzionato sopra e salvare tutto in un file speciale — un notebook IPython.

Navigazione
- Piano di decollo in orbita
- Installazione e configurazione del sistema di base
- Installazione del software di base e configurazione del sistema
- Installazione e configurazione di JupyterLab
- Condividiamo i dati con l'host
- Ciao, Mondo!
- Espandiamo le funzionalità di Python
- Testiamo i moduli in JupyterLab
- Cos'altro?
Piano di decollo in orbita ^

Stiliamo un piano d'azione breve, per semplificare la realizzazione del diagramma sopra:
- Installeremo e avvieremo un contenitore basato sulla distribuzione Alpine Linux. Utilizzeremo questa distribuzione poiché è orientata alla minimalità e installeremo solo il software essenziale, nulla di superfluo.
- Aggiungeremo un disco virtuale aggiuntivo nel contenitore che chiameremo —
hostfse montiamo al file system radice. Questo disco permetterà di utilizzare i file sull'host dalla directory specificata all'interno del contenitore. In questo modo, i dati saranno indipendenti dal contenitore. In caso di eliminazione del contenitore, i dati rimarranno sull'host. Inoltre, questo schema è utile per separare alcuni dati tra molti contenitori senza utilizzare i meccanismi di rete standard del sistema di distribuzione del contenitore. - Installeremo Bash, sudo, le librerie necessarie, aggiungeremo e configureremo un utente di sistema
- Installeremo Python, i moduli e compileremo le dipendenze binarie per essi
- Installeremo e avvieremo JupyterLab, configureremo l'aspetto, installeremo le estensioni per esso.
In questo articolo inizieremo con l'avvio di un contenitore, non tratteremo l'installazione e la configurazione di LXD, tutto questo puoi trovarlo in un altro articolo — Le funzionalità di base di LXD — sistemi di contenitori in Linux.
Installazione e configurazione del sistema di base ^
Creiamo un contenitore con il comando in cui specifichiamo l'immagine — alpine3, un identificatore per il contenitore — jupyterlab e, se necessario, profili di configurazione:
lxc init alpine3 jupyterlab --profile=default --profile=hddrootQui utilizzo il profilo di configurazione hddroot che indica di creare un contenitore con la partizione root in Storage Pool situato su un disco HDD fisico:
lxc profile show hddroot
config: {}
description: ""
devices:
root:
path: \/
pool: hddpool
type: disk
name: hddroot
used_by: []lxc storage show hddpool
config:
size: 10GB
source: /dev/loop1
volatile.initial_source: /dev/loop1
description: ""
name: hddpool
driver: btrfs
used_by:
- /1.0/images/ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
- /1.0/profiles/hddroot
status: Created
locations:
- noneQuesto mi consente di sperimentare con i contenitori su un disco HDD risparmiando risorse del disco SSD che ho nel mio sistema 🙂 per il quale ho creato un profilo di configurazione separato ssdroot.
Dopo aver creato il contenitore, si trova nello stato STOPPED, quindi dobbiamo avviarlo eseguendo al suo interno il sistema init:
lxc start jupyterlabVisualizziamo l'elenco dei contenitori in LXD utilizzando l'opzione -c che specifica quali ccolonne visualizzare sullo schermo:
lxc list -c ns4b
+------------+---------+-------------------+--------------+
| NAME | STATE | IPV4 | STORAGE POOL |
+------------+---------+-------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.198 (eth0) | hddpool |
+------------+---------+-------------------+--------------+Al momento della creazione del contenitore, l'indirizzo IP è stato scelto casualmente, poiché abbiamo utilizzato il profilo di configurazione default che è stato precedentemente configurato nell'articolo Le funzionalità di base di LXD — sistemi di contenitori in Linux.
Modificheremo questo indirizzo IP in uno più memorabile, creando un'interfaccia di rete a livello di contenitore, piuttosto che a livello di profilo di configurazione come avviene attualmente nella configurazione esistente. Non è obbligatorio farlo, puoi saltare questo passaggio.
Creiamo un'interfaccia di rete eth0 che colleghiamo allo switch (ponte di rete) lxdbr0 nel quale abbiamo abilitato il NAT secondo l'articolo precedente e ora il contenitore avrà accesso a Internet, e assegnamo all'interfaccia un indirizzo IP statico — 10.0.5.5:
lxc config device add jupyterlab eth0 nic name=eth0 nictype=bridged parent=lxdbr0 ipv4.address=10.0.5.5Dopo aver aggiunto il dispositivo, è necessario riavviare il contenitore:
lxc restart jupyterlabControlliamo lo stato del contenitore:
lxc list -c ns4b
+------------+---------+------------------+--------------+
| NAME | STATE | IPV4 | STORAGE POOL |
+------------+---------+------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.5 (eth0) | hddpool |
+------------+---------+------------------+--------------+Installazione del software di base e configurazione del sistema ^
Per gestire il nostro contenitore, è necessario installare il seguente software:
Pacchetto
Descrizione
bash
La shell GNU Bourne Again
bash-completion
Completamento programmabile per la shell bash
sudo
Dare ad alcuni utenti la possibilità di eseguire alcuni comandi come root
shadow
Suite di strumenti per la gestione di password e account con supporto per file shadow e PAM
tzdata
Fonti per i dati sui fusi orari e sull'ora legale
nano
Clone dell'editor Pico con miglioramenti
Inoltre, puoi installare il supporto per le pagine man installando i seguenti pacchetti — man man-pages mdocml-apropos less
lxc exec jupyterlab -- apk add bash bash-completion sudo shadow tzdata nanoAnalizziamo i comandi e le opzioni che abbiamo utilizzato:
lxc— Invoca il client LXDexec— Metodo del client LXD che esegue il comando nel contenitorejupyterlab— Identificatore del contenitore--— Opzione speciale che indica di non interpretare ulteriormente le opzioni come opzioni perlxce passare l'intera riga rimanente così com'è al contenitoreapk— Gestore pacchetti della distribuzione Alpine Linuxadd— Metodo del gestore pacchetti che installa i pacchetti indicati dopo il comando
Successivamente, imposteremo il fuso orario nel sistema Europe/Moscow:
lxc exec jupyterlab -- cp /usr/share/zoneinfo/Europe/Moscow /etc/localtimeDopo aver impostato il fuso orario, il pacchetto tzdata non è più necessario, occuperà spazio, quindi lo rimuoviamo:
lxc exec jupyterlab -- apk del tzdataControlliamo il fuso orario:
lxc exec jupyterlab -- date
Mer Apr 15 10:49:56 MSK 2020Per non perdere troppo tempo nella configurazione di Bash per i nuovi utenti all'interno del contenitore, procederemo copiando i file skel predefiniti dal sistema host. Questo permetterà di abbellire Bash nel contenitore in modalità interattiva. Il mio sistema host è Manjaro Linux e i file copiati /etc/skel/.bash_profile, /etc/skel/.bashrc, /etc/skel/.dir_colors sono adatti in linea di massima per Alpine Linux e non causano problemi critici, ma potrebbe essere diverso il vostro sistema e dovrete risolvere eventuali errori di avvio di Bash nel contenitore.
Copiamo i file skel nel contenitore. L'opzione --create-dirs creerà le directory necessarie, se non esistono:
lxc file push /etc/skel/.bash_profile jupyterlab/etc/skel/.bash_profile --create-dirs
lxc file push /etc/skel/.bashrc jupyterlab/etc/skel/.bashrc
lxc file push /etc/skel/.dir_colors jupyterlab/etc/skel/.dir_colorsPer l'utente root già esistente, copiamo nella home directory i file skel appena copiati nel contenitore:
lxc exec jupyterlab -- cp /etc/skel/.bash_profile /root/.bash_profile
lxc exec jupyterlab -- cp /etc/skel/.bashrc /root/.bashrc
lxc exec jupyterlab -- cp /etc/skel/.dir_colors /root/.dir_colorsIn Alpine Linux, per gli utenti viene installata la shell di sistema /bin/sh, la sostituiremo con root sul Bash:
lxc exec jupyterlab -- usermod --shell=/bin/bash rootPer root se l'utente non è senza password, deve impostare una password. Il comando seguente genererà e installerà una nuova password casuale che vedrete sullo schermo della console dopo l'esecuzione:
lxc exec jupyterlab -- /bin/bash -c "PASSWD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12); echo "root:$PASSWD" | chpasswd && echo "New Password: $PASSWD""
New Password: sFiXEvBswuWAInoltre, creiamo un nuovo utente di sistema — jupyter per il quale configureremo successivamente JupyterLab:
lxc exec jupyterlab -- useradd --create-home --shell=/bin/bash jupyterGeneriamo e impostiamo una password per lui:
lxc exec jupyterlab -- /bin/bash -c "PASSWD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12); echo "jupyter:$PASSWD" | chpasswd && echo "New Password: $PASSWD""
New Password: ZIcbzWrF8tkiSuccessivamente, eseguiremo due comandi: il primo creerà un gruppo di sistema sudo, e il secondo aggiungerà l'utente a questo gruppo jupyter:
lxc exec jupyterlab -- groupadd --system sudo
lxc exec jupyterlab -- groupmems --group sudo --add jupyterControlliamo in quali gruppi è presente l'utente jupyter:
lxc exec jupyterlab -- id -Gn jupyter
jupyter sudoTutto ok, andiamo avanti.
Consentiamo a tutti gli utenti che appartengono al gruppo sudo di utilizzare il comando sudo. Esegui il seguente script, dove sed decommenta la riga del parametro nel file di configurazione /etc/sudoers:
lxc exec jupyterlab -- /bin/bash -c "sed --in-place -e '/^#[ t]*%sudo[ t]*ALL=(ALL)[ t]*ALL$/ s/^[# ]*// ' /etc/sudoers"Installazione e configurazione di JupyterLab ^
JupyterLab — questa è un'applicazione Python, quindi dobbiamo prima installare questo interprete. Inoltre, JupyterLab installeremo utilizzando il gestore pacchetti di Python pip, e non quello di sistema, perché nel repository di sistema potrebbe essere obsoleto, e quindi dovremo risolvere manualmente le dipendenze installando i seguenti pacchetti — python3 python3-dev gcc libc-dev zeromq-dev:
lxc exec jupyterlab -- apk add python3 python3-dev gcc libc-dev zeromq-devAggiorniamo i moduli Python e il gestore pacchetti pip alla versione attuale:
lxc exec jupyterlab -- python3 -m pip install --upgrade pip setuptools wheelInstalliamo JupyterLab tramite il gestore pacchetti pip:
lxc exec jupyterlab -- python3 -m pip install jupyterlabPoiché le estensioni in JupyterLab sono sperimentali e non sono ufficialmente incluse nel pacchetto jupyterlab, dobbiamo installare e configurare questo manualmente.
Installeremo NodeJS e il gestore pacchetti per esso — NPM, poiché JupyterLab li utilizza per le sue estensioni:
lxc exec jupyterlab -- apk add nodejs npmAffinché le estensioni per JupyterLab che installeremo funzionino, devono essere installate nella directory utente poiché l'applicazione verrà eseguita dall'utente jupyter. Il problema è che non c'è un parametro nel comando di avvio attraverso il quale puoi passare la directory, l'applicazione riconosce solo la variabile d'ambiente e quindi dobbiamo definirla. A tal fine, scriveremo il comando di esportazione della variabile JUPYTERLAB_DIR nell'ambiente utente jupyter, nel file .bashrc, che viene eseguito ogni volta che l'utente accede al sistema:
lxc exec jupyterlab -- su -l jupyter -c "echo -e "nexport JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab" >> .bashrc"Con il prossimo comando installeremo un'estensione speciale — il gestore delle estensioni in JupyterLab:
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter labextension install --no-build @jupyter-widgets/jupyterlab-manager"Adesso è tutto pronto per il primo avvio JupyterLab, ma possiamo anche installare alcune estensioni utili:
toc— Indice, genera un elenco di intestazioni nell'articolo/nel notebookjupyterlab-horizon-theme— Tema dell'interfaccia utentejupyterlab_neon_theme— Tema dell'interfaccia utentejupyterlab-ubu-theme— Un'altra tema dall'autore di questo articolo 🙂 Ma in questo caso, verrà mostrata l'installazione dal repository GitHub
Quindi, esegui le seguenti comandi in sequenza per installare queste estensioni:
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter labextension install --no-build @jupyterlab/toc @mohirio/jupyterlab-horizon-theme @yeebc/jupyterlab_neon_theme"lxc exec jupyterlab -- su -l jupyter -c "wget -c https://github.com/microcoder/jupyterlab-ubu-theme/archive/master.zip"lxc exec jupyterlab -- su -l jupyter -c "unzip -q master.zip && rm master.zip"lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter labextension install --no-build jupyterlab-ubu-theme-master"lxc exec jupyterlab -- su -l jupyter -c "rm -r jupyterlab-ubu-theme-master"Dopo aver installato le estensioni, dobbiamo compilarle, poiché in precedenza, durante l'installazione, abbiamo specificato la chiave --no-build per risparmiare tempo. Adesso accelereremo notevolmente compilandole tutte insieme in una sola volta:
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter lab build"Ora esegui i seguenti due comandi per il primo avvio JupyterLab. Potremmo avviarlo con un solo comando, ma in tal caso, il comando di avvio, che si ha in mente, sarà memorizzato dal bash nel contenitore, e non sull'host, dove di comandi ce ne sono già abbastanza per essere registrati nella cronologia 🙂
Accediamo nel contenitore come utente jupyter:
lxc exec jupyterlab -- su -l jupyterSuccessivamente esegui JupyterLab con le chiavi e i parametri come indicato:
[jupyter@jupyterlab ~]$ jupyter lab --ip=0.0.0.0 --no-browserVai nel browser web all'indirizzo http://10.0.5.5:8888 e nella pagina aperta inserisci token di accesso che vedrai nella console. Copialo e incollalo nella pagina, quindi fai clic su Accesso. Dopo l'accesso, vai nel menu delle estensioni a sinistra, come mostrato nell'immagine sottostante, dove ti verrà chiesto, attivando il gestore delle estensioni, di accettare i rischi sulla sicurezza nell'installare estensioni di terze parti per cui il team JupyterLab development non si assume responsabilità:

Tuttavia, noi isoliamo completamente JupyterLab e lo inseriamo in un contenitore, affinché estensioni di terze parti che richiedono e utilizzano NodeJS non possano rubare dati dal disco, tranne quelli che apriremo all'interno del contenitore. È improbabile che riescano ad accedere ai tuoi documenti privati sull'host /home e che i processi all'interno del contenitore possano accedere, e se ci riescono, necessitano di privilegi sui file nel sistema host, poiché stiamo eseguendo il contenitore in modalità non privilegiata. In base a queste informazioni, puoi valutare il rischio di attivare estensioni in JupyterLab.
I notebook IPython creati (pagine in JupyterLab) ora verranno creati nella directory home dell'utente — /home/jupyter, ma nei nostri piani c'è quello di separare i dati (condividerli) tra host e contenitore, quindi, torna nella console e interrompi JupyterLab premendo il tasto di scelta rapida — CTRL+C e rispondendo y alla richiesta. Quindi termina la sessione interattiva dell'utente jupyter premendo il tasto di scelta rapida CTRL+D.
Condividiamo i dati con l'host ^
Per condividere i dati con l'host, è necessario creare un dispositivo nel contenitore che lo consenta e per fare ciò eseguire il seguente comando dove specifichiamo le seguenti chiavi:
lxc config device add— Il comando aggiunge la configurazione del dispositivojupyter— L'identificativo del contenitore a cui viene aggiunta la configurazionehostfs— L'identificativo del dispositivo. Puoi fornire qualsiasi nome.disk— Viene specificato il tipo di dispositivopath— Viene specificato il percorso nel contenitore in cui LXD monterà questo dispositivosource— Viene specificata la sorgente, il percorso della directory sull'host che desideri condividere con il contenitore. Specifica il percorso secondo le tue preferenze
lxc config device add jupyterlab hostfs disk path=\/mnt\/hostfs source=\/home\/dv\/projects\/ipython-notebooksPer la directory /home/dv/projects/ipython-notebooks deve essere impostata l'autorizzazione all'utente del contenitore che attualmente ha UID pari a SubUID + UID, consulta il capitolo Sicurezza. Privilegi dei contenitori nell'articolo Le funzionalità di base di LXD — sistemi di contenitori in Linux.
Imposta le autorizzazioni sull'host, dove il proprietario sarà l'utente del contenitore jupyter, e la variabile $USER indicherà il tuo utente host come gruppo:
sudo chown 1001000:$USER \home\/dv\/projects\/ipython-notebooksCiao, Mondo! ^
Se hai ancora una sessione della console aperta nel contenitore con JupyterLab, riavviala con la nuova chiave --notebook-dir specificando il valore /mnt/hostfs come percorso fino alla radice dei notebook nel contenitore per il dispositivo che abbiamo creato nel passaggio precedente:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\/mnt\/hostfsPoi vai alla pagina http://10.0.5.5:8888 e crea il tuo primo notebook cliccando sul pulsante sulla pagina come mostrato nella figura qui sotto:

Poi nel campo della pagina inserisci il codice in Python che stamperà il classico Hello World!. Al termine dell'inserimento premi CTRL+ENTER o il pulsante «play» nella barra degli strumenti in alto affinché JupyterLab lo esegua:

Quasi tutto è pronto per l'uso, ma sarà noioso se non installiamo moduli Python aggiuntivi (applicazioni complete) che consentono di ampliare significativamente le funzionalità standard di Python in JupyterLab, quindi andiamo avanti 🙂
P.S. È interessante notare che la vecchia implementazione Jupyter con nome in codice Jupyter Notebook non è scomparsa ed esiste parallelamente a JupyterLab. Per passare alla vecchia versione vai al link aggiungendo alla URL il suffisso/tree, mentre il passaggio alla nuova versione avviene con il suffisso /lab, ma non è necessario specificarlo:
- Jupyter Notebook — http://10.0.5.5:8888/tree
- Jupyter Lab — http://10.0.5.5:8888/lab
Espandiamo le funzionalità di Python ^
In questa sezione installeremo potenti moduli del linguaggio Python come NumPy, Pandas, Matplotlib, IPyWidgets i cui risultati vengono integrati nei notebook JupyterLab.
Prima di installare i moduli Python elencati tramite il gestore di pacchetti pip dobbiamo prima risolvere le dipendenze di sistema in Alpine Linux:
g++— Necessario per compilare i moduli, poiché alcuni di essi sono implementati in C++ e si collegano a Python a runtime come moduli binarifreetype-dev— dipendenza per il modulo Python Matplotlib
Installiamo le dipendenze:
lxc exec jupyterlab -- apk add g++ freetype-devC'è un problema, nello stato attuale della distribuzione Alpine Linux non è possibile compilare una nuova versione di NumPy, si verificherà un errore di compilazione che non sono riuscito a risolvere:
ERROR: Impossibile costruire i pacchetti wheel per numpy che utilizzano PEP 517 e non possono essere installati direttamente
Pertanto, installeremo questo modulo come pacchetto di sistema che distribuisce già una versione compilata, ma leggermente più vecchia rispetto a quella disponibile ora sul sito:
lxc exec jupyterlab -- apk add py3-numpy py3-numpy-devSuccessivamente, installiamo i moduli Python tramite il gestore di pacchetti pip. Abbiate pazienza, poiché alcuni moduli verranno compilati e ciò richiederà alcuni minuti. Sul mio computer la compilazione ha impiegato circa 15 minuti:
lxc exec jupyterlab -- python3 -m pip install pandas ipywidgets matplotlibPuliamo le cache di installazione:
lxc exec jupyterlab -- rm -rf /home/*/.cache/pip/*
lxc exec jupyterlab -- rm -rf /root/.cache/pip/*Testiamo i moduli in JupyterLab ^
Se hai avviato JupyterLab, riavvialo per attivare i nuovi moduli installati. Per fare ciò, nella sessione della console premi CTRL+C dove è avviato e digita y alla richiesta di arresto, quindi riavvialo JupyterLab premendo il tasto freccia "su" sulla tastiera, per non dover digitare nuovamente il comando, e poi Invio per avviare:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\/mnt\/hostfsVai alla pagina http://10.0.5.5:8888/lab o aggiorna la pagina nel browser, quindi inserisci il seguente codice in una nuova cella del notebook:
%matplotlib inline
from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np
def f(m, b):
plt.figure(2)
x = np.linspace(-10, 10, num=1000)
plt.plot(x, m * x + b)
plt.ylim(-5, 5)
plt.show()
interactive_plot = interactive(f, m=(-2.0, 2.0), b=(-3, 3, 0.5))
output = interactive_plot.children[-1]
output.layout.height = '350px'
interactive_plotDovresti ottenere un risultato simile all'immagine qui sotto, dove IPyWidgets genera un elemento UI sulla pagina che interagisce in modo interattivo con il codice sorgente, e inoltre Matplotlib mostra il risultato del codice sotto forma di immagine come grafico della funzione:

Molti esempi IPyWidgets puoi trovare nei tutorial qui
Cos'altro? ^
Fate bravi se siete rimasti e siete arrivati fino alla fine dell'articolo. Ho deciso di non pubblicare uno script pronto alla fine dell'articolo che lo installerebbe JupyterLab con un «clic», per incoraggiare i lavoratori 🙂 Ma potete farlo da soli, dato che già sapete come farlo, raccogliendo i comandi in un unico script Bash 🙂
Inoltre, potete:
- Assegnare un nome di rete al contenitore invece di un indirizzo IP scrivendolo in modo semplice
/etc/hostse digitare l'indirizzo nel browser http://jupyter.local:8888 - Sperimentare con i limiti delle risorse per il contenitore, per questo leggete il capitolo su le possibilità di base di LXD o ottenere ulteriori informazioni dal sito dello sviluppatore di LXD.
- Cambiare il tema:

E molto altro potete fare! Questo è tutto. Vi auguro successo!
AGGIORNAMENTO: 15.04.2020 18:30 — Corretto errori nel capitolo «Hello, World!»
AGGIORNAMENTO: 16.04.2020 10:00 — Corretto e aggiunto testo nella descrizione dell'attivazione del gestore delle estensioni JupyterLab
AGGIORNAMENTO: 16.04.2020 10:40 — Corretto errori riscontrati nel testo e modificato leggermente in meglio il capitolo «Installazione del software di base e configurazione del sistema»
Fonte: habr.com
