Spouštíme Jupyter na orbitu LXD

Zkoušeli jste někdy experimentovat s kódem nebo systémovými nástroji v Linuxu tak, abyste se nemuseli bát o základní systém a neshodili všechno s potížemi v případě chyby kódu, který se má spouštět s rootovskými právy?

A co když je třeba otestovat nebo spustit celý klastr různých mikroslužeb na jednom stroji? Sto nebo dokonce tisíc?

S virtuálními stroji řízenými hypervizorem lze tyto úkoly zvládnout, ale za jakou cenu? Například kontejner v LXD založený na distribuci Alpine Linux minimálně spotřebovává 7,60 MB RAM, a kde kořenový oddíl po spuštění zabírá 9,5 MB! Co ty na to, Ilone Musku? Doporučuji se seznámit se základními možnostmi LXD — systémem kontejnerů v Linuxu.

Poté, co se víc-méně vyjasnilo, co to jsou kontejnery LXD, pojďme dál a přemýšlejme, co kdyby existovala taková platforma-kombajn, kde by bylo možné bezpečně spouštět kód pro hostitele, generovat grafy, dynamicky (interaktivně) spojovat UI widgety s vaším kódem, doplňovat kód textem s blackjackem… a formátováním? Něco jako interaktivní blog? Wow… Chci! Chci! 🙂

Podívej se pod článek, kde spustíme v kontejneru JupyterLab — novou generaci uživatelského rozhraní místo zastaralého Jupyter Notebooku, a také nainstalujeme takové moduly Pythonu jako NumPy, Pandas, Matplotlib, IPyWidgets které umožní dělat všechno uvedené výše a uchovávat to do speciálního souboru — IPython notebooku.

Spouštíme Jupyter na orbitu LXD

Plán vzletu na orbitu ^

Spouštíme Jupyter na orbitu LXD

Navrhneme stručný plán akcí, abychom měli jednodušší realizaci výše uvedené schématu:

  • Nainstalujeme a spustíme kontejner založený na distribuci Alpine Linux. Tento distribuci použijeme, protože je zaměřen na minimalismus a nainstalujeme do něj pouze nezbytný software, nic navíc.
  • Přidáme další virtuální disk do kontejneru, kterému přiřadíme název — hostfs a nainstalujeme na kořenový systém souborů. Tento disk umožní používat soubory na hostiteli z určeného adresáře uvnitř kontejneru. Tímto způsobem budou data nezávislá na kontejneru. V případě odstranění kontejneru data zůstanou na hostiteli. Také je tento systém užitečný pro oddělení certaines dat mezi mnoha kontejnery bez použití standardních síťových mechanismů distribučního kontejneru.
  • Nainstalujeme Bash, sudo, potřebné knihovny, přidáme a nastavíme systémového uživatele.
  • Nainstalujeme Python, moduly a zkompilujeme pro ně binární závislosti.
  • Nainstalujeme a spustíme. JupyterLab, nastavíme vzhled, nainstalujeme rozšíření pro něj.

V tomto článku začneme se spuštěním kontejneru, nebudeme se zabývat instalací a nastavením LXD, to všechno můžete nalézt v jiném článku — Základní možnosti LXD — systém kontejnerů v Linuxu.

Instalace a nastavení základního systému ^

Vytváříme kontejner příkazem, ve kterém uvádíme obraz — alpine3, identifikační číslo pro kontejner — jupyterlab a při potřebě profily konfigurace:

lxc init alpine3 jupyterlab --profile=default --profile=hddroot

Zde používám profil konfigurace hddroot který určuje vytvoření kontejneru s root oddílem v Úložiště umístěném na fyzickém HDD disku:

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: Vytvořeno
locations:
- žádné

To mi dává možnost experimentovat s kontejnery na HDD disku a šetřit zdroje SSD disku, který mám také v systému 🙂 pro který jsem vytvořil samostatný konfigurační profil. ssdroot.

Po vytvoření kontejneru se nachází ve stavu STOPPED, proto ho musíme spustit spuštěním init systému:

lxc start jupyterlab

Zobrazíme seznam kontejnerů v LXD pomocí klíče -c který určuje, které ssloupce se zobrazí na obrazovce:

lxc list -c ns4b
+------------+---------+-------------------+--------------+
|    NAME    |  STATE  |       IPV4        | STORAGE POOL |
+------------+---------+-------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.198 (eth0) | hddpool      |
+------------+---------+-------------------+--------------+

Při vytváření kontejneru byla IP adresa zvolena náhodně, protože jsme použili profily konfigurace default který byl dříve nakonfigurován v článku. Základní možnosti LXD — systém kontejnerů v Linuxu.

Změníme tuto IP adresu na zapamatovatelnější tím, že vytvoříme síťové rozhraní na úrovni kontejneru, nikoli na úrovni konfiguračního profilu, jako je tomu nyní v aktuální konfiguraci. Není to nutné, můžete to přeskočit.

Vytváříme síťové rozhraní eth0 které propojujeme se switchem (síťovým mostem) lxdbr0 ve kterém jsme aktivovali NAT podle předchozího článku, a kontejner nyní bude mít přístup k internetu, a také rozhraní přiřadíme statickou IP adresu — 10.0.5.5:

lxc config device add jupyterlab eth0 nic name=eth0 nictype=bridged parent=lxdbr0 ipv4.address=10.0.5.5

Po přidání zařízení je nutné kontejner restartovat:

lxc restart jupyterlab

Kontrolujeme stav kontejneru:

lxc list -c ns4b
+------------+---------+------------------+--------------+
|    NAME    |  STATE  |       IPV4       | STORAGE POOL |
+------------+---------+------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.5 (eth0)  | hddpool      |
+------------+---------+------------------+--------------+

Instalace základního softwaru a nastavení systému ^

Pro správu našeho kontejneru je nutné nainstalovat následující software:

Package
Popis

bash
GNU Bourne Again shell

bash-completion
Programovatelné dokončení pro shell bash

sudo
Umožnit určitým uživatelům spouštět některé příkazy jako root

shadow
Sada nástrojů pro správu hesel a účtů s podporou pro soubory shadow a PAM

tzdata
Zdroje pro časová pásma a data o letním čase

nano
Klon editoru Pico s vylepšeními

Dále můžete nainstalovat podporu v systému man-pages instalací následujících balíčků — man man-pages mdocml-apropos less

lxc exec jupyterlab -- apk add bash bash-completion sudo shadow tzdata nano

Probereme příkazy a klíče, které jsme použili:

  • lxc — Volání klienta LXD
  • exec — Metoda klienta LXD, která spouští příkaz v kontejneru
  • jupyterlab — Identifikátor kontejneru
  • -- — Speciální klíč, který udává, že klíče dále nebudou interpretovány jako klíče pro lxc a předat celý zbývající řetězec tak, jak je, do kontejneru
  • apk Správce balíčků distribuce Alpine Linux
  • add — Metoda správce balíčků, která instaluje uvedené po příkazu balíčky

Nyní nastavíme v systému časové pásmo Europe/Moscow:

lxc exec jupyterlab -- cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime

Po nastavení časového pásma již balíček tzdata v systému není potřebný, bude zabírat místo, proto jej odstraníme:

lxc exec jupyterlab -- apk del tzdata

Kontrolujeme časové pásmo:

lxc exec jupyterlab -- date

Wed Apr 15 10:49:56 MSK 2020

Abychom nepřicházeli o čas při nastavování Bash pro nové uživatele v kontejneru, následujícími kroky zkopírujeme do něj z hostitelského systému již hotové skel soubory. To umožní vylepšit Bash v kontejneru v interaktivním režimu. Můj hostitelský systém je Manjaro Linux a kopírované soubory /etc/skel/.bash_profile, /etc/skel/.bashrc, /etc/skel/.dir_colors v zásadě fungují na Alpine Linux a nezpůsobují kritické problémy, ale u vás to může být jiná distribuce a budete si muset poradit sami v případě chyby spouštěného Bash v kontejneru.

Kopírujeme skel soubory do kontejneru. Klíč --create-dirs vytvoří potřebné adresáře, pokud neexistují:

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

Pro již existujícího root uživatele zkopírujeme do domácího adresáře právě zkopírované skel soubory do kontejneru:

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

V Alpine Linux je systémový shell nastaven pro uživatele /bin/sh, nahradíme ho u root uživatele Bash:

lxc exec jupyterlab -- usermod --shell=/bin/bash root

Aby root uživatel nebyl bezheslový, musí si nastavit heslo. Následující příkaz vygeneruje a nastaví mu nové náhodné heslo, které uvidíte na obrazovce po jeho provedení:

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""

Nové heslo: sFiXEvBswuWA

Také vytvoříme nového systémového uživatele — jupyter pro kterého později nastavíme JupyterLab:

lxc exec jupyterlab -- useradd --create-home --shell=/bin/bash jupyter

Vygenerujeme a nastavíme mu heslo:

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""

Nové heslo: ZIcbzWrF8tki

Dále provedeme dva příkazy, první vytvoří systémovou skupinu sudo, a druhý do ní přidá uživatele jupyter:

lxc exec jupyterlab -- groupadd --system sudo
lxc exec jupyterlab -- groupmems --group sudo --add jupyter

Podíváme se, do jakých skupin uživatel patří jupyter:

lxc exec jupyterlab -- id -Gn jupyter

jupyter sudo

Všechno – v pořádku, jdeme dál.

Povolení všem uživatelům, kteří jsou ve skupině sudo používat příkaz sudo. Pro to spusťte následující skript, který sed odkomentovává řádek parametru v konfiguračním souboru /etc/sudoers:

lxc exec jupyterlab -- /bin/bash -c "sed --in-place -e '/^#[ t]*%sudo[ t]*ALL=(ALL)[ t]*ALL$/ s/^[# ]*// ' /etc/sudoers"

Instalace a nastavení JupyterLab ^

JupyterLab – je to aplikace Python, takže nejprve musíme nainstalovat tento interpret. JupyterLab Také budeme instalovat pomocí Python správcovského balíčku pip, a nikoli systémového, protože v systémovém repozitáři může být zastaralý, a proto musíme ručně povolit závislosti tím, že nainstalujeme následující balíčky — python3 python3-dev gcc libc-dev zeromq-dev:

lxc exec jupyterlab -- apk add python3 python3-dev gcc libc-dev zeromq-dev

Aktualizujeme python moduly a správce balíčků pip na aktuální verzi:

lxc exec jupyterlab -- python3 -m pip install --upgrade pip setuptools wheel

Instalujeme JupyterLab přes správce balíčků pip:

lxc exec jupyterlab -- python3 -m pip install jupyterlab

Protože rozšíření v JupyterLab jsou experimentální a oficiálně nejsou dodávána s balíčkem jupyterlab, musíme je nainstalovat a nakonfigurovat ručně.

Nainstalujeme NodeJS a balíčkový manažer pro něj — NPM, protože JupyterLab je používá pro svá rozšíření:

lxc exec jupyterlab -- apk add nodejs npm

Aby rozšíření pro JupyterLab které nainstalujeme, fungovala, je nutné je instalovat do uživatelského adresáře, protože aplikace se spustí pod uživatelským účtem jupyter. Problém je v tom, že v příkazu pro spuštění není žádný parametr, kterým bychom mohli předat adresář, aplikace přijímá pouze proměnnou prostředí, a proto ji musíme definovat. K tomu použijeme příkaz pro export proměnné JUPYTERLAB_DIR v uživatelském prostředí jupyter, do souboru .bashrc, který se provádí pokaždé, když se uživatel přihlásí do systému:

lxc exec jupyterlab -- su -l jupyter -c "echo -e "nexport JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab" >> .bashrc"

Dalším příkazem nainstalujeme speciální rozšíření — manažer rozšíření v 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"

Teď už je vše připraveno k prvním spuštění JupyterLab, ale můžeme také nainstalovat několik užitečných rozšíření:

  • toc — Obsah, generuje seznam nadpisů v dokumentu/notebooku
  • jupyterlab-horizon-theme — Téma vzhledu UI
  • jupyterlab_neon_theme — Téma vzhledu UI
  • jupyterlab-ubu-theme — Ještě jedno téma vzhledu od autora tohoto článku 🙂 Ale v tomto případě bude instalace provedena z repozitáře GitHub

Takže postupně zadejte následující příkazy pro instalaci těchto rozšíření:

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"

Po instalaci rozšíření je musíme zkompilovat, protože jsme při instalaci zadali klíč --no-build pro úsporu času. Nyní se výrazně zrychlíme tím, že je zkompilujeme najednou:

lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter lab build"

Nyní spusťte následující dva příkazy pro první spuštění JupyterLab. Bylo by možné ho spustit jediným příkazem, ale v tomto případě by se spouštěcí příkaz, který byste měli mít na paměti, uložil do bash'e v kontejneru, a ne na hostiteli, kde už je tak dost příkazů pro záznam do historie 🙂

Přihlásíme se do kontejneru jako uživatel jupyter:

lxc exec jupyterlab -- su -l jupyter

Dále spusťte JupyterLab s klíči a parametry jak je uvedeno:

[jupyter@jupyterlab ~]$ jupyter lab --ip=0.0.0.0 --no-browser

Přejděte v internetovém prohlížeči na adresu http://10.0.5.5:8888 a na otevřené stránce zadejte token přístup, který uvidíte v konzoli. Zkopírujte ho a vložte na stránku, poté klikněte na Přihlášení. Po přihlášení přejděte vlevo do nabídky rozšíření, jak je znázorněno na obrázku níže, kde budete vyzváni, abyste při aktivaci správce rozšíření přijali na sebe rizika spojená s instalací rozšíření od třetích stran, za která tým JupyterLab development nenese odpovědnost:

Spouštíme Jupyter na orbitu LXD

Nicméně, právě proto zcela izolujeme JupyterLab a umisťujeme ho do kontejneru, aby třetí strany využívající NodeJS nemohly minimálně ukradnout data na disku kromě těch, které otevřeme uvnitř kontejneru. Dostupnost vašich soukromých dokumentů na hostiteli /home z kontejnerových procesů se pravděpodobně nepodaří, a pokud se podaří, bude k tomu zapotřebí oprávnění k souborům na hostitelské systému, jelikož spouštíme kontejner v neprivilegovaném režimu. Na základě těchto informací můžete zhodnotit rizika spojená se zapnutím rozšíření v JupyterLab.

Vytvářené IPython notebooky (stránky v JupyterLab) nyní budou vytvářeny v domácím adresáři uživatele — /home/jupyter, ale v našich plánech je sdílet data (propojit) mezi hostitelem a kontejnerem, takže se vraťte do konzole a zastavte JupyterLab provedením hotkey — CTRL+C a odpovězením y na dotaz. Poté ukončete interaktivní relaci uživatele jupyter provedením hotkey CTRL+D.

Sdílíme data s hostitelem ^

Abychom sdíleli data s hostitelem, je třeba vytvořit v kontejneru takové zařízení, které to umožňuje, a proto zadejte následující příkaz, kde uvedeme následující klíče:

  • lxc config device add — Příkaz přidává konfiguraci zařízení
  • jupyter — Identifikátor kontejneru, do kterého se přidává konfigurace
  • hostfs — Identifikátor zařízení. Můžete zadat libovolný název.
  • disk — Udává se typ zařízení
  • path — Udává se cesta v kontejneru, ke které LXD připojí toto zařízení
  • source — Udává se zdroj, cesta k adresáři na hostiteli, který chcete sdílet s kontejnerem. Zadejte cestu podle svých preferencí
lxc config device add jupyterlab hostfs disk path=\/mnt\/hostfs source=\/home\/dv\/projects\/ipython-notebooks

Pro adresář /home/dv/projects/ipython-notebooks musí být nainstalováno oprávnění pro uživatele kontejneru, který nyní má UID rovný SubUID + UID, viz kapitola Bezpečnost. Oprávnění kontejnerů v článku Základní možnosti LXD — systém kontejnerů v Linuxu.

Nastavíme oprávnění na hostiteli, kde vlastníkem bude uživatel kontejneru jupyter, a proměnná $USER určí vašeho hostitelského uživatele jako skupinu:

sudo chown 1001000:$USER \/home\/dv\/projects\/ipython-notebooks

Hello, World! ^

Pokud máte ještě otevřenou konzolovou relaci v kontejneru s JupyterLab, restartujte ji s novým klíčem --notebook-dir nastavením hodnoty /mnt/hostfs jako cestu k hlavnímu adresáři notebooků v kontejneru pro zařízení, které jsme vytvořili v předchozím kroku:

jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\/mnt\/hostfs

Poté přejděte na stránku http://10.0.5.5:8888 a vytvořte váš první notebook kliknutím na tlačítko na stránce, jak je znázorněno na obrázku níže:

Spouštíme Jupyter na orbitu LXD

Poté do pole na stránce zadejte kód v jazyce Python, který vypíše klasické Hello World!. Po dokončení zadávání stiskněte CTRL+ENTER nebo tlačítko „play“ na panelu nástrojů nahoře, aby JupyterLab tento příkaz provedl:

Spouštíme Jupyter na orbitu LXD

Tímto je téměř vše připraveno k použití, ale bylo by nezajímavé, kdybychom nenainstalovali další moduly Pythonu (plnohodnotné aplikace), které výrazně rozšiřují standardní možnosti Pythonu v JupyterLab, proto pokračujeme dále 🙂

P.S. Zajímavé je, že stará implementace Jupyter s kódovým označením Jupyter Notebook nikam nezmizela a existuje souběžně s JupyterLab. Pro přechod na starou verzi přejděte na odkaz a přidejte do adresy příponu/tree, a přechod na novou verzi se provádí s příponou /lab, ale není nutné ji uvádět:

Rozšiřujeme možnosti Pythonu ^

V této části nainstalujeme takové mocné moduly jazyka Python, jako jsou NumPy, Pandas, Matplotlib, IPyWidgets výsledky práce, které se integrují do notebooků JupyterLab.

Než nainstalujeme uvedené moduly Pythonu prostřednictvím správce balíků, pip musíme nejprve povolit systémové závislosti v Alpine Linux:

  • g++ — Potřebné pro kompilaci modulů, protože některé z nich jsou realizovány v jazyce C++ a připojují se k Pythonu za runtime jako binární moduly
  • freetype-dev — závislost pro Python modul Matplotlib

Instalujeme závislosti:

lxc exec jupyterlab -- apk add g++ freetype-dev

Je tu jeden problém, v aktuálním stavu distribuce Alpine Linux nelze zkompilovat novou verzi NumPy, objeví se chyba kompilace, kterou se mi nepodařilo vyřešit:

CHYBA: Nemohli jsme zkompilovat kola pro numpy, která používají PEP 517 a nelze je nainstalovat přímo

Proto tento modul nainstalujeme jako systémový balíček, který distribuuje již zkompilovanou verzi, ale o něco starší, než je dostupná nyní na webu:

lxc exec jupyterlab -- apk add py3-numpy py3-numpy-dev

Dále instalujeme Python moduly přes správce balíčků pip. Buďte trpěliví, protože některé moduly budou kompilovány a to zabere několik minut. Na mém stroji kompilace trvala ~15 minut:

lxc exec jupyterlab -- python3 -m pip install pandas ipywidgets matplotlib

Čistíme keše instalací:

lxc exec jupyterlab -- rm -rf /home/*/.cache/pip/*
lxc exec jupyterlab -- rm -rf /root/.cache/pip/*

Testujeme moduly v JupyterLab ^

Pokud máte spuštěno JupyterLab, restartujte ho, aby se nové nainstalované moduly aktivovaly. K tomu v konzolové relaci stiskněte CTRL+C tam, kde je spuštěn, a zadejte y na výzvu k zastavení, a poté znovu spusťte JupyterLab stisknutím šipky na klávesnici "nahoru", abyste nemuseli znovu zadávat příkaz a pak Enter chcete-li spustit:

jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\/mnt\/hostfs

Přejděte na stránku http://10.0.5.5:8888/lab nebo obnovte stránku v prohlížeči a poté zadejte následující kód do nové buňky notebooku:

%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

Měli byste dostat výsledek jako na obrázku níže, kde IPyWidgets generuje UI prvek na stránce, který interaktivně spolupracuje se zdrojovým kódem a také Matplotlib vypisuje výsledek kódu ve formě obrázku jako graf funkce:

Spouštíme Jupyter na orbitu LXD

Mnoho příkladů IPyWidgets můžete najít v tutoriálech zde

Co dál? ^

Jste skvělí, pokud jste zůstali a došli až na konec článku. Záměrně jsem na konci článku nedal hotový skript pro instalaci JupyterLab v "jednom kliknutí", abych povzbudil pracanty 🙂 Ale můžete to udělat sami, protože už víte jak, shromáždit příkazy do jednoho Bash skriptu 🙂

Také můžete:

  • Nastavit síťové jméno pro kontejner místo IP adresy tím, že ho napíšete do jednoduchého /etc/hosts a v prohlížeči zadávat adresu http://jupyter.local:8888
  • Hrajte si s omezením zdrojů pro kontejner, k tomu si přečtěte kapitolu v základních možnostech LXD nebo získáte více informací na stránkách vývojáře LXD.
  • Změnit vzhled:

Spouštíme Jupyter na orbitu LXD

A ještě mnohem víc můžete! To je vše. Přeji vám hodně úspěchů!

AKTUALIZACE: 15.04.2020 18:30 — Opraveny chyby v kapitole „Hello, World!“
AKTUALIZACE: 16.04.2020 10:00 — Upraven a přidán text do popisu aktivace správce rozšíření JupyterLab
AKTUALIZACE: 16.04.2020 10:40 — Opraveny nalezené chyby v textu a kapitola „Instalace základního softwaru a konfigurace systému“ byla mírně vylepšena

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster