Обновление пакетов в Debian и Ubuntu почти всегда выглядит одинаково: apt update, затем apt upgrade. Но вокруг этой операции часто нужны действия: сохранить снимок состояния пакетов, проверить свободное место, отправить уведомление, записать список установленных .deb в журнал. APT умеет вызывать собственные команды в определённые моменты — это и есть хуки. Они выполняются от root и дают удобную автоматизацию без патчей и обёрток. Ниже — как устроены хуки, где их размещать и какие сценарии действительно полезны.
Где APT ищет хуки
Конфигурация APT собирается из файлов в /etc/apt/apt.conf.d/, которые читаются в лексикографическом порядке. Имена вида 99local-hooks ставят ваши настройки после дистрибутивных. Синтаксис напоминает ISC-конфиги: имя параметра, значение в кавычках, точка с запятой. Списки команд задаются блоком в фигурных скобках.
sudo tee /etc/apt/apt.conf.d/99local-hooks >/dev/null <<'EOF'
DPkg::Post-Invoke { "/usr/local/sbin/apt-state-snapshot"; };
EOF
sudo chmod 0644 /etc/apt/apt.conf.d/99local-hooks
apt-config dump | grep -i invoke
Команды запускаются через /bin/sh от имени root, без интерактивного терминала. Вывод попадает в поток APT и может испортить прогресс-бар, поэтому всё лишнее отправляйте в logger или systemd-cat. PATH в этом окружении может быть минимальным: используйте абсолютные пути.
Точки вызова и их семантика
У APT несколько моментов вызова, и важно понимать разницу. DPkg::Pre-Invoke выполняется до запуска dpkg и может отменить операцию ненулевым кодом. DPkg::Post-Invoke — после завершения dpkg. DPkg::Post-Invoke-Success срабатывает только при успехе. DPkg::Pre-Install-Pkgs дополнительно получает на stdin список путей к .deb.
Для обновления индексов есть APT::Update::Pre-Invoke, APT::Update::Post-Invoke и APT::Update::Post-Invoke-Success. Сбой команды в Pre-хуке приводит к завершению операции с ошибкой; сбой пост-хука уже не откатывает установленные пакеты, но может изменить код возврата и запутать автоматизацию. Поэтому в пост-хуках добавляйте || true только осознанно: молчаливое проглатывание ошибок мешает диагностике.
Снимок состояния пакетов
Первый полезный сценарий — журнал состояния после каждой операции. Он пригодится при разборе инцидента: видно, какая версия пакета была установлена в конкретный момент. Скрипт не вызывает apt и не создаёт рекурсии.
#!/bin/sh
set -eu
umask 027
dir=/var/backups/apt-state
mkdir -p "$dir"
stamp=$(date +%Y%m%dT%H%M%S)
{
date -Is
dpkg-query -W -f='${Package}\t${Version}\t${Status}\n'
} > "$dir/$stamp.tsv"
find "$dir" -type f -name '*.tsv' -mtime +30 -delete
Подключите его к DPkg::Post-Invoke-Success. Файлы копятся в /var/backups/apt-state, старые удаляются через 30 дней. Проверьте результат командой ls -lt /var/backups/apt-state и сравните два снимка через diff. Это не резервная копия системы и не способ отката, а журнал для аудита.
Проверка свободного места
Второй сценарий защищает от установки, которая заполнит корень и оставит систему без журналов. Хук читает доступное место и завершает работу с ошибкой, если порог не достигнут.
#!/bin/sh
set -eu
avail=$(df -Pk /usr | awk 'NR==2 {print $4}')
need=$((2 * 1024 * 1024)) # 2 GiB в килобайтах
if [ "$avail" -lt "$need" ]; then
echo "apt-hook: мало места на /usr, доступно ${avail}K" >&2
exit 1
fi
Подключите скрипт к DPkg::Pre-Invoke. Порог подберите под размер обновлений: для рабочей станции хватит двух гигабайт, для сервера с крупными пакетами — больше. Учитывайте, что хук срабатывает и при удалении пакетов, когда места может быть мало, но операция полезна. Если это мешает, проверяйте переменную окружения или аргументы, которые передаёт APT, и пропускайте проверку для удаления.
Список пакетов на stdin
DPkg::Pre-Install-Pkgs передаёт команде пути к устанавливаемым .deb, по одному в строке. Это удобно для инвентаризации и проверки источника пакета. Читайте stdin построчно и не пытайтесь запустить apt внутри.
#!/bin/sh
set -eu
logger -t apt-hook "начало установки пакетов"
while IFS= read -r deb; do
logger -t apt-hook "пакет: $deb"
done
logger -t apt-hook "список пакетов получен"
Записи ищите через journalctl -t apt-hook -n 30. Не выводите список в stdout: APT использует поток для своего интерфейса. Если нужно различать установку, обновление и удаление, используйте версию протокола 2 или 3 через DPkg::Tools::options::<команда>::Version, но для большинства задач достаточно путей к файлам.
Уведомления и защита от повторов
Пост-хук может отправить уведомление в journald, откуда его заберёт система мониторинга. Чтобы два параллельных APT-процесса не мешали друг другу, поставьте блокировку и таймаут. Это особенно важно на серверах, где ночью работает unattended-upgrades.
#!/bin/sh
set -eu
exec 9>/run/apt-hook.lock
flock -n 9 || exit 0
timeout 10 logger -t apt-hook "операция APT завершена: $(date -Is)"
Хук должен быть идемпотентным: повторный запуск не должен ломать данные или создавать дубли. Не храните в скриптах пароли и токены — файл читается root, но история правок и резервные копии часто доступны шире, чем кажется. Права на скрипт: владелец root, режим 0755, каталог без записи для остальных.
Ошибки, которые ломают автоматизацию
- Вызов
aptилиdpkgвнутри хука создаёт рекурсию и блокировку. - Долгий сетевой запрос без
timeoutподвешивает установку. - Вывод в stdout ломает прогресс и парсеры автоматизации.
- Расчёт на переменные окружения интерактивного пользователя.
- Хук срабатывает и в контейнерах, и при unattended-upgrades — проверяйте условия.
- Отсутствие
#clearпри замене чужого списка команд.
Для отладки включите Debug::RunScripts: APT покажет, какие внешние команды вызывает. Конфигурацию удобно проверять в одноразовой виртуальной машине, а не на рабочем сервере. Команда apt-config dump показывает итоговое дерево настроек, включая унаследованные списки.
sudo apt-get -o Debug::RunScripts=1 install --reinstall hello
sudo apt-config dump | grep -A2 'DPkg::'
sudo systemctl status apt-daily.timer
Не используйте apt-get -s как доказательство работы хука: в режиме симуляции dpkg не вызывается, и часть точек может не сработать. Проверяйте на реальной, но одноразовой системе.
Чек-лист
- Файл хука лежит в
/etc/apt/apt.conf.d/с правами 0644. - Скрипты принадлежат root и не используют относительные пути.
- В пост-хуках есть блокировка и таймаут.
- Вывод уходит в journald, а не в stdout.
- Проверено поведение при unattended-upgrades и в контейнере.
- Есть тестовая машина для проверки перед раскаткой.
Итог
APT-хуки — простой способ добавить в обновление пакетов аудит, проверку ресурсов и уведомления, не меняя сам APT. Работают они от root, поэтому требуют аккуратности: абсолютные пути, блокировки, таймауты и понятные журналы. Начните с одного снимка состояния, проверьте его на тестовой машине и только потом добавляйте проверки, способные отменить установку.