Привет!
В этой статье хочется поделится с вами настройкой девелоперского окружения под разработку скриптов на Python и запуск их в изолированном окружении, практически за пару кликов в редакторе VS Code.
В качестве изолированного окружения будет рассмотрена первичная настройка и создание контейнера на базе LXC под управлением ОС Ubuntu 17.10. Почему не Docker? Сложно ответить, выдать какие-то аргументы. От части концепция виртуализации ОС субъективно ближе по духу, чем виртуализация процессов, но принцип который здесь будет изложен, с некоторыми поправками, подойдет к запуску кода в Docker-контейнерах.
Что вы получите?
- Научитесь запускать в один клик python-скрипты в контейнере, а также удаленный debug из VS Code
- Узнаете как установить LXD, настроить сеть и запустить первый контейнер
- Ознакомитесь с установкой и настройкой редактора исходного кода VS Code, набором расширений которые позволят комфортно писать и отлаживать код.
Чего здесь не будет?
- Не будет полного обзора LXC/LXD, вам придется самостоятельно найти дополнительную информацию о Linux-контейнерах.
- Не будет рассмотрен python модуль virtualenv для создания виртуального окружения на хосте разработчика как альтернатива контейнерам.
- Не будет сравнения в духе Docker vs LXD, выводы вы сделаете самостоятельно на основании вашего опыта.
Содержание:
- Выбор backend хранилища
- Установка пакета LXD
- Инициализация LXD
- Конфигурация сети LXС
- Создание профиля конфигурации контейнера
- Создание контейнера
- Установка ПО в контейнере
- Установка VS Code и расширений
- Настройка конфигурации VS Code
- Линковка каталогов Хост <-> Контейнер
- Настройка VS Code на запуск скриптов в контейнере
- Удаленный Debug в VS Code
Выбор backend хранилища
Перед установкой LXD необходимо определиться, какой тип backend хранилища для контейнеров будет использоваться. Позже изменить будет нельзя. Доступно несколько вариантов: ZFS, btrfs, LVM или файловая система хоста. Для сравнения возможностей доступна таблица сравнения в официальной документации. Предлагаю выбрать ZFS который позволит в будущем легко создавать COW (copy-on-write) снимки (snapshots) контейнеров. Установим в системе пакет утилит работы с ZFS:
$ sudo apt install zfsutils-linux
Установка пакета LXD
Здесь нет сложностей, пакет входит в стандартную поставку системы, поэтому командуем в консоли:
$ sudo apt install lxd
После установки LXD, текущий пользователь будет добавлен в системную группу lxd, но чтобы продолжить настройку с необходимыми привелегиями в системе, необходимо сейчас выполнить переактивацию сессии пользователя. Просто выйдите и заново войдите в сессию вашего Desktop Meneger’а или перезагрузитесь полностью. Проверить, состоит ли текущая сессия пользователя в группе lxd можно через команду id:
$ id
uid=1000(user1) gid=1000(user1) groups=1000(user1),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),118(lpadmin),128(sambashare),129(lxd)
Если вы видите группу lxd, то всё впорядке, можно продолжать дальше.
Инициализация LXD
Инициализация LXD проходит в интерактивном режиме от имени текущего пользователя который состоит в группе lxd. Выполните команду и следуйте инструкциям и комментариям представленным ниже:
$ lxd init
# Здесь нас спрашивают, хотим ли мы сейчас создать пул хранилища? Не путать с пулом ZFS!
# По умолчанию ответ <yes>, нажимаем <ENTER>
Do you want to configure a new storage pool (yes/no) [default=yes]?
# Задаем желаемое имя для пула хранилища и нажимаем <ENTER>
Name of the new storage pool [default=default]: lxdpool
# Здесь нам нужно определить тип пула хранилища, выбираем ZFS
Name of the storage backend to use (dir, lvm, zfs) [default=zfs]: zfs
# Создать сейчас ZFS пул? Т.е. в пуле хранилища lxdpool создастся ZFS пул.
# Соглашаемся и нажимаем <ENTER>
Create a new ZFS pool (yes/no) [default=yes]?
# Здесь спрашивают, хотим ли мы использовать существующее блочное устройство?
# Например, это может быть ранее нами подготовленный файл созданный через утилиту dd
# или это может быть отдельный жеский диск или партиция.
# По умолчанию ответ равен <no>, жмем <ENTER>
Would you like to use an existing block device (yes/no) [default=no]?
# Каким размером создать loop-устройство (zfs pool)?
# Указываем число в GB, минимально 1GB
Size in GB of the new loop device (1GB minimum) [default=15GB]: 5
# Желаете сделать LXD доступным по сети?
# По умолчанию LXD не слушает сеть, единственный способ говорить с ним - через локальный unix сокет /var/lib/lxd/unix.socket
# Отвечаем <no>, жмем <ENTER>:
Would you like LXD to be available over the network (yes/no) [default=no]?
# Желаете чтобы образы в локальном репозитории автоматически обновлялись?
Would you like stale cached images to be updated automatically (yes/no) [default=yes]? no
# Желаете сейчас создать сетевой мост? Отвечаем <no>, мы его позже создадим и настроим
Would you like to create a new network bridge (yes/no) [default=yes]? no
# LXD has been successfully configured.
Конфигурация сети LXС
Если сеть не настроить сейчас, а приступить к созданию контейнеров, то при их создании мы получим предупреждение, что контейнер не присоединен ни к одной из сетей. Это не будет являться ошибкой, просто к ним не будет сетевого доступа через интерфейсы. В начале можно создать необходимое количество контейнеров, а затем сконфигурировать сеть и присоединить к ней контейнеры с помощью команды lxc network attach. Сейчас нам необходимо определится, какой тип сети строить. Доступно несколько вариантов построения LXC-сетей, мы же выберем вариант bridge (простой программный коммутатор между хостом и контейнерами) и построим сеть на его основе.
При создании сетевого моста мы можем сразу задать свойства после указания его имени или положиться на автоконфигурацию которая задаст подсеть IPv4 и IPv6 случайным выбором. По умолчанию, параметр равен ipv4.nat=false и по каким-то причинам пакеты транзитного трафика с контейнеров не будут маршрутизироваться дальше хоста на другие локальные сегменты сети даже тогда, когда в ядре хоста явно включить IP forwarding. Видимо NAT в LXD больше чем транслятор адресов с локальных сегментов сети в глобальные, так как при активации этого параметра также активируется IP forwarding на хосте, поэтому мы явно его включим:
$ lxc network create lxdbr0 ipv6.address=none ipv4.address=10.0.3.1/24 ipv4.nat=true
Network lxdbr0 created
$ lxc network list
+--------+----------+---------+-------------+---------+
| NAME | TYPE | MANAGED | DESCRIPTION | USED BY |
+--------+----------+---------+-------------+---------+
| ens33 | physical | NO | | 0 |
+--------+----------+---------+-------------+---------+
| lxdbr0 | bridge | YES | | 0 |
+--------+----------+---------+-------------+---------+
Создание профиля конфигурации контейнера
Конфигурация контейнера условно делится на два уровня, глобальная и локальная. Глобальная конфигурация задается в профиле конфигурации который присоединяется к конкретному контейнеру или группе контейнеров, а локальная конфигурация применяется только для конкретного контейнера. Т.е., все установки в профиле наследуются контейнером, а локальные установки имеют приоритет над профилем. К одному контейнеру можно присоединить несколько профилей.
Создадим профиль LXD для того, чтобы во вновь создаваемых контейнерах по умолчанию был интерфейс типа nic который будет слинкован с коммутатором (сетевым мостом) lxdbr0 на хосте:
$ lxc profile copy default network0
$ lxc profile device add network0 eth0 nic parent=lxdbr0 nictype=bridged
Создание контейнера
Для примера создадим контейнер и назовем его pytest, который будет доступен из хоста по этому имени. Выполним команду launch которая загрузит образ из репозитория ubuntu: версию 17.10 и архитектуры amd64 и из этого образа создаст контейнер. Дополнительно, опцией укажем, что этому контейнеру необходимо задать конфигурацию из профиля network0:
$ lxc launch ubuntu:17.10/amd64 --profile network0 pytest
Далее пойдет загрузка из сети образа. Немного подождем, а чуть позже можем увидеть доступные контейнеры в системе и их состояние:
$ lxc list
+--------+---------+------------------+------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+--------+---------+------------------+------+------------+-----------+
| pytest | RUNNING | 10.0.3.13 (eth0) | | PERSISTENT | 0 |
+--------+---------+------------------+------+------------+-----------+
Установка ПО в контейнере
Здесь мы с вами установим необходимое ПО в контейнере. Запустим внутри контейнера пакетный менеджер который актуализирует систему:
$ lxc exec pytest -- /bin/bash -c 'apt update && apt upgrade -y'
Следом, установим менеджер пакетов pip (Python package installer), а также python-модуль ptvsd (Visual Studio remote debugging server for Python). Модуль ptvsd необходимо установить обязательно версии 3.0.0, на более свежих версиях (актуальная 3.2.1.0) я получил ошибку, которая обозначена в issue на github.com. Автор расширения Python к VS Code которое взаимодействует с ptvsd как временное решение рекомендует использовать версию 3.0.0.
$ lxc exec pytest -- /bin/bash -c 'apt install python3-pip -y'
$ lxc exec pytest -- /bin/bash -c 'pip3 install ptvsd==3.0.0'
По части LXD мы с вами завершили настройку.
Установка VS Code и расширений
Варианты установки приложения описаны на официальной страничке по адресу code.visualstudio.com/docs/setup/linux, мы же немного модифицируем команды:
$ cd ~
$ wget -c https://packages.microsoft.com/keys/microsoft.asc
$ cat microsoft.asc | gpg --dearmor > microsoft.gpg
$ sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
$ sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
$ sudo apt update
$ sudo apt install code
После выполнения у нас в системе появится VS Code последней версии из репозитория Microsoft.
P.S. На момент написания статьи вышла новая версия 1.18.0 в которой, в стабильную сборку вошли на мой взгляд одни из наиболее ожидаемых (2 года) сообществом возможности «Multi-root workspaces» и «Git indicators in the Explorer» Обо всех изменениях можно почитать на этой страничке.
Приступим к установке расширений. Если вы впервые устанавливаете VS Code, например, после использования Atom (как автор), то рекомендую обратить внимание на расширения в списке ниже, а для достижения цели этой статьи будет достаточно установить только Python и Code Runner. Расширения устанавливаются с marketplace'а в левой части редактора.
- IntelliSense for CSS class names
- Python
- Jinja
- Django Template
- file-icons
- One Dark Pro
- Subtle Match Brackets
- TODO Highlight
- Code Runner
Настройка конфигурации VS Code
Следующие настройки необязательны, они прямо не относятся к запуску Python скриптов в контейнерах, но это приблизит вас к тому, если хотите чтобы было визуально как в Atom'е.
В открытом редакторе откройте настроки редактора: file->Preferences->Settings. В открывшемся окне, справа, в USER_SETTINGS внесите следующие изменения и сохраните (Ctrl + S):
{
// Общие настройки
"telemetry.enableTelemetry": false,
"editor.fontFamily": "Menlo, Consolas, 'DejaVu Sans Mono', monospace",
"editor.lineHeight": 21,
"editor.minimap.enabled": true,
"editor.minimap.showSlider": "always",
"editor.minimap.renderCharacters": false,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"explorer.openEditors.visible": 0,
// Настройка интегрированного терминала:
"terminal.integrated.lineHeight": 1.25,
"terminal.integrated.scrollback": 2000,
// Настройка темы:
"workbench.iconTheme": "file-icons",
"workbench.colorTheme": "One Dark Pro",
"workbench.colorCustomizations": {
// https://code.visualstudio.com/docs/getstarted/theme-color-reference
"editor.lineHighlightBackground": "#2C323C"
},
// Настройка подсветки скобок
"editor.matchBrackets": false,
"subtleBrackets.styles": {
"global": {
"color": "#61afef",
"borderColor": "#61afef",
"borderWidth": "1px",
"borderStyle": "none none solid none"
}
},
// Python
"python.pythonPath": "python3",
// Настройка кодо-запускателя:
"code-runner.executorMap": {
"python": "python3 $fileName"
}
}
Линковка каталогов Хост <-> Контейнер
Мы с вами уже проделали большую работу и настало время заключительной части. Сейчас у нас почти всё готово для запуска скриптов в контейнере.
Создадим каталог projects, например, по адресу /home/user1/projects. В нём будут располагаться наши будущие python-проекты. Этот каталог мы слинкуем с контейнером, т.е., в контейнере создадим устройство типа disk и свяжим этот каталог с внутренним /mnt/hostfs. Выполняем следующие команды:
$ mkdir ~/projects
$ lxc config device add pytest hostfs disk path=/mnt/hostfs source=/home/user1/projects/
Если мы локально создадим каталог или файл в каталоге /home/user1/projects, то он немедленно отразится в контейнере по адресу /mnt/hostfs. Убедиться в этом можно запустив интерактивно оболочку BASH в контейнере и выполнив команду ls:
$ lxc exec pytest -- /bin/bash
$ ls -l /mnt/hostfs/
Настройка VS Code на запуск скриптов в контейнере
И так, сейчас мы можем вернуться в наш редактор, если вы его закрыли, то вновь откройте. Нажмите в меню File -> New File. Создастся новая вкладка в которую нужно внести следующий python-код, который мы будем запускать в контейнере. Сохраните его в каталоге /home/user1/projects/pytest/test.py, а затем файл (вкладку) можно закрыть.
import os
#### BEGIN. Visual Studio remote debugging server for Python
# import ptvsd
# print('Waiting connection on port 3000')
# ptvsd.enable_attach('my_secret', address=('0.0.0.0', 3000))
# ptvsd.wait_for_attach()
# ptvsd.break_into_debugger()
#### END. Visual Studio remote debugging server for Python
with open('/etc/lsb-release') as f:
lsb_release = f.readlines()
info = {
'HOSTNAME': os.uname().nodename,
'DISTRIB': lsb_release[3][21:-2],
'USER': os.environ['USER'],
'LOGNAME': os.environ['LOGNAME'],
'HOME': os.environ['HOME'],
'PWD': os.environ['PWD']
}
print(info)
VS Code «скажет», что у вас не установлен модуль pylint, но он нам сейчас не нужен, вы его можете установить позже, самостоятельно, через менеджер модулей pip. Сейчас нужно открыть workspace в VS Code, для этого слева в File Explorer нажмите кнопку Open Folder и выберите каталог /home/user1/projects/:
VS Code переинициализируется, полностью закроются все окна и откроется workspace в котором будет виден файл test.py. Откройте его, дважды кликнув на имя слева в File Explorer VS Code.
Вызовите настройки workspace'а, нажав в меню File -> Preferences -> Setting, далее в окне справа вверху вместо USER_SETTINGS выберите из выпадающего списка WORKSPACE_SETTINGS:
Внесите и сохраните следующие настройки так, чтобы у вас получилось вот так:
{
"code-runner.executorMap": {
// Запуск от имени пользователя root:
// "python": "lxc exec pytest -- /bin/bash -c 'python3 /mnt/hostfs/pytest/$fileName'"
// Запуск от имени встроенного в контейнер пользователя ubuntu:
"python": "lxc exec pytest -- /bin/bash -c 'su -l ubuntu -c "python3 /mnt/hostfs/pytest/$fileName"'"
},
"code-runner.runInTerminal": true
}
В файловом эксплорере вы можете обнаружить файл settings.json в каталоге .vscode, там сохраняются настройки воркспейса (текущего проекта).
Вернитесь к открытому файлу test.py и запустите его на исполнение через команду из контекстного меню Run code:
В выводе интегрированного терминала вы увидите информацию в которой можно убедится, что код исполнялся на удаленной машине (контейнере). Теперь, всякий раз, когда нужно запустить проект в контейнере, достаточно внести настройки в WORKSPACE_SETTINGS или создать файл .vscode/settings.json относительно каждого проекта и затем немного подправить пути в настройках.
Удаленный Debug в VS Code
Раскомментируйте строчки в файле test.py и сохраните этот файл:
#### BEGIN. Visual Studio remote debugging server for Python
import ptvsd
print('Waiting connection on port 3000')
ptvsd.enable_attach('my_secret', address=('0.0.0.0', 3000))
ptvsd.wait_for_attach()
ptvsd.break_into_debugger()
#### END. Visual Studio remote debugging server for Python
Всякий раз, когда есть необходимость удаленного дебага, эти строчки кода необходимо вставить в тот файл, который запускается на исполнение. Модуль ptvsd на стороне контейнера открывает порт 3000 и ожидает подключение внешнего дебаггера. При желании, вы можете сконфигурировать ptvsd на свой лад.
Запустите код, вызвав контекстное меню Run Code. В интегрированном терминале вы увидите сообщение, что скрипт ожидает подключения на порту 3000. Перейдите в дебаггер, и создайте новую конфигурацию для этого проекта:
У вас откроется файл launch.json на редактирование в котором уже будут предустановки. Внесите в него следующий блок кода:
{
"name": "Python LXC",
"type": "python",
"request": "attach",
"localRoot": "${workspaceRoot}",
"remoteRoot": "/mnt/hostfs/pytest",
"host": "pytest",
"port": 3000,
"secret": "my_secret"
}
Сохраните его, он появится в каталоге .vscode/launch.json рядом с файлом setting.json. Затем, выберите слева в низподающем списке Python LXC, это та конфигурация дебаггера, которую мы только что внесли:
Нажмите Start Debugging. В окне с файлом test.py вы увидите работающий дебагер и подтверждение, что код запущен удаленно:
На этом всё. Настройка завершена. Всё что нужно в дальнейшем для нового проекта python, это сконфигурировать два конфиг-файла .vscode/settings.json и .vscode/launche.json. Если у вас есть вопросы по теме, прошу задавать в комментариях. Спасибо за внимание, успешных вам проектов!
Автор: microcoder