Глибока оптимізація Linux-серверів під production-навантаження

event 04.03.2026 10:00
| category DevOps | person iron_will | comment 0 | visibility 68 | |

Запустити сервер в Linux - справа нескладна. Але налаштувати його так, щоб він витримував тисячі одночасних з'єднань, мінімізував латентність і не «падав» під піковим навантаженням - це вже інженерна задача, яка потребує системного підходу. Дистрибутиви Linux поставляються з універсальними налаштуваннями, придатними для широкого кола сценаріїв, але жодним чином не оптимальними для production-середовища.У цій статті розглянемо конкретні техніки глибокої оптимізації: від параметрів ядра та файлових дескрипторів до планувальника введення-виведення та мережевого стеку. Матеріал орієнтований на інженерів, які вже мають досвід адміністрування Linux і хочуть перейти від базового налаштування до справжнього performance tuning.

Усі наведені приклади перевірені на Ubuntu 22.04 LTS та RHEL 9 з ядром 5.15+. Частина параметрів є загальними для більшості дистрибутивів, частина - специфічна для певних версій ядра. Завжди тестуйте зміни в staging-середовищі перед застосуванням у production.

Важливо розуміти: оптимізація без профілювання - це стрілянина наосліп. Перш ніж змінювати будь-який параметр, зафіксуйте базові метрики: CPU utilization, memory pressure, I/O wait, network throughput і latency. Це дасть змогу об'єктивно оцінити ефект кожної зміни.

Оптимізація параметрів ядра через sysctl

Файл /etc/sysctl.conf (або файли в /etc/sysctl.d/) - перша точка входу для системного тюнінгу. Параметри ядра безпосередньо впливають на поведінку мережевого стеку, файлової системи та менеджменту пам'яті.

Мережевий стек

# /etc/sysctl.d/99-production.conf

# Збільшення розміру буферів TCP
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728

# Черга для нових з'єднань (важливо при DDoS і пікових навантаженнях)
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# Повторне використання TIME_WAIT сокетів
net.ipv4.tcp_tw_reuse = 1

# Збільшення розміру черги мережевого пристрою
net.core.netdev_max_backlog = 50000

# TCP keepalive — виявлення «мертвих» з'єднань
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 10
net.ipv4.tcp_keepalive_probes = 6

# BBR — сучасний алгоритм контролю перевантаження (ядро 4.9+)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

Після редагування файлу застосуйте зміни без перезавантаження:

sysctl -p /etc/sysctl.d/99-production.conf

Чому BBR? Класичний алгоритм CUBIC реагує на втрати пакетів як сигнал перевантаження мережі. BBR (Bottleneck Bandwidth and Round-trip propagation time) натомість моделює пропускну здатність каналу і RTT, що дає суттєвий приріст throughput особливо у мережах з великою затримкою або незначними втратами.

Управління пам'яттю та swap

# Зменшення агресивності використання swap (0–100)
# Для серверів з великою кількістю RAM рекомендується 10
vm.swappiness = 10

# Частота синхронізації брудних сторінок на диск
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5

# Прозорі величезні сторінки (Transparent Huge Pages)
# Для баз даних (PostgreSQL, MySQL) часто краще вимкнути
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

Для збереження налаштування THP після перезавантаження додайте в /etc/rc.local або створіть systemd unit-файл.

Ліміти файлових дескрипторів

Одна з найпоширеніших причин деградації high-load сервісів - вичерпання файлових дескрипторів. За замовчуванням Linux дозволяє відносно небагато відкритих файлів на процес і на систему загалом.

Системні ліміти

# Перегляд поточних лімітів
cat /proc/sys/fs/file-max
ulimit -n

# Збільшення системного ліміту
echo "fs.file-max = 2097152" >> /etc/sysctl.d/99-production.conf
sysctl -p /etc/sysctl.d/99-production.conf

Ліміти для процесів через limits.conf

# /etc/security/limits.conf
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65535
* hard nproc 65535
root soft nofile 1048576
root hard nofile 1048576

Ліміти для systemd-сервісів

Якщо ваш застосунок запускається через systemd, limits.conf може не застосовуватись — потрібно явно вказати ліміти в unit-файлі:

[Service]
LimitNOFILE=1048576
LimitNPROC=65535
LimitMEMLOCK=infinity

Перевірте поточний стан дескрипторів для конкретного процесу:

# Перегляд лімітів для процесу за PID
cat /proc/<PID>/limits

# Кількість відкритих файлів процесу
ls /proc/<PID>/fd | wc -l

Планувальник введення-виведення (I/O Scheduler)

Вибір планувальника I/O критично важливий для серверів із інтенсивним дисковим навантаженням. Linux підтримує кілька планувальників, і оптимальний вибір залежить від типу накопичувача.

Визначення та зміна планувальника

# Перегляд поточного планувальника для диска
cat /sys/block/sda/queue/scheduler

# Зміна планувальника без перезавантаження
echo mq-deadline > /sys/block/sda/queue/scheduler
# або
echo none > /sys/block/nvme0n1/queue/scheduler

Рекомендації за типом накопичувача:

  • NVMe SSD - none (No-op). NVMe має власну чергу команд (NVMe queues), і додатковий планувальник лише додає накладні витрати.
  • SATA SSD - mq-deadline або kyber. Забезпечують мінімальну латентність при збереженні впорядкованості запитів.
  • HDD - bfq (Budget Fair Queuing). Оптимізує пропускну здатність, мінімізуючи переміщення головки диска.

Параметри черги диска

# Глибина черги (queue depth) — для NVMe можна збільшити
cat /sys/block/nvme0n1/queue/nr_requests
echo 2048 > /sys/block/nvme0n1/queue/nr_requests

# Read-ahead — розмір блоку попереднього зчитування (в секторах по 512 байт)
# Для баз даних з випадковим доступом зменшіть до 0–256
# Для потокової обробки даних збільшіть до 2048–8192
blockdev --setra 256 /dev/sda

Для збереження налаштувань між перезавантаженнями використовуйте udev rules:

# /etc/udev/rules.d/60-io-scheduler.rules
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"

Оптимізація файлової системи

Налаштування монтування ext4 та XFS

Параметри монтування безпосередньо впливають на продуктивність файлової системи та надійність даних.

# /etc/fstab — приклад для ext4 на production-сервері
/dev/sda1 /data ext4 defaults,noatime,nodiratime,data=ordered,barrier=1 0 2

# Для XFS
/dev/sdb1 /logs xfs defaults,noatime,nodiratime,logbufs=8,logbsize=256k 0 2

noatime - вимикає оновлення часу останнього доступу до файлу при кожному читанні. Це значно зменшує кількість записів на диск, особливо при інтенсивному читанні. Альтернатива - relatime, яка оновлює atime лише якщо він старший за mtime.

data=ordered - забезпечує консистентність даних без суттєвих втрат у продуктивності. data=writeback швидший, але менш безпечний при раптовому вимкненні.

Налаштування журналювання XFS

# Розміщення журналу на швидкому накопичувачі (окремий диск/розділ)
mkfs.xfs -l logdev=/dev/nvme0n1p1,size=2000b /dev/sdb1

# Монтування з зовнішнім журналом
mount -o logdev=/dev/nvme0n1p1 /dev/sdb1 /data

Оптимізація мережевих з'єднань: NUMA та CPU affinity

На серверах із NUMA-архітектурою (Non-Uniform Memory Access) неправильний розподіл навантаження між NUMA-вузлами може суттєво збільшити латентність доступу до пам'яті.

Аналіз NUMA-топології

# Перегляд NUMA-топології
numactl --hardware

# Запуск процесу на конкретному NUMA-вузлі
numactl --cpunodebind=0 --membind=0 ./your-application

# Прив'язка мережевих переривань до CPU-ядер того ж NUMA-вузла, що і NIC
cat /proc/interrupts | grep eth0

IRQ Affinity для мережевих адаптерів

# Автоматична балансування переривань (встановити irqbalance)
systemctl enable --now irqbalance

# Для тонкого налаштування — ручне призначення
# Знайти переривання мережевого адаптера
grep eth0 /proc/interrupts

# Призначити переривання конкретному CPU
echo 2 > /proc/irq/<IRQ_NUMBER>/smp_affinity_list

Для багатоядерних систем із 10GbE+ рекомендується використовувати RSS (Receive Side Scaling) та RPS (Receive Packet Steering), щоб розподілити обробку мережевого трафіку між кількома ядрами:

# Увімкнення RPS для всіх CPU
echo ffff > /sys/class/net/eth0/queues/rx-0/rps_cpus

Профілювання та моніторинг продуктивності

Жодна оптимізація не має сенсу без вимірювання. Ось мінімальний набір інструментів для production-аналізу.

Системні утиліти

# Аналіз навантаження в реальному часі
top -H -p <PID>       # з потоками
htop                  # інтерактивний
vmstat 1 10           # пам'ять, swap, I/O, CPU кожну секунду
iostat -xz 1          # детальна статистика I/O
ss -s                 # статистика сокетів
sar -n DEV 1 10       # мережева статистика

Perf для профілювання ядра

# Запис профілю CPU на 30 секунд
perf record -g -p <PID> -- sleep 30

# Аналіз результатів
perf report --stdio

# Flame graph (за допомогою brendangregg/FlameGraph)
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg

BPF/eBPF інструменти (bpftrace, bcc)

# Латентність системних викликів
bpftrace -e 'tracepoint:syscalls:sys_enter_read { @start[tid] = nsecs; }
             tracepoint:syscalls:sys_exit_read  { @latency = hist(nsecs - @start[tid]); }'

# Аналіз блокуючих I/O операцій
biolatency -D  # розподіл латентності I/O за дисками

Практичний кейс: оптимізація під high-concurrency API-сервер

Розглянемо реальний сценарій: REST API сервер на базі Nginx + backend на Go, який обслуговує 50 000 RPS із p99 латентністю понад 200 мс при норматив 50 мс.

Діагностика:

# Перевірка черги з'єднань
ss -lnt | grep :443
netstat -s | grep -i "failed\|overflow\|drop"

# Виявлено: SYN drops через переповнену чергу
# net.ipv4.tcp_max_syn_backlog було 128

Застосовані зміни:

  1. Збільшено net.core.somaxconn до 65535 та tcp_max_syn_backlog до 65535.
  2. Увімкнено BBR замість CUBIC.
  3. Збільшено LimitNOFILE для systemd unit Nginx до 1048576.
  4. Вимкнено THP (Transparent Huge Pages) - Go runtime погано взаємодіє з THP через фрагментацію.
  5. Прив'язано NIC interrupts до CPU ядер 0–7, а Go runtime налаштовано на GOMAXPROCS=8 із pinning на ядра 8–15.

Результат: p99 латентність знизилась з 210 мс до 38 мс. Пропускна здатність зросла з 50 000 до 78 000 RPS без зміни hardware.

Автоматизація та Infrastructure as Code

Ручне застосування sysctl-параметрів на кожному сервері - неприйнятний підхід у масштабованій інфраструктурі. Усі зміни повинні бути закодовані та версіоновані.

Ansible playbook для системного тюнінгу

---
- name: Linux production tuning
  hosts: production
  become: true
  tasks:
    - name: Apply sysctl parameters
      ansible.posix.sysctl:
        name: "{{ item.key }}"
        value: "{{ item.value }}"
        state: present
        reload: true
        sysctl_file: /etc/sysctl.d/99-production.conf
      loop:
        - { key: "net.core.somaxconn", value: "65535" }
        - { key: "net.ipv4.tcp_tw_reuse", value: "1" }
        - { key: "vm.swappiness", value: "10" }
        - { key: "net.ipv4.tcp_congestion_control", value: "bbr" }
        - { key: "net.core.default_qdisc", value: "fq" }

    - name: Set system-wide file descriptor limits
      pam_limits:
        domain: "*"
        limit_type: "{{ item.type }}"
        limit_item: nofile
        value: "1048576"
      loop:
        - { type: soft }
        - { type: hard }

    - name: Disable Transparent Huge Pages
      shell: |
        echo never > /sys/kernel/mm/transparent_hugepage/enabled
        echo never > /sys/kernel/mm/transparent_hugepage/defrag
      changed_when: true

    - name: Persist THP settings via systemd
      copy:
        dest: /etc/systemd/system/disable-thp.service
        content: |
          [Unit]
          Description=Disable Transparent Huge Pages
          After=network.target

          [Service]
          Type=oneshot
          ExecStart=/bin/sh -c "echo never > /sys/kernel/mm/transparent_hugepage/enabled"
          ExecStart=/bin/sh -c "echo never > /sys/kernel/mm/transparent_hugepage/defrag"
          RemainAfterExit=yes

          [Install]
          WantedBy=multi-user.target
      notify: Enable disable-thp service

  handlers:
    - name: Enable disable-thp service
      systemd:
        name: disable-thp
        enabled: true
        state: started
        daemon_reload: true

Висновки

Глибока оптимізація Linux-сервера під production - це не одноразова дія, а безперервний процес вимірювання, аналізу та корегування. Ключові принципи, які варто засвоїти:

Вимірюйте перед змінами. Baseline-метрики - обов'язковий крок. Без них неможливо оцінити ефективність оптимізації або виявити регресію.

Розуміння, а не копіювання. Кожен параметр має фізичний сенс. Сліпе копіювання «magic sysctl values» з інтернету без розуміння контексту може погіршити ситуацію.

Тестуйте в ізоляції. Змінюйте один параметр за раз і фіксуйте результат. Пакетне застосування змін унеможливлює визначення причинно-наслідкових зв'язків.

Автоматизуйте та версіонуйте. Усі оптимізації повинні бути в системі контролю версій і застосовуватися через IaC-інструменти. «Сніжинкові сервери» з ручними налаштуваннями - ворог надійної інфраструктури.

Контекст вирішує все. Оптимальний планувальник I/O для PostgreSQL на NVMe та для Elasticsearch на HDD - різні. Алгоритм TCP-конгестії для CDN і для внутрішнього мікросервісу - теж різні. Завжди враховуйте специфіку навантаження свого застосунку.

Наведені в статті налаштування є відправною точкою, а не остаточним рецептом. Production-система кожної компанії унікальна, і справжня оптимізація завжди вимагає глибокого розуміння власного навантаження.

Related posts

Kubernetes: сучасна платформа оркестрації контейнерів для production-середовищ

Вступ Kubernetes став де-факто стандартом для запуску контейнеризованих застосунків у production-середовищах. Якщо Docker вирішив проблему пакування застосунку разом із залежностями, то Kubernetes вирішує значно складніше завдання - як масштабувати,...

category DevOps person iron_will event 19/04/2026

Файлові системи ext4, XFS, Btrfs - що обрати для production

Вступ Вибір файлової системи в Linux-середовищі - це не просто технічна деталь, а стратегічне рішення, яке безпосередньо впливає на продуктивність, надійність і масштабованість інфраструктури. У production-системах, де критичними є стабільність і пе...

category DevOps person iron_will event 14/04/2026

Zero Trust архітектура на практиці: принципи, впровадження та технічні кейси

Вступ Класичні моделі кібербезпеки, що базуються на периметрі мережі, давно втратили ефективність. Сучасні ІТ-інфраструктури характеризуються гібридністю, розподіленістю та активним використанням хмарних сервісів. У таких умовах концепція «довіряй,...

category Security person iron_will event 05/04/2026

Керування користувачами та правами доступу в Linux на enterprise-рівні

Вступ У сучасних корпоративних ІТ-інфраструктурах системи на базі Linux є критично важливими компонентами - від веб-серверів і контейнерних платформ до систем зберігання даних і DevOps-інструментів. У цьому контексті керування користувачами та права...

category Security person iron_will event 30/03/2026

Корисні команди Linux (Ubuntu): практичний довідник для системних адміністраторів та DevOps

Вступ Linux є основою більшості сучасної серверної інфраструктури. Веб-сервери, системи контейнеризації, хмарні платформи, CI/CD-пайплайни та мережеві сервіси у переважній більшості випадків працюють саме на Linux. Серед різних дистрибутивів особлив...

category CheatSheets person iron_will event 13/03/2026

PowerShell: Корисні скрипти, які знадобляться кожному

PowerShell уже давно перестав бути просто оболонкою для адміністрування Windows. Сьогодні це повноцінна платформа для автоматизації рутинних задач, управління інфраструктурою та інтеграції з різними сервісами. Незалежно від того, чи ви системний адмі...

category DevOps person iron_will event 09/03/2026
cookie
This website uses cookies to improve your experience. Learn more