https://habrahabr.ru/post/342080/- Разработка под Linux
- Программирование
- Виртуализация
- Visual Studio
- Python
Привет, Хабр!
В этой статье хочется поделится с вами настройкой девелоперского окружения под разработку скриптов на 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 необходимо определиться, какой тип 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'а в левой части редактора.
Настройка конфигурации 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/pytest/:
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. Если у вас есть вопросы по теме, прошу задавать в комментариях. Спасибо за внимание, успешных вам проектов!
UPD от 10.11.2017 21:35 MSK.
В комментариях, пользователь
SirEdvin задал вопрос о возможности автодополнения (или IntelliSense в терминах Microsoft) в редакторе тех python-модулей, которые установлены только в контейнере, т.е., этих модулей нет на локальной машине на которой запускается VS Code.
В статье этот момент был упущен, поэтому с радостью дополняю небольшой главою:
Настройка автодополнения (IntelliSense) для внешних python-библиотек
Например, в вашем проекте используется модуль, который не поставляется в коробке по умолчанию с python, и вы этот модуль установили только в контейнере. Как же сделать так, чтобы VS Code «понимал», автодополнял, выводил подсказки в редакторе при этом не устанавливая модуль на хосте, а получал из модуля в контейнере?
Для решения этого вопроса, откройте настройки WORKSPACE_SETTINGS по адресу:
ВАШ_ПУТЬ_К_ПРОЕКТУ/.vscode/settings.json и добавьте параметр
python.autoComplete.extraPaths, а в качестве ключей укажите список каталогов контейнера в которых находятся python модули.
Например, наш контейнер доступен в файловой системе хоста по адресу:
/var/lib/lxd/containers/pytest/rootfs/. Это корневая файловая система контейнера
pytest. Зная, где Python устанавливает внешние библиотеки, мы с легкостью можем добавить этот путь, чтобы получить полный путь к внешним библиотекам. Для таких модулей как numpy полный путь будет:
/var/lib/lxd/containers/pytest/rootfs/usr/local/lib/python3.6/dist-packages. Этот путь мы добавим в настройку
python.autoComplete.extraPaths, вот так:
Сохраните настройки и вернитесь к вашему проекту. Попробуйте импортировать модуль numpy и вызвать автодополнение клавишей CTRL + SPACE. У вас должно получиться так как на экране:
Если сразу не получается, не расстраивайтесь, VS Code кеширует результаты и это сразу может не заработать, попробуйте закрыть-открыть редактор, поиграться с клавишами CTRL+SPACE после ключевого слова from… import.
На этом всё :)