Lancio di Jupyter in orbita LXD

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.

Lancio di Jupyter in orbita LXD

Piano di decollo orbitale ^

Lancio di Jupyter in orbita LXD

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 LXD
  • exec - Metodo client LXD che esegue un comando nel contenitore
  • jupyterlab — Identificativo del contenitore
  • -- - Una chiave speciale che specifica di non interpretare ulteriori chiavi come chiavi per lxc e passare il resto della corda così com'è nel contenitore
  • apk — Gestore di pacchetti della distribuzione Alpine Linux
  • add — 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 sudoe 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 .bashrcche 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/taccuino
  • jupyterlab-horizon-theme - Tema dell'interfaccia utente
  • jupyterlab_neon_theme - Tema dell'interfaccia utente
  • jupyterlab-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:

Lancio di Jupyter in orbita LXD

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 dispositivo
  • jupyter — ID del contenitore a cui viene aggiunta la configurazione
  • hostfs - ID del dispositivo. È possibile impostare qualsiasi nome.
  • disk — È indicato il tipo di dispositivo
  • path — Specifica il percorso nel contenitore su cui LXD monterà questo dispositivo
  • source — 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 jupytere 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:

Lancio di Jupyter in orbita LXD

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:

Lancio di Jupyter in orbita LXD

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:

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 binari
  • freetype-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:

Lancio di Jupyter in orbita LXD

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:

Lancio di Jupyter in orbita LXD

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

Aggiungi un commento