Плановое обновление пакетов - рядовая операция, которая иногда оборачивается непредсказуемым падением производительности. Сервер, ещё вчера державший 10 000 соединений без видимых задержек, начинает захлёбываться на пиковых нагрузках. Причина редко кроется в багах нового ядра или демона Docker. Чаще всего проблема в сбросе кастомизированных параметров на значения по умолчанию, которые разработчики дистрибутива сочли безопасными для общего случая, но не для вашего специфичного highload-сценария.
Этот материал даёт пошаговый алгоритм возврата контроля над системой. Вы узнаете, как зафиксировать факт деградации через сравнение вывода sysctl -a, какие параметры ядра Linux напрямую влияют на latency и throughput сетевого стека, как проверить конфигурацию Nginx на предмет сброшенных директив и как убедиться, что Docker после обновления не переключился на медленный драйвер хранения. Все значения проверены на практике и рассчитаны на немедленное применение в production-окружении.
Почему после обновления сервер стал работать медленнее: быстрая диагностика
Типичные симптомы post-upgrade деградации: рост 95-го и 99-го перцентилей latency при прежнем объёме трафика, падение Requests per Second (RPS) на 15–30% без изменения кодовой базы приложения, увеличение утилизации CPU в режиме ядра (пространство system в top/htop). Первый шаг - не паника, а холодное сравнение текущего состояния системы с эталонным снимком, который вы сделали до обновления. Если снимка нет - придётся восстанавливать картину по косвенным признакам.
Сравнение параметров ядра: sysctl -a до и после
Параметры ядра, управляющие сетевым стеком и виртуальной памятью, наиболее уязвимы к перезаписи. Разработчики мейнтейнеры пакетов с ядром периодически меняют дефолты в /etc/sysctl.conf или /etc/sysctl.d/. Чтобы выявить расхождение, сохраните текущий слепок и сравните его с резервной копией:
sysctl -a > /tmp/sysctl_after_upgrade.txt
diff -u /root/backups/sysctl_before_upgrade.txt /tmp/sysctl_after_upgrade.txt
Если бэкапа нет - проверьте ключевые параметры, которые чаще всего меняются в новых версиях ядра и systemd:
- net.core.somaxconn - максимальная очередь входящих соединений. Дефолт 128 почти всегда недостаточен для Nginx, обслуживающего больше 1000 одновременных клиентов. После обновления значение часто сбрасывается с 65535 на 4096 или даже 128.
- net.ipv4.tcp_tw_reuse - разрешение повторного использования TIME-WAIT сокетов. Критично для серверов, генерирующих множество исходящих соединений к бэкендам. Сброс в 0 вызывает быстрое исчерпание пула локальных портов.
- vm.swappiness - агрессивность свопинга. Значение по умолчанию 60 для сервера с 64 ГБ ОЗУ и базами данных в памяти - прямой путь к деградации latency из-за неоправданного вытеснения страниц в swap.
Более детальный разбор параметров ядра и готовые конфигурации для разных сценариев нагрузки вы найдёте в руководстве по практической настройке Linux-серверов для DevOps.
Анализ логов пакетного менеджера и версий ПО
Логи пакетного менеджера покажут, какие именно компоненты были затронуты обновлением. Для Debian/Ubuntu:
grep 'upgrade ' /var/log/dpkg.log | tail -50
Для RHEL/CentOS/Rocky Linux:
yum history info last
После идентификации обновлённых пакетов проверьте их версии и текущие настройки. Для Nginx команда nginx -T выведет полную конфигурацию с путями к файлам - сверьте её с вашим эталоном в Git. Для Docker выполните docker info | grep -E 'Storage Driver|Cgroup Version'. Обновление containerd или переход на cgroups v2 без адаптации лимитов ресурсов - частая причина троттлинга контейнеров.
Тонкая настройка ядра Linux: ключевые параметры sysctl для сетевого стека и планировщика
Параметры ядра - фундамент производительности всей системы. Нельзя выжать максимум из Nginx или Docker, если сетевой стек или планировщик CPU работают с ограничениями двадцатилетней давности. Приведённые ниже значения рассчитаны на современные серверы с 16+ ядрами и 64+ ГБ ОЗУ. Тестируйте каждый параметр на staging-стенде перед применением в production.
Оптимизация TCP и сокетов для высокой конкуренции
Сетевой стек Linux по умолчанию настроен на консервативное потребление памяти. Для сервера, принимающего 10 000+ одновременных соединений, это прямой путь к отбрасыванию пакетов на уровне ядра ещё до того, как Nginx успеет их обработать.
Ключевые параметры для /etc/sysctl.d/99-network-tuning.conf:
# Максимальная очередь входящих соединений для всех сокетов
net.core.somaxconn = 65535
# Очередь SYN-пакетов до установки соединения
net.ipv4.tcp_max_syn_backlog = 8192
# Разрешить повторное использование TIME-WAIT сокетов для новых соединений
net.ipv4.tcp_tw_reuse = 1
# Ускоряем закрытие TIME-WAIT сокетов (30 -> 15 секунд)
net.ipv4.tcp_fin_timeout = 15
# Максимальная очередь пакетов на сетевом интерфейсе до обработки ядром
net.core.netdev_max_backlog = 16384
# Диапазон локальных портов для исходящих соединений (к бэкендам, API)
net.ipv4.ip_local_port_range = 1024 65535
Параметр tcp_tw_reuse безопасен для внутренних сетей и соединений с бэкендами. Не включайте tcp_tw_recycle - он удалён из ядра с версии 4.12 и ломает работу за NAT. После применения (sysctl -p /etc/sysctl.d/99-network-tuning.conf) проверьте, что значения применились: sysctl net.core.somaxconn.
Настройка виртуальной памяти и планировщика для стабильности
Непредсказуемые задержки часто вызваны не сетью, а подсистемой управления памятью. Ядро, следуя дефолтному vm.swappiness = 60, начинает свопить страницы задолго до реального исчерпания ОЗУ. Для сервера, где latency важнее утилизации памяти, это неприемлемо.
# Минимальная склонность к свопингу (0 - только при OOM, 1-10 - почти без свопа)
vm.swappiness = 1
# Агрессивное удержание inode/dentry кэша в памяти (ускоряет файловые операции)
vm.vfs_cache_pressure = 50
# Отключаем автоматическую группировку задач - планировщик не домысливает за нас
kernel.sched_autogroup_enabled = 0
# Минимизируем миграцию задач между ядрами CPU
kernel.sched_migration_cost_ns = 5000000
Для Docker-хостов снижение vm.swappiness до 1 критически важно. Контейнеры с лимитами памяти, достигая своего потолка, уходят в swap хост-системы, что вызывает резкий рост latency всего стека. Мониторинг через docker stats и vmstat 1 покажет, уходит ли система в swap под нагрузкой.
Оптимизация конфигурации Nginx: устранение узких мест после обновления
Пакетные менеджеры при обновлении Nginx часто перезаписывают /etc/nginx/nginx.conf, предлагая сохранить изменения или установить версию мейнтейнера. Даже если вы выбрали сохранение, новые включённые по умолчанию модули или изменившиеся дефолты могут повлиять на поведение. Команда nginx -T 2>&1 | grep -E 'worker_processes|worker_connections|multi_accept|keepalive' покажет, что реально загружено в память.
Настройка воркеров и соединений под количество ядер CPU
Nginx использует событийно-управляемую модель, где каждый воркер обрабатывает тысячи соединений в неблокирующем режиме. Ключевые директивы в /etc/nginx/nginx.conf:
user www-data;
# Автоматически установить число воркеров по количеству ядер
worker_processes auto;
# Максимальное число открытых файлов на воркер
worker_rlimit_nofile 65535;
events {
# Одновременные соединения на воркер
worker_connections 4096;
# Принимать все новые соединения за один вызов epoll_wait
multi_accept on;
# Использовать epoll (обязательно для Linux)
use epoll;
}
Формула для расчёта worker_connections: worker_processes * worker_connections = максимальное число одновременных клиентов. При 16 ядрах и worker_connections 4096 сервер обработает 65 536 соединений. Убедитесь, что системный лимит ulimit -n для пользователя www-data выше этого значения. Подробнее о поиске узких мест в связке Nginx + приложение читайте в гайде по диагностике веб-приложений.
Буферизация, keepalive и сжатие для снижения latency
После обновления Nginx может изменить поведение буферов или отключить keepalive к upstream-серверам. Для API-шлюза, где каждый миллисекундный раунд-трип к бэкенду умножается на тысячи запросов, это критично.
http {
# Буфер для тела запроса клиента (в памяти, без записи на диск)
client_body_buffer_size 128k;
# Таймаут keepalive с клиентом (держать соединение открытым)
keepalive_timeout 65;
# Число запросов в одном keepalive-соединении
keepalive_requests 1000;
# Отправлять заголовок ответа и начало файла в одном TCP-пакете
tcp_nopush on;
# Не буферизовать мелкие пакеты - отправлять сразу
tcp_nodelay on;
# Сжатие gzip для текстовых ответов
gzip on;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml;
# Upstream к бэкенду
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
# Держать пул keepalive-соединений к бэкенду
keepalive 32;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
Директива keepalive 32 в блоке upstream создаёт пул из 32 постоянных соединений к каждому бэкенду. Это устраняет накладные расходы на TCP handshake для каждого запроса. Без неё Nginx устанавливает новое соединение на каждый проксируемый запрос, что добавляет 2-3 мс latency даже в локальной сети. Для углублённой настройки отказоустойчивости Nginx используйте руководство по продвинутой настройке балансировки.
Восстановление производительности Docker: драйверы хранения, cgroups и лимиты
Обновление Docker или containerd может незаметно изменить драйвер хранения или версию cgroups. Симптомы: контейнеры запускаются, но операции ввода-вывода внутри них замедляются в 2-5 раз, а лимиты по CPU перестают соблюдаться. Первая команда после обновления демона: docker info | grep -E 'Storage Driver|Cgroup Driver|Cgroup Version'.
Выбор и настройка драйвера хранения overlay2
Overlay2 - стандартный и наиболее производительный драйвер для современных ядер Linux. Если после обновления вы видите devicemapper или vfs - производительность контейнеров уже деградировала. Конфигурация в /etc/docker/daemon.json:
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
Параметр override_kernel_check разрешает использовать overlay2 даже на ядрах, где Docker считает его нестабильным. Применяйте только если уверены в своём ядре (4.x и новее). После изменения - systemctl restart docker. Внимание: смена драйвера удаляет все существующие контейнеры и образы. Делайте бэкап.
Управление ресурсами контейнеров через cgroups v2
Современные дистрибутивы (Ubuntu 22.04+, Debian 12+, RHEL 9+) по умолчанию используют cgroups v2. Старые конфигурации с лимитами, заданными через --memory и --cpus в стиле v1, могут игнорироваться или применяться некорректно. Проверьте версию: stat -fc %T /sys/fs/cgroup/ - вывод cgroup2fs означает v2.
В docker-compose v3.8+ лимиты задаются в блоке deploy.resources:
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 2048M
reservations:
cpus: '0.5'
memory: 512M
Мониторинг фактического потребления - docker stats --no-stream. Если контейнер упирается в лимит CPU, столбец CPU % покажет значение, близкое к выделенному лимиту, а не к 100% ядра хоста. Троттлинг по памяти виден по ненулевым значениям в столбце MEM USAGE / LIMIT и резкому росту latency внутри контейнера.
Проверка результатов: бенчмарки и мониторинг latency/throughput
После применения всех настроек объективная оценка обязательна. Субъективное «стало быстрее» не подходит для отчёта и не гарантирует, что проблема решена для всех сценариев нагрузки. Используйте минимум два инструмента: один для HTTP-нагрузки, второй для системного профилирования.
Нагрузочное тестирование Nginx с помощью wrk
wrk - легковесный генератор HTTP-нагрузки, способный создать десятки тысяч соединений с одного хоста. Пример теста статического файла:
wrk -t12 -c400 -d30s --latency http://localhost/static/test.html
Ключевые метрики в выводе:
- Requests/sec - пропускная способность. Сравните с эталонным значением до обновления.
- Transfer/sec - объём переданных данных. Падение при том же RPS указывает на проблемы с TCP-окном или MTU.
- Latency Distribution - 50%, 75%, 90%, 99% перцентили. Рост 99-го перцентиля при стабильном среднем - признак эпизодических блокировок (GC, своп, дисковый I/O).
Прогоните тест трижды с интервалом 30 секунд для прогрева кэшей. Сравните результаты до и после оптимизации. Прирост RPS на 20-40% при снижении latency - ожидаемый результат для сервера, где были сброшены параметры ядра и Nginx.
Профилирование Docker-контейнера и хостовой системы
Во время нагрузочного теста откройте вторую сессию и запустите мониторинг:
# Потребление ресурсов контейнерами
docker stats --no-stream
# Утилизация CPU и средняя нагрузка
htop
# Дисковый ввод-вывод (поле %util - загрузка диска)
iostat -x 1
# Потребление CPU конкретными задачами
pidstat 1
Если iostat показывает 100% util для диска, а wrk - низкий RPS, узкое место в дисковой подсистеме. Проверьте, не переключился ли Docker на драйвер vfs или не включился ли swap. Если htop показывает высокий %sy (system) при умеренной нагрузке - проблема в сетевом стеке ядра, пересмотрите параметры net.core и net.ipv4.
Как сохранить настройки и не допустить деградации в будущем
Повторение цикла «обновление - деградация - диагностика - восстановление» неприемлемо для production-среды. Два инструмента - etckeeper и Ansible - закрывают вопрос сохранности конфигураций и автоматического применения кастомных параметров после любого обновления пакетов.
Версионирование конфигураций с etckeeper
Etckeeper интегрируется с apt/yum и автоматически создаёт коммиты в Git-репозиторий /etc до и после каждой операции с пакетами. Установка:
apt install etckeeper # или yum install etckeeper
cd /etc
# Инициализация Git-репозитория (если не был создан автоматически)
etckeeper init
git commit -m "Initial commit of /etc"
После любого обновления системы вы можете посмотреть, что изменилось в /etc: cd /etc && git log -p. Откат конкретного файла: git checkout HEAD~1 -- sysctl.d/99-network-tuning.conf. Для быстрого восстановления Nginx после неудачного обновления используйте чек-лист резервного копирования и восстановления Nginx.
Автоматизация применения тюнинга через Ansible
Ручная правка sysctl.conf на десяти серверах - источник дрейфа конфигураций и ошибок. Ansible-плейбук гарантирует идемпотентное применение параметров:
- name: Apply kernel tuning
hosts: production
become: yes
tasks:
- name: Copy sysctl configuration
copy:
src: files/99-network-tuning.conf
dest: /etc/sysctl.d/99-network-tuning.conf
owner: root
group: root
mode: '0644'
notify: reload sysctl
- name: Copy Nginx main configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: reload nginx
handlers:
- name: reload sysctl
command: sysctl -p /etc/sysctl.d/99-network-tuning.conf
- name: reload nginx
command: nginx -t && nginx -s reload
Храните плейбуки и шаблоны в Git-репозитории. Запуск ansible-playbook apply-tuning.yml после каждого обновления системы вернёт все параметры к требуемым значениям за секунды. Для комплексного мониторинга результатов настройте алерты на ключевые метрики Nginx по шпаргалке по пяти критическим метрикам - вы узнаете о деградации раньше, чем её заметят пользователи.
Размещение production-стенда на надёжной облачной инфраструктуре упрощает создание тестовых копий для проверки обновлений. Timeweb Cloud предоставляет VDS/VPS с мгновенным снапшотированием - вы можете клонировать боевой сервер, прогнать обновление и бенчмарки на копии, и только затем применить изменения к оригиналу.