Среда разработки: делаем рабочие процессы удобнее

20 октября 2016

Vagrant

Среда разработки: делаем рабочие процессы удобнее

Для удобного процесса разработки, быстрого переключения между проектами и эффективного взаимодействия бекэнд и фронтенд команд мы в WB—Tech работаем в виртуальном окружении Vagrant и VirtualBox.

Vagrant — кросс-платформенное ПО для создания виртуальной среды разработки. Для ускорения развертывания виртуальной машины можно использовать компилированные, версированные боксы. Версийность боксов в Vagrant описывается при помощи JSON документа.

{    "name": "box_name",    "description": "This box description.",    "versions": [        {            "version": "42.0",            "providers": [                {                    "name": "virtualbox",                    "url": "http://somewhere.com/precise64_010_virtualbox.box",                    "checksum_type": "sha1",                    "checksum": "foo"                }            ]        }    ]}

В самом Vagrantfile указать путь к метаданным в атрибуте config.vm.box_url.

config.vm.box = "box_name"config.vm.box_version = "42.0"config.vm.box_url = "http://somewhere.com/path/to/metadata.json"

Лайфхак: при обновлении версии бокса мы используем Nginx с дополнительным модулем, потому что описывать документ каждый раз вручную не практично. Формирование метаданных сделано при помощи простого скрипта на Lua. Мы хостим боксы самостоятельно с помощью Lua.

Почему именно Lua

Lua — скриптовый язык программирования. По возможностям, идеологии и реализации язык ближе всего к JavaScript, однако отличается более мощными и гибкими конструкциями.

Несмотря на то что Lua не содержит понятия класса и объекта в явном виде, механизмы ООП, в том числе множественное наследование, легко реализуются с использованием метатаблиц. Эти таблицы также отвечают за перегрузку операций и прочее.

Реализуемая модель ООП — прототипная (как и в JavaScript). Интерпретатор языка — свободно распространяемый с открытыми исходными текстами на языке C.

Установка и зависимости

Описание приведено для операционных систем семейства Debian.

  • Прежде всего нужен Nginx с модулем lua-nginx-module. Установите готовый пакет nginx-extras, либо соберите вручную.
  • Потребуется интерпретатор Lua, чтобы протестировать скрипт в интерактивной консоли.
  • Для компиляции модулей потребуется утилита make.
  • Менеджер пакетов luarocks: для поиска файлов в директории используем модуль luaposix, для конвертации словаря в JSON — JSON4Lua.
$ sudo apt-get -y install make nginx-extras lua5.1 luarocks$ # install lua modules$ sudo luarocks install luaposix$ sudo luarocks install JSON4Lua

«Здравствуй, подлунный мир!»

Фраза “Hello world!” на Lua так же проста, как и на Python.

Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio> print "Hello world!"Hello world!

Теперь попробуем тоже самое при помощи полнофункционального Nginx Lua API.

server {    listen    80;    location /hello-world {        content_by_lua '            ngx.header.content_type = "text/plain"            ngx.say("Hello world!")        ';    }}
$ curl http://10.1.1.111/hello-worldHello world!

Для исполнения скрипта служит директива content_by_lua, для которого Nginx получает ответ через API. Если скрипт большой, не обязательно описывать его внутри конфигурации, можно подключить через директиву content_by_lua_file.

Vagrant метаданные

Настройка Nginx

На сервере мы складываем боксы в директорию hosted, создавая поддиректорию для каждого проекта. Сами боксы со строго указанным форматом имени {provider}-{version.subversion}.box.

Формируется такое дерево.

$ tree hosted/hosted/├── foo│   ├── docker-1.0.box│   ├── docker-1.3.box│   ├── virtualbox-1.0.box│   ├── virtualbox-1.4.box│   └── virtualbox-1.7.box└── bar    ├── virtualbox-1.0.box    ├── virtualbox-1.1.box    └── virtualbox-1.2.box2 directories, 8 files

Настроим Nginx так, чтобы для любого бокса начиналось скачивание, а для имени проекта возвращались вычисленные метаданные.

server {    listen    80;    set $box_url 'http://10.1.1.111/%s/%s-%s.box';    set $box_prefix '/home/vagrant/proj/hosted/';    location ~ /*\.box$ {        root /home/vagrant/proj/hosted;        # just return box    }    location ~ /(?<box_name>\w+)/?$ {        content_by_lua_file /home/vagrant/proj/app/handler.lua;    }}

Переменные $box_url и $box_prefix будут использоваться при формировании метаданных.

Вычисление метаданных с помощью Lua

Теперь сформируем метаданные для версирования Vagrant боксов. Идея в том, что по запросу Lua будет осуществлять поиск сохраненных боксов в заданной директории на сервере, вычислять их хеш-суммы и создавать ответ в формате метаданных Vagrant.

Используя glob из библиотеки posix, найдем все боксы.

local box_root = ngx.var.box_prefix .. ngx.var.box_name .. '/'local posix = require "posix"local glob = posix.glob (box_root .. '*.box')-- Если боксы не найдены, можно сразу возвращать 404if not glob then    ngx.status = ngx.HTTP_NOT_FOUND    return ngx.exit (ngx.HTTP_NOT_FOUND)end

Итерациями пройдем по найденным боксам и сформируем словарь с найденными версиями.

local versions = {}-- Discover the boxesfor _, box in ipairs (glob) do    -- Обрабатываем найденый бокс, определяя версию и формируя описание    local provider, version = make_provider (box)    if version then        if versions[version] == nil then            -- Если версия встречается впервые, создаем запись для новой версии            versions[version] = {                version = version,                providers = {provider}            }        else            -- Если версия уже была описана, обновляем список провайдеров            table.insert (versions[version]['providers'], provider)        end    endend

Для вычисления хеш-суммы больших файлов боксов используем утилиты
OC — sha1sum, sha256sum, md5sum с помощью вызова процесса через io.popen.

local hash = 'sha1'function get_hash (filepath)    -- Вычисляем хешсумму используя вызов консольной утилиты sha1sum    local command = string.format ('%ssum %s | cut -d " " -f1', hash, filepath)    local hashsum = assert (io.popen (command, 'r'))    local result = string.gsub (hashsum:read ('*a'), '\n', '')    hashsum:close ()    return resultend

Функция make_provider выполняется для каждого найденного бокса. Подразумевается, что боксы хранятся на сервере со строго заданным форматом имени: {provider}-{version.subversion}.box

Разбираем версию и имя провайдера, после чего формируем словарь, описывающий данный бокс.

local function make_provider (filepath)    -- Make vagrant provider from given file    local box_provider, box_version = string.match (        filepath, string.format ('%s(%%a+)-(.+).box', box_root))    return {        -- Название провайдера virtualbox или docker        name = box_provider,        -- Прямая ссылка на бокс, которую будет запрашивать vagrant        url = string.format (ngx.var.box_url, ngx.var.box_name, box_provider, box_version),        -- Алгоритм хешсуммы sha1, sha256, md5        checksum_type = hash,        -- Строка со значением хешсуммы        checksum = get_hash(filepath)    }, box_versionend

Обработав все боксы и сформировав список версий, обернем все в дополнительный словарь.

-- Make result responselocal vagrant = {    name = ngx.var.box_name,    description = string.format ("Boxes for %s proj", ngx.var.box_name),    versions = {}}for _, version in pairs (versions) do    table.insert (vagrant['versions'], version)end

Ответ сервера JSON с найденными версиями.

ngx.header.content_type = "application/json; charset=utf-8"local json = require "json"ngx.say (json.encode (vagrant))

Полученный скрипт формирует JSON ответ c метаинформацией о боксах.

$ curl http://10.1.1.111/example | jq  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                 Dload  Upload   Total   Spent    Left  Speed100   943    0   943    0     0  13412      0 --:--:-- --:--:-- --:--:-- 13471
{  "versions": [    {      "version": "1.7",      "providers": [        {          "url": "http://10.1.1.111/example/virtualbox-1.7.box",          "checksum": "3221c0fd58a4b2430efc5eeaf09cb8eaf877f3a9",          "name": "virtualbox",          "checksum_type": "sha1"        }      ]    },    {      "version": "1.3",      "providers": [        {          "url": "http://10.1.1.111/example/docker-1.3.box",          "checksum": "def7148aa7ded879dbf5944af4785c2b09aba97a",          "name": "docker",          "checksum_type": "sha1"        }      ]    },    {      "version": "1.4",      "providers": [        {          "url": "http://10.1.1.111/example/virtualbox-1.4.box",          "checksum": "63b06d8c065f5c2522c356d4d6ceb718ec3f8198",          "name": "virtualbox",          "checksum_type": "sha1"        }      ]    },    {      "version": "1.0",      "providers": [        {          "url": "http://10.1.1.111/example/docker-1.0.box",          "checksum": "65cb550765d251604dcfeedc36ea61f66ce205c4",          "name": "docker",          "checksum_type": "sha1"        },        {          "url": "http://10.1.1.111/example/virtualbox-1.0.box",          "checksum": "c0a9d5c3d6679cfcc4b1374e3ad42465f3dd596e",          "name": "virtualbox",          "checksum_type": "sha1"        }      ]    }  ],  "name": "example",  "description": "Boxes for example proj"}

Полный пример скрипта можно посмотреть в репозитории на Github. И при желании поиграться, запустив настроенный Vagrant.

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

Автор статьи
Кирилл Гришанин
Последние 10 лет руковожу командой аналитиков, дизайнеров и разработчиков

Подпишитесь на блог WB—Tech

Никакого спама, только анонсы новых статей

Наши клиенты

WBTech - Клиенты - МТС
WBTech - Клиенты - Высшая Школа Экономики
WBTech - Клиенты - Лента
WBTech - Клиенты - Верный
WBTech - Клиенты - Рамблер
WBTech - Клиенты - Совкомбанк
WBTech - Клиенты - Билайн
WBTech - Клиенты - Леруа Мерлен
WBTech - Клиенты - Ульяна Сергеенко
WBTech - Клиенты - Правительство Москвы
ИП Гришанин Кирилл Олегович
ИНН 774313842609
Коворкинг Starthub
Б. Новодмитровская ул., 36, стр. 12, вход 6, Москва, Россия, 127015
Коворкинг Wework
Ahad Ha'am 54, Tel Aviv-Yafo, Израиль