Ваш веб‑сервер працював нормально. Ви нічого не розгортали. А потім раптом: 502 Bad Gateway і 504 Gateway Timeout по всьому сайту, ніби похмура фронта погоди. Клієнти оновлюють сторінки. Дашборди червоніють. Хтось пропонує «перезапустити nginx», ніби це священний ритуал.
Іноді перезапуск — пластир. Іноді це найшвидший спосіб знищити докази. Мета тут — знайти справжнє вузьке місце в Ubuntu 24.04 — upstream‑процеси, сокети, DNS, голод по CPU або затримки зберігання — і виправити це без гадань.
Що насправді означають 502/504 (і чого вони не означають)
Коли користувач бачить 502/504, він звинувачує «веб‑сервер». Насправді веб‑сервер часто просто доставник повідомлення.
502 Bad Gateway: «Я поговорив з upstream і там була нісенітниця або нічого.»
- Типове значення: Nginx/Apache діяв як проксі, і з’єднання з upstream не вдалося або upstream повернув недійсну відповідь.
- Поширені реальні причини: upstream‑процес аварійно завершився, зміни прав сокета, upstream слухає не той порт, невідповідність TLS, невідповідність протоколу, бекенд повертає «сміття», бо він помирає.
504 Gateway Timeout: «Я чекав upstream, але він не відповів вчасно.»
- Типове значення: проксі встановив з’єднання (або намагався), але upstream не відповів у відведений час.
- Поширені реальні причини: upstream перевантажений, повільні виклики до БД, затримки зберігання, зависання DNS, виснаження пулу з’єднань, дедлоки, виснаження ресурсів ядра.
Ось операційна істина: 502/504 зазвичай є симптомами вузького місця, а не бага в nginx. Nginx має бути нудним. Коли він таким не є — значить щось інше зробило його цікавим.
Жарт #1: Перезапуск nginx, щоб виправити 504, — це як вимкнути радіо, щоб вирішити затори на дорозі. Ви зменшуєте шум, але не проблему.
Швидкий план діагностики (перший/другий/третій)
Якщо нічого іншого не робите — зробіть це. Мета: визначити вузьке місце менш ніж за 10 хвилин та зібрати докази, які можна передати наступній людині без сорому.
Перший: підтвердьте, хто генерує 502/504, і збережіть логи негайно
- Перевірте логи помилок проксі (nginx або Apache) на точний рядок помилки від upstream.
- Зіставте часові мітки з логами бекенду (php-fpm, gunicorn, node тощо).
- Перевірте journald systemd на перезапуски, OOM‑вбивства, помилки прав доступу.
Рішення: якщо ви бачите “connect() failed (111: Connection refused)” — це проблема досяжності upstream. Якщо бачите “upstream timed out” — це проблема продуктивності або зависання. Якщо бачите “permission denied” — зазвичай це проблема сокета/SELinux/AppArmor/режиму файлу.
Другий: визначте, чи це обчислення, пам’ять, мережа чи зберігання
- CPU + load + iowait: якщо iowait зростає — ви не CPU‑зв’язані; ви чекаєте на диски.
- Тиск пам’яті: OOM‑вбивства та своп‑шторми виглядають як випадкові тайм‑ауті.
- Мережа та DNS: upstream може «бути повільним», бо проксі не може швидко розв’язати ім’я або підключитися.
Рішення: якщо wa високий — переходьте до перевірок зберігання. Якщо si/so (swap in/out) активний — дивіться на пам’ять. Якщо багато SYN‑SENT або TIME_WAIT — дивіться мережу/порти.
Третій: ізолюйте невдалий хоп
- Протестуйте з проксі до upstream (loopback, unix‑сокет, локальний порт, віддалена служба).
- Протестуйте з upstream до його залежностей (БД, кеш, об’єктне сховище).
- Якщо проблеми лише з деякими запитами: перевірте таймаути, ліміти воркерів, черги та пули з’єднань.
Рішення: виправляйте найвужче місце спочатку. Не «тюнінгуйте все» в паніці — так ви отримаєте другий збій, креативніший за перший.
Практично: 12+ завдань з командами, виводами та рішеннями
Ось завдання, які я виконую в продакшені, коли з’являються 502/504 «нібито нізвідки». Кожна команда включає: що вона показує, що означає вивід і що робити далі.
Завдання 1: Перевірте, чи nginx — компонент, що викидає помилки
cr0x@server:~$ sudo tail -n 50 /var/log/nginx/error.log
2025/12/28 09:41:12 [error] 3192#3192: *884 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.25, server: example.com, request: "GET /api/orders HTTP/1.1", upstream: "http://127.0.0.1:8000/api/orders", host: "example.com"
2025/12/28 09:41:15 [error] 3192#3192: *901 connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 198.51.100.9, server: example.com, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.3-fpm.sock:", host: "example.com"
Значення: Два різні режими відмов: один — таймаут від HTTP upstream на 127.0.0.1:8000; інший — проблема прав доступу до unix‑сокета PHP‑FPM.
Рішення: Не трактуйте це як один інцидент. Розділіть проблему: виправте права сокета для PHP‑шляхів і продуктивність/таймаути для API‑upstream.
Завдання 2: Підтвердіть активну конфігурацію та цілі upstream (щоб не налажати в неправильному файлі)
cr0x@server:~$ sudo nginx -T | sed -n '1,120p'
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
Значення: Ви дивитесь на живу конфігурацію, яку nginx використовуватиме, включно з include. Це рятує від класичної помилки: редагувати файл, який реально не завантажено.
Рішення: Знайдіть у цьому виводі ваш upstream‑стенц‑блок або fastcgi_pass‑рядки та перевірте, що вони відповідають реальності (шлях сокета, порт, ім’я хоста).
Завдання 3: Перевірте, чи upstream‑процеси живі і не зациклюються на перезапусках
cr0x@server:~$ systemctl status php8.3-fpm --no-pager
● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
Loaded: loaded (/usr/lib/systemd/system/php8.3-fpm.service; enabled; preset: enabled)
Active: active (running) since Sun 2025-12-28 09:10:04 UTC; 33min ago
Docs: man:php-fpm8.3(8)
Process: 1258 ExecStartPre=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/8.3/fpm/pool.d/www.conf 83 (code=exited, status=0/SUCCESS)
Main PID: 1269 (php-fpm8.3)
Tasks: 21 (limit: 18841)
Memory: 230.5M
CPU: 1min 42.902s
Значення: php‑fpm працює. Це не гарантує, що nginx має доступ до його сокета. Це лише означає, що сервіс не впав.
Рішення: Якщо nginx показує permission denied — перевірте власника/режим сокета і користувача nginx (зазвичай www‑data).
Завдання 4: Перевірте права unix‑сокета для PHP‑FPM
cr0x@server:~$ ls -l /run/php/php8.3-fpm.sock
srw-rw---- 1 root www-data 0 Dec 28 09:10 /run/php/php8.3-fpm.sock
Значення: Сокет належить root:www-data з режимом 660. nginx за замовчуванням працює як www‑data, тому це має працювати.
Рішення: Якщо бачите srw------- root root або подібне, виправте listen.owner, listen.group та listen.mode у конфі пулу, потім перезавантажте php‑fpm.
Завдання 5: Підтвердіть користувача воркерів nginx (не припускайте)
cr0x@server:~$ ps -o user,group,pid,cmd -C nginx | head
USER GROUP PID CMD
root root 3121 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data www-data 3192 nginx: worker process
www-data www-data 3193 nginx: worker process
Значення: Воркер‑процеси працюють як www‑data, отже група власника сокета має включати www‑data або права мають дозволяти доступ.
Рішення: Якщо воркери працюють під іншим користувачем (контейнери, кастомні збірки) — узгодьте права сокета відповідно.
Завдання 6: Знайдіть, чи порт upstream слухає і хто ним володіє
cr0x@server:~$ sudo ss -ltnp | grep -E ':8000|:8080|:9000'
LISTEN 0 4096 127.0.0.1:8000 0.0.0.0:* users:(("gunicorn",pid=2204,fd=5))
LISTEN 0 4096 127.0.0.1:9000 0.0.0.0:* users:(("php-fpm8.3",pid=1269,fd=8))
Значення: gunicorn слухає на 127.0.0.1:8000. Якщо nginx все ще таймаутить, gunicorn живий, але повільний або застряг, або шлях блокується всередині програми.
Рішення: Перейдіть до логів бекенду і перевірки ресурсної конкуренції. Якщо порт не слухає — виправте сервіс або його адресу прив’язки.
Завдання 7: Відтворіть запит з хоста проксі (обхід рівня проксі)
cr0x@server:~$ curl -sS -D- -o /dev/null http://127.0.0.1:8000/api/orders
HTTP/1.1 200 OK
Server: gunicorn
Date: Sun, 28 Dec 2025 09:44:01 GMT
Connection: close
Content-Type: application/json
Значення: Ендпоїнт зараз відповідає швидко. Це свідчить про переривчасте чергування, вичерпання воркерів або спайки залежностей, а не сталу невірну конфігурацію.
Рішення: Перевірте обмеження конкурентності та затримки в період інциденту; не святкуйте перемогу лише тому, що один curl спрацював.
Завдання 8: Шукайте таймаути upstream і голод воркерів в логах застосунку
cr0x@server:~$ sudo journalctl -u gunicorn --since "2025-12-28 09:35" --no-pager | tail -n 30
Dec 28 09:40:58 app1 gunicorn[2204]: [2025-12-28 09:40:58 +0000] [2204] [CRITICAL] WORKER TIMEOUT (pid:2311)
Dec 28 09:40:58 app1 gunicorn[2311]: [2025-12-28 09:40:58 +0000] [2311] [ERROR] Error handling request /api/orders
Dec 28 09:40:59 app1 gunicorn[2204]: [2025-12-28 09:40:59 +0000] [2204] [INFO] Worker exiting (pid: 2311)
Dec 28 09:41:00 app1 gunicorn[2204]: [2025-12-28 09:41:00 +0000] [2204] [INFO] Booting worker with pid: 2418
Значення: воркери gunicorn тайм‑аутяться і перезапускаються. Nginx бачить це як upstream‑таймаути/502 залежно від таймінгу.
Рішення: Не просто збільшуйте gunicorn‑таймаут. Знайдіть, чому запити зависають: блокування БД, затримки зберігання, дедлоки потоків, DNS‑зависання, повільні зовнішні виклики.
Завдання 9: Перевірте системне навантаження, CPU і iowait під час події
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 812344 48216 923400 0 0 24 18 210 390 6 2 90 2 0
3 1 0 789120 48220 921900 0 0 120 2040 480 1200 8 4 58 30 0
5 2 0 760112 48220 920100 0 0 80 1990 520 1350 10 5 50 35 0
4 2 0 748000 48224 918500 0 0 60 2100 510 1300 9 4 52 31 0
2 1 0 770200 48224 920300 0 0 40 980 360 900 7 3 78 12 0
Значення: iowait (wa) стрибає до 30–35%. Це не веб‑проблема. Це ядро каже, що ваші процеси чекають на зберігання.
Рішення: Перейдіть до перевірок затримок диска, насичення файлової системи і з’ясуйте, що б’є по диску (journald, logrotate, резервні копії, DB‑чекпоінти, антивірус і т.і.).
Завдання 10: Визначте, який процес створює тиск на диск
cr0x@server:~$ sudo iotop -oPa -n 3
Total DISK READ: 1.65 M/s | Total DISK WRITE: 38.90 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
2210 be/4 postgres 0.00 B/s 22.30 M/s 0.00 % 9.10 % postgres: checkpointer
3140 be/4 root 0.00 B/s 8.40 M/s 0.00 % 2.20 % systemd-journald
987 be/4 www-data 1.20 M/s 0.00 B/s 0.00 % 1.30 % gunicorn: worker [app]
Значення: Postgres checkpointer інтенсивно пише. journald теж пише. Ці записи можуть блокувати читання залежно від підсистеми зберігання.
Рішення: Якщо це збігається з тайм‑аутами — перевірте стан Postgres і затримки зберігання. Можливо, треба налаштувати чекпоінти БД, перемістити логи на швидший диск або зупинити «галасливого сусіда».
Завдання 11: Перевірте затримки блочного пристрою та його насичення
cr0x@server:~$ sudo iostat -x 1 3
avg-cpu: %user %nice %system %iowait %steal %idle
7.82 0.00 3.21 28.44 0.00 60.53
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz aqu-sz %util
nvme0n1 32.0 1824.0 0.0 0.0 8.20 57.0 620.0 41280.0 0.0 0.0 42.10 66.6 6.20 98.0
Значення: Пристрій завантажений на 98% і w_await 42ms. Це не «добре». Це черга.
Рішення: Зменшіть write amplification (логи, чекпоінти), перевірте, чи підлягає основне сховище обмеженням, і підтвердьте, що ви не влучили у хмарні ліміти IOPS.
Завдання 12: Перевірте OOM‑вбивства і тиск пам’яті (тихий убивця)
cr0x@server:~$ sudo journalctl -k --since "2025-12-28 09:00" | grep -i -E 'oom|killed process' | tail -n 20
Dec 28 09:38:11 app1 kernel: Out of memory: Killed process 2311 (gunicorn) total-vm:1452200kB, anon-rss:612400kB, file-rss:0kB, shmem-rss:0kB, UID:33 pgtables:2200kB oom_score_adj:0
Значення: Ядро вбило процес gunicorn. Nginx бачить, як бекенд зникає посеред запиту: 502, 504 або і те, і те.
Рішення: Зупиніть кровотечу: зменшіть конкурентність або пам’ятковий слід, додайте RAM, виправте витоки та додайте cgroup‑обмеження, щоб «вбивство» було передбачуваним (рестарт сервісу), а не хаотичним.
Завдання 13: Перевірте стани з’єднань на предмет виснаження портів або переповнення upstream
cr0x@server:~$ ss -s
Total: 3087 (kernel 0)
TCP: 1987 (estab 412, closed 1450, orphaned 0, synrecv 0, timewait 1450/0), ports 0
Transport Total IP IPv6
RAW 0 0 0
UDP 12 10 2
TCP 537 509 28
INET 549 519 30
FRAG 0 0 0
Значення: TIME_WAIT багато. Це не обов’язково проблема, але може стати нею, якщо ви робите багато короткотривалих upstream‑з’єднань і вичерпуєте епемерні порти.
Рішення: Якщо бачите «cannot assign requested address» у логах — переходьте на keepalive/пули, і обережно перегляньте net.ipv4.ip_local_port_range та налаштування повторного використання портів.
Завдання 14: Підтвердіть, що DNS не зависає при розв’язуванні upstream
cr0x@server:~$ resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Link 2 (ens5)
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 10.0.0.2
DNS Servers: 10.0.0.2 10.0.0.3
Значення: systemd‑resolved задіяний. Якщо DNS‑сервер повільний або недоступний — розв’язування upstream‑імен може блокувати обробку запитів.
Рішення: Якщо upstream використовує імена хостів (не IP) — перевірте швидкість розв’язування і кешування. Для nginx розгляньте варіант розв’язування при старті з фіксованими IP або використання директиви resolver з таймаутами.
Завдання 15: Підтвердіть, що AppArmor не блокує доступ до сокетів/файлів (Ubuntu любить AppArmor)
cr0x@server:~$ sudo dmesg | grep -i apparmor | tail -n 10
[ 8921.224911] audit: type=1400 audit(1766914872.112:188): apparmor="DENIED" operation="connect" profile="/usr/sbin/nginx" name="/run/php/php8.3-fpm.sock" pid=3192 comm="nginx" requested_mask="wr" denied_mask="wr" fsuid=33 ouid=0
Значення: nginxу відмовлено у з’єднанні з сокетом PHP‑FPM через AppArmor. Це виглядає точно як «permission denied», але chmod цього не вирішить.
Рішення: Налаштуйте профіль AppArmor, щоб дозволити цей шлях сокета, або вирівняйте шлях під очікуваний. Потім перезавантажте AppArmor і nginx.
Справжні причини раптових 502/504 на Ubuntu 24.04
«Раптові» відмови зазвичай є повільними збоїми, які ви не бачили — доки проксі не почав чесно віддавати HTTP‑коди.
1) Бекенд живий, але забитий (черги та блокування голови черги)
Більшість веб‑бекендів мають обмеження конкурентності: воркери gunicorn, діти PHP‑FPM, цикл подій Node, пул потоків Java. Коли конкурентність насичується — нові запити ставляться в чергу. Проксі чекає. Зрештою настає таймаут.
Як це виглядає: лог nginx каже «upstream timed out», логи бекенду показують таймаути воркерів, повільні запити або нічого, бо воркери заблоковані.
Що робити: знайти, що блокує. Найшвидші підозрювані: блокування БД, зовнішні API без таймаутів, повільний диск для шаблонів/завантажень/сесій та синхронне логування під навантаженням.
2) Затримки зберігання — прихований upstream (карає всіх однаково)
Якщо iowait зростає — система не «зайнята». Вона зависла. Ваші потоки застосунку чекають на операції чит/запис диска: файли БД, сховища сесій, персистентний кеш, записи логів, тимчасові файли, завантаження Python‑модулів, скиди PHP opcache — що завгодно.
На Ubuntu 24.04 ви також можете побачити більш агресивне логування або інші значення за замовчуванням у службах systemd, ніж на старих інсталяціях. Не тому, що Ubuntu зла. А тому, що ваш робочий навантаження інше.
Що робити: доведіть затримку за допомогою iostat -x, ідентифікуйте записувачів через iotop, та шукайте періодичні завдання (резервні копії, logrotate, обслуговування БД). Виправлення: зменшити обсяг записів, перемістити «гарячі» шляхи на швидше сховище або підвищити IOPS у хмарі.
3) Регресії прав/сокетів (unix‑сокети: швидкі, чудові, іноді примхливі)
Unix‑сокети відмінні для локального проксінгу: низькі накладні витрати, чіткий контроль доступу. Вони також ламаються дуже специфічними способами — зазвичай після оновлення пакета, дрейфу конфігів або нової service unit, що змінює каталоги рантайму.
Як це виглядає: лог nginx: «connect() to unix:/run/… failed (13: Permission denied)» або «No such file or directory.»
Що робити: перевірте існування сокета, власника/режим, користувача воркерів nginx і AppArmor‑відмови. Потім виправте конфіг пулу, а не симптом.
4) Перезапуски systemd та «корисна» поведінка watchdog
Ubuntu 24.04 невибачливо системд‑орієнтована. Якщо ваш бекенд падає — systemd може перезапустити його швидко. Це створює сплески відмов, коли nginx бачить скидання з’єднань/відмови з’єднань.
Як це виглядає: «connection refused» у логах проксі, journalctl -u показує часті перезапуски, можливо ExitCode з вказівкою на помилку конфігурації, відсутні змінні середовища або міграції, що не виконані.
Що робити: зупиніть шторм перезапусків достатньо довго, щоб прочитати логи. Використайте systemctl edit, щоб додати розумні RestartSec і StartLimitIntervalSec, потім виправте джерелом аварій.
5) Зависання DNS і міф «це просто ім’я хоста»
Деякі проксі розв’язують імена upstream при старті. Деякі роблять це періодично. Деякі покладаються на системний резолвер, який блокує, якщо повільний. Якщо DNS зависає — upstream‑з’єднання зависають. Ідуть тайм‑аути.
Як це виглядає: переривчасті 504, особливо після змін у мережі. Логи можуть показувати host not found in upstream або нічого очевидного, крім збільшеного часу підключення.
Що робити: перевірте здоров’я резолвера, підтвердіть кешування, зменшіть залежність від «живого» DNS для критичних upstream або сконфігуруйте явні резолвери та таймаути в nginx, де це доречно.
6) Виснаження ресурсів ядра/мережі: епемерні порти, conntrack, дескриптори файлів
Коли трафік зростає, ви вичерпати не лише CPU. Ви можете вичерпати «речі»: дескриптори файлів, локальні порти, записи conntrack, backlog прослуховування, accept‑чергу. Ядро починає відмовляти у запитах. Nginx перетворює це на 502/504, залежно від стадії відмови.
Як це виглядає: багато TIME_WAIT, помилки «cannot assign requested address», «too many open files» або попередження про accept‑очікування. Користувачі бачать більше тайм‑аутів, ніж чітких помилок.
Що робити: перевірте стани з’єднань, підніміть ліміти обережно, увімкніть keepalive там, де безпечно, і уникайте створення нового TCP‑з’єднання на кожен запит, як у 2009 році.
7) Невідповідність таймаутів між рівнями (проксі vs бекенд vs LB)
Таймаути — командна гра. Якщо ваш балансувальник часу таймауту 60s, nginx 30s, а gunicorn 120s — ви отримаєте низку часткових відмов і повторів, що підсилюють навантаження.
Як це виглядає: клієнт бачить 504 на 30s, бекенд продовжує працювати, потім намагається записати у закритий сокет. Логи показують broken pipe, reset by peer, довгі тривалості запитів.
Що робити: виберіть єдиний зрозумілий бюджет таймаутів. Переконайтеся, що upstream має власні внутрішні таймаути для БД і зовнішніх викликів, коротші за таймаут проксі.
Одна ідея, яку варто тримати на стіні, парафразована від відомого операційного голосу: Все ламається, постійно — ваше завдання зробити відмову передбачуваною і видимою.
— Werner Vogels
Жарт #2: 504 — це спосіб вашого сервера сказати «Я вас не ігнорую, я просто дуже сильно думаю».
Типові помилки: симптом → корінь → виправлення
1) Симптом: 502 з «connect() failed (111: Connection refused)»
Корінь: upstream‑процес вимкнений, прив’язаний до іншої адреси або в цикл перезапусків; ви вказуєте nginx неправильний порт.
Виправлення: перевірте слухач за допомогою ss -ltnp; перевірте systemctl status і journalctl -u; виправте адресу прив’язки (127.0.0.1 проти 0.0.0.0) і переконайтеся, що сервіс стартує коректно.
2) Симптом: 502 з «Permission denied» до unix‑сокета
Корінь: невідповідність власника/групи/режиму сокета або AppArmor‑відмова.
Виправлення: налаштуйте PHP‑FPM pool listen.owner/listen.group/listen.mode та вирівняйте користувача nginx; якщо AppArmor блокує — оновіть профіль, щоб дозволити /run/php/php8.3-fpm.sock.
3) Симптом: 504 «upstream timed out while reading response header»
Корінь: бекенд приймає з’єднання, але не відповідає вчасно — голод воркерів, повільна БД, iowait диска або зовнішні API‑зависання.
Виправлення: підтвердіть таймаути воркерів бекенду; перевірте iowait з vmstat/iostat; знайдіть повільні залежності; додайте таймаути для зовнішніх викликів; зменшіть черги, масштабуючи або оптимізуючи.
4) Симптом: помилки зростають під час вікна logrotate/backup
Корінь: насичення диска, стиснення логів, створення знімків або резервні копії, що споживають I/O і CPU. Часто приховано на спільних хмарних дисках.
Виправлення: перенесіть важкі завдання, обмежте I/O, перемістіть логи/БД на окремі томи, налаштуйте чекпоінти БД і переконайтеся, що резервні копії інкрементні, а не «копіюй весь всесвіт щоночі».
5) Симптом: лише деякі ендпоїнти 504, інші в порядку
Корінь: конкретний кодовий шлях викликає повільний запит до БД, блокування, читання великого файлу або синхронний виклик залежності.
Виправлення: інструментуйте латентність по ендпоїнтах; перевірте логи повільних запитів; додайте індекси або кеш; потоково передавайте великі відповіді; винесіть важкі задачі з основного шляху запиту.
6) Симптом: 502 після оновлення пакетів (оновлення до Ubuntu 24.04)
Корінь: зміни в unit файлах сервісів, підняття версії PHP, зміна шляхів сокетів, несумісна конфігурація або відключені модулі.
Виправлення: перевірте nginx upstream/fastcgi шляхи; запустіть nginx -T; перевірте службу php‑fpm і шляхи сокетів; підтвердіть увімкнені сайти й модулі.
7) Симптом: 504 за проксі/балансувальником корпоративної мережі
Корінь: невідповідність таймаутів на краю, провали health‑check, або затримки TLS‑handshake. Іноді балансувальник робить ретраї, подвоюючи навантаження.
Виправлення: вирівняйте бюджет таймаутів між LB/nginx/app; переконайтесь, що health‑check ендпоїнт швидкий і не залежить від важких залежностей; забезпечте адекватні налаштування keepalive і TLS.
Три корпоративні міні‑історії (анонімізовані, правдоподібні, технічно точні)
Міні‑історія 1: Інцидент через неправильне припущення
Вони мали охайну архітектуру: nginx на Ubuntu, PHP‑FPM на тому самому хості, unix‑сокет між ними. Це працювало роками. Потім відтворили образ VM для Ubuntu 24.04, і сайт почав кидати 502 за лічені хвилини після першого трафіку.
On‑call зробив те, що роблять on‑call: перезапустив nginx. Помилки впали, потім повернулися. Хтось звинуватив «погане деплоймент», але в коді нічого не змінилося. Це було правдою. Та це не мало значення.
Неправильне припущення було тонким: вони думали, що права файлів такі ж, як раніше. У новому образі крок жорсткішої політики змінив користувача master/worker nginx і посилив профілі AppArmor. Сокет здавався правильним на погляд. Власник був нормальний. Режим — теж. Але AppArmor забороняв операцію connect.
Коли вони подивилися dmesg і побачили відмову, фікс був соромно простим: дозволити шлях сокета у профілі nginx (або вирівняти сокет до очікуваного шляху). 502 одразу зникли.
Постмертем не був «AppArmor поганий». Постмертем був «ми припустили, що захисний контроль не вплине на рантайм». Це припущення і призвело до виклику.
Міні‑історія 2: Оптимізація, що відбилася боком
Інша команда хотіла зменшити час відповіді і CPU. Вони ввімкнули більш агресивний access logging і додали поля таймінгів upstream для графіків. Це спрацювало. Дашборди стали гарніші. Его — теж.
Потім трафік збільшився, і під час піку почали з’являтися 504. Команда апу клялася, що нічого не змінювала. DB‑команда теж. Логи проксі показували таймаути upstream, але метрики бекенду виглядали «загалом нормально».
Кульмінацією стало те, що оптимізація створила новий гарячий шлях. Логи перетворилися на синхронні дискові записи на хмарному томі з обмеженими IOPS. Під навантаженням диск уперся в стелю. iowait підскочив. Воркери застигли. Проксі чекав, потім таймаутив. Класичний 504.
Вони виправили це, зробивши логи менш витратними: буферизація, зниження рівня логування і переміщення важкого логування з основного диска. Більш значущий висновок: трактуйте спостережуваність як частину виробничого навантаження, а не як безкоштовні цукерки.
Міні‑історія 3: Нудна, але правильна практика, що врятувала день
Компанія, з якою я працював, мала політику, яку всі глузували: «Не перезапускайте сервіси, поки не зберете 10 хвилин доказів». Люди нарікали, що це гальмує інцидент‑реакцію. Керівництво все одно тримало правило, бо раніше через це вже обпеклися.
Одного дня з’явилися 502 по всьому мульти‑тенант кластеру. Легким ходом було перезапустити все. Вони цього не зробили. Вони зібрали логи nginx, журнали бекенду і швидкий знімок iostat/vmstat з двох уражених вузлів.
Докази показали закономірність: iowait стрибав саме тоді, коли запускалося планове завдання. Це завдання локально ротаційно стисувало величезні логи, що збіглося з DB‑чекпоінтом. Підсистема зберігання не витримала, і upstream‑запити нагромадилися в черзі.
Оскільки були дані, виправлення було швидким і прицільним: перенести завдання, додати обмеження I/O, налаштувати чекпоінти БД і розподілити логи на інший том. Ніяких «ми думаємо». Ніяких героїчних припущень. Просто дисципліна, яка не дала повторного збою.
Чеклісти / покроковий план
Чекліст A: Зупинити кровотечу, не втрачаючи доказів
- Збережіть останні 200 рядків логів помилок проксі та access‑логи за період відмов.
- Збережіть логи сервісів бекенду за ті ж часові мітки.
- Зробіть знімок
vmstat 1 5іiostat -x 1 3з ураженого вузла. - Якщо сайт «плавиться»: скоротіть навантаження (rate limit, тимчасово вимкніть неважливі ендпоїнти, зменшіть дорогі функції) перед масовими рестартами.
Чекліст B: Підтвердіть невдалий хоп
- З хоста проксі
curlна upstream напряму (loopback/порт/сокет). - З upstream‑хоста протестуйте залежності (БД/кеш) з короткими таймаутами.
- Перевірте стани з’єднань (
ss -s) і дескриптори файлів, якщо підозра є. - Підтвердіть швидкість розв’язування DNS, якщо upstream‑адреси — імена хостів.
Чекліст C: Виправити безпечно (мінімальна зміна, що відновлює сервіс)
- Якщо права/сокет/AppArmor: виправте політику/конфіг; перезавантажте сервіси; перевірте одним запитом.
- Якщо голод воркерів: зменшіть трафік, обережно підвищіть ємність воркерів і виправте повільну залежність або блокуючий виклик.
- Якщо насичення зберігання: зупиніть «галасливого» записувача, перенесіть завдання, або перемістіть гарячі I/O на швидший диск.
- Вирівняйте таймаути LB → nginx → app → залежності, щоб уникнути штормів повторів.
Чекліст D: Запобігти повторенню
- Додайте алерти на iowait, %util диска та глибину черги бекенду — не лише на HTTP‑помилки.
- Слідкуйте за перцентилями часу відповіді upstream і кількістю таймаутів.
- Документуйте живу топологію upstream (порти, сокети, сервіси) і тримайте її актуальною.
- Проводьте навантажувальне тестування після оновлень ОС і змін у спостережуваності.
Цікаві факти та історичний контекст
- HTTP 502 і 504 — це помилки шлюзу: вони призначені для проміжних елементів — проксі, шлюзів і балансувальників — що скаржаться на upstream, а не на себе.
- Nginx став стандартним реверс‑проксі: здебільшого тому, що він добре працює при високій конкурентності з невеликим використанням пам’яті порівняно зі старими моделями «процес‑на‑з’єднання».
- Unix‑доменні сокети старші за сучасні веб‑стеки і залишаються одним із простих та швидких IPC‑механізмів на Linux для локальних сервісів.
- systemd нормалізував «restart on failure» у Linux‑флотах; це підвищило доступність, але також зробило цикли падінь легше приховати без моніторингу логів.
- AppArmor — поширений механізм безпеки Ubuntu; він часто пояснює «permission denied», коли файлові права виглядають правильними.
- TIME_WAIT‑шторми древні: вони дошкуляють високонавантаженим сервісам з ранніх часів TCP‑важких архітектур, особливо без keepalive.
- Хмарні диски часто мають пороги продуктивності (IOPS/пропускна здатність). Затримки можуть виглядати «раптовими», коли ви переходите поріг.
- Невирівняні таймаути підсилюють повтори: один шар таймаутиться, відбувається повтор, навантаження росте і система падає швидше, ніж якби ви дочекалися.
FAQ
1) Чи слід перезапускати nginx при появі 502/504?
Тільки після того, як ви зібрали логи та базові системні сигнали. Перезапуск може тимчасово очистити застряглі воркери чи сокети, але він також знищить сліди. Якщо це справжнє вузьке місце (БД/диск), помилки повернуться.
2) Який найшвидший спосіб відрізнити корінь 502 від 504?
Прочитайте рядок в помилках nginx. «Connection refused» вказує на досяжність/сервіс‑вниз. «Upstream timed out» вказує на повільний/завислий upstream або залежність. «Permission denied» — на права сокета або AppArmor.
3) Чому я бачу 504, але в логах бекенду є 200‑відповіді?
Бо бекенд продовжував працювати після того, як проксі відмовився. Таймаут проксі сплив, він закрив з’єднання, а бекенд пізніше записав відповідь у нікуди. Вирівняйте таймаути і додайте внутрішні таймаути для залежностей.
4) Чи може зберігання дійсно спричиняти 504, якщо CPU низький?
Так. Низький CPU при високому iowait — класичний підпис. Ваші потоки блокуються в ядрі, чекаючи дискових операцій. Проксі теж чекає — доки не спливе таймаут.
5) Чому Ubuntu 24.04 «раптово» почала це робити?
Сам по собі Ubuntu 24.04 зазвичай не винна; оновлення часто змінює версії та значення за замовчуванням (версії php‑fpm, юніти сервісів, профілі безпеки). Ці зміни виявляють існуючу крихкість: тісні ліміти, крихкі шляхи сокетів або запас по диску, якого ніколи не було.
6) Як зрозуміти, чи причетний AppArmor?
Шукайте AppArmor DENIED у dmesg або в системному журналі ядра. Якщо бачите відмови, що стосуються nginx, php‑fpm сокетів або файлів бекенду — трактуйте це як проблему політики, а не chmod‑задачу.
7) Чи є збільшення nginx proxy_read_timeout реальним рішенням?
Іноді — але частіше це відтермінування, а не виправлення. Збільшуйте таймаути лише якщо робота справді довга і контрольована (експорти, звіти), і лише якщо бекенд може це витримати без блокування воркерів.
8) Як відрізнити перевантаження бекенду від єдиної повільної залежності?
Перевантаження бекенду показує збільшення часу в черзі, виснаження воркерів і загальне зростання латентності. Одна повільна залежність проявляється специфічними ендпоїнтами, корелює з блокуваннями БД, кеш‑місами або затримками зовнішніх API. Використовуйте метрики по ендпоїнтах і кореляцію логів за request ID.
9) Чому 502/504 з’являються хвилями?
Тому що система коливається: трафік підтягує її до насичення, таймаути тригерять повтори, воркери перезапускаються, кеші остигають/підгріваються, черги зберігання стікають, і ви отримуєте повторюваний патерн відмов. Вирішіть вузьке місце — і коливання припиняться.
Наступні кроки, які варто зробити
Якщо ви в середині інциденту: візьміть швидкий план діагностики і пройдіть його від початку до кінця. Не «робіть трохи всього». Збирайте докази, ідентифікуйте невдалий хоп і виправляйте найвужче обмеження спочатку.
Якщо інцидент позаду і ви хочете його не повторювати:
- Зробіть зберігання видимим: сповіщайте про %util диска,
awaitта iowait. Веб‑таймаути часто починаються з дискової латентності. - Вирівняйте таймаути: переконайтеся, що LB, nginx, app server і виклики до БД мають узгоджений бюджет таймаутів.
- Задокументуйте очікування щодо сокетів і безпеки: якщо ви покладаєтесь на unix‑сокети, документуйте шляхи і права; включіть перевірки AppArmor у процеси розгортання.
- Зменшіть несподівані завдання: знайдіть періодичні резервні/лог‑завдання і переконайтеся, що вони не використовують той самий I/O‑шлях, що й критична латентність.
- Практикуйте ops, що починаються з доказів: нудна звичка збирати логи і стан системи перед рестартом — ось як позбавитися легенд про 504.
502/504 — не містики. Це проксі, що каже вам, куди дивитися. Прислухайтеся до нього, зберіть докази і виправте систему, а не симптом.