Восстановление и повышение производительности систем после обновлений: настройка ядра, Nginx и Docker | AdminWiki

Восстановление и повышение производительности систем после обновлений: настройка ядра, Nginx и Docker

08 августа 2026 11 мин. чтения
Содержание статьи

Плановое обновление пакетов - рядовая операция, которая иногда оборачивается непредсказуемым падением производительности. Сервер, ещё вчера державший 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 с мгновенным снапшотированием - вы можете клонировать боевой сервер, прогнать обновление и бенчмарки на копии, и только затем применить изменения к оригиналу.

Поделиться:
Сохранить гайд? В закладки браузера