Как безопасно обновить Linux-сервер через apt

Опубликовано 14 мин. чтения

Разница между upgrade, full-upgrade и dist-upgrade, придержанные пакеты, запросы dpkg о файлах конфигурации, перезагрузка после обновления ядра с needrestart и смена версии дистрибутива как отдельная задача.

На свежем сервере обновление — чистая формальность: ломаться там просто нечему. Но как только на сервере работают боевые службы, всё решают детали: пройдёт ли обновление незаметно или после него окажется перезаписан файл конфигурации, останется служба со старой библиотекой в памяти, а работающее ядро будет уже не тем, что лежит на накопителе. В этой статье все эти детали разобраны по порядку. Краткая версия для только что выданного сервера собрана в чек-листе для нового root-сервера.

Все сведения относятся к Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS и Ubuntu 22.04 LTS. Команды рассчитаны на работу от имени root. Если вы работаете под обычным пользователем, добавляйте перед каждой командой sudo.

Прежде чем ввести первую команду: путь назад

Обновление может дорого обойтись тремя способами: диалог о файле конфигурации перезапишет настройки SSH, новое ядро не загрузится, или соединение оборвётся посреди транзакции dpkg. Все три случая управляемы, но только если позаботиться об этом заранее.

Сессия, которая переживёт обрыв соединения

Если SSH-соединение оборвётся во время распаковки, работающий процесс получит SIGHUP, и dpkg завершится посреди транзакции. В итоге вы получите наполовину настроенную базу пакетов. Поэтому длительные обновления запускайте в такой сессии, которая продолжит работать на сервере, даже когда ваш терминал исчезнет:

apt install -y tmux
tmux new -s upgrade

Если соединение пропало, войдите заново и верните сессию командой tmux attach -t upgrade. Всё это время процесс продолжал работать.

Как попасть на сервер, когда SSH больше не отвечает

На KVM root-серверах и выделенных серверах KernelHost вам доступна VNC-консоль в личном кабинете. Она не зависит от сетевого стека гостевой системы и работает даже тогда, когда ни одна служба уже не слушает порт. Зайдите через неё один раз заранее и убедитесь, что знаете пароль root.

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

grep -E '^GRUB_TIMEOUT|^GRUB_TIMEOUT_STYLE' /etc/default/grub

Если там указано GRUB_TIMEOUT_STYLE=hidden или GRUB_TIMEOUT=0, поставьте GRUB_TIMEOUT_STYLE=menu и GRUB_TIMEOUT=5, а затем выполните update-grub. В аварийной ситуации выберите в VNC-консоли в разделе «Advanced options» предыдущее ядро.

Проверить и зафиксировать состояние

Три предварительные проверки, которые находят что-нибудь чаще, чем принято думать:

df -h / /boot /var
dpkg --audit
apt-get check

Контрольная проверка: dpkg --audit ничего не выводит, apt-get check отрабатывает без строк с ошибками, а в /boot свободно не меньше 300 МБ. Заполненный /boot — самая частая причина сорвавшегося обновления ядра, подробнее об этом в статье диск заполнен в Linux. Если dpkg --audit о чём-то сообщает, сначала почините это командой dpkg --configure -a.

Затем сохраните текущее состояние, чтобы позже было с чем сравнивать:

dpkg --get-selections > /root/packages-before-upgrade.txt
apt-mark showhold > /root/holds-before-upgrade.txt
cp -a /etc/apt /root/apt-config-$(date +%F)

update, upgrade, full-upgrade и dist-upgrade

Эти четыре слова путают постоянно. Разница между ними не косметическая: именно от неё зависит, установится ли новое ядро и разрешено ли пакетам исчезать.

КомандаНовые пакетыУдаляет пакетыКогда применять
apt updateнетнеттолько скачивает списки пакетов, в системе ничего не меняет
apt-get upgradeнетнетсамый осторожный вариант, придерживает обновления ядра
apt upgradeда, если этого требует зависимостьнетобычный случай на боевых системах
apt full-upgradeдада, при необходимостикогда вы осознанно допускаете удаление
apt-get dist-upgradeдада, при необходимостиболее старое название того же самого

Отсюда следуют два вывода. Первый: dist-upgrade не имеет никакого отношения к переходу на новую версию дистрибутива. Название историческое, а сама команда остаётся в пределах вашей текущей версии.

Второй: именно разница между apt upgrade и apt-get upgrade объясняет, почему некоторые серверы остаются без нового ядра. В Debian ядро подключается через метапакет linux-image-amd64, в Ubuntu через linux-image-generic или linux-image-virtual. При каждой смене ABI этот метапакет начинает указывать на новый пакет, номер версии которого входит в имя. apt-get upgrade принципиально не устанавливает новые пакеты и поэтому оставляет ядро лежать нетронутым, а apt upgrade его ставит, потому что этого требует зависимость. Если вы используете apt-get upgrade в скрипте обслуживания, добавьте туда ещё и --with-new-pkgs.

О выборе инструмента: при запуске из скрипта apt выводит строку WARNING: apt does not have a stable CLI interface. Use with caution in scripts. Это не сообщение об ошибке, но предупреждение по делу. В скриптах и ролях Ansible место у apt-get, а в интерактивной работе удобнее apt.

Порядок действий с проверкой после каждого шага

Шаг 1: получить списки пакетов.

apt update

Контрольная проверка: в выводе нет ни одной строки, начинающейся с Err: или W:. В конце стоит либо All packages are up to date., либо число, за которым следует packages can be upgraded. Любая строка с ошибкой здесь означает, что дальше вы работали бы с неполной картиной.

Шаг 2: посмотреть, что именно придёт. Этого шага нет в большинстве инструкций, а он самый важный во всём порядке действий.

apt list --upgradable
apt full-upgrade -s

Ключ -s только моделирует и ничего не меняет. Строки под заголовком The following packages will be REMOVED: — единственное, что действительно нужно проверить. Если там пусто, full-upgrade так же безобиден, как и upgrade. Если там оказался нужный вам пакет, возьмите apt upgrade, а причину разберите отдельно.

В Debian дополнительно стоит выполнить apt install apt-listchanges. Этот пакет показывает перед установкой changelog и, что важнее, файлы NEWS от сопровождающих пакетов. Именно там написано то, что придётся делать руками.

Шаг 3: установить обновления.

apt upgrade

Не отходите от терминала. Процесс задаёт вопросы, а -y отвечает только на собственный вопрос apt, но не на запросы по файлам конфигурации. Их задаёт dpkg, и он терпеливо ждёт, пока кто-нибудь ответит.

Контрольная проверка:

apt list --upgradable
dpkg --audit
systemctl --failed
journalctl -p 3 -b --no-pager | tail -n 20

Ожидается следующее: apt list --upgradable не выводит ничего, кроме Listing..., dpkg --audit молчит, а systemctl --failed сообщает 0 loaded units listed. Что произошло на самом деле, постоянно хранится в /var/log/apt/history.log, а полный вывод dpkg в /var/log/apt/term.log.

Придержанные пакеты: две разные причины

Если apt пропускает пакеты, у этого бывают две принципиально разные причины, которые регулярно путают.

Первая: настоящая блокировка. Кто-то явно зафиксировал пакет:

apt-mark showhold

Если команда что-то выводит, это было осознанное решение, чаще всего для баз данных или модулей ядра. Снимается блокировка командой apt-mark unhold ИМЯ_ПАКЕТА, ставится командой apt-mark hold ИМЯ_ПАКЕТА. Блокировку на уровне dpkg видно через dpkg --get-selections | grep -w hold.

Вторая: сообщение The following packages have been kept back:. Это не блокировка. Оно означает, что ради такого обновления apt пришлось бы дополнительно установить один пакет или удалить другой, а использованная команда этого делать не вправе. Проверить это можно одной строкой, ничего не меняя:

apt full-upgrade -s | head -n 20

Если пакет там появляется, объяснение найдено, и apt full-upgrade снимает вопрос. В Debian 13 новое поколение apt форматирует этот вывод иначе, но по содержанию не меняется ничего.

Особый случай Ubuntu. Ubuntu выкатывает обновления поэтапно, и не все серверы получают их в один день. Поэтому пакет может оказаться придержанным, хотя ни блокировки нет, ни зависимость не мешает. Диагностика идёт методом исключения: apt-mark showhold пуст, apt full-upgrade -s не показывает удалений, а apt-cache policy ИМЯ_ПАКЕТА всё равно сообщает о более новом кандидате. Тогда подождите несколько дней или заберите обновление досрочно:

apt -o APT::Get::Always-Include-Phased-Updates=true upgrade

В Debian 13 и Debian 12 поэтапной выкатки нет, там эта причина отпадает сразу.

Когда apt спрашивает про файл конфигурации

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

Configuration file '/etc/ssh/sshd_config'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** sshd_config (Y/I/N/O/D/Z) [default=N] ?

По умолчанию выбрано N, то есть сохранить вашу версию. Это безопасный ответ, но не во всех случаях правильный.

СитуацияОтвет
Вы уже не помните, что именно менялиD, посмотреть разницу и только потом решать
Ваши правки внесены осознанно, а пакет меняет только комментарииN, затем сравнить с файлом .dpkg-dist
Файл создан инструментом вроде cloud-init или AnsibleN, затем запустить этот инструмент заново
Новые значения важны для безопасности, а ваша правка не нужнаY, затем заново внести свои настройки
Файл критичный, и вы не увереныZ, скопировать файл в сторону, выйти из shell, затем N

С /etc/ssh/sshd_config нужна особая осторожность: ответ Y может вернуть PermitRootLogin и PasswordAuthentication к значениям из пакета, а именно от них зависит ваш доступ. Поэтому собственные настройки SSH стоит держать в отдельном файле в каталоге /etc/ssh/sshd_config.d/, как описано в статье защита SSH и вход по ключу. Файл, которого в пакете вообще нет, никогда не вызовет такой запрос.

Что бы вы ни ответили, dpkg ничего не выбрасывает. При N новая версия ложится рядом с расширением .dpkg-dist, при Y ваша прежняя сохраняется как .dpkg-old. Разбор этих файлов и есть настоящая работа после обновления:

find /etc -name '*.dpkg-dist' -o -name '*.dpkg-old' -o -name '*.dpkg-new' -o -name '*.ucf-dist'
diff -u /etc/ssh/sshd_config /etc/ssh/sshd_config.dpkg-dist

Для автоматических запусков, например в скрипте обслуживания, поведение задают заранее. Такая комбинация сохраняет вашу версию и ничего не спрашивает:

DEBIAN_FRONTEND=noninteractive apt-get -y \
  -o Dpkg::Options::="--force-confdef" \
  -o Dpkg::Options::="--force-confold" \
  upgrade

--force-confnew делает ровно противоположное и всегда берёт версию из пакета. На сервере с собственной конфигурацией это редко то, чего вы хотите.

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

Новое ядро при установке попадает в /boot и в меню загрузки. А работающее ядро остаётся в памяти без изменений вплоть до перезагрузки. Поэтому сервер может быть одновременно полностью обновлённым и уязвимым. Проще всего проверить это сравнением:

uname -r
ls -1 /boot/vmlinuz-*

Если в /boot лежит версия выше той, которую сообщает uname -r, перезагрузка назрела. В Ubuntu 24.04 и 22.04 есть ещё и файл-признак, который учитывает также обновления библиотек:

test -f /var/run/reboot-required && cat /var/run/reboot-required.pkgs

Второй файл перечисляет пакеты, которые запросили перезагрузку. В Debian 13 и Debian 12 этот признак создаётся не всегда надёжно, поэтому там правильный путь — needrestart.

needrestart: какие службы всё ещё используют старую библиотеку

Обновление OpenSSL или glibc заменяет файл на накопителе. Но каждый процесс, который уже загрузил его, продолжает работать со старой версией. needrestart находит именно такие процессы. В Ubuntu 24.04 и 22.04 он предустановлен и после каждого обновления открывает полноэкранный диалог, а в Debian его нужно доустановить:

apt install -y needrestart
needrestart -b

Режим -b выдаёт машиночитаемый вывод. Два значения в нём решающие: NEEDRESTART-KSTA со значением 1 означает, что работает ожидаемое ядро, а любое другое значение говорит, что наготове уже более новое. Каждая строка NEEDRESTART-SVC называет службу, которую следует перезапустить.

Поведением управляет переменная окружения NEEDRESTART_MODE: a перезапускает службы автоматически, l только выводит список, i спрашивает. На постоянной основе то же самое задаётся в /etc/needrestart/needrestart.conf. Для скриптов переменная подходит лучше, потому что она ничего не меняет в конфигурации:

NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive apt-get -y upgrade

Об одном ограничении стоит знать: needrestart смотрит на процессы хостовой системы. Службы внутри контейнеров он не обновляет, там нужны новые образы.

Смена версии дистрибутива — отдельная категория

Переход с Debian 12 на Debian 13 или с Ubuntu 22.04 на 24.04 — это не обновление в описанном выше смысле. Он подменяет источники пакетов и заменяет практически каждый пакет. Здесь всегда действуют три правила: одна версия за один заход, никаких пропущенных промежуточных версий и заранее сделанная резервная копия, из которой сервер можно восстановить.

В Debian сначала переключают источники. Debian 12 использует для этого /etc/apt/sources.list, Debian 13 — файл /etc/apt/sources.list.d/debian.sources в новом формате. Дальше идёт намеренно двухступенчатый порядок действий, который так и предписан в примечаниях к выпуску:

apt update
apt-get upgrade --without-new-pkgs
apt full-upgrade

Средний шаг сначала обновляет пакеты, которые обходятся без перестройки зависимостей. Так число одновременно перемещаемых пакетов остаётся небольшим, а прерванный процесс поддаётся ремонту. Не забудьте про компонент non-free-firmware: начиная с Debian 12 он вынесен отдельно и в старых файлах источников отсутствует. Если ваш apt знает команду apt modernize-sources (проверяется через apt --version), она переведёт старый файл источников в новый формат.

В Ubuntu для этого есть собственный инструмент, и пользоваться следует исключительно им:

apt install -y ubuntu-release-upgrader-core
do-release-upgrade -c
do-release-upgrade

Ключ -c только проверяет и ничего не меняет. Предложат ли вам переход, зависит от параметра Prompt в /etc/update-manager/release-upgrades: при значении lts предлагается только переход на следующую LTS-версию, причём лишь после её первого промежуточного выпуска. Путь с 20.04 на 24.04 обязательно проходит через 22.04.

Деталь, которая многих удивляет: если do-release-upgrade запущен через SSH, он поднимает дополнительную службу SSH на порту 1022 как запасной путь и предупреждает, что этот порт, возможно, придётся открыть в файрволе. Пользуйтесь этим, но не полагайтесь только на него. Сессия tmux и доступ через VNC-консоль надёжнее.

Уборка после обновления

После крупного обновления остаются три вида мусора: скачанные файлы пакетов, осиротевшие пакеты и остатки конфигурации от удалённых пакетов.

apt autoremove --purge
apt autoclean

autoclean удаляет из /var/cache/apt/archives только те файлы, которые всё равно больше не предлагаются. apt clean очищает кэш полностью, то есть освобождает больше места, но зато превращает каждую повторную установку в новую загрузку.

Остатки конфигурации узнаются по статусу dpkg rc, то есть пакет удалён, а конфигурация ещё на месте:

dpkg -l | awk '/^rc/ {print $2}'

Всё, что там перечислено, окончательно удаляется командой apt purge ИМЯ_ПАКЕТА. Со старыми ядрами нужна большая аккуратность, потому что одна ошибка сделает сервер незагружаемым:

uname -r
dpkg -l 'linux-image-*' | awk '/^ii/ {print $2}'

Никогда не удаляйте ядро, которое называет первая строка, и дополнительно оставьте одно рабочее ядро постарше, чтобы в меню загрузки был запасной вариант. Обычно apt autoremove --purge и так делает это правильно.

Частые ошибки и их решения

E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1234 (unattended-upgr): уже идёт вторая операция с пакетами, чаще всего автоматическое обновление безопасности. Её не надо прерывать, надо дать ей доработать до конца. Полный порядок действий вместе с ремонтом описан в статье apt не удалось получить блокировку.

E: dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem.: предыдущий запуск был прерван, как правило обрывом соединения. Выполните ровно эту команду и после неё повторите обновление.

E: Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution).: пакет установлен, а его зависимостей не хватает. apt --fix-broken install доустанавливает их. Если ошибка возвращается, дело почти всегда в стороннем источнике, который предлагает пакеты для другой версии дистрибутива.

E: Release file for http://deb.debian.org/debian/dists/trixie/InRelease is not valid yet (invalid for another 5h 3min 2s).: часы сервера отстают. Проверьте через timedatectl status, сообщается ли System clock synchronized: yes, поправьте время и повторите apt update. Репозиторий здесь ни при чём.

W: GPG error: ... The following signatures couldn't be verified because the public key is not available: NO_PUBKEY ..., а следом E: The repository '...' is not signed.: ключ подписи стороннего источника отсутствует или был заменён. Сегодня ключ кладут в /etc/apt/keyrings/ и ссылаются на него в записи источника через signed-by либо через поле Signed-By:. apt-key объявлен устаревшим, и пользоваться им больше не следует.

E: Repository '... InRelease' changed its 'Suite' value from 'stable' to 'oldstable': это штатная ситуация, когда выходит новая версия Debian, а ваши источники указывают на stable вместо кодового имени. Подтвердите один раз через apt update --allow-releaseinfo-change, а затем переведите источники на кодовое имя. Иначе сервер, который следует за stable, однажды сменит версию дистрибутива без вашего ведома.

E: The repository 'http://... Release' does not have a Release file.: источник ничего не предлагает для вашей версии, обычно потому, что сторонний репозиторий ещё не поддерживает кодовое имя или дистрибутив достиг конца срока поддержки. Отключите проблемную строку и проверьте источник.

No space left on device посреди распаковки: заполнен /boot или /var. Приведите систему в порядок командой dpkg --configure -a, освободите место и повторите. Перед каждым обновлением ядра стоит заглянуть в df -h /boot.

debconf: unable to initialize frontend: Dialog: это только уведомление, а не сбой. Оно появляется без полноценного терминала, например внутри скрипта. С DEBIAN_FRONTEND=noninteractive оно исчезает.

Четыре системы в сравнении

ТемаDebian 13Debian 12Ubuntu 24.04Ubuntu 22.04
Файл источниковsources.list.d/debian.sourcessources.listsources.list.d/ubuntu.sourcessources.list
needrestartдоустановитьдоустановитьпредустановленпредустановлен
Признак перезагрузкиненадёжный, используйте needrestartненадёжный, используйте needrestart/var/run/reboot-required/var/run/reboot-required
Поэтапная выкатканетнетдада
Смена версиипереключить источники, затем в два шагаdo-release-upgrade

Итоговая проверка

То, что команда вернулась без ошибок, ещё не значит, что с системой всё в порядке. А вот эти шесть проверок действительно о чём-то говорят:

  1. apt list --upgradable не выводит ничего, кроме Listing....
  2. apt-mark showhold содержит только то, что вы заблокировали осознанно.
  3. dpkg --audit молчит.
  4. needrestart -b сообщает NEEDRESTART-KSTA: 1 и не оставляет служб, ожидающих перезапуска.
  5. systemctl --failed ничего не перечисляет.
  6. find /etc -name '*.dpkg-dist' больше ничего не находит, потому что вы разобрали каждое расхождение.

Если четвёртый пункт требует перезагрузки, запланируйте её и выполните. Сервер, который месяцами ждёт отложенной перезагрузки, копит ровно те уязвимости, ради закрытия которых вы и обновлялись. После этого в последний раз проверьте uname -r и systemctl --failed. Перезагрузка — это тот момент, когда становится видно, поднимается ли всё обратно.

Частые вопросы

Чем apt upgrade отличается от apt full-upgrade?
apt upgrade устанавливает новые пакеты, если этого требует зависимость, но никогда не удаляет уже установленный пакет. Если обновление потребовало бы удаления, apt просто пропускает такой пакет. apt full-upgrade вправе ещё и удалять, поэтому доводит систему до нового состояния полностью. На боевой системе сначала посмотрите через apt full-upgrade -s, что именно было бы удалено. Если под строкой The following packages will be REMOVED пусто, обе команды равнозначны.
Означает ли dist-upgrade переход на следующую версию дистрибутива?
Нет, и это одна из самых частых путаниц вообще. apt-get dist-upgrade соответствует apt full-upgrade и остаётся в пределах вашей текущей версии. Настоящая смена версии требует, чтобы вы заранее переключили источники пакетов, если речь о Debian, либо воспользовались do-release-upgrade, если это Ubuntu.
Почему apt пишет «The following packages have been kept back»?
Потому что обновление этих пакетов потребовало бы установки или удаления, которых использованная команда делать не вправе. Это не блокировка. Проверьте через apt-mark showhold, действительно ли стоит блокировка, и через apt full-upgrade -s, не стоит ли за этим зависимость. В Ubuntu добавляется третья причина: обновления там выкатываются поэтапно, так что пакет придёт сам через несколько дней.
apt спрашивает, заменять ли файл конфигурации. Что отвечать?
По умолчанию выбрано N, то есть сохранить вашу версию, и это безопасный ответ. Если вы уже не помните, что именно меняли, сначала нажмите D и посмотрите разницу. Как бы вы ни решили, dpkg положит рядом вторую версию с расширением .dpkg-dist или .dpkg-old. Эти файлы потом стоит разобрать, иначе ваши настройки и новые значения из пакета начнут расходиться.
Как понять, что после обновления нужна перезагрузка?
Сравните uname -r с файлами в /boot. Если там лежит версия выше, значит работает ещё старое ядро. В Ubuntu 24.04 и 22.04 дополнительно есть /var/run/reboot-required, а /var/run/reboot-required.pkgs называет пакеты, которые её потребовали. В Debian 13 и Debian 12 этот файл создаётся не всегда надёжно, поэтому там правильный путь — needrestart.
Зачем нужен needrestart, если я всё равно перезагружаю сервер?
Потому что не каждое обновление оправдывает перезагрузку. После обновления OpenSSL или glibc работающие службы продолжают использовать старую библиотеку в памяти, пока их не перезапустят. needrestart перечисляет именно такие службы и умеет прицельно их перезапустить, не останавливая весь сервер. И только для ядра обойтись без перезагрузки не получится.
Как обновляться без присмотра, чтобы запуск не завис на вопросе?
Задайте DEBIAN_FRONTEND=noninteractive и заранее определите поведение для файлов конфигурации через опции dpkg --force-confdef и --force-confold, которые сохраняют вашу версию. На системах с needrestart к этому добавляется NEEDRESTART_MODE=a, иначе появится полноэкранный диалог. Одного только -y не хватит, потому что он отвечает лишь на собственный вопрос apt.
Можно ли обновлением закрыть себе доступ к серверу?
Да, если при запросе по /etc/ssh/sshd_config вы примете версию из пакета и потеряете свои настройки входа, или если новое ядро не загрузится. И то и другое поправимо: на KVM root-серверах и выделенных серверах KernelHost вы заходите через VNC-консоль в личном кабинете, независимо от SSH. А для ядра, которое не загружается, выберите там в меню загрузки в разделе Advanced options предыдущее ядро.

apt dpkg Debian Ubuntu Ядро Linux needrestart Управление пакетами Обслуживание серверов