Vi è mai capitato di dover sperimentare codice o utilità di sistema in Linux per non preoccuparvi del sistema base e non smontare tutto in caso di errore nel codice che dovrebbe essere eseguito con i privilegi di root?
Ma che dire del fatto che, supponiamo, sia necessario testare o eseguire un intero cluster di vari microservizi su una macchina? Cento o addirittura mille?
Con le macchine virtuali gestite da un hypervisor, tali problemi possono e saranno risolti, ma a quale costo? Ad esempio, un contenitore in LXD basato sulla distribuzione Alpine Linux consuma solo 7.60MB
RAM e dove occupa la partizione root dopo l'avvio 9.5MB
! Che te ne pare, Elon Musk? Consiglio di dare un'occhiata funzionalità di base di LXD: un sistema contenitore in Linux
Dopo che è diventato chiaro in generale cosa sono i contenitori LXD, andiamo oltre e pensiamo, e se esistesse una piattaforma di raccolta in cui è possibile eseguire in sicurezza il codice per l'host, generare grafici, collegare dinamicamente (interattivamente) i widget dell'interfaccia utente con il codice, integrare il codice con il testo con blackjack... formattazione? Una sorta di blog interattivo? Wow... lo voglio! Volere! 🙂
Guarda sotto il gatto dove lanceremo in un contenitore laboratorio giove - la prossima generazione di interfaccia utente al posto dell'obsoleto Jupyter Notebook e installeremo anche moduli Python come NumPy, Pandas, matplotlib, IPyWidget che ti consentirà di fare tutto ciò che è elencato sopra e di salvarlo in un file speciale: un laptop IPython.
Navigazione
- Piano di decollo orbitale
- Installazione e configurazione del sistema base
- Installazione del software di base e configurazione del sistema
- Installazione e configurazione di JupyterLab
- Condivisione dei dati con l'host
- Ciao mondo!
- Espansione delle capacità di Python
- Test dei moduli in JupyterLab
- Cos'altro?
Piano di decollo orbitale ^
Descriviamo un breve piano d’azione per facilitarci l’implementazione dello schema di cui sopra:
- Installiamo e lanciamo un contenitore basato sul kit di distribuzione Linux alpino. Utilizzeremo questa distribuzione perché mira al minimalismo e installeremo al suo interno solo il software più necessario, niente di superfluo.
- Aggiungiamo un ulteriore disco virtuale nel contenitore e diamogli un nome:
hostfs
e montarlo sul file system root. Questo disco consentirà di utilizzare i file sull'host da una determinata directory all'interno del contenitore. Pertanto, i nostri dati saranno indipendenti dal contenitore. Se il contenitore viene eliminato, i dati rimarranno sull'host. Inoltre, questo schema è utile per condividere gli stessi dati tra molti contenitori senza utilizzare i meccanismi di rete standard della distribuzione dei contenitori. - Installiamo Bash, sudo, le librerie necessarie, aggiungiamo e configuriamo un utente di sistema
- Installiamo Python, i moduli e compiliamo le dipendenze binarie per loro
- Installiamo e lanciamo laboratorio giove, personalizza l'aspetto, installa le estensioni.
In questo articolo inizieremo con il lancio del container, non prenderemo in considerazione l'installazione e la configurazione di LXD, tutto questo lo troverete in un altro articolo - Funzionalità di base dei sistemi contenitore LXD - Linux.
Installazione e configurazione del sistema base ^
Creiamo un contenitore con il comando in cui specifichiamo l'immagine - alpine3
, identificatore per il contenitore - jupyterlab
e, se necessario, profili di configurazione:
lxc init alpine3 jupyterlab --profile=default --profile=hddroot
Qui sto usando un profilo di configurazione hddroot
che specifica di creare un contenitore con una partizione root in Pool di archiviazione 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:
- none
Questo mi dà l'opportunità di sperimentare i contenitori sul disco HDD, risparmiando le risorse del disco SSD, disponibile anche nel mio sistema 🙂 per il quale ho creato un profilo di configurazione separato ssdroot
.
Dopo la creazione, il contenitore si trova nello stato STOPPED
, quindi dobbiamo avviarlo eseguendo il sistema init al suo interno:
lxc start jupyterlab
Visualizziamo un elenco di contenitori in LXD utilizzando la chiave -c
che indica quale cVisualizzazione delle colonne:
lxc list -c ns4b
+------------+---------+-------------------+--------------+
| NAME | STATE | IPV4 | STORAGE POOL |
+------------+---------+-------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.198 (eth0) | hddpool |
+------------+---------+-------------------+--------------+
Durante la creazione del contenitore, l'indirizzo IP è stato scelto in modo casuale, poiché abbiamo utilizzato un profilo di configurazione default
che era stato precedentemente configurato nell'articolo Funzionalità di base dei sistemi contenitore LXD - Linux.
Modificheremo questo indirizzo IP con uno più memorizzabile creando un'interfaccia di rete a livello di contenitore e non a livello di profilo di configurazione come è ora nella configurazione corrente. Non sei obbligato a farlo, puoi saltarlo.
Creazione di un'interfaccia di rete eth0
che colleghiamo allo switch (bridge di rete) lxdbr0
in cui abbiamo abilitato NAT secondo l'articolo precedente e il contenitore ora avrà accesso a Internet e assegniamo anche un indirizzo IP statico all'interfaccia - 10.0.5.5
:
lxc config device add jupyterlab eth0 nic name=eth0 nictype=bridged parent=lxdbr0 ipv4.address=10.0.5.5
Dopo aver aggiunto un dispositivo, è necessario riavviare il contenitore:
lxc restart jupyterlab
Verifica dello 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 amministrare il nostro contenitore, è necessario installare il seguente software:
CONFEZIONE
Descrizione
bash
La shell GNU Bourne Again
bash-completamento
Completamento programmabile per la shell bash
sudo
Dare ad alcuni utenti la possibilità di eseguire alcuni comandi come root
ombra
Suite di strumenti per la gestione di password e account con supporto per file shadow e PAM
tzdata
Fonti per i dati relativi al fuso orario e all'ora legale
nano
Clone dell'editor Pico con miglioramenti
Inoltre, è possibile installare il supporto nelle pagine man del sistema installando i seguenti pacchetti − man man-pages mdocml-apropos less
lxc exec jupyterlab -- apk add bash bash-completion sudo shadow tzdata nano
Diamo un'occhiata ai comandi e ai tasti che abbiamo utilizzato:
lxc
— Chiama il client LXDexec
- Metodo client LXD che esegue un comando nel contenitorejupyterlab
— Identificativo del contenitore--
- Una chiave speciale che specifica di non interpretare ulteriori chiavi come chiavi perlxc
e passare il resto della corda così com'è nel contenitoreapk
— Gestore di pacchetti della distribuzione Alpine Linuxadd
— Un metodo di gestione dei pacchetti che installa i pacchetti specificati dopo il comando
Successivamente, imposteremo un fuso orario nel sistema Europe/Moscow
:
lxc exec jupyterlab -- cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime
Dopo aver installato il fuso orario, il pacchetto tzdata
non è più necessario nel sistema, occuperà spazio, quindi eliminiamolo:
lxc exec jupyterlab -- apk del tzdata
Controllo del fuso orario:
lxc exec jupyterlab -- date
Wed Apr 15 10:49:56 MSK 2020
Per non perdere molto tempo nella configurazione di Bash per i nuovi utenti nel contenitore, nei passaggi seguenti copieremo i file skel già pronti dal sistema host su di esso. Ciò ti consentirà di abbellire Bash in un contenitore in modo interattivo. Il mio sistema host è Manjaro Linux e i file vengono copiati /etc/skel/.bash_profile
, /etc/skel/.bashrc
, /etc/skel/.dir_colors
in linea di principio sono adatti per Alpine Linux e non causano problemi critici, ma potresti avere una distribuzione diversa e devi capire autonomamente se si verifica un errore durante l'esecuzione di Bash nel contenitore.
Copia i file skel nel contenitore. Chiave --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_colors
Per un utente root già esistente, copia i file skel appena copiati nel contenitore nella directory home:
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_colors
Alpine Linux installa una shell di sistema per gli utenti /bin/sh
, lo sostituiremo con root
utente in Bash:
lxc exec jupyterlab -- usermod --shell=/bin/bash root
Che root
l'utente non era senza password, deve impostare una password. Il seguente comando genererà e imposterà per lui una nuova password casuale, che vedrai sullo schermo della console dopo la sua 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: sFiXEvBswuWA
Inoltre, creiamo un nuovo utente di sistema: jupyter
per il quale configureremo in seguito laboratorio giove:
lxc exec jupyterlab -- useradd --create-home --shell=/bin/bash jupyter
Generiamo e impostiamo una password per questo:
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: ZIcbzWrF8tki
Successivamente, eseguiremo due comandi, il primo creerà un gruppo di sistema sudo
e il secondo aggiungerà un utente ad esso jupyter
:
lxc exec jupyterlab -- groupadd --system sudo
lxc exec jupyterlab -- groupmems --group sudo --add jupyter
Vediamo a quali gruppi appartiene l'utente jupyter
:
lxc exec jupyterlab -- id -Gn jupyter
jupyter sudo
È tutto ok, andiamo avanti.
Consenti a tutti gli utenti che sono membri del gruppo sudo
utilizzare il comando sudo
. Per fare ciò, esegui il seguente script, dove sed
rimuove il commento dalla 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 ^
laboratorio giove è un'applicazione Python, quindi dobbiamo prima installare questo interprete. Anche, laboratorio giove installeremo utilizzando il gestore pacchetti Python pip
, e non quello di sistema, perché potrebbe essere obsoleto nel repository di sistema e quindi dobbiamo risolverne 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-dev
Aggiorniamo i moduli Python e il gestore pacchetti pip
alla versione attuale:
lxc exec jupyterlab -- python3 -m pip install --upgrade pip setuptools wheel
Set laboratorio giove tramite il gestore pacchetti pip
:
lxc exec jupyterlab -- python3 -m pip install jupyterlab
Dal momento che le estensioni in laboratorio giove sono sperimentali e non vengono ufficialmente forniti con il pacchetto jupyterlab, quindi dobbiamo installarli e configurarli manualmente.
Installiamo NodeJS e il relativo gestore pacchetti - NPM, da allora laboratorio giove li usa per le sue estensioni:
lxc exec jupyterlab -- apk add nodejs npm
Alle estensioni per laboratorio giove che installeremo hanno funzionato, devono essere installati nella directory dell'utente poiché l'applicazione verrà avviata dall'utente jupyter
. Il problema è che nel comando launch non c'è nessun parametro che possa essere passato ad una directory; l'applicazione accetta solo una variabile d'ambiente e quindi dobbiamo definirla. Per fare ciò, scriveremo il comando di esportazione della variabile JUPYTERLAB_DIR
nell'ambiente dell'utente jupyter
, archiviare .bashrc
che viene eseguito ogni volta che l'utente accede:
lxc exec jupyterlab -- su -l jupyter -c "echo -e "nexport JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab" >> .bashrc"
Il comando successivo installerà un'estensione speciale: il gestore estensioni in laboratorio giove:
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 lancio laboratorio giove, ma possiamo comunque installare alcune estensioni utili:
toc
— Sommario: genera un elenco di titoli in un articolo/taccuinojupyterlab-horizon-theme
- Tema dell'interfaccia utentejupyterlab_neon_theme
- Tema dell'interfaccia utentejupyterlab-ubu-theme
- Un altro tema dell'autore questo articolo :) Ma in questo caso verrà mostrata l'installazione dal repository GitHub
Quindi, esegui i 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. Ora accelereremo notevolmente compilandoli insieme in una volta sola:
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter lab build"
Ora esegui i due comandi seguenti per eseguirlo per la prima volta laboratorio giove. Sarebbe possibile avviarlo con un comando, ma in questo caso il comando di avvio, che è difficile da ricordare nella tua mente, verrà ricordato da bash nel contenitore, e non sull'host, dove ci sono già abbastanza comandi per registrarli nella storia :)
Accedi al contenitore come utente jupyter
:
lxc exec jupyterlab -- su -l jupyter
Quindi, corri laboratorio giove con tasti e parametri come indicato:
[jupyter@jupyterlab ~]$ jupyter lab --ip=0.0.0.0 --no-browser
Vai all'indirizzo nel tuo browser web http://10.0.5.5:8888 e nella pagina che si apre entra token accesso che vedrai nella console. Copialo e incollalo sulla pagina, quindi fai clic Accedi. Dopo aver effettuato l'accesso, recatevi nel menu estensioni a sinistra, come mostrato nella figura seguente, dove vi verrà chiesto, al momento dell'attivazione del gestore estensioni, di correre rischi per la sicurezza installando estensioni di terze parti per le quali il comando Sviluppo JupyterLab non è responsabile:
Tuttavia isoliamo il tutto laboratorio giove e posizionarlo in un contenitore in modo che le estensioni di terze parti che richiedono e utilizzano NodeJS non possano almeno rubare dati sul disco diversi da quelli che apriamo all'interno del contenitore. Accedi ai tuoi documenti privati sull'host in /home
è improbabile che i processi dal contenitore abbiano esito positivo e, in tal caso, è necessario disporre dei privilegi sui file nel sistema host, poiché eseguiamo il contenitore in modalità non privilegiata. Sulla base di queste informazioni, puoi valutare il rischio di includere estensioni in laboratorio giove.
Notebook IPython creati (pagine in laboratorio giove) verrà ora creato nella directory home dell'utente - /home/jupyter
, ma i nostri piani sono di dividere i dati (condivisione) tra l'host e il contenitore, quindi torna alla console e interrompi laboratorio giove eseguendo il tasto di scelta rapida - CTRL+C
e rispondere y
su richiesta. Quindi termina la sessione interattiva dell'utente jupyter
completando un tasto di scelta rapida CTRL+D
.
Condivisione dei dati con l'host ^
Per condividere i dati con l'host, è necessario creare un dispositivo nel contenitore che consenta di farlo e per farlo eseguire il comando seguente in cui specifichiamo le seguenti chiavi:
lxc config device add
— Il comando aggiunge la configurazione del dispositivojupyter
— ID del contenitore a cui viene aggiunta la configurazionehostfs
- ID del dispositivo. È possibile impostare qualsiasi nome.disk
— È indicato il tipo di dispositivopath
— Specifica il percorso nel contenitore su cui LXD monterà questo dispositivosource
— Specifica l'origine, il percorso della directory sull'host che desideri condividere con il contenitore. Specifica il percorso in base alle tue preferenze
lxc config device add jupyterlab hostfs disk path=/mnt/hostfs source=/home/dv/projects/ipython-notebooks
Per il catalogo /home/dv/projects/ipython-notebooks
l'autorizzazione deve essere impostata sull'utente contenitore che attualmente ha un UID uguale a SubUID + UID
, vedere il capitolo Sicurezza. Privilegi del contenitore nell'articolo Funzionalità di base dei sistemi contenitore LXD - Linux.
Imposta l'autorizzazione sull'host, dove il proprietario sarà l'utente del contenitore jupyter
e la variabile $USER
specificherà l'utente host come gruppo:
sudo chown 1001000:$USER /home/dv/projects/ipython-notebooks
Ciao mondo! ^
Se hai ancora una sessione della console aperta nel contenitore con laboratorio giove, quindi riavviarlo con una nuova chiave --notebook-dir
impostando il valore /mnt/hostfs
come percorso alla radice dei laptop nel contenitore per il dispositivo che abbiamo creato nel passaggio precedente:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=/mnt/hostfs
Quindi vai alla pagina http://10.0.5.5:8888 e crea il tuo primo laptop facendo clic sul pulsante nella pagina come mostrato nell'immagine qui sotto:
Quindi, nel campo della pagina, inserisci il codice Python che visualizzerà il classico Hello World!
. Una volta terminato l'inserimento, premere CTRL+ENTER
o il pulsante "Riproduci" sulla barra degli strumenti in alto per fare in modo che JupyterLab faccia questo:
A questo punto quasi tutto è pronto per l'uso, ma non sarà interessante se non installiamo moduli Python aggiuntivi (applicazioni a tutti gli effetti) che possono espandere significativamente le capacità standard di Python in laboratorio giovequindi andiamo avanti :)
PS La cosa interessante è che la vecchia implementazione Giove sotto il nome in codice Notebook Jupyter non è scomparso ed esiste parallelamente a laboratorio giove. Per passare alla vecchia versione seguire il link aggiungendo il suffisso nell'indirizzo/tree
, e il passaggio alla nuova versione viene effettuato con il suffisso /lab
, ma non è necessario specificarlo:
- Taccuino di Jupyter - http://10.0.5.5:8888/tree
- Laboratorio Jupyter - http://10.0.5.5:8888/lab
Espansione delle capacità di Python ^
In questa sezione installeremo moduli potenti del linguaggio Python come NumPy, Pandas, matplotlib, IPyWidget i cui risultati sono integrati nei laptop laboratorio giove.
Prima di installare i moduli Python elencati tramite il gestore pacchetti pip
dobbiamo prima risolvere le dipendenze del sistema in Alpine Linux:
g++
— Necessario per la compilazione dei moduli, poiché alcuni di essi sono implementati nel linguaggio C++ e connettersi a Python in fase di esecuzione come moduli binarifreetype-dev
- dipendenza per il modulo Python matplotlib
Installazione delle dipendenze:
lxc exec jupyterlab -- apk add g++ freetype-dev
C'è un problema: allo stato attuale della distribuzione Alpine Linux non sarà possibile compilare la nuova versione di NumPy; apparirà un errore di compilazione che non sono riuscito a risolvere:
ERRORE: Impossibile costruire ruote per Numpy che utilizzano PEP 517 e non possono essere installate direttamente
Installeremo quindi questo modulo come un pacchetto di sistema che distribuisce una versione già compilata, ma un po' più vecchia di quella attualmente disponibile sul sito:
lxc exec jupyterlab -- apk add py3-numpy py3-numpy-dev
Successivamente, installa i moduli Python tramite il gestore pacchetti pip
. Si prega di pazientare poiché alcuni moduli verranno compilati e potrebbero richiedere alcuni minuti. Sulla mia macchina, la compilazione ha richiesto circa 15 minuti:
lxc exec jupyterlab -- python3 -m pip install pandas ipywidgets matplotlib
Cancellazione delle cache di installazione:
lxc exec jupyterlab -- rm -rf /home/*/.cache/pip/*
lxc exec jupyterlab -- rm -rf /root/.cache/pip/*
Test dei moduli in JupyterLab ^
Se stai correndo laboratorio giove, riavviarlo in modo che i moduli appena installati vengano attivati. A tale scopo, in una sessione della console, fare clic su CTRL+C
dove lo hai acceso ed entra y
per interrompere la richiesta e quindi riavviarla laboratorio giove premendo la freccia su della tastiera per non inserire nuovamente il comando e poi Enter
per avviarlo:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=/mnt/hostfs
ерейдите на страницу http://10.0.5.5:8888/lab oppure aggiorna la pagina nel browser, quindi inserisci il seguente codice in una nuova cella del taccuino:
%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_plot
Dovresti ottenere un risultato come nell'immagine qui sotto, dove IPyWidget genera un elemento dell'interfaccia utente sulla pagina che interagisce in modo interattivo con il codice sorgente e inoltre matplotlib visualizza il risultato del codice sotto forma di immagine come grafico di funzione:
Molti esempi IPyWidget lo puoi trovare nei tutorial qui
Cos'altro? ^
Complimenti se sei rimasto e sei arrivato alla fine dell'articolo. Alla fine dell'articolo non ho deliberatamente pubblicato uno script già pronto da installare laboratorio giove in “un clic” per incoraggiare i lavoratori :) Ma puoi farlo da solo, dato che sai già come, avendo raccolto i comandi in un unico script Bash :)
Puoi anche:
- Imposta un nome di rete per il contenitore invece di un indirizzo IP scrivendolo in un semplice
/etc/hosts
e digitare l'indirizzo nel browser http://jupyter.local:8888 - Gioca con il limite delle risorse per il contenitore, per questo leggi il capitolo in funzionalità LXD di base o ottieni maggiori informazioni sul sito degli sviluppatori LXD.
- Cambia il tema:
E molto altro ancora puoi fare! È tutto. Vi auguro il successo!
AGGIORNAMENTO: 15.04.2020/18/30 XNUMX:XNUMX - Corretti errori nel capitolo "Hello, World!"
AGGIORNAMENTO: 16.04.2020/10/00 XNUMX:XNUMX — Corretto e aggiunto testo nella descrizione dell'attivazione del gestore estensioni laboratorio giove
AGGIORNAMENTO: 16.04.2020/10/40 XNUMX:XNUMX — Corretti errori riscontrati nel testo e leggermente modificato in meglio il capitolo “Installazione del software di base e configurazione del sistema”
Fonte: habr.com