воскресенье, 11 августа 2019 г.

Создание своего репозитория Debian при помощи aptly

Несмотря на то, что в официальных репозиториях Debian можно найти почти всё, что может понадобиться, иногда всё-таки приходится самостоятельно собирать пакеты. Я стараюсь не замусоривать систему программами, установленными из исходных текстов и модулями различных языков программирования, установленными через их стандартные инструменты, вроде cpan, pip или gem.

Собирать пакеты приходится по нескольким причинам:
  • В репозитории нет нужного пакета. Иногда бывает можно найти неофициальные репозитории для нужного релиза операционной системы.
  • Через репозитории дистрибутива доступна только старая версия пакета, в которой не хватает нужных функций. В этом случае иногда помогают официальные репозитории backport с более свежими версиями пакетов, собранными для предыдущих релизов операционной системы.
  • В пакете из репозитория есть ошибка. Даже в стабильном дистрибутиве встречаются ошибки, которые не исправляются ментейнерами. Тут придётся либо обновлять пакет, либо исправлять в нём ошибку.
  • В репозитории есть нужная версия пакета, но собранная без поддержки определённой функции. Тут без вариантов - нужно дорабатывать и пересобирать пакет.
Например, в 2013 году я начал настраивать на работе серверы под Debian Wheezy. С тех пор у меня накопилось некоторое количество самосборных пакетов:
  • python-grab - не было в репозитории
  • python-flask-httpauth - не было в репозитории
  • python-pycurl - не было в репозитории
  • libdancer-plugin-database-core-perl - не было в репозитории
  • libnet-ssh-expect-perl - не было в репозитории
  • wordpress-plugin-simple-ldap-login - не было в репозитории
  • python-mysqldb - модуль версии 1.2.3 заменён на более свежую версию 1.2.5 из-за некорректной работы с collation utf8_bin: python-mysqldb: utf8_bin collation will not convert to Unicode strings
  • libsnmp и компания - пакеты были пересобраны из-за того, что не все производители оборудования с поддержкой SNMPv3 обеспечивают монотонный рост счётчика количества перезагрузок и времени с момента перезагрузки: Стандарт SNMPv3 и суровая действительность USM_TIME_WINDOW
  • python-netsnmp - модуль пересобран из-за ошибки в формировании SET-запросов со значениями типа IPv4: Исправление Python-прослойки библиотеки Net-SNMP
  • php5 и компания - среди этих пакетов был пакет php5-snmp, зависящий от пакета libsnmp
  • openntpd - в пакете была иcправлена ошибка в обработке таймаута ответов от серверов DNS: Таймаут DNS в OpenNTPd
  • uwsgi - включена поддержка Linux capabilities: Пересборка uwsgi с поддержкой Linux Capabilities
  • zabbix - собрана более свежая версия пакета, наложена серия нестандартных патчей. Например, добавлены отдельные настройки таймаута и количества повторов для опроса по протоколу SNMP: Установка и настройка Zabbix 2.2.0 в Debian Wheezy
  • python-paramiko и python-ecdsa - модуль paramiko 1.7.7.1-3.1 обновлён до версии 1.16.0-1, т.к. версия paramiko из репозитория имела ошибки в поддержке протокола SFTP: python-paramiko: sftp connections hangs. Модуль python-ecdsa был тоже обновлён, т.к. более новая версия модуля paramiko требовала и более свежую версию модуля ecdsa.
Если у вас есть только один компьютер и вам нужна лишь пара нестандартных программ, то заморачиваться сборкой пакетов, возможно, не стоит. Когда же нестандартных программ набирается с десяток, их приходится периодически обновлять, а потом ещё и устанавливать на десяток компьютеров, то может оказаться проще потратить время на изучение различных способов сборки пакетов и организацию собственного репозитория.

Есть масса способов собрать пакет для Debian:
  • Можно доработать пакет с исходными текстами и собрать как новый пакет с исходными текстами, так и собрать из него новые двоичные пакеты.
  • Для сборки пакетов с модулями python существует утилита python-stdeb: Создание deb-пакетов для модулей Python
  • Для сборки пакетов с модулями perl существует утилита dh-make-perl.
  • Можно попробовать сконвертировать готовый пакет из другого формата при помощи утилиты alien.
  • Можно попробовать собрать пакет при помощи утилиты checkinstall.
  • Наконец, можно просто создать пакет вручную при помощи утилиты dpkg-deb: 5.13 Как мне сделать собственный .deb пакет?
Собственный репозиторий позволяет экономить время, т.к. становится возможным не думать о том, установлены ли в системе пакеты нужных версий со всеми доработками: нужно всего-лишь прописывать в список подключенных репозиториев на каждой системе строчку со своим репозиторием.

Когда утилита aptly ещё не попалась мне на глаза, я делал репозитории при помощи утилиты apt-ftparchive. Не помню подробностей, но точно помню, что была она не очень удобной в использовании.

Потом мне попалась утилита aptly, которая обладала более богатыми возможностями и была более удобна в использовании. Стоит, правда, сказать, что утилита настолько богата возможностями, что удобство использования остаётся довольно относительным. Обновлять репозитории мне приходилось нечасто, поэтому каждый раз я с трудом вспоминал, какими командами я пользовался в прошлый раз. Я стал записывать команды в wiki-страницу и пользоваться своими заметками. Правда, структурированы они были довольно плохо, поэтому в блог я ничего не публиковал. Теперь же решил упорядочить эти заметки и выложить их в блог.

Установка пакета

Установим утилиту:
# apt-get install aptly
Утилита написана на языке go, в скомпилированном виде тянет за собой минимум зависимостей: это утилиты для работы с архивами и утилиты для манипуляции GPG-подписями.

Создание локальных репозиториев

Список репозиториев пока пуст:
$ aptly repo list
No local repositories found, create one with `aptly repo create ...`.
Создадим новый репозиторий с именем stretch:
$ aptly repo create stretch

Local repo [stretch] successfully added.
You can run 'aptly repo add stretch ...' to add packages to repository.

Добавление пакетов в локальный репозиторий

Добавляем в репозиторий stretch все двоичные пакеты из текущего каталога:
$ aptly repo add stretch *.deb
Loading packages...
[+] libauthen-radius-perl_0.26-1ufanet_all added
[+] libnet-ssh-expect-perl_1.09-1_all added
[+] zabbix-agent-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-agent_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-frontend-php_1:3.4.12-1+stretch-ufanet2_all added
[+] zabbix-get-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-get_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-java-gateway_1:3.4.12-1+stretch-ufanet2_all added
[+] zabbix-proxy-mysql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-proxy-mysql_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-proxy-pgsql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-proxy-pgsql_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-proxy-sqlite3-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-proxy-sqlite3_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-sender-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-sender_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-server-mysql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-server-mysql_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-server-pgsql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 added
[+] zabbix-server-pgsql_1:3.4.12-1+stretch-ufanet2_amd64 added
Следом добавляем в репозиторий stretch все пакеты с исходными текстами из текущего каталога:
$ aptly repo add stretch *.dsc
Loading packages...
[+] libauthen-radius-perl_0.26-1ufanet_source added
[+] libnet-ssh-expect-perl_1.09-1_source added
[+] zabbix_1:3.4.12-1+stretch-ufanet2_source added
По умолчанию программа размещает пакеты и базу данных в каталоге ~/.aptly

Публикация локальных репозиториев

Список опубликованных репозиториев пока пуст:
$ aptly publish list
No snapshots/local repos have been published. Publish a snapshot by running `aptly publish snapshot ...`.
Если сейчас попытаться опубликовать репозиторий, то программа сообщит о том, что не указан дистрибутив:
$ aptly publish repo stretch ncc
ERROR: unable to publish: unable to guess distribution name, please specify explicitly
Посмотрим текущие настройки репозитория:
$ aptly repo show stretch
Name: stretch
Comment: 
Default Distribution: 
Default Component: main
Number of packages: 23
Укажем дистрибутив репозитория:
$ aptly repo edit -distribution="stretch" stretch
Local repo [stretch] successfully updated.
Опубликуем дистрибутив без PGP-подписи:
$ aptly publish repo -skip-signing=true stretch ncc
Loading packages...
Generating metadata files and linking package files...
Finalizing metadata files...

Local repo stretch has been successfully published.
Please setup your webserver to serve directory '/home/stupin/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
  deb http://your-server/ncc/ stretch main
  deb-src http://your-server/ncc/ stretch main
Don't forget to add your GPG key to apt with apt-key.

You can also use `aptly serve` to publish your repositories over HTTP quickly.
Посмотрим список опубликованных репозиториев:
$ aptly publish list Published repositories:

ncc/stretch [amd64, source] publishes {main: [stretch]}
Опубликованные репозитории находятся в каталоге ~/.aptly/public, откуда их можно скопировать и разместить на веб-сервере. В примере выше локальный репозиторий stretch был опубликован в каталоге ncc, поэтому найти его можно будет в каталоге ~/.aptly/public/ncc

Поиск и удаление пакетов из локального репозитория

Для поиска пакетов, в имени которых присутствует слово zabbix и версия которых выше 1:3.4, а также пакетов, в имени которых присутствует подстрока -perl, можно воспользоваться следующей командой:
$ aptly package search '(Name (~zabbix), Version (>1:3.4)) | Name (~-perl)'
Как видно, скобки используются для группировки выражений, запятая соответствует логическому И, вертикальная черта соответствует логическому ИЛИ. Тильда обозначает совпадение с регулярным выражением, знак больше используется в своём обычном смысле. Более подробно правила фильтрации описаны в документации на странице: Package queries.

Для удаления пакетов из репозиториев используются те же самые выражения, которые используются при поиске пакетов. Например, удалим все пакеты, в имени которых присутствует подстрока zabbix:
$ aptly repo remove stretch 'Name (~zabbix)'
Loading packages...
[-] zabbix-proxy-mysql_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-proxy-mysql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-sender-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-proxy-sqlite3-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-server-mysql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix_1:3.4.12-1+stretch-ufanet2_source removed
[-] zabbix-java-gateway_1:3.4.12-1+stretch-ufanet2_all removed
[-] zabbix-server-pgsql_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-agent-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-proxy-pgsql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-server-mysql_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-get_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-frontend-php_1:3.4.12-1+stretch-ufanet2_all removed
[-] zabbix-sender_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-get-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-server-pgsql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-agent_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-proxy-sqlite3_1:3.4.12-1+stretch-ufanet2_amd64 removed
[-] zabbix-proxy-pgsql_1:3.4.12-1+stretch-ufanet2_amd64 removed
Для удаления пакетов, в имени которых есть подстрока -perl, можно воспользоваться следующей командой:
$ aptly repo remove stretch 'Name (~-perl)'
Loading packages...
[-] libnet-ssh-expect-perl_1.09-1_all removed
[-] libauthen-radius-perl_0.26-1ufanet_source removed
[-] libauthen-radius-perl_0.26-1ufanet_all removed
[-] libnet-ssh-expect-perl_1.09-1_source removed

Перемещение пакетов между локальными репозиториями

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

Например, вот так можно выполнить перемещение всех пакетов, у которых в версии фигурирует подстрока stretch, из репозитория wheezy в репозиторий stretch:
$ aptly repo move wheezy stretch 'Version (~stretch)'
Loading packages...
[o] zabbix-server-pgsql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-sender_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-proxy-pgsql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-get-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-server-pgsql_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-proxy-pgsql_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-frontend-php_1:3.4.12-1+stretch-ufanet2_all moved
[o] zabbix-get_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-proxy-sqlite3-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-proxy-mysql_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-server-mysql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-java-gateway_1:3.4.12-1+stretch-ufanet2_all moved
[o] zabbix-server-mysql_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-sender-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-proxy-sqlite3_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-agent-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-agent_1:3.4.12-1+stretch-ufanet2_amd64 moved
[o] zabbix-proxy-mysql-dbgsym_1:3.4.12-1+stretch-ufanet2_amd64 moved

Обновление опубликованного репозитория

После первичной публикации репозитория, его нельзя опубликовать повторно. Чтобы изменения в репозитории применились к опубликованному репозиторию, нужно вызвать команду обновления:
$ aptly publish update -skip-signing=true stretch ncc
Loading packages...
Generating metadata files and linking package files...
Finalizing metadata files...
Cleaning up prefix "ncc" components main...
Publish for local repo ncc/stretch [amd64, source] publishes {main: [stretch]} has been successfully updated.

Удаление устаревших пакетов

После удаления пакетов из репозитория, они не удаляются из хранилища автоматически. Во-первых, пакет остаётся в хранилище до тех пор, пока на него ссылается хотя бы один из репозиториев - локальных или опубликованных. Во-вторых, даже если на пакет больше нет ссылок, он не будет удалён до тех пор, пока не будет явно вызвана команда очистки:
$ aptly db cleanup
Loading mirrors, local repos, snapshots and published repos...
Loading list of all packages...
Deleting unreferenced packages (23)...
Building list of files referenced by packages...
Building list of files in package pool...
Deleting unreferenced files (29)...
Disk space freed: 33.61 MiB...
Compacting database...

Удаление опубликованных репозиториев

Для удаления опубликованного репозитория можно воспользоваться соответствующей командой:
$ aptly publish drop stretch ncc
Removing /home/stupin/.aptly/public/ncc/dists...
Removing /home/stupin/.aptly/public/ncc/pool...

Published repository has been removed successfully.
Можно посмотреть в список опубликованных репозиториев и убедиться, что репозитория больше нет среди опубликованных:
$ aptly publish list
No snapshots/local repos have been published. Publish a snapshot by running `aptly publish snapshot ...`.

Удаление локального репозитория

Посмотрим на список локальных репозиториев:
$ aptly repo list
List of local repos:
 * [stretch] (packages: 0)

To get more information about local repository, run `aptly repo show <name>`.
Как видно, в репозитории нет пакетов, поэтому его можно удалить.

Для удаления локального репозитория stretch можно воспользоваться такой командой:
$ aptly repo drop stretch
Local repo `stretch` has been removed.
Посмотрим на список локальных репозиториев снова:
$ aptly repo list
No local repositories found, create one with `aptly repo create ...`.

P. S.

Есть у собственного репозитория и отрицательные стороны. Если у вас нет времени на то, чтобы обновить операционную систему до нового релиза, то собственный репозиторий с нестандартными пакетами будет ещё одним доводом в пользу того, чтобы как можно дольше оттягивать момент обновления операционной системы до нового релиза. С одной стороны, в новом релизе часть проблемых пакетов обновится и их больше не нужно будет пересобирать, а с другой стороны - всё равно нужно будет пересобрать доработанные пакеты и собрать пакеты, которых нет в стандартном репозитории обновлённой системы. И ещё при обновлении существует вероятность, что в новых пакетах будут не только исправлены старые ошибки, но и внесены новые.

Так, на работе всё тот же Debian Wheezy, который вышел в 2013 году, использовался мной аж до 2019 года. За это время успело выйти три новых релиза: Squeezy, Jessie, Stretch. Некоторые коллеги удивлялись, почему я продолжаю использовать этот устаревший релиз. Друге даже не догадывались об этом, т.к. им достаточно, чтобы обновлялся фасад здания, а с ржавыми трубами им дела иметь не приходилось. В 2018 году завершился срок продлённой поддержки Debian Wheezy. В 2019 году репозиторий пропал с зеркал и стало очевидно, что оттягивать обновление больше нельзя. Пришлось отложить всю текущую работу и заняться обновлениями, несмотря молчаливое и явное недовольство окружающих. Нельзя бесконечно копить технический долг - иногда нужно его возвращать.

Комментариев нет: