Привет!
Меня зовут Сергей, я работаю инфраструктурным инженером в команде API платформы tinkoff.ru.

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

Nginx — это многофункциональный и активно развивающийся прокси-сервер. Он отличается большим количеством модулей, вот далеко не полный список. Каждый проект налагает определенные требования к функционалу балансировщика и версии Nginx (например наличие http/2 и проксирование grpc), и составу его модулей.

Нам хочется видеть свежую версию с нужным набором модулей, работающую под определенным дистрибутивом Linux. В нашем случае это deb- и rpm-based системы. Вариант с контейнерами в данной статье не рассматривается.

Нам хочется оперативно изменять функционал наших балансировщиков. И здесь сразу встает вопрос — как этого добиться, затратив как можно меньше ресурсов? А еще бы лучше наладить процесс так, чтобы мы могли задать конечное число входных параметров, а на выходе получить артефакт в виде deb/rpm пакета для нужной ОС.

В итоге можно сформулировать ряд проблем:

  • Не всегда есть пакеты со свежей версией Nginx.
  • Нет пакетов с нужными модулями.
  • Компиляция и сборка пакета вручную занимает много времени и попросту утомительна.
  • Нет описания, как собран тот или иной инстанс Nginx.

Чтобы решить эти проблемы, напрашивается некий инструмент, который принимал бы на вход спецификацию в человекочитаемом формате и собирал по ней пакет Nginx с нужным функционалом.

Не найдя подходящего для нас варианта на просторах гитхаба, мы решили создать свой инструмент — nginx-builder.

Спецификации


В нашем инструменте мы хотели создавать описание спецификации в виде кода, который затем можно положить в Git-репозиторий. Для этого выбрали привычный для подобных вещей формат — yaml. Пример спецификации:

nginx_version: 1.14.1
output_package: deb
modules:
  - module:
      name: nginx-auth-ldap
      git_url: https://github.com/kvspb/nginx-auth-ldap.git
      git_branch: master
      dependencies:
        - libldap2-dev
  - module:
      name: ngx_http_substitutions_filter_module
      git_url: https://github.com/yaoweibin/ngx_http_substitutions_filter_module.git
  - module:
      name: headers-more-nginx-module
      web_url: https://github.com/openresty/headers-more-nginx-module/archive/v0.261.zip
  - module:
      name: nginx-module-vts
      git_url: https://github.com/vozlt/nginx-module-vts.git
      git_tag: v0.1.18
  - module:
      name: ngx_devel_kit
      git_url: https://github.com/simplresty/ngx_devel_kit.git
      git_tag: v0.3.0
  - module:
      name: ngx_cache_purge
      git_url: https://github.com/FRiCKLE/ngx_cache_purge.git
  - module:
      name: ngx_http_dyups_module
      git_url: https://github.com/yzprofile/ngx_http_dyups_module.git
  - module:
      name: nginx-brotli
      git_url: https://github.com/eustas/ngx_brotli.git
      git_tag: v0.1.2
  - module:
      name: nginx_upstream_check_module
      git_url: https://github.com/yaoweibin/nginx_upstream_check_module.git
  - module:
      name: njs
      git_url: https://github.com/nginx/njs.git
      git_tag: 0.2.5
      config_folder_path: nginx

Здесь мы указываем, что хотим видеть deb-пакет с версией Nginx 1.14.2 с нужным набором модулей. Секция с модулями — опциональная. Для каждого из них можно задать:

  • Название.
  • Адрес, где его можно получить:
    • Git-репозиторий. Также можно указать ветку или тег.
    • Веб-ссылка на архив.
    • Локальная ссылка на архив.

Некоторые модули требуют установки дополнительных зависимостей, например для nginx-auth-ldap нужен установленный libldap2-dev. Необходимые зависимости также можно указать при описании модуля.

Окружение


В нашем инструменте можно быстро получать окружение с установленными утилитами для компиляции, сборки пакета и другим вспомогательным ПО. Здесь как нельзя лучше подходит docker-контейнер со всем необходимым (в репозитории уже есть пара примеров docker-файлов для ubuntu и centos).

После того, как спецификация составлена и подготовлено окружение, мы запускаем наш сборщик, предварительно установив его зависимости:

pip3 install -r requirements.txt
./main.py build -f [конфиг_файл].yaml -r [номер_ревизии]

Номер ревизии здесь опционален и служит для версионирования сборок. Он записывается в метаинформацию пакета, что позволяет легко обновлять его на серверах.
По логам можно наблюдать за происходящим. Вот пример основных моментов:

builder - INFO - Parse yaml file: example.config.yaml
builder - INFO - Download scripts for build deb package
builder - INFO - Downloading nginx src...
builder - INFO - --> http://nginx.org/download/nginx-1.14.1.tar.gz
builder - INFO - Downloading 3d-party modules...
builder - INFO - Module nginx-auth-ldap will download by branch
builder - INFO - -- Done: nginx-auth-ldap
builder - INFO - -- Done: ngx_http_substitutions_filter_module
builder - INFO - Module headers-more-nginx-module will downloading
builder - INFO - Module nginx-module-vts will download by tag
builder - INFO - -- Done: nginx-module-vts
builder - INFO - Module ngx_devel_kit will download by tag
builder - INFO - -- Done: ngx_devel_kit
builder - INFO - -- Done: ngx_cache_purge
builder - INFO - -- Done: ngx_http_dyups_module
builder - INFO - Downloading dependencies
builder - INFO - Building .deb package
builder - INFO - Running 'dh_make'...
builder - INFO - Running 'dpkg-buildpackage'...
dpkg-deb: building package 'nginx' in '../nginx_1.14.1-1_amd64.deb'.

Так буквально парой команд мы создаем окружение и нужную сборку Nginx, и пакет появляется в директории, откуда запущен скрипт.

Встраивание


Также мы можем встроить наш инструмент в CI/CD процессы. В этом может помочь любая из множества существующих на сегодняшний день CI-систем, например Teamcity или Gitlab CI.

В итоге при каждом изменении спецификации в Git-репозитории автоматически запускается сборка артефакта. Номер ревизии привязывается к счетчику запусков билда.
Потратив еще немного времени, можно настроить отправку артефакта в локальный репозиторий пакетов, Nexus, Artifactory и так далее.

Дополнительным плюсом является то, что конфигурационный yaml-файл можно подключить в Ansible или другую систему автоматического конфигурирования, и брать оттуда номер версии и типа пакета, которые мы хотим задеплоить.

Что дальше


Проект еще не завершен. Вот над чем мы работаем сейчас:

  • Расширяем возможность конфигурирования, но при этом сохраняем ее максимально простой. Не хочется определять тысячу параметров, если нужно всего два, а остальное подходит по умолчанию. К этому относятся флаги компиляции (сейчас изменить их можно во внутреннем файле конфигурации src/config.py), пути установки, пользователя для запуска.
  • Добавляем варианты автоматической отправки пакета в различные хранилища артефактов.
  • Выполнения пользовательской команды при загрузке модуля (например для использования github.com/nginx-modules/nginx_upstream_check_module нужно сначала применить патч определенной версии)
  • Добавляем проведение тестов:
    • Пакет корректно устанавливается.
    • Nginx имеет нужную версию и собран с требуемыми флагами и модулями.
    • Создаются нужные пути, учетные записи и так далее.

Но пользоваться этим инструментом вы можете уже сейчас, а также предлагать доработки — github.com/TinkoffCreditSystems/Nginx-builder wellcome!

Комментарии (4)


  1. click0
    29.05.2019 16:45

    А вы пробовали это на POSIX Shell это делать?


  1. Sleuthhound
    29.05.2019 17:35

    Не найдя подходящего для нас варианта на просторах гитхаба


    Эм, возможно ключевое слово здесь «подходящего для нас», но а так есть как минимум github.com/cubicdaiya/nginx-build который выполняет схожие функции + умеет собирать OpenResty.

    А так, спасибо за ваше решение, попробую на досуге.


    1. therb1
      30.05.2019 14:28

      Мы увидели чрезмерность в данном решении из-за выбора языка разработки. Мы не увидели в GOlang каких-то решений которые были бы нам смертельно необходимы. Наша целевая аудитория это системные администраторы/devops инженеры(Да простит меня osminog)/SRE
      и они в большинстве своем знают python чаще чем GOlang.


  1. crazylh
    29.05.2019 18:42

    А потом приходишь в рассылку nginx и тебе "попробуйте удалить все сторонние модули и воспроизвести проблему без них".