Приходилось ли вам эксперементировать с кодом или системными утилитами в Linux так, чтобы не трястись за базовую систему и не снести всё с потрохами в случае ошибки кода который должен запустится с root-привилегиями?
А как на счет того, что допустим, необходимо протестировать или запустить в проде целый кластер разнообразных микросервисов на одной машине? Сотню или даже тысячу?
С виртуальными машинами управляемые гипервизором такие задачи решить может и получится, но какой ценой? Например, контейнер в LXD на базе дистрибутива Alpine Linux минимально потребляет всего 7.60MB ОЗУ, и где корневой раздел после запуска занимает 9.5MB! Как тебе такое, Илон Маск? Рекомендую ознакомиться с базовыми возможностями LXD — системы контейнеров в Linux
После того, как в целом стало ясно, что такое контейнеры LXD, пойдем дальше и подумаем, а что, если бы была такая платформа-комбайн, где можно было бы безопасно запускать код для хоста, генерировать графики, динамически (интерактивно) связывать UI-виджеты с твоим кодом, дополнять код текстом с блекджеком… форматированием? Что-то типа интерактивного блога? Вауу… Хочу! Хочу! 🙂
Заглядывай под кат где мы запустим в контейнере JupyterLab — следующей генерации пользовательского интерфейса вместо устаревшего Jupyter Notebook, а также установим такие модули Python как NumPy, Pandas, Matplotlib, IPyWidgets которые позволят вытворять всё перечисленное выше и сохранять это всё в специальном файле — IPython-ноутбуке.
Навигация
- План взлёта на орбиту
- Установка и настройка базовой системы
- Установка базового софта и настройка системы
- Установка и настройка JupyterLab
- Разделяем данные с хостом
- Hello, World!
- Расширяем возможности Python
- Тестируем модули в JupyterLab
- Что ещё?
План взлёта на орбиту ^
Накидаем краткий план действий, чтобы нам было проще реализовать схему выше:
- Установим и запустим контейнер на базе дистрибутива Alpine Linux. Мы будем использовать этот дистрибутив так как он направлен на минималистичность и установим в него только самый необходимый софт, ничего лишнего.
- Добавим дополнительный виртуальный диск в контейнере которому зададим имя —
hostfsи смонтируем к корневой ФС. Этот диск даст возможность использовать файлы на хосте из заданного каталога внутри контейнера. Тем самым данные будут у нас независимы от контейнера. В случае удаления контейнера, данные остануться на хосте. Также, эта схема полезна для разделения одних данных между многими контейнерами не используя штатные сетевые механизмы дистрибутива контейнера. - Установим Bash, sudo, необходимые библиотеки, добавим и настроим системного пользователя
- Установим Python, модули и скомпилируем для них бинарные зависимости
- Установим и запустим JupyterLab, настроим внешний вид, установим расширения для него.
В этой статье мы с вами начнём с запуска контейнера, не будем здесь рассматаривать установку и настройку LXD, всё это вы можете найти в другой статье — Базовые возможности LXD — системы контейнеров в Linux
Установка и настройка базовой системы ^
Создаём контейнер командой в которой указываем образ — alpine3, идентификатор для контейнера — jupyterlab и при необходимости профили конфигурации:
lxc init alpine3 jupyterlab --profile=default --profile=hddroot
Здесь я использую профиль конфигурации hddroot который указывает создать контейнер с root-разделом в Storage Pool расположенным на физическом 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 | +------------+---------+------------------+--------------+
Установка базового софта и настройка системы ^
Для администрирования нашего контейнера необходимо установить следующий софт:
| Package | Description |
|---|---|
| bash | The GNU Bourne Again shell |
| bash-completion | Programmable completion for the bash shell |
| sudo | Give certain users the ability to run some commands as root |
| shadow | Password and account management tool suite with support for shadow files and PAM |
| tzdata | Sources for time zone and daylight saving time data |
| nano | 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— Идентификатор контейнера--— Специальный ключ, который указывает клиенту LXD дальше в строке не интерпретировать ключи, а передать всю строку как есть в контейнер.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-пользователь не был беспарольным, ему нужно установить пароль. Следующая команда установит пароль jupyter для root:
lxc exec jupyterlab -- /bin/bash -c "echo \"root:jupyter\" | chpasswd"
Вы можете использовать свой пароль или сгенерировать и установить пароль следующим Bash скриптом:
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 под которого настроим jupyterlab:
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 ^
JupyterLab — это Python приложение, поэтому мы должны прежде установить этот интерпретатор. Также, JupyterLab мы будем устанавливать с помощью питоновского пакетного менеджера 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
Устанавливаем JupyterLab через пакетный менеджер pip:
lxc exec jupyterlab -- python3 -m pip install jupyterlab
Так как расширения в JupyterLab являются эксперементальными и официально они не поставляеются вместе с пакетом jupyterlab, поэтому, мы должны установить и настроить это вучную.
Установим NodeJS и менеджер пакетов для него — NPM, так как JupyterLab использует их для своих расширений:
lxc exec jupyterlab -- apk add nodejs npm
Чтобы расширения для JupyterLab которые мы установим работали, их нужно устанавливать в пользовательскую директорию так как приложение будет запускаться от пользователя jupyter. Проблема в том, что нет параметра в команде запуска которой можно передать каталог, приложение воспринимает только переменную окружения и поэтому мы её должны определить. Для этого, мы пропишем команду экспорта переменной JUPYTERLAB_DIR в окружении пользователя jupyter, в файл .bashrc, который выполняется какждый раз при входе пользователя в систему:
lxc exec jupyterlab -- su -l jupyter -c "echo -e \"\nexport JUPYTERLAB_DIR=\$HOME/.local/share/jupyter/lab\" >> .bashrc"
Следующей командой установим специальное расширение — менеджер расширений в 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"
Сейчас уже всё готово для первого запуска JupyterLab, но мы можем еще установить несколько полезных расширений:
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"
Сейчас выполните следующие две команды для первого запуска JupyterLab. Можно было бы его запустить одной командой, но в этом случае, команда запуска, которую в уме трудо держать, она будет запоминаться bash’ем в контейнере, а не на хосте, где и так команд хватает для записи их в историю 🙂
Логинимся в контейнере как пользователь jupyter:
lxc exec jupyterlab -- su -l jupyter
Далее запустите JupyterLab с ключами и параметрами как указано:
[jupyter@jupyterlab ~]$ jupyter lab --ip=0.0.0.0 --no-browser
Перейдите в web-браузере по адресу http://10.0.5.5:8888 и на открывшейся странице введите token доступа который вы увидите в консоли. Скопируйте его и вставьте на странице, затем нажмите Login. После входа, перейдите слева в меню расширений, как показано на рисунке ниже, где вы должны активировать менеджер расширений и принять на себя риски по безопасности устанавливая расширения от третих лиц за которые команда JupyterLab development ответственности не несёт 🙂
Созданные IPython-ноутбуки (страницы в JupyterLab) будут создаваться в домашней директории пользователя jupyter, но в наших планах разделить данные между хостом и контейнером, поэтому, вернитесь в консоль и остановите JupyterLab выполнив hotkey — CTRL+C и ответив y на запрос. Затем разорвите интерактивную ссесию пользователя jupyter выполнив хоткей CTRL+D.
P.S. Интересно то, что старая реализация Jupyter под кодовым именем Jupyter Notebook никуда не делась и она существует параллельно с JupyterLab. Для перехода к старой версии перейдите по ссылке добавив в адресе суффикс/tree, а переход к новой версии осуществляется с суффиксом /lab, но его не обязательно указывать:
- Jupyter Notebook — http://10.0.5.5:8888/tree
- Jupyter Lab — http://10.0.5.5:8888/lab
Разделяем данные с хостом ^
Чтобы разделить данные с хостом, нужно создать в контейнере такое устройство, которое это позволяет делать и для этого выполните следующую команду где мы указываем следующие ключи:
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
Hello, World! ^
Откройте новую сессию пользователя jupyter в контейнере:
lxc exec jupyterlab -- su -l jupyter
Затем, мы можем указать JupyterLab использовать наше новое устройство в контейнере, задав путь для корня ноутбуков указав его в ключе --notebook-dir:
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 в JupyterLab, поэтому, двигаемся дальше 🙂
Расширяем возможности Python ^
В этом разделе мы установим такие мощные модули языка Python как NumPy, Pandas, Matplotlib, IPyWidgets результаты работы которых интегрируются в ноутбуки JupyterLab.
Прежде чем установить перечисленные модули Python через пакетный менеджер pip мы должны вначале разрешить системные зависимости в Alpine Linux:
g++— Нужен для компиляции модулей, так как некоторые из них реализованы на этом языке и подключаются к Python в рантайме как бинарные модулиfreetype-dev— зависимость для Python модуля Matplotlib
Устанавливаем зависимости:
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 ^
Если у вас ещё запущен JupyterLab, перезапустите его, чтобы новые установленные модули активировались. Для этого в консольной сессии нажмите CTRL+C там где он у вас запущен и введите y на запрос остановки, а затем запустите заново JupyterLab нажав стрелочку на клавиатуре "вверх", чтобы не вводить команду заново и потом 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-элемент на странице и Matplotlib который отрисовывает картинку в виде результата:
Многие примеры IPyWidgets вы можете найти в туториалах здесь
Что ещё? ^
Вы молодцы, если остались и дошли до самого конца статьи. Я специально не стал выкладывать готовый скрипт который бы установил JupyterLab в "один клик" в конце статьи, чтобы поощрить труженников 🙂 Но вы можете это сделать самостоятельно, так как уже знаете как, собрав команды в единый Bash скрипт 🙂
Также, вы можете:
- Задать имя для хоста вместо IP-адреса прописав его в простом
/etc/hosts - Поиграться с ограничением ресурсов для контейнера, для этого прочтите главу в базовых возможностях LXD или получите больше информации на сайте разработчика LXD.
- Поменять тему оформления:
И много чего ещё вы можете! На этом я с вами прощаюсь, пока!
ссылка на оригинал статьи https://habr.com/ru/post/497100/

Добавить комментарий