Автообновление Linux
У одного и того же механизма — пакетных автообновлений — два типичных артефакта в эксплуатации.
На одиночном сервере — сервис «моргает» утрами. По журналу видно, что в 06:xx или около того он был остановлен и сразу же запущен, причиной никто конкретно не назначен, и админ месяцами ищет «плавающий ночной даунтайм», списывая его то на сеть, то на GC.
На кластере из трёх–пяти узлов — кластер устроен правильно с точки зрения избыточности, переживает падение одного узла, и в какой-то момент сам себя роняет: на всех узлах в одно и то же утро прилетело обновление с перезапуском сервиса. Кворума не осталось, клиент видит «недоступно».
Источник у обоих один. Разберём.
Откуда вообще приходят автообновления
Debian / Ubuntu — unattended-upgrades
В Ubuntu 24.04 и Debian 12 пакет unattended-upgrades
устанавливается по умолчанию и включён, как минимум для канала
security. Раскладка такая:
/etc/apt/apt.conf.d/20auto-upgrades— общий рубильник: включено ли вообще автоматическое обновление и нужно ли каждый день делатьapt update./etc/apt/apt.conf.d/50unattended-upgrades— политика: какие репозитории трогать (Origins-Pattern), какие пакеты исключить (Package-Blacklist), писать ли отчёт на почту, разрешать ли автоматическую перезагрузку хоста (Automatic-Reboot).
Сам процесс запускается двумя systemd-таймерами:
apt-daily.timer— обновляет индекс пакетов и скачивает то, что разрешено. Срабатывает дважды в сутки, со случайной задержкой до 12 часов.apt-daily-upgrade.timer— собственно ставит обновления. По умолчаниюOnCalendar=*-*-* 6:00сRandomizedDelaySec=60m.
Здесь уже видна потенциальная ловушка: окно установки — один час после шести утра, плюс-минус секундное распределение. У вас три одинаково настроенных узла — все три попали в это окно. Если для двух из них таймер случайно выпал в один и тот же 10-минутный интервал — обновление пакета с вашим сервисом запустилось на обоих почти синхронно.
Fedora и RedOS — dnf-automatic
В семействе RPM есть свой аналог: пакет dnf-automatic. В
отличие от Debian-семейства, он не установлен по умолчанию ни в
Fedora, ни в RedOS — администратор включает его сознательно:
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic.timer
Конфигурация — в /etc/dnf/automatic.conf. Ключевые поля:
download_updates— скачивать ли заранее.apply_updates— устанавливать или только скачать (по умолчаниюno— то есть «только скачать», но это часто меняют).upgrade_type—default(всё) илиsecurity(только security-канал).
Триггер — таймер dnf-automatic.timer. По умолчанию
OnCalendar=*-*-* 6:00 с RandomizedDelaySec=1h. Та же узкая
одночасовая полоса, что и в Ubuntu, та же проблема для кластера.
В RedOS Murom 7.x используется тот же dnf-automatic. Семантически
ничего нового.
Что происходит при обновлении пакета
Допустим, в репозиторий вышла новая версия пакета, в нём — бинарь сервиса с systemd-юнитом. Дальше пакетный менеджер делает следующее:
Распаковывает новый бинарь поверх старого.
Запускает скриптлеты пакета (
postinstв.deb,%post/%posttransв.rpm). Посмотреть их можно черезdpkg -eилиrpm -q --scripts <pkg>.В типовом случае postinstall дёргает
systemctl daemon-reload, а дальше —systemctl try-restart <service>илиrestart, если юнит был запущен. Точная команда зависит от того, как пакет собрали:dh_installsystemdв Debian-семействе по умолчанию делает перезапуск; в RPM это решение мейнтейнера.В Ubuntu 22.04+ поверх этого работает ещё
needrestart: он сканирует все процессы в системе и через/proc/PID/mapsнаходит те, у которых обновлённый (а на самом деле — уже удалённый из файловой системы и заменённый новым) исполняемый файл или библиотека остались примонтированными в адресное пространство. Такие процессы он помечает к перезапуску.Важная деталь: перезапуск касается не только сервиса, чей пакет обновился. Если в очередное
unattended-upgradeпопал security-фикс на популярную системную библиотеку —libssl3,libcurl4,libsystemd0,libc6,zlib1g— за ней перезапустится всё, что с ней динамически слинковано:nginx,postgresql,sshd,redis, ваши собственные демоны. На практике это самая частая причина «кластер падает раз в две недели по непонятной причине»: обновляется не сам сервис, а маленький пакет в его транзитивных зависимостях.Посмотреть, кто что держит:
sudo needrestart -b # сводный список того, # что собирается перезапустить sudo lsof | grep '(deleted)' # процессы с уже удалёнными # файлами в адресном пространствеПоведение
needrestartв неинтерактивном режиме (то есть вunattended-upgrades) задаётся в/etc/needrestart/needrestart.confключом$nrconf{restart}:'a'— перезапускать всё автоматически (типовая настройка в Ubuntu Server по умолчанию);'i'— спрашивать у пользователя (на сервере без TTY означает «не перезапускать»);'l'— только записать список в журнал, ничего не трогать.
Заодно полезно знать: статически слинкованные бинари (Go с
CGO_ENABLED=0, Rust с musl-target, нативные с-static) от обновления системных библиотек не страдают — у их процессов в/proc/PID/mapsнет внешних.so, иneedrestartих не трогает.
Итог: внутри одного apt upgrade или dnf upgrade ваш сервис —
и не один он — действительно может быть перезапущен. Без вашего
вмешательства, без уведомления и одновременно с обновлением на
других узлах того же кластера.
Артефакт 1: одиночный сервер моргает по утрам
Одиночный сервер. На нём крутится сервис: веб-приложение, прокси, база, брокер очередей — не важно. Жалоба от пользователей или из мониторинга: «каждое утро около шести сервис недоступен 10–30 секунд». В течение дня — никаких проблем. Утром — повторяется.
Это не утечка памяти, не GC-пауза, не сетевой провал и не зависание после первой большой нагрузки. Скорее всего это автообновление какого-то пакета, в чьих транзитивных зависимостях оказался ваш сервис или одна из его системных библиотек.
Проверить за две минуты:
# Что вообще запускалось ночью?
sudo journalctl -u apt-daily-upgrade.service --since "7 days ago"
sudo journalctl -u dnf-automatic.service --since "7 days ago"
# Что обновлялось — список пакетов с временем
sudo tail -n 200 /var/log/apt/history.log
sudo tail -n 200 /var/log/dnf.log
# Перезапускался ли ваш сервис в эти же моменты?
sudo journalctl -u <ваш-сервис>.service --since "7 days ago" \
| grep -E "Stopping|Started|Stopped"
Если временные метки совпадают — диагноз подтверждён. Дальше варианты:
- Принять. Если сессии переподключаются автоматически и пользователь это не замечает — пусть моргает; зато безопасность накатывается сама.
- Разнести по времени (см. ниже), чтобы окно обновления было не во время бизнес-часов и не в одинаковое время каждый день.
- Запретить трогать конкретный пакет: добавить его в
Package-Blacklistв/etc/apt/apt.conf.d/50unattended-upgradesили вexclude=в/etc/dnf/automatic.conf. Сервис не будет обновляться вообще, перезапусков не будет — но и security-фиксов не будет; принимаемое решение. - Запретить автоматический перезапуск сервисов через
needrestartв режиме «только список» (см. ниже) — обновлять пакеты можно, но рестарт делает админ или CI.
Артефакт 2: кластер теряет большинство по совпадению окон
Простой счёт. Допустим:
- Три узла кластера, одинаково настроенные.
- Стандартный таймер
apt-daily-upgrade.timerсRandomizedDelaySec=60m. Распределение равномерное по 60-минутному окну. - Обновление пакета сервиса занимает 20 секунд, перезапуск — ещё 5–10.
Вероятность того, что два узла из трёх попадут в окно перекрытия в 30 секунд — порядка 1,5–2 % на одно ночное обновление. На дистанции в год это срабатывает примерно раз. На пяти узлах — уже заметно чаще; и достаточно одного раза, чтобы кластер на короткое время потерял большинство и оборвал текущие сессии.
Если ваше приложение допускает потерю одного узла, но не двух одновременно (типовая ситуация для систем с кворумом — Raft, Paxos, большинство брокеров с N+1 резервом), это проблема обнаружения: вы её увидите как редкие, плохо воспроизводимые «ночные провалы» доступности.
Как разнести во времени
Идея простая: расписание автообновления не должно быть одинаковым на всех узлах. Дальше вопрос — насколько системно вы это разносите.
Минимум: разный OnCalendar на каждом узле
На каждом узле кластера переопределяете таймер локальным drop-in:
sudo systemctl edit apt-daily-upgrade.timer
Содержимое drop-in для первого узла:
[Timer]
OnCalendar=
OnCalendar=*-*-* 03:00
RandomizedDelaySec=10m
Для второго:
[Timer]
OnCalendar=
OnCalendar=*-*-* 05:00
RandomizedDelaySec=10m
И так далее. Пустая OnCalendar= обязательна — она сбрасывает
значение из юнита, заданное по умолчанию, иначе расписаний будет
два. Применить:
sudo systemctl daemon-reload
sudo systemctl restart apt-daily-upgrade.timer
sudo systemctl list-timers --all | grep apt-daily-upgrade
Для dnf-automatic.timer всё то же самое, имя только другое:
systemctl edit dnf-automatic.timer.
Получше: убрать автоматический перезапуск сервиса
Если вы не хотите, чтобы пакетное обновление трогало ваш сервис в
принципе — даже если бинарь обновился — можно настроить
needrestart в режиме «только список, без действий»:
sudo $EDITOR /etc/needrestart/needrestart.conf
# $nrconf{restart} = 'l'; # list only
В режиме l он только запишет в журнал, какие сервисы следовало
бы перезапустить — и оставит решение администратору.
Для Debian/Ubuntu без needrestart можно временно блокировать запуск
сервисов через policy-rc.d:
sudo install -m 0755 /dev/stdin /usr/sbin/policy-rc.d <<'EOF'
#!/bin/sh
exit 101
EOF
exit 101 означает «действие не разрешено» — dh_installsystemd это
понимает и перезапуск не выполняет. Сервис при этом продолжит
работать со старым бинарём, пока вы не перезапустите его вручную.
На системах с настроенными Linux capabilities и долго живущими
подключениями это часто как раз то, что нужно — перезапуск делает
дежурный или CI.
Серьёзно: координация через внешний инструмент
Когда узлов много или они в нескольких регионах — разные OnCalendar
становятся хрупкими (легко забыть один при добавлении). Тогда нужна
координация уровнем выше пакетного менеджера:
- Ansible с
serial: 1(илиserial: "30%") — обновляет кластер по одному, с проверкой работоспособности между шагами; - Kubernetes через
PodDisruptionBudgetи rolling-update — гарантирует, что одновременно недоступно не более N узлов; - Salt orchestrate / Chef runlist — то же.
Автообновление пакета при этом обычно отключают совсем:
# Debian/Ubuntu
sudo systemctl disable --now apt-daily-upgrade.timer
# Fedora/RedOS
sudo systemctl disable --now dnf-automatic.timer
И полагаются на оркестратор, у которого есть карта кластера и понимание кворума.
На первый взгляд кажется лишней суетой ради «всё равно когда обновится» — но цена разъехавшегося NAS, ушедшего в split-brain посреди ночи, обычно сильно выше, чем час, разово потраченный на правильный конфиг расписания и перезапусков.