Зависимости, удержания и пиннинг
Пакеты Debian объявляют Depends, Recommends, Suggests и Conflicts. apt разрешает полный граф зависимостей до касания диска. Hold замораживает пакет на текущей версии; пиннинг контролирует, какая версия побеждает, когда несколько источников предоставляют один пакет.
Ты запускаешь apt upgrade — и он хочет обновить ядро. Твой production-сервер использует кастомный модуль ядра, несовместимый с новой версией. Или ты закрепил конкретную версию PostgreSQL, потому что ваш DBA говорит, что минорное обновление ломает функцию репликации. Или ты смешиваешь пакеты из Debian stable и backports, а apt продолжает «полезно» обновлять пакеты, которые ты хотел оставить на stable-версии. Все три проблемы решаются двумя инструментами: удержаниями и пиннингом. Ошибись здесь — и «актуальный» сервер либо незаметно сломан, либо навсегда завис на небезопасном пакете.
После этого урока ты сможешь объяснить разницу между Depends, Recommends и Suggests; читать ограничения версий вида (>= 2.0) и (= 1.18.0-6ubuntu14); удерживать пакет на текущей версии с помощью apt-mark; безопасно снимать удержание; и писать пин в /etc/apt/preferences.d/ для управления тем, какой репозиторий побеждает для конкретного пакета.
Типы зависимостей имеют разную семантику — apt обрабатывает их по-разному. Понимание разницы объясняет, почему apt install --no-install-recommends может значительно сократить использование диска на сервере.
# Просмотр всех полей зависимостей пакета
apt-cache show nginx | grep -E '^(Depends|Recommends|Suggests|Pre-Depends|Conflicts|Breaks):'
# Depends: ДОЛЖНЫ быть установлены; apt откажет без них
# Recommends: устанавливаются по умолчанию, но пропускаемые; --no-install-recommends пропускает их
# Suggests: только информационные; никогда не устанавливаются автоматически
# Pre-Depends: как Depends, но должны быть настроены ДО распаковки этого пакета
# Conflicts: не может быть установлен вместе с перечисленным пакетом
# Breaks: перечисленный пакет будет некорректно работать рядом с этимНа минимальном сервере --no-install-recommends часто оправдан:
# В Dockerfile: экономит 30-60% на размере образа для многих пакетов
RUN apt-get install -y --no-install-recommends nginxPre-Depends встречаются редко, но критически важны: они существуют для пакетов вроде libc6, которые должны быть полностью настроены до того, как будет тронуто что-либо ещё в системе, потому что сам dpkg от них зависит.
Ограничения версий позволяют мейнтейнерам точно выражать совместимость. Ты увидишь их в выводе apt-cache show и в собственных спецификациях зависимостей:
Depends: libssl3 (>= 3.0.0), libpcre3 (>= 1:8.31), nginx-common (= 1.18.0-6ubuntu14.4)
| Ограничение | Значение |
|---|---|
(>= 1.2) | версия 1.2 или любая более поздняя |
(<= 2.0) | версия 2.0 или любая более ранняя |
(= 1.2.3-4) | ровно эта версия, включая суффикс ревизии Debian |
(>> 1.0) | строго больше 1.0 (редко) |
(<< 2.0) | строго меньше 2.0 (редко) |
# Проверить, удовлетворяет ли установленный пакет ограничению
dpkg --compare-versions "1.18.0-6ubuntu14.4" ">=" "1.18.0" && echo "satisfies" || echo "does not"
# satisfies
# Именно это apt выполняет внутри при разрешении зависимостейПрефикс эпохи Debian (1: в 1:8.31) переопределяет обычную сортировку версий. Версия пакета 1:0.1 сортируется выше, чем 9.99, потому что номер эпохи побеждает. Эпохи существуют, когда схема версионирования upstream изменилась, а новый номер версии численно ниже старого.
apt-mark hold замораживает пакет на установленной версии. apt upgrade будет полностью пропускать удержанные пакеты — они не обновятся, даже если доступна новая версия.
# Удержать пакет на текущей версии
sudo apt-mark hold nginx
# nginx set on hold.
# Проверить
apt-mark showhold
# nginx
# Проверить статус dpkg — теперь показывает "hi" (hold + installed)
dpkg -l nginx
# hi nginx 1.18.0-6ubuntu14.4 ...
# apt upgrade предупредит об удержанных пакетах:
# The following packages have been kept back: nginx
# Снять удержание, когда готов к обновлению
sudo apt-mark unhold nginxСамое распространённое применение: удержание ядра на сервере с out-of-tree модулем до выхода совместимой сборки от вендора. Подводный камень: удержанный пакет может блокировать обновления безопасности пакетов, которые от него зависят. Периодически запускай apt upgrade и проверяй, что остаётся в «kept back» — удержание должно быть осознанным, проверенным решением, а не тем, о чём ты забыл полгода назад.
Пиннинг управляет тем, какую версию apt предпочитает, когда несколько репозиториев предоставляют один пакет. Файл /etc/apt/preferences (или файлы в /etc/apt/preferences.d/) назначает числовые приоритеты. Побеждает наибольший приоритет.
# /etc/apt/preferences.d/backports-pin
# Приоритеты по умолчанию:
# 500 = пакеты из целевого релиза (основной suite)
# 100 = установленные пакеты и пакеты из нецелевых релизов
# 1 = «никогда не устанавливать из этого источника автоматически»
# -1 = «никогда не устанавливать, даже если явно запрошено»
# Закрепить nginx на версии из backports (приоритет 900 > 500 по умолчанию)
Package: nginx
Pin: release a=jammy-backports
Pin-Priority: 900
# Все остальные пакеты из backports с БОЛЕЕ НИЗКИМ приоритетом (предотвращает массовые обновления)
Package: *
Pin: release a=jammy-backports
Pin-Priority: 100# Проверить эффективную политику для пакета — показывает, какой репозиторий побеждает
apt-cache policy nginx
# Installed: 1.18.0-6ubuntu14.4
# Candidate: 1.25.3-1~jammy ← backports побеждает из-за приоритета 900
# Version table:
# 1.25.3-1~jammy 900 ← из jammy-backports (закреплён)
# ...
# 1.18.0-6ubuntu14.4 500 ← из jammy (стандартный приоритет)Приоритет пиннинга выше 1000 даже понизит версию установленного пакета — крайне опасно в production, используется только для экстренных откатов.
Конфликты зависимостей возникают, когда два пакета не могут сосуществовать. Conflicts: означает «dpkg откажется устанавливать оба одновременно». Breaks: означает «я буду работать, но перечисленный пакет будет некорректно функционировать рядом со мной».
# Пример: попытка установить два конфликтующих пакета
sudo apt install apache2 nginx-full
# The following packages conflict:
# nginx-full conflicts with apache2 (оба слушают порт 80 по умолчанию)
# Решение: сначала удалить конфликтующий пакет
sudo apt remove apache2
sudo apt install nginx-full
# Симулировать действия apt без касания диска
apt-get install -s nginx-full # -s = simulate
# Когда apt говорит "held broken packages" при обновлении:
sudo apt install -f # fix broken: устанавливает недостающие зависимостиapt install -f (fix) — первый ответ на сломанное состояние зависимостей. Он не устанавливает случайные вещи — он читает текущее состояние dpkg и устанавливает или удаляет ровно то, что нужно для достижения согласованного состояния. Если -f сам завершается ошибкой — читай её внимательно: обычно в ней назван конкретный конфликтующий пакет.
Безопасное смешивание Debian stable и backports для одного пакета.
Цель: запустить nginx из bookworm-backports на сервере Debian 12, оставив всё остальное на stable. Без пиннинга apt upgrade со временем подтянет все обновления из backports.
# 1. Добавить backports в sources (формат deb822)
cat > /etc/apt/sources.list.d/backports.sources << 'EOF'
Types: deb
URIs: http://deb.debian.org/debian
Suites: bookworm-backports
Components: main
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
# 2. Написать файл пина
cat > /etc/apt/preferences.d/backports-selective << 'EOF'
# Подавить автоматические обновления из backports для всего
Package: *
Pin: release a=bookworm-backports
Pin-Priority: 100
# Кроме nginx — предпочесть backports
Package: nginx nginx-common nginx-core
Pin: release a=bookworm-backports
Pin-Priority: 900
EOF
# 3. Обновить и проверить
sudo apt update
apt-cache policy nginx
# Candidate должен показывать версию из backports
# 4. Установить (версия из backports побеждает из-за приоритета 900)
sudo apt install nginx
# 5. Убедиться, что удержания не мешают
apt-mark showhold
# (ничего не удержано — пиннинг отличается от удержания)Ключевое различие: удержание замораживает уже установленную версию. Пин управляет тем, какую версию apt выбирает кандидатом — работает даже до установки.
▸Частая ошибка
Бессрочное удержание критически важного с точки зрения безопасности пакета — распространённая ошибка, которая кажется безопасной, но таковой не является. Удержание предотвращает автоматическое обновление, но также блокирует обновления безопасности именно этого пакета. Удержанный nginx с известной CVE никогда не обновится, пока ты явно не снимешь удержание. Правильный паттерн: удерживай на определённый период («пока вендор нашего кастомного модуля не выпустит обновление»), установи напоминание в календаре и снимай удержание как можно скорее. apt-mark showhold должен быть частью еженедельной гигиены сервера, а не неожиданным открытием спустя полгода.
Ты закрепил пакет в /etc/apt/preferences.d/ с Pin-Priority: 900 из backports, и отдельно запустил apt-mark hold для того же пакета. apt upgrade сообщает, что пакет 'kept back'. Какой механизм фактически предотвращает обновление?
Depends должны быть удовлетворены для установки; Recommends устанавливаются по умолчанию, пропускаемые с --no-install-recommends; Suggests только информационные. Ограничения версий (>=, =, <<) позволяют мейнтейнерам выражать точную совместимость. apt-mark hold замораживает пакет и предотвращает его касание apt upgrade — используй apt-mark showhold для аудита, удержания могут молча блокировать обновления безопасности. Пиннинг через /etc/apt/preferences.d/ назначает числовые приоритеты: по умолчанию 500, backports по умолчанию 100, выше 500 продвигает пакеты репозитория. Пин выше 1000 форсирует понижение версии. Hold против pin: удержание блокирует обновление установленного; пин выбирает, какая версия является кандидатом.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.