Наверно, имеет смысл начать из далека, как я люблю :)

Уже 4-й месяц я работаю релиз-инженером, постигаю премудрости процесса ci-cd. Ясное дело, что курс получается лайтовый, так как во-первых я с этим процессом до некоторого времени был знаком по-наслышке, а во-вторых там, где я работаю он используется не на полную катушку, а в-третьих это не дев-опс по-русски, когда в одно рыло и разработка и тестирование и подготовка к выкладке в бой и боевая эксплуатация и даже закупка серверов.

Где-то год назад я пообщал что сделаю билдер для слакварных пакетов. Ну то есть тыкнул кнопочку и собрал всё что надо, в нужной последовательности.

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

Тем не менее на данный момент у меня есть билдер, который таки может собирать софт и зависимости. Долго раздумывать, что взять в качестве костяка системы я не стал, отталкивался от задач: сборка пакетов в "чистом" окружении, а это уже подразумевает, что на сборку каждого пакета надо готовить свой экземпляр сборочного окружения. В голову приходит... хм... докер.

Ну, докер, значит докер... Значит, надо подготовить образ, из которого будет подниматься сборочная среда. "Минимальный" образ слакваре это... брабанная дробь... 9Гб и туда входит полная установка слакваре. В слакваре нет понятия зависимостей и всё что есть в поставке при таком подходе автоматически становится базой. В принципе это не страшно, так как из одного образа можно поднимать десятки сборщиков и они будут отличаться только привнесёнными на этапе сборки изменениями, а их не так-то много.

Хорошо, со средой сборки определились, а как мы настроим сам процесс? чем управлять? В природе существует масса управлялок процессом сборки, но такие самые популярные которые приходят в голову это GitLab CI, Jenkins, BuildBot. Остальное либо за деньги, либо не то чтобы популярно и найти поддержку будет не просто.

На работе мы используем Jenkins, другие команды коллег - gitlab ci. Но gitlab ci при скромных возможностях в качестве ci предлагает гораздо бОльшие возможности по коммандной разработке, а это не совсем то, что мне надо. BuildBot хорош, скромен по потребляемым ресурсам (в отличие от дженкинса, который на яве). Но я всё-таки выбрал Jenkins внутри Tomcat-а.

В процессе построения билдера мне надо было решить вопрос, связанный с зависимостями. Не так сильно важны зависимости на стороне билдера (пользователю, во всяком случае), как со стороны вопроса установки пакетов. Например, тот же xmpp-клиент Gajim хочет принести с собой ещё с десяток питонных модулей, ставить их ручками? по списку? это неинтересно. Благо, этот вопрос уже решён силами сторонних разработчиков и из самых, наверно, удобных и почти что изящных решний - slapt-get. По заверениям автора slapt-get имитирует работу apt-get для debian-образных дистрибутивов.

Хочется немного поворчать на тему slapt-get и его имитации apt-get-а. Нет, не имитирует. Если бы он опциями имитировал хотябы, а то они все идут с двойным минусом, а это несколько неудобно. Нету мощного движка по работе с зависимостями - установка пакета с зависимостями проходит на ура, а вот с удалением... с удалением неиспользуемых зависимостей уже проблемка. Не удаляет. Краеугольные моменты, например, докачка пакетов формально заявлены и slapt-get честно пытается докачать пакеты, но это работает только если предыдущее обновление прервалось на первом пакете и он был скачан не до конца. Если облом произошёл на следующих пакетах, то slapt-get, получив ответ от сервера 416 Not in range, вылетает с ошибкой, мол сервер отказал, хотя на стороне сервера всё тип-топ. Разработчик рекомендовал почистить кэш в этом случае и слился, то есть чинить этот момент попросту не стал. Понятное дело, что slapt-get является по факту обёрткой для slackware pkg tools, ясно, что разработчик сделал громкое заявление и претензию на лавры apt-get-а, но надо понимать, что apt для своей работы не только использует dpkg, но и хранит свою базюльку относительно пакетов, slapt-get этим не занимается, отсюда и невозможность удалить зависимости для пакета. Впрочем, для постороения графа зависимостей не обязательно хранить/формировать какую-то дополнительную базейку, информация о slack-required под рукой есть, а поставлен ли пакет ручками или притянут автоматически по зависимостям... вот для этого нужна дополнительная локальная база + для хранения хэшей от содержимого файлов.

Но, достаточно о slapt-get-е, однако следует упомянуть, что он несколько хитёр в отношении "плоских" репозиториев: в метаданных поле Location должно заполняться строго как ".", пустым не должно быть, но и не должно быть "./", хотя в понятиях шелла или веб-сервера и пустое поле и "." и "./" в результате приходят к одному знаменателю. Это тоже к вопросу о slapt-get... В репозитории я запользовал полный набор возможностей slapt-get: это slack-required, slack-recommends, slack-conflicts. И, естественно, добавил его в образ билдера.

Для генерации метаданных я выбрал подход Eric-а Hameleers-а (aka Alien BOB), когда исходные кусочки метаданных пред-генерируются на моменте сборки пакета, а скрипт, генерирующий метаданные для репозитория уже берёт эти кусочки и фактически из них собирает метаданные для slapt-get-а. Автор slapt-get-а в своём мануале предлагает распаковывать каждый пакет вынимать метаданные из него, строить список файлов в пакете и тд... словом, ОЧЕНЬ дорогой по процессору и диску метод и на каждую перегенерацию метаданных при таком подходе на большом репозитории должно уходить неприличное количество времени. Да даже для маленького репозитория всё ровно получается довольно больно.

В результате у меня получилось сделать "работающий прототип" сборщика пакетов. Почему прототипа? - у него есть несколько ограничений: в том числе 1 сорс = 1 пакет, "плоский" репозиторий (впрочем, в объёмном я пока нужды не вижу) ну и тот факт, что для сборки нужен Jenkins :)

Собственно, на этом можно было бы закончить повествование, но хотелось бы добавить, что имея в стоке такой инструмент как rpm, я очень долго думал насчёт сборки пакетов именно средствами rpm, уж больно инструментарий хорош, а вместе с yum-ом - это вообще роскошный набор. По большому счёту yum|rpm - это один из лучших пакетных менеджеров на сцене, есть ещё apt+rpm, но в любом случае rpm, несмотря на свою питонную сущность является очень практичным пакетным менеджером, хорошо документированным (в отличие от dpkg) и с развитым инструментарием, по которому (в отличие от deb-helpers|sbuild|pbuilder и проч) есть внятная и достаточно исчерпывающая документация. Да и в базе идёт именно rpm, а не что-то другое. Само решение мне далось с трудом, так как уж очень велики плюсы rpm и компании в сравнении с slackware package tools и slapt-get: это и отлично работающий инструментарий и куча уже написанных spec-файлов и удобство сборки пакетов и полноценная система зависимостей и прочие мелкие, но приятные бонусы. Но вместе с тем и некоторые очевидные минусы: все инструменты включая rpm-devtools, yum+yum-utls|dnf или apt-rpm пришлось бы собирать ручонками, кроме того, не нативность пакетов и, как следствие, не полное дерево зависимостей и невозможность использования такого мощного средства как mock (впрочем, docker вполне себе заменяет mock). После долгих размышлений я всё-таки решил не делать монстра франкенштейна, а остановиться на родном формате slackware с расширениями для slapt-get, хотя это и не самый оптимальный выбор.

Возможно, в некотором будущем я всё-таки сделаю билдер на основе buildbot, но это будет отдельная история. А пока что это монструозный Jenkins.

Next Post