Виртуальная машина Vagrant для разработки в команде

от автора

На хабре уже обсуждалась тема создания виртуальных машин vagrant c комментариями «зачем мне это нужно?». В своей статье я хочу ответить на этот вопрос. Когда у вас есть проект, и в процессе его реализации к вашей команде присоединяются новые сотрудники, возникает проблема отсутствия у них идентичных условий для запуска и тестирования проекта на своих машинах. И тут вам в помощь приходит виртуальная машина Vagrant. На примере django-проекта, созданного в моей статье по etl, я хочу показать, как каждый новый участник вашего проекта за четыре консольные команды развернёт у себя среду разработки со всеми зависимостями. Это позволит в кратчайшие сроки приступить к процессу работы.

В качестве альтернативы виртуальной машине можно использовать docker-контейнеры. Но есть нюанс, что для работы с контейнерами требуется соответствующая квалификация.

Итак, представим, что у нас есть джанго-проект, который мы хотим развивать в команде.

Мы создадим виртуальную машину для дальнейшей разработки с предустановленным pyenv для управления версиями питона и poetry для управления зависимостями.Также настроим интерпретатор в IDE PyCharm Pro и VS Code.

Все исходники из данной статьи выложены в репозиторий.

Устанавливаем ПО

На хостовой (основной) операционной системе каждого участника проекта должны быть:

Обозначим структуру проекта

Маленький django-проект находится в папке etl. Конфигурационная папка проекта config, папка приложения etl_app. Про остальные файлы и папки читайте далее.

.            ├── Makefile ├── Vagrantfile ├── etl │   ├── README.md │   ├── __init__.py │   ├── config │   │   ├── __init__.py │   │   ├── asgi.py │   │   ├── celery.py │   │   ├── settings.py │   │   ├── settings_local.py │   │   ├── urls.py │   │   └── wsgi.py │   ├── etl_app │   │   ├── __init__.py │   │   ├── apps.py │   │   ├── etl.py │   │   └── tasks.py │   └── manage.py ├── poetry.lock ├── pyproject.toml └── vagrant     ├── playbook.yml     ├── postgres.yml     ├── requirements.yml     └── templates         └── settings_local.j2 

Начинаем с Ansible

Ansible — система управления конфигурациями для Linux. Конфигурировать мы будем создаваемую виртуальную машину через сценарий (далее плейбуки) в формате YAML с описанием требуемых состояний управляемой системы. Сценарий — это описание состояния ресурсов системы, в котором она должна находиться в конкретный момент времени, включая установленные пакеты, запущенные службы, созданные файлы и многое другое. Ansible проверяет, что каждый из ресурсов системы находится в ожидаемом состоянии и пытается исправить состояние ресурса, если оно не соответствует ожидаемому.

Для выполнения задач используется система модулей. Каждая задача представляет собой её имя, используемый модуль и список параметров, характеризующих задачу. Система поддерживает переменные, фильтры обработки переменных (поддержка осуществляется библиотекой Jinja2), условное выполнение задач, параллелизацию, шаблоны файлов. Адреса и настройки целевых систем содержатся в файлах «инвентаря» (inventory). Поддерживается группирование. Для реализации набора однотипных задач существует система ролей.

Из всего вышеперечисленного мы воспользуемся ролями, чтобы писать меньше кода, и шаблонизатором Jinja2.

Все плейбуки пишем в папке vagrant.

requirements.txt

В нашем проекте будет использована роль python_developer из репозитория ролей ansible-galaxy. Она (роль) представляет из себя готовый набор задач, которые установят для нас pyenv и poetry. Прописываем роль в файле зависимостей requirements.txt:

- name: staticdev.python-developer   src: https://github.com/staticdev/ansible-role-python-developer.git

postgres.yml

В этом плейбуке мы пропишем набор задачи по созданию базы данных и схем в ней:

--- - name: Add postgreuser permission for createbd   become: true   become_user: postgres   shell: psql -U postgres -c 'ALTER USER {{ postgres_user }} CREATEDB'  - name: Create database   become: true   become_user: postgres   postgresql_db:     name: "{{ postgres_db }}"     encoding: UTF-8     owner: "{{ postgres_user }}"  - name: Create schemas   become: true   become_user: postgres   community.postgresql.postgresql_schema:     db: "{{ postgres_db }}"     name:       - main       - my_schema     owner: "{{ postgres_user }}"

Обратите внимание на строки вида {{ some_var }} — это переменные. Их мы зададим в следующем плейбуке

playbook.yml

Здесь мы пишем наш основной сценарий. Он определяет — что надо установить на нашу виртуальную машину, а также использует описанные ранее плейбуки.

--- - hosts: all   vars:     app_settings_dir: config     postgres_user: sergei     postgres_password: sergei     postgres_db: demo     python_version: 3.10.6   roles:     - role: staticdev.python-developer       vars:         pyenv_global:                              - "{{ python_version }}"         pyenv_python_versions:           - "{{ python_version }}"   tasks:     - name: add GPG key       ansible.builtin.apt_key:         url: https://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search=0x1655A0AB68576280         id: 68576280         state: present       become: yes      - name: Install base needed apt-packages       ansible.builtin.apt:         pkg:           - wget           - unzip           - redis-server           - wget           - python3-psycopg2           - acl         state: latest       become: yes       become_method: sudo      - name: Set up Postgres repo       shell: |         echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list         wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -       args:         warn: no       become: yes      - name: Install postgresql       apt:         name: postgresql-15         update_cache: yes       become: yes      - name: Start and enable postgres       ansible.builtin.service:         name: postgresql         enabled: yes         state: started       become: yes      - name: Create user       community.postgresql.postgresql_user:         name: "{{ postgres_user }}"         password: "{{ postgres_password }}"       become: yes       become_user: postgres      - name: Create db       include_tasks: postgres.yml      - name: Ensure Redis is started       service: name=redis-server state=started enabled=yes       become: yes       become_method: sudo      - name: Exporting broker url to env       lineinfile:         dest: "/etc/environment"         state: present         regexp: "^REDIS_URL="         line: "REDIS_URL=redis://127.0.0.1:6379/0"       become: yes       become_method: sudo      - name: Exporting db url to env       lineinfile:         dest: "/etc/environment"         state: present         regexp: "^DATABASE_URL="         line: "DATABASE_URL=postgres://{{ postgres_user }}:{{ postgres_password }}@127.0.0.1:5432/{{ postgres_db }}"       become: yes       become_method: sudo      - name: Make settings file from template       template:         src: /vagrant/vagrant/templates/settings_local.j2         dest: "/vagrant/etl/{{ app_settings_dir }}/settings_local.py"      - name: set python version for project       ansible.builtin.command: /home/vagrant/.local/bin/poetry env use /home/vagrant/pyenv/shims/python       args:         chdir: /vagrant/ 

Разделы плейбука:

  • vars — задаем переменные

  • roles — запускаем роли (предопределенный набор задач). pyenv_python_versions — список версий питона, которые вы хотите установить на ВМ, pyenv_global— какую из этих версий сделать глобальной.

  • tasks — перечень задач, которые образуют набор консольных команд. Только таски ещё отслеживают ответы выполняемых команд. Если какая-либа из команд вернут ошибку, то выполнение плейбука прекратится. В примере выше я устанавливаю Redis (строки 29 и 73). Вы вольны устанавливать любой набор пакетов.

Чуть подробнее остановимся на задаче Make settings file from template. Этой команда берет шаблон файла settings_local.j2 по пути src, прогоняет его через шаблонизатор Jinja2 — см. DEBUG в фрагменте кода ниже. Затем копирует готовый модуль в расположение dest. Самая очевидная польза, это возможность исключить файл settings_local.py конфигурационной папки проекта из индекса гита. Как следствие — settings_local.py это действительно локальный (простите за тавтологию) модуль. Такая методика позволяет для прода задействовать другие значения переменных.

Был settings_local.j2:

import os import dj_database_url  DEBUG = {{ debug|lower|capitalize }}  ... 

Стал settings_local.py:

"""Локальные настройки проекта""" import os import dj_database_url  DEBUG = True  ... 

Готовим к запуску виртуальную машину Vagrant

Для запуска виртуальной машины нам потребуется Vagrantfile:

# -*- mode: ruby -*- # vi: set ft=ruby :  Vagrant.configure("2") do |config|   config.vm.box = "ubuntu/focal64"    config.vm.network "forwarded_port", guest: 8000, host: 8001   config.vm.network "forwarded_port", guest: 5432, host: 5433   config.vm.provider "virtualbox" do |v|     v.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"]     v.customize ["modifyvm", :id, "--uartmode1", "file", File::NULL]     v.memory = 2048   end    # ansible_local   config.vm.provision "ansible_local" do |ansible|     ansible.verbose = "vv"     ansible.galaxy_role_file = 'vagrant/requirements.yml'     ansible.galaxy_roles_path = '/home/vagrant/.ansible/roles'     ansible.galaxy_command = 'ansible-galaxy install --ignore-errors --force --role-file=%{role_file}'     ansible.playbook = "vagrant/playbook.yml"   end end

Пояснения по номерам строк:

  • 5 — образ операционной системы

  • 7- проброс 8000-ый порт ВМ, который как правило используется для запуска сервера разработки Django, на 8001-ый порт хостовой машины. Оба порта могут быть изменены по вашему усмотрению.

  • 8 — порт 5432 СУБД Postgres пробрасываем на порт 5433 хостовой машины. Порт 5433 может быть изменен по вашему усмотрению.

  • 12 — объем оперативной памяти ВМ в Мбайтах.

  • 16 — объявляем блок команд для запуска ansible.

  • 17 — уровень детализации в консоли, регулируется количеством символов v.

  • 18 — путь файлам зависимостей.

  • 19 — путь для хранения файлов ролей asnible. Подробнее об этом в документации. В данному случае они будут хранится в папке пользователя и не фигурировать в проекте.

  • 20 — консольная команда установки зависимостей ansible. Я её «кастомизировал» через--ignore_errors, чтобы не было ошибки при повторной установке зависимостей.

  • 21 — путь до основного плейбука, который и запускает всю «магию».

Запускаем виртуальную машину

Создание и каждый последующий запуск производится с помощью консольной команды vagrant up. Полный алгоритм выглядит так:

  1. В директории проекта запустить консольную команду vagrant up.

  2. Сразу (не дожидаясь выполнения п.1) запустить окно Oracle VM virtual box. Сделать активной создаваемую виртуальную машину (дополнительных окон открывать не надо). До полного запуска ВМ это окно держать активным.

Первый запуск может длиться 15-20 минут, потому что идет создание ВМ и установка пакетов. В это время следите, чтобы компьютер не переходил в «сон». Возможно для первого запуска понадобится VPN.

Другие команды для управления ВМ:

  • vagrant halt — остановить ВМ

  • vagrant ssh — зайти на ВМ

  • vagrant destroy — «разобрать» ВМ

Используем make

Для работы с виртуальной машиной нам достаточно команд из предыдущего раздела. Но дополнительно мы можем наиболее часто употребимые вынести в make. Процесс установки на Ubuntu описан здесь, на Windows здесь.

Создадим Makefile:

# инициализировать виртуальную машину up: vagrant up  # установить зависимости проекта install: vagrant ssh -c "cd /vagrant/ && poetry install"  # сделать дата-миграции бд migrate: vagrant ssh -c "cd /vagrant/etl && poetry run python manage.py makemigrations && poetry run python manage.py migrate"  # запустить сервер разработки runserver: vagrant ssh -c "cd /vagrant/etl && poetry run python manage.py runserver 0.0.0.0:8000" 

Обратите внимание на последнюю команду. Благодаря пробросу портов в Vagrantfile сервер разработки будет доступен на хостовой машине по адресу http://localhost:8001.

Настраиваем IDE

Для полноценной интеграции интерпретатора в оболочку IDE, последние должны уметь работать через ssh-подключение. Параметры для подключения:

  • host 127.0.0.1

  • port 2222. Если вы одновременно запускаете не одну ВМ, то у каждой последующей будет другой порт. Будьте внимательны. Порт вы увидите в отчете команды по инициализации ВМ.

  • username vagrant

  • key pair (подключение идет по ключу, путь относительно директории, где находится Vagrantfile): .vagrant/machines/default/virtualbox/private_key

PyCharm Pro

К сожалению, интеграция с интерпретатором в версии Community исключена, т.к. отсутствует функционал ssh-подключения. В этом случае можно в терминале зайти на виртуальную машину и все запуски кода производить из консоли виртуальной машины.

Подключение к удаленной БД в Community также невозможно. Но pgAdmin (более чем) полноценно заменит функционал PyCharm Pro в этой части.

Настройка интерпретатора

Инструкция доступна по ссылке на сайте разработчика IDE. Дополнительное к параметрам подключения по ssh, нам понадобится путь до интерпретатора внутри ВМ. Его можно получить составной командой в консоли виртуальной машиныcd /vagrant/ && poetry run which python

Подключение к БД

К базе данных подключаемся также по ssh. Инструкция доступна по ссылке на сайте разработчика IDE. Данные для подключения возьмите из начала текущего раздела. Логин/пароль/имя БД у нас указаны в playbook.yml в разделе vars.

VS Code

Интеграция с интерпретатором производится в два шага:

1) Зафиксируйте настройку подключения к в ВМ в конфигурации ssh-клиента вашей хостовой машины. Для этот выполните команду vagrant ssh-config на хостовой машине в папке проекта. Вывод будет примерно такой:

Host default   HostName 127.0.0.1   User vagrant   Port 2222   UserKnownHostsFile /dev/null   StrictHostKeyChecking no   PasswordAuthentication no   IdentityFile <some path>/.vagrant/machines/default/virtualbox/private_key   IdentitiesOnly yes   LogLevel FATAL

Добавьте эти строки в конфигурационный файл вашего ssh-клиента. Как правило это ~/.ssh/config. default замените на что-либо вам понятное и относящееся к проекту.

2) Настройте в VS Code интерпретатор по ssh-подключению. Предварительно убедитесь, что у вас установлены плагины Remote SSH и Python от Microsoft.

Подключаемся к ВМ:

В консоли виртуальной машины составной командойcd /vagrant/ && poetry run which python получаем путь до интерпретатора. Например /home/vagrant/.cache/pypoetry/virtualenvs/etl-base-vs8V2ZPt-py3.10/bin/python

Указываем этот путь в настройках плагина Python:

в окне поиска набираем > и выбираем Python: Select Interpreter» title=»в окне поиска набираем > и выбираем Python: Select Interpreter» width=»1120″ height=»108″ data-src=»https://habrastorage.org/getpro/habr/upload_files/f30/2b4/3e9/f302b43e974117b629e5b89b55b5298d.png»/><figcaption>в окне поиска набираем > и выбираем Python: Select Interpreter</figcaption></figure>
<figure class=Вставляем полученный ранее путь до интерпретатора
Вставляем полученный ранее путь до интерпретатора

Подробная инструкция есть по ссылке на сайте разработчика IDE.

Подводим итоги

Мы создали виртуальную машину без нагрузки в виде GUI. Показали как можно работать с ней с помощью программ, установленных на хостовой машине. Для «полного счастья» можно установить ssh-клиент с графическим интерфейсом, чтобы управлять файлами в привычной среде, как вариант — MobaXterm.

В начале статьи я обещал, что каждый участник сможет развернуть ваш проект у себя на машине за четыре консольные команды. Вот они:

git clone <repo>  # клонируем репозиторий make up  # поднимаем ВМ make install  # устанавливаем зависимости проекта make migrate  # создаем требуемые таблицы базы данных

Благодарю за внимание, жду ваших комментариев и с удовольствием отвечу на все вопросы.


ссылка на оригинал статьи https://habr.com/ru/post/713476/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *