Чи доводилося вам експериментувати з кодом або системними утилітами в Linux так, щоб не трястися за базову систему і не знести все з тельбухом у разі помилки коду який повинен запуститися з root-привілеями?
А як щодо того, що припустимо, необхідно протестувати або запустити цілий кластер різноманітних мікросервісів на одній машині? Сотню чи навіть тисячу?
З віртуальними машинами керовані гіпервізором такі завдання вирішити може й вийде, але якою ціною? Наприклад, контейнер в LXD на базі дистрибутива Alpine Linux мінімально споживає всього 7.60MB
ОЗУ, і де кореневий розділ після запуску займає 9.5MB
! Як тобі таке, Ілон Маск? Рекомендую ознайомитись з базовими можливостями LXD - системи контейнерів у Linux
Після того, як загалом стало ясно, що таке контейнери LXD, підемо далі і подумаємо, а що, якби була така платформа-комбайн, де можна було б безпечно запускати код для хоста, генерувати графіки, динамічно (інтерактивно) зв'язувати UI- віджети з твоїм кодом, доповнювати код текстом із блэкджеком… форматуванням? Щось на кшталт інтерактивного блогу? Вауу… Хочу! Хочу! 🙂
Заглядай під кат де ми запустимо в контейнері лабораторія Юпітера — наступної генерації інтерфейсу користувача замість застарілого Jupyter Notebook, а також встановимо такі модулі Python як numpy, Панди, Матплотліб, IPyWidgets які дозволять витворяти все перераховане вище та зберігати це все у спеціальному файлі – IPython-ноутбуку.
Навігація
- План зльоту на орбіту
- Встановлення та налаштування базової системи
- Встановлення базового софту та налаштування системи
- Встановлення та налаштування JupyterLab
- Поділяємо дані з хостом
- Привіт Світ!
- Розширюємо можливості Python
- Тестуємо модулі в JupyterLab
- Що ще?
План зльоту на орбіту ^
Накидаємо короткий план дій, щоб нам було простіше реалізувати схему вище:
- Встановимо та запустимо контейнер на базі дистрибутива Alpine Linux. Ми будемо використовувати цей дистрибутив так як він спрямований на мінімалістичність і встановимо в нього тільки найнеобхідніший софт, нічого зайвого.
- Додамо додатковий віртуальний диск у контейнері якому задамо ім'я
hostfs
та змонтуємо до кореневої ФС. Цей диск дозволить використовувати файли на хості із заданого каталогу всередині контейнера. Таким чином, дані будуть у нас незалежні від контейнера. У разі видалення контейнера дані залишаться на хості. Також, ця схема корисна для поділу одних даних між багатьма контейнерами, не використовуючи штатні мережеві механізми дистрибутива контейнера. - Встановимо Bash, sudo, необхідні бібліотеки, додамо та налаштуємо системного користувача
- Встановимо Python, модулі та скомпілюємо для них бінарні залежності
- Встановимо та запустимо лабораторія Юпітера, Налаштуємо зовнішній вигляд, встановимо розширення для нього.
У цій статті ми з вами почнемо з запуску контейнера, не розглядатимемо встановлення та налаштування LXD, все це ви можете знайти в іншій статті. Базові можливості LXD - системи контейнерів у Linux.
Встановлення та налаштування базової системи ^
Створюємо контейнер командою, в якій вказуємо образ. alpine3
ідентифікатор для контейнера jupyterlab
та за необхідності профілі конфігурації:
lxc init alpine3 jupyterlab --profile=default --profile=hddroot
Тут я використовую профіль конфігурації hddroot
який вказує створити контейнер з root-розділом в Пул зберігання розташований на фізичному HDD диску:
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
Це дає мені можливість експерементувати з контейнерами на диску HDD заощаджуючи ресурси SSD диска який також є в моїй системі 🙂 для якого у мене створений окремий профіль конфігурації ssdroot
.
Після створення контейнера він перебуває в стані STOPPED
тому нам треба його стартанути запустивши в ньому init-систему:
lxc start jupyterlab
Виведемо список контейнерів у LXD використовуючи ключ -c
який вказує які columns вивести на екран:
lxc list -c ns4b
+------------+---------+-------------------+--------------+
| NAME | STATE | IPV4 | STORAGE POOL |
+------------+---------+-------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.198 (eth0) | hddpool |
+------------+---------+-------------------+--------------+
При створенні контейнера IP адреса вибралася випадковим чином, оскільки ми використовували профіль конфігурації default
який був раніше налаштований у статті Базові можливості LXD - системи контейнерів у Linux.
Ми змінимо цю IP-адресу на більш запам'ятовується, створивши мережевий інтерфейс лише на рівні контейнера, а чи не лише на рівні профілю конфігурації як це нині у поточної конфігурації. Це не обов'язково робити, ви можете пропустити це.
Створюємо мережевий інтерфейс eth0
який лінкуємо з комутатором (мережевим мостом) lxdbr0
в якому ми включили NAT за минулою статтею та контейнером зараз буде доступ в Інтернет, а також інтерфейсу призначаємо статичну IP адресу 10.0.5.5
:
lxc config device add jupyterlab eth0 nic name=eth0 nictype=bridged parent=lxdbr0 ipv4.address=10.0.5.5
Після додавання пристрою контейнер необхідно перезавантажити:
lxc restart jupyterlab
Перевіряємо статус контейнера:
lxc list -c ns4b
+------------+---------+------------------+--------------+
| NAME | STATE | IPV4 | STORAGE POOL |
+------------+---------+------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.5 (eth0) | hddpool |
+------------+---------+------------------+--------------+
Встановлення базового софту та налаштування системи ^
Для адміністрування нашого контейнера необхідно встановити наступний софт:
пакет
Опис
бити
The GNU Bourne Again shell
bash-завершення
Programmable completion for bash shell
Суду
Give certain users ability to run some commands as root
тінь
Password and account management tool suite with support for shadow files and PAM
тздата
Sources for time zone and daylight saving time data
нано
Pico editor clone with enhancements
Додатково, ви можете встановити підтримку в системі man-pages, встановивши наступні пакети. man man-pages mdocml-apropos less
lxc exec jupyterlab -- apk add bash bash-completion sudo shadow tzdata nano
Розберемо команди та ключі, які ми використовували:
lxc
- Виклик клієнта LXDexec
— Метод клієнта LXD, який запускає команду у контейнеріjupyterlab
- Ідентифікатор контейнера--
— Спеціальний ключ, який вказує не інтерпретувати далі ключі як ключіlxc
і передати весь рядок, що залишився, як є в контейнерapk
- Пакетний менеджер дистрибутива Alpine Linuxadd
— Метод пакетного менеджера, який встановлює вказані після команди пакети
Далі, встановимо в системі тайм-зону Europe/Moscow
:
lxc exec jupyterlab -- cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime
Після встановлення тайм-зони, пакет tzdata
в системі більше не потрібен, він займатиме місце, тому видалимо його:
lxc exec jupyterlab -- apk del tzdata
Перевіряємо тайм-зону:
lxc exec jupyterlab -- date
Wed Apr 15 10:49:56 MSK 2020
Щоб не витрачати багато часу на налаштування Bash для нових користувачів у контейнері, наступними діями ми скопіюємо в нього з хостової системи вже готові skel-файли. Це дозволить прикрасити Bash у контейнері в інтрактивному режимі. У мене хостова система - Manjaro Linux і копіювані файли /etc/skel/.bash_profile
, /etc/skel/.bashrc
, /etc/skel/.dir_colors
в принципі підходять до Alpine Linux і критичних проблем не викликають, але у вас це може бути інший дистрибутив і вам потрібно самостійно розібратися у разі помилки Bash, що запускається в контейнері.
Копіюємо skel-файли у контейнер. Ключ --create-dirs
створить необхідні директорії, якщо вони не існують:
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
Для існуючого root-користувача скопіюємо в домашню директорію щойно скопійовані в контейнер skel-файли:
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 для користувачів встановлюється системна оболонка /bin/sh
, ми замінимо її у root
користувача на Bash:
lxc exec jupyterlab -- usermod --shell=/bin/bash root
Щоб root
Користувач не був безпарольним, йому потрібно встановити пароль. Наступна команда згенерує та встановить йому новий випадковий пароль, який ви побачите на екрані консолі після її виконання:
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
Також створимо нового системного користувача. jupyter
для якого пізніше налаштуємо лабораторія Юпітера:
lxc exec jupyterlab -- useradd --create-home --shell=/bin/bash jupyter
Згенеруємо та встановимо йому пароль:
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
Далі виконаємо дві команди, перша створить системну групу sudo
, а друга додасть до неї користувача jupyter
:
lxc exec jupyterlab -- groupadd --system sudo
lxc exec jupyterlab -- groupmems --group sudo --add jupyter
Переглянемо, до яких груп входить користувач jupyter
:
lxc exec jupyterlab -- id -Gn jupyter
jupyter sudo
Все - ок, рухаємося далі.
Дозволимо всім користувачам, які входять до групи sudo
використовувати команду sudo
. Для цього виконайте наступний скрипт, де sed
розкоментує рядок параметра у конфігураційному файлі /etc/sudoers
:
lxc exec jupyterlab -- /bin/bash -c "sed --in-place -e '/^#[ t]*%sudo[ t]*ALL=(ALL)[ t]*ALL$/ s/^[# ]*//' /etc/sudoers"
Встановлення та налаштування JupyterLab ^
лабораторія Юпітера — це Python додаток, тому ми повинні спочатку встановити цей інтерпретатор. Також, лабораторія Юпітера ми будемо встановлювати за допомогою пітонівського пакетного менеджера pip
, а не системного, тому що в системному репозиторії він може бути застарілим, і тому ми повинні вручну дозволити залежності для нього встановивши такі пакети. python3 python3-dev gcc libc-dev zeromq-dev
:
lxc exec jupyterlab -- apk add python3 python3-dev gcc libc-dev zeromq-dev
Обновимо python-модулі та пакетний менеджер pip
до актуальної версії:
lxc exec jupyterlab -- python3 -m pip install --upgrade pip setuptools wheel
Встановлюємо лабораторія Юпітера через пакетний менеджер pip
:
lxc exec jupyterlab -- python3 -m pip install jupyterlab
Оскільки розширення в лабораторія Юпітера є експериментальними та офіційно вони не поставляються разом з пакетом jupyterlab, тому, ми повинні встановити та налаштувати це вручну.
Встановимо NodeJS і менеджер пакетів для нього - NPM, оскільки лабораторія Юпітера використовує їх для своїх розширень:
lxc exec jupyterlab -- apk add nodejs npm
Щоб розширення для лабораторія Юпітера які ми встановимо працювали, їх потрібно встановлювати в директорію користувача так як додаток буде запускатися від користувача jupyter
. Проблема в тому, що немає параметра в команді запуску якої можна передати каталог, програма сприймає лише змінну оточення і тому ми її повинні визначити. Для цього ми пропишемо команду експорту змінної JUPYTERLAB_DIR
в оточенні користувача jupyter
у файл .bashrc
, який виконується щоразу під час входу користувача до системи:
lxc exec jupyterlab -- su -l jupyter -c "echo -e "nexport JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab" >> .bashrc"
Наступною командою встановимо спеціальне розширення — менеджер розширень у лабораторія Юпітера:
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter labextension install --no-build @jupyter-widgets/jupyterlab-manager"
Наразі вже все готове для першого запуску лабораторія Юпітера, але ми можемо встановити кілька корисних розширень:
toc
— Table of Contents, генерує список заголовків у статті/ноутбуціjupyterlab-horizon-theme
- Тема оформлення UIjupyterlab_neon_theme
- Тема оформлення UIjupyterlab-ubu-theme
- Ще одна тема оформлення від автора цієї статті 🙂 Але в цьому випадку буде показана установка з репозиторію GitHub
Отже, виконайте наступні команди, щоб встановити ці розширення:
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"
Після встановлення розширень ми повинні їх скомпілювати, тому що раніше при установці вказували ключ --no-build
для економії часу. Зараз ми значно прискоримося скомпілювавши їх разом за один раз:
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter lab build"
Наразі виконайте наступні дві команди для першого запуску лабораторія Юпітера. Можна було б його запустити однією командою, але в цьому випадку, команда запуску, яку в умі важко тримати, вона запам'ятовуватиметься bash'ем у контейнері, а не на хості, де і так команд вистачає для запису їх в історію 🙂
Логін у контейнері як користувач jupyter
:
lxc exec jupyterlab -- su -l jupyter
Далі запустіть лабораторія Юпітера з ключами та параметрами як зазначено:
[jupyter@jupyterlab ~]$ jupyter lab --ip=0.0.0.0 --no-browser
Перейдіть на веб-браузер за адресою http://10.0.5.5:8888 і на сторінці введіть знак доступу який ви побачите у консолі. Скопіюйте його та вставте на сторінці, потім натисніть Увійти. Після входу, перейдіть зліва в меню розширень, як показано на малюнку нижче, де вам запропонують при активації менеджера розширень прийняти на себе ризики безпеки встановлюючи розширення від третіх осіб за які команда JupyterLab development відповідальності не несе:
Однак ми якраз для цього ізолюємо цілком лабораторія Юпітера і поміщаємо його в контейнер, щоб сторонні розширення, що вимагають і використовують NodeJS, не змогли як мінімум викрасти дані на диску крім тих, які ми відкриємо всередині контейнера. Дістатися до ваших приватних документів на хості /home
процесам з контейнера навряд чи вийде, а якщо і вийде, то на це потрібно мати привілеї на файли в хостовій системі, тому що ми запускаємо контейнер в непривілейованому режимі. Виходячи з цієї інформації ви можете оцінити ризик включення розширень у лабораторія Юпітера.
Створені IPython-ноутбуки (сторінки в лабораторія Юпітера) зараз будуть створюватися в домашній директорії користувача /home/jupyter
, але в наших планах розділити дані (розшарити) між хостом і контейнером, тому поверніться в консоль і зупиніть лабораторія Юпітера виконавши hotkey - CTRL+C
і відповівши y
на запит. Потім розірвіть інтерактивну сесію користувача jupyter
виконавши хоткей CTRL+D
.
Поділяємо дані з хостом ^
Щоб розділити дані з хостом, потрібно створити в контейнері такий пристрій, який це дозволяє робити і для цього виконайте наступну команду, де ми вказуємо наступні ключі:
lxc config device add
— Команда додає конфігурацію пристроюjupyter
— Ідентифікатор контейнера, до якого додається конфігураціяhostfs
- Ідентифікатор пристрою. Ви можете поставити будь-яке ім'я.disk
— Вказується тип пристроюpath
— Вказується шлях у контейнері, до якого LXD змонтує цей пристрій.source
- Вказується джерело, шлях до каталогу на хості який ви хочете розділити з контейнером. Вкажіть шлях відповідно до ваших уподобань
lxc config device add jupyterlab hostfs disk path=/mnt/hostfs source=/home/dv/projects/ipython-notebooks
Для каталогу /home/dv/projects/ipython-notebooks
має бути встановлена роздільна здатність контейнерного користувача який зараз має UID рівний SubUID + UID
дивіться розділ Безпека. Привілеї контейнерів у статті Базові можливості LXD - системи контейнерів у Linux.
Встановлюємо дозвіл на хості, де власником буде контейнерний користувач jupyter
, а змінна $USER
вкаже вашого хостового користувача як групу:
sudo chown 1001000:$USER /home/dv/projects/ipython-notebooks
Привіт Світ! ^
Якщо у вас ще відкрита консольна сесія в контейнері з лабораторія Юпітера, то перезапустіть її з новим ключем --notebook-dir
задавши значення /mnt/hostfs
як шлях до кореня ноутбуків у контейнері для пристрою, який ми створили в попередньому кроці:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=/mnt/hostfs
Потім перейдіть на сторінку http://10.0.5.5:8888 і створіть перший ваш ноутбук натиснувши кнопку на сторінці як вказано на малюнку нижче:
Потім у полі на сторінці введіть код мовою Python, який виведе класичний Hello World!
. Після введення натисніть CTRL+ENTER
або кнопку «play» на панелі інструментів зверху, щоб JupyterLab виконав це:
На цьому майже все готове до використання, але буде нецікаво, якщо ми не встановимо додаткові Python-модулі (повноцінні додатки), які дозволяють значно розширити стандартні можливості Python у лабораторія Юпітератому рухаємося далі 🙂
PS Цікаво те, що стара реалізація Юпітер під кодовим ім'ям Jupyter Notebook нікуди не поділася і вона існує паралельно з лабораторія Юпітера. Для переходу до старої версії перейдіть за посиланням, додавши на адресу суфікс/tree
, а перехід до нової версії здійснюється із суфіксом /lab
, Але його не обов'язково вказувати:
- Jupyter Notebook http://10.0.5.5:8888/tree
- Jupyter Lab - http://10.0.5.5:8888/lab
Розширюємо можливості Python ^
У цьому розділі ми встановимо такі потужні модулі мови Python як numpy, Панди, Матплотліб, IPyWidgets результати роботи яких інтегруються в ноутбуки лабораторія Юпітера.
Перш ніж встановити перераховані модулі Python через пакетний менеджер pip
ми повинні спочатку дозволити системні залежності в Alpine Linux:
g++
— Потрібен для компіляції модулів, оскільки деякі з них реалізовані мовою C + + і підключаються до Python у рантаймі як бінарні модуліfreetype-dev
- Залежність для Python модуля Матплотліб
Встановлюємо залежності:
lxc exec jupyterlab -- apk add g++ freetype-dev
Є одна проблема, в поточному стані дистрибутива Alpine Linux скомпілювати нову версію NumPy не вийде, вилетить помилка компіляції, яку мені не вдалося вирішити:
ERROR: Could not build wheels for numpy which use PEP 517 and cannot be installed directly
Тому цей модуль ми встановимо як системний пакет, який поширює вже скомпільовану версію, але трохи старіший, ніж доступна зараз на сайті:
lxc exec jupyterlab -- apk add py3-numpy py3-numpy-dev
Далі встановлюємо Python-модулі через пакетний менеджер pip
. Наберіться терпіння, оскільки деякі модулі компілюватимуться і це займе кілька хвилин. На моїй машині компіляція зайняла ~15 хвилин:
lxc exec jupyterlab -- python3 -m pip install pandas ipywidgets matplotlib
Чистимо кеші установок:
lxc exec jupyterlab -- rm -rf /home/*/.cache/pip/*
lxc exec jupyterlab -- rm -rf /root/.cache/pip/*
Тестуємо модулі в JupyterLab ^
Якщо у вас запущено лабораторія Юпітера, перезапустіть його, щоб активувати нові модулі. Для цього у консольній сесії натисніть CTRL+C
там, де він у вас запущений і введіть y
на зупинку, а потім запустіть заново лабораторія Юпітера натиснувши стрілку на клавіатурі «вгору», щоб не вводити команду заново і потім Enter
щоб запустити:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=/mnt/hostfs
Перейдіть на сторінку http://10.0.5.5:8888/lab або оновіть сторінку в браузері, а потім введіть наступний код у новому комірці ноутбука:
%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
У вас повинен вийти результат як на малюнку нижче, де IPyWidgets генерує UI-елемент на сторінці, який інтерактивно взаємодіє з вихідним кодом, а також Матплотліб виводить результат коду у вигляді картинки як графік функції:
Багато прикладів IPyWidgets ви можете знайти в туторіалах тут
Що ще? ^
Ви молодці, якщо залишилися і дійшли до кінця статті. Я спеціально не став викладати готовий скрипт наприкінці статті, який би встановив лабораторія Юпітера в «один клік», щоб заохотити трудівників 🙂 Але ви можете це зробити самостійно, тому що вже знаєте як зібравши команди в єдиний Bash скрипт 🙂
Також, ви можете:
- Задати мережеве ім'я для контейнера замість IP адреси, прописавши його в простому
/etc/hosts
та у браузері набирати адресу http://jupyter.local:8888 - Погратися з обмеженням ресурсів для контейнера, для цього прочитайте розділ у базових можливостях LXD або отримайте більше інформації на сайті розробника LXD.
- Змінити тему оформлення:
І багато чого ще можете! На цьому все. Бажаю вам успіху!
UPDATE: 15.04.2020 18:30 — Виправив помилки у розділі «Hello, World!»
UPDATE: 16.04.2020 10:00 — Скоригував та додав текст в описі активації менеджера розширень лабораторія Юпітера
UPDATE: 16.04.2020 10:40 — Виправив знайдені помилки в тексті і трохи змінив на краще розділ «Встановлення базового софту та налаштування системи»
Джерело: habr.com