Debian 13 «Broken pipe»: коли це нешкідливо і коли — перше попередження (випадок №15)

Було корисно?

Ви дивитесь логи на хості з Debian 13 і знову це з’явилося: write: broken pipe,
EPIPE, або класичний Python BrokenPipeError: [Errno 32].
Іноді нічого й не ламається. Іноді користувачі починають оновлювати порожню сторінку, а ваша черга on-call — резюме.

«Broken pipe» — одне з тих повідомлень, що водночас буденне й дуже показове.
Воно може означати «клієнт втомився і пішов» — або «ваша система була настільки повільною, що клієнт здався, і тепер ваш беклог може обвалитися».
Навчитися швидко визначати, в якому світі ви опинилися — це трюк: за п’ять хвилин, а не за п’ятдесят.

Що насправді означає «broken pipe» в Debian 13

В Linux «broken pipe» майже завжди — це помилка EPIPE (errno 32), яку отримує процес,
що намагається записати у pipe або сокет, на іншому кінці якого вже немає читача.
Читачем може бути:

  • Інший процес у shell‑пайплайні (producer | consumer), який вийшов раніше.
  • Віддалений TCP‑пір, що закрив з’єднання (іноді ввічливо через FIN, іноді грубо через RST).
  • Проксі (Nginx, HAProxy), який закрив upstream або downstream під час вашого запису.
  • SSH‑клієнт, який зник через Wi‑Fi, режим сну або таймаути простою.

Поведінка ядра важлива: для пайпів і сокетів запис у від’єднаного піра зазвичай викликає
SIGPIPE. Багато програм не хочуть раптово помирати, тому ігнорують або обробляють SIGPIPE,
а потім бачать помилку EPIPE від write(2) / send(2).
Тому ви й бачите повідомлення на кшталт:

  • write() failed (32: Broken pipe)
  • sendfile() failed (32: Broken pipe)
  • BrokenPipeError: [Errno 32] Broken pipe

Тонкість: «broken pipe» — не корінна причина. Це симптом нижчого рівня.
Корінна причина — «читач пішов», і читач пішов не просто так. Інколи це нормальна поведінка.
Інколи — це перший дзвінок про падіння продуктивності, яке ось‑ось стане інцидентом.

Цитата, яку варто запам’ятати під час триажу: «Сподівання — це не стратегія.» — генерал Гордон Р. Салліван.
«Broken pipe» каже вам: припиніть сподіватися і почніть вимірювати.

Де ви побачите це в Debian 13

Debian 13 базується на systemd, тому головний свідок — journald. Ви побачите EPIPE у:

  • Логах додатків (Python, Go, Java, Node, Rust), що пишуть у сокети або stdout/stderr.
  • Логах веб‑серверів (Nginx/Apache), які скаржаться на клієнтів або upstream.
  • SSH‑сесіях (логи з боку сервера, попередження на боці клієнта).
  • Shell‑скриптах з пайплайнами, де одна сторона виходить раніше (head, grep -m).
  • Інструментах для бекапу/переносу (rsync, tar over ssh, curl uploads), коли піри закривають з’єднання.

Само повідомлення часто точне, але не дуже корисне. Ваше завдання — вирішити:
це очікуване відключення, чи симптом затримки, скидання, перевантаження або неправильної конфігурації?

Швидкий план діагностики (перший/другий/третій)

Коли «broken pipe» починає з’являтися і хтось питає «це погано?», не сперечайтеся. Виділіть час.
Ось найшвидший шлях до відповіді, яка витримає перевірку у постмортемі.

Перший крок: підтвердити межі та радіус ураження (2 хвилини)

  • Це один хост чи багато?
  • Це один сервіс чи кожен сервіс, що працює по TCP?
  • Чи корелюється це з помилками видимими для користувача (5xx, таймаути) чи це лише шум у логах?

Якщо це один гучний додаток, а користувачі в порядку — швидше за все нешкідливо (але логування треба виправити).
Якщо це кілька сервісів або весь флот — трактуйте як попередження про системну затримку або мережеві проблеми.

Другий: вирішіть, чи це нетерплячість клієнта чи проблеми сервера (5 хвилин)

  • Перевірте, чи є таймаути, зростання черг або підвищена латентність запитів у той же час.
  • Шукайте TCP‑сброси, повторні передачі або різке створення/закриття з’єднань.
  • Шукайте затримки сховища, якщо ваш сервіс пише логи, завантажує або стріміть дані.

Третій: ізолюйте лінк, що падає (10 хвилин)

  • Відключення на боці клієнта (браузер закритий, мобільна мережа, idle timeout балансувальника).
  • Поведінка проксі (Nginx buffering, upstream keepalive, HTTP/2 stream resets).
  • Пресія ядра/ресурсів (OOM, CPU steal, черга сокетів, дескриптори файлів).
  • Затримки сховища (journald sync, fsync‑шторм, повільні диски), що викликають затримки обробки запитів.

Ваша мета не «зробити помилку зникнути». Ваша мета відповісти: хто закрив першим, і чому?

Нешкідливий шум чи перше попередження: як відрізнити

Зазвичай нешкідливі шаблони

Це поширені та часто нормальні випадки:

  • Інтерактивні SSH‑сесії: ноутбук «уснув», Wi‑Fi переключився, NAT‑запис протух. Сервер може показати broken pipe, коли намагається писати в «мертву» сесію.
  • Пайплайни: yes | head, journalctl | grep -m 1. Споживач виходить раніше; продюсер скаржиться.
  • Клієнти, що відміняють завантаження: користувач пішов зі сторінки; сервер ще надсилає і отримує EPIPE.
  • Перевірки здоров’я та проби: деякі проби підключаються і швидко кидають з’єднання, особливо при помилковій конфігурації.

У таких випадках «виправлення» зазвичай — приглушити шум у логах, правильно обробляти SIGPIPE або налаштувати таймаути.
Не ганяйтесь за примарами о 3 ранку.

Сигнали тривоги (варто звернути увагу)

Тепер про неприємні випадки:

  • Різкий спайк у кількох сервісах: часто мережева нестабільність, зміни в проксі або загальна затримка спільної залежності.
  • Broken pipe у парі з таймаутами / 499 / 502 / 504: класичний випадок «клієнт здався», поки сервер ще працював.
  • Broken pipe під час завантажень/стрімів: може вказувати на проблеми з MTU, втратою пакетів, сбросами або обмеженнями балансувальника.
  • Кореляція з CPU iowait або затримками диска: сервер повільний; клієнти відключаються; ви бачите EPIPE під час запису відповіді чи логів.
  • З’явилося після «оптимізації»: зміни буферування, keepalive, агресивні таймаути або перемикання TCP offload.

Межа між нешкідливим і серйозним зазвичай — це кореляція.
Самі по собі broken pipe не завдають шкоди; шкодять умови, що їх породили.

Жарт №1: Broken pipe — це спосіб операційної системи сказати «вони поклали трубку». Це фактично поліфантом з боку ядра.

Цікаві факти та історичний контекст

  • «Broken pipe» старший за TCP: походить із ранніх Unix‑пайплайнів, де один процес писав, а інший читав.
  • SIGPIPE існує, щоб зупинити безконтрольних записувачів: без нього програма могла б писати в нікуди нескінченно, марнуючи CPU.
  • HTTP статус 499 — підказка: Nginx використовує 499 для «клієнт закрив запит», що часто супроводжується broken pipe на сервері.
  • EPIPE vs ECONNRESET — тонка різниця: EPIPE зазвичай означає «ви писали після закриття піра»; ECONNRESET часто означає «пір скинув звʼязок різко». Обидві помилки можуть з’являтися в залежності від таймінгу.
  • Проксі підсилюють симптом: один нетерплячий клієнт за проксі може створити серверні broken pipe, що виглядають як upstream‑проблема.
  • TCP keepalive не універсальне рішення: воно повільно виявляє мертві піри за замовчуванням (години), і багато збоїв — «живі, але недосяжні» через stateful middlebox.
  • Бафери pipe в Linux змінювалися з часом: більші, динамічні буфери зменшують конкуренцію, але не усувають SIGPIPE/EPIPE, коли читачі зникають.
  • Journald може бути частиною проблеми: якщо логування блокується через проблеми з диском, додатки можуть зависнути настільки, що піри відключаться і породять broken pipe.
  • «Broken pipe» може бути успішним кейсом: інструменти на кшталт head навмисно закриваються раніше. Помилка продюсера — очікувана і часто ігнорується.

Практичні задачі: команди, виводи, рішення (12+)

Це реальні кроки. Кожна задача включає команду, що типовий вивід означає, і рішення, яке ви приймаєте.
Виконуйте їх на Debian 13 від root або з sudo, де потрібно.

Задача 1: Знайти точні сервіси, що видають «broken pipe» (journald)

cr0x@server:~$ sudo journalctl -S -2h | grep -i -E 'broken pipe|EPIPE|SIGPIPE' | head -n 20
Dec 30 08:12:41 api-01 gunicorn[2198]: BrokenPipeError: [Errno 32] Broken pipe
Dec 30 08:12:42 web-01 nginx[1120]: *18452 sendfile() failed (32: Broken pipe) while sending response to client
Dec 30 08:12:43 api-01 gunicorn[2198]: Ignoring EPIPE in worker

Що це означає: у вас щонайменше два джерела: Gunicorn (рівень додатка) і Nginx (edge/проксі).
Рішення: пріоритезуйте шар, що ближчий до користувачів (Nginx). Якщо Nginx ламається — це може бути клієнти, що кидають зʼєднання, або upstream, що зависає.

Задача 2: Кількісно оцінити спайк у часі (швидка гістограма)

cr0x@server:~$ sudo journalctl -S -2h -o short-iso | grep -i 'broken pipe' | awk '{print substr($1,1,16)}' | sort | uniq -c | tail
   12 2025-12-30T08:05
   18 2025-12-30T08:06
   61 2025-12-30T08:07
   59 2025-12-30T08:08
   14 2025-12-30T08:09

Що це означає: сплеск між 08:07–08:08. Це не випадковий фоновий шум.
Рішення: зв’яжіть цей інтервал з навантаженням, деплоєм, мережевими подіями або затримками сховища.

Задача 3: Перевірити Nginx на клієнт‑закриті запити (499) та проблеми upstream (5xx)

cr0x@server:~$ sudo awk '$9 ~ /^(499|502|504)$/ {print $4, $9, $7}' /var/log/nginx/access.log | tail -n 10
[30/Dec/2025:08:07:12 499 /api/v1/report
[30/Dec/2025:08:07:13 499 /api/v1/report
[30/Dec/2025:08:07:15 504 /api/v1/report
[30/Dec/2025:08:07:18 502 /api/v1/report

Що це означає: клієнти закривають (499) і також є gateway‑помилки (502/504).
Рішення: розглядайте це як реальну проблему. Далі — розслідуйте затримки upstream і мережеві скидання. 499 часто йдуть слідом за повільним upstream.

Задача 4: Подивитися контекст помилок Nginx навколо EPIPE

cr0x@server:~$ sudo grep -n -i 'broken pipe' /var/log/nginx/error.log | tail -n 5
183271:2025/12/30 08:07:12 [info] 1120#1120: *18452 sendfile() failed (32: Broken pipe) while sending response to client, client: 203.0.113.41, server: _, request: "GET /api/v1/report HTTP/1.1", host: "example"
183289:2025/12/30 08:07:18 [error] 1120#1120: *18488 upstream prematurely closed connection while reading response header from upstream, client: 203.0.113.55, server: _, request: "GET /api/v1/report HTTP/1.1", upstream: "http://10.0.2.20:8000/api/v1/report"

Що це означає: у вас одночасно downstream broken pipe (клієнт пішов) і upstream‑нестабільність (upstream закрив раніше).
Рішення: йдіть upstream: перевірте здоров’я app server, таймаути, падіння воркерів або ресурсний тиск.

Задача 5: Перевірити завантаження системи та iowait (чи це історія про зависання?)

cr0x@server:~$ uptime
 08:10:22 up 41 days,  6:12,  2 users,  load average: 12.48, 11.97, 10.55
cr0x@server:~$ mpstat 1 5
Linux 6.12.0-amd64 (api-01)  12/30/2025  _x86_64_  (16 CPU)

08:10:27 AM  all  %usr %nice %sys %iowait %irq %soft %steal %idle
08:10:28 AM  all  22.1  0.0  9.8   31.4  0.0   1.2   0.0  35.5

Що це означає: високе завантаження з великим iowait. CPU чекають на сховище.
Рішення: перестаньте звинувачувати «мережу», поки не виключите затримки диска та проблеми з логуванням/fsync.

Задача 6: Визначити найбільших споживачів I/O (по процесах)

cr0x@server:~$ sudo iotop -o -b -n 3
Total DISK READ: 8.42 M/s | Total DISK WRITE: 52.11 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
 2198 be/4   www-data  0.00 B/s   9.14 M/s    0.00 %  42.13 % gunicorn: worker [api]
  786 be/3   root      0.00 B/s   7.32 M/s    0.00 %  18.55 % systemd-journald
 3011 be/4   postgres  0.00 B/s  16.44 M/s    0.00 %  22.01 % postgres: checkpointer

Що це означає: journald і чекпойнтер БД інтенсивно пишуть; додаток теж пише.
Рішення: корелюйте з піками; розгляньте обмеження обсягу логів, переміщення логів на швидше сховище або налаштування чекпойнтів БД.

Задача 7: Перевірити затримку диска та насичення

cr0x@server:~$ iostat -xz 1 3
Device            r/s     w/s   rMB/s   wMB/s  avgrq-sz  avgqu-sz await  r_await  w_await  %util
nvme0n1          9.20   421.3    0.36   52.10     252.1     18.44  41.2     8.7     42.1   99.2

Що це означає: пристрій завантажений ~99% з 40ms write await — не катастрофа, але достатньо, щоб підштовхнути латентність запиту понад таймаути клієнта/проксі.
Рішення: вважайте сховище головним підозрюваним. «Broken pipe» може бути першим видимим симптомом повільних записів.

Задача 8: Підтвердити, чи відбуваються TCP‑сброси (лічильники ядра)

cr0x@server:~$ nstat -az | egrep 'TcpExtTCPRcvCoalesce|TcpExtListenOverflows|TcpExtListenDrops|TcpAbortOnTimeout|TcpAbortOnData|TcpEstabResets|TcpOutRsts'
TcpExtListenOverflows        0
TcpExtListenDrops            0
TcpAbortOnTimeout            37
TcpAbortOnData               0
TcpEstabResets               91
TcpOutRsts                   148

Що це означає: aborts on timeout і resets ненульові і можуть зростати.
Рішення: якщо ці лічильники зростають у той же інтервал, що і broken pipe — досліджуйте мережевий шлях і таймаути в додатку. Перевірте також conntrack/поведінку балансувальника.

Задача 9: Інспектувати активні з’єднання і churn

cr0x@server:~$ ss -s
Total: 2817 (kernel 0)
TCP:   2149 (estab 327, closed 1571, orphaned 0, timewait 1418)

Transport Total     IP        IPv6
RAW       0         0         0
UDP       19        14        5
TCP       578       438       140
INET      597       452       145
FRAG      0         0         0

Що це означає: багато closed/timewait з’єднань; це churn. Не обов’язково неправильно, але це «смак», якщо він різко підскочив.
Рішення: якщо очікували keepalive/reuse, а бачите зараз churn — перегляньте налаштування keepalive проксі, поведінку клієнтів і недавні зміни TLS/HTTP2.

Задача 10: Перевірити тиск на дескриптори файлів (класичний мовчазний вбивця)

cr0x@server:~$ cat /proc/sys/fs/file-nr
4128	0	9223372036854775807
cr0x@server:~$ sudo lsof -p 1120 2>/dev/null | wc -l
1832

Що це означає: глобального виснаження FD немає, але у Nginx багато відкритих. Якщо ліміти на процес низькі — ви б побачили помилки accept.
Рішення: якщо бачите too many open files разом з EPIPE — виправте ulimits і повторне використання з’єднань. Якщо ні — рухайтесь далі.

Задача 11: Підтвердити, що EPIPE у shell‑пайплайні — просто пайплайн

cr0x@server:~$ yes "spam" | head -n 1
spam
yes: standard output: Broken pipe

Що це означає: повністю нормальна ситуація: head виходить після одного рядка, yes продовжує писати і отримує EPIPE.
Рішення: якщо ваш моніторинг сповіщає про цей вивід як «інцидент», виправте моніторинг. Не виправляйте Unix.

Задача 12: Налагодження SSH «broken pipe» (keepalive та таймаути простою)

cr0x@server:~$ grep -n -E 'ClientAliveInterval|ClientAliveCountMax' /etc/ssh/sshd_config
124:ClientAliveInterval 60
125:ClientAliveCountMax 3
cr0x@server:~$ sudo journalctl -u ssh -S -2h | tail -n 5
Dec 30 08:02:10 bastion-01 sshd[18812]: packet_write_wait: Connection to 198.51.100.23 port 53722: Broken pipe

Що це означає: сервер намагається писати в мертвий клієнт. Є налаштування keepalive; middlebox-и все одно можуть скидати idle‑сесії.
Рішення: якщо SSH бізнес‑критичний — налаштуйте і серверні ClientAlive*, і клієнтські ServerAliveInterval. Якщо це поодинокі випадки — прийміть як фізику.

Задача 13: Відстежити процес, що кидає SIGPIPE/EPIPE (strace на PID)

cr0x@server:~$ sudo strace -p 2198 -f -e trace=write,sendto,sendmsg -s 80
strace: Process 2198 attached
sendto(17, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n...", 4096, MSG_NOSIGNAL, NULL, 0) = -1 EPIPE (Broken pipe)

Що це означає: додаток пише відповідь і пір уже пішов. MSG_NOSIGNAL вказує, що додаток уникає SIGPIPE і обробляє EPIPE.
Рішення: визначте, чи пір — це Nginx (upstream socket) чи прямий клієнт. Далі перевірте таймаути та буферизацію upstream.

Задача 14: Перевірити kill’и ядра через OOM (що може виглядати як «upstream closed»)

cr0x@server:~$ sudo journalctl -k -S -2h | grep -i -E 'oom|killed process' | tail -n 10
Dec 30 08:07:16 api-01 kernel: Out of memory: Killed process 2241 (gunicorn) total-vm:812344kB, anon-rss:312144kB, file-rss:0kB, shmem-rss:0kB

Що це означає: ваш upstream помер, і Nginx повідомить про upstream close/reset; клієнти побачать broken pipes/таймаути.
Рішення: це причина інциденту, а не симптом. Виправляйте ліміти пам’яті, витоки або конкурентність. Не налаштовуйте Nginx, щоб «приховати» проблему.

Задача 15: Перевірити навантаження journald і обмеження швидкості

cr0x@server:~$ sudo journalctl --disk-usage
Archived and active journals take up 3.8G in the file system.
cr0x@server:~$ sudo grep -n -E 'RateLimitIntervalSec|RateLimitBurst|SystemMaxUse' /etc/systemd/journald.conf
19:RateLimitIntervalSec=30s
20:RateLimitBurst=10000
33:SystemMaxUse=2G

Що це означає: використання диска journald перевищує налаштований максимум (можливо, конфіг не перезавантажено або є кілька місць з журналами).
Рішення: якщо journald трясе диск — зменште гучність логів, підправте ліміти і перезапустіть journald у вікні технічного обслуговування. Логування не повинно ламати API.

Три корпоративні міні-історії з практики

Міні-історія 1: Інцидент через хибне припущення («broken pipe означає, що клієнт ненадійний»)

Середня компанія мала флот Debian за Nginx. Їхня playbook на on‑call трактував «broken pipe» як «мобільні користувачі — мобільні».
Це в основному працювало для їхнього споживчого додатка — поки вони не запустили партнерський API, яким користувалися бекенд‑сервіси, а не люди.

В один вівторок логи засвітилися broken pipe. On‑call знизав плечима: «партнери мають поганий мережевий звʼязок».
Але їхній партнер не був у ліфті з телефоном. Це був сервіс у дата‑центрі, що дзвонив по приватному інтерконекту.
Партнер відкрив тикет: «таймаути, часткові відповіді».

Справжня проблема була в їхньому upstream: нова фіча додала синхронну генерацію PDF у шлях запиту.
Під навантаженням CPU підскакував, записи на диск зростали (тимчасові файли), і латентність перевищила 10‑секундний таймаут партнера.
Партнер закривав з’єднання; Nginx продовжував намагатися писати і повідомляв EPIPE.

Висновок постмортему: «клієнт закрив» — це не виправдання; це підказка.
Якщо певний клас клієнтів раптом «стає ненадійним», сервер або змінився, або сповільнився настільки, що мережа тепер виглядає джерелом проблем.

Виправлення: перемістили генерацію PDF у бекґраундну чергу, повертали job ID і звузили таймаути Nginx, щоб помилки фейлили швидко з явними повідомленнями замість повільного зростання broken pipe.

Міні-історія 2: Оптимізація, що обернулася проти (буферизація і таймаути)

Інша команда хотіла зменшити латентність стрімінгових відповідей. Вони зменшили proxy buffering в Nginx і збільшили повторне використання keepalive.
На папері це означало «менше пам’яті, швидше перший байт, менше з’єднань». На практиці — «більша вразливість до повільного upstream».

При зниженому буферуванні Nginx почав форвардити upstream‑чанки клієнтам негайно.
Коли upstream‑обробники застряли посеред відповіді (запит до БД чекає на лок), клієнти сиділи з відкритим сокетом.
Їхній балансувальник мав idle‑таймаут коротший за повільні відповіді.

Тож LB закрив клієнтське з’єднання. Nginx спробував продовжити відправку і логував broken pipes.
Гірше: upstream все ще виконувалось, витрачало CPU і тримало з’єднання до БД, хоча клієнт уже пішов.
Вони фактично платили повну ціну за запити, яких ніхто не побачить.

Перша реакція команди — підвищити таймаути повсюдно. Це втихомирило логи «broken pipe», але зробило платформу більш крихкою:
повільні запити жили довше, черги росли, tail latency погіршився. Вони поміняли гучний симптом на більший радіус ураження.

Виправлення: повернули розумне буферування для цього endpoint, додали серверні скасування де можливо,
і навмисно вирівняли таймаути (клієнт < LB < Nginx < upstream) з чіткими бюджетами.
«Broken pipe» впав, бо система припинила виконувати марну роботу після того, як клієнт пішов.

Міні-історія 3: Скучно правильна практика, що врятувала ситуацію (кореляція і базові зрізи)

Доросла організація мала просту звичку: кожна передача черги on‑call включала «тижневий базовий зріз» латентності, рівня помилок,
TCP‑скидань і дискового await. Без наворотів, без ML. Просто відомий‑хороший стан.

Коли в четвер під вечір broken pipes підскочили, вони не сперечалися про «нормально/ненормально».
Вони відкрили базу: TCP‑скидання були стабільні, але дисковий await подвоївся і пропускна здатність journald зросла.
Це відразу перекваліфікувало інцидент з «мережевого» на «сховище/логування».

Виявилось, випадково включений debug‑лог на гарячому endpoint у production.
Це збільшило обсяг логів настільки, що диск був насичений на хвилини. Обробники запитів зависали на записі логів,
клієнти таймаутилися, і Nginx бачив broken pipes.

Виправлення було банальним: вимкнули debug, обмежили вибухи логів і відправили вербозні логи на виділений вузол при потребі.
Вони також мали затверджену зміну для тимчасового збільшення IOPS для лог‑розділу у віртуалізованому сховищі, яку використали як тимчасовий клапан.

Без героїчних дій, без «перезапустити все». Просто швидка правильна діагностика, бо вони знали, як виглядає «нормально».

Типові помилки: симптом → корінна причина → виправлення

1) «Broken pipe» в Nginx error.log під час завантажень

  • Симптом: sendfile() failed (32: Broken pipe) while sending response to client
  • Корінна причина: клієнт закрив з’єднання (перехід зі сторінки, відміна, таймаут LB).
  • Виправлення: вважати як інформаційне повідомлення, якщо не корелює з 499/5xx. Якщо гучно — понизити рівень логування для цього повідомлення або налаштувати семплінг access‑логів. Не відключайте корисні логи глобально.

2) Spike EPIPE після ввімкнення gzip або великих відповідей

  • Симптом: більше EPIPE на endpoint‑ах з великими payload, плюс підвищений час відповіді.
  • Корінна причина: CPU насичення або повільний upstream збільшують час до останнього байта; клієнти/LB здаються.
  • Виправлення: бенчмаркувати компресію, обмежити розмір payload, використовувати кешування або виносити важку генерацію з запиту. Вирівняти таймаути і розглянути буферизацію.

3) «upstream prematurely closed connection» плюс broken pipe

  • Симптом: Nginx показує upstream закрив раніше; клієнти бачать 502; логи також показують broken pipe.
  • Корінна причина: upstream‑додаток впав/перезапустився, отримав OOM‑kill або hit‑нув внутрішній таймаут і закрив сокет.
  • Виправлення: перевірте OOM‑логи ядра, логи падінь додатку і рестарти процесів. Виправляйте пам’ять, конкурентність та health checks. Не маскуйте це довшими proxy‑таймаутами.

4) SSH‑сесії часто закінчуються «Broken pipe»

  • Симптом: в логах сервера packet_write_wait ... Broken pipe, користувачі скаржаться на відключення.
  • Корінна причина: idle‑таймаути в NAT/LB/фаєрволі, сон ноутбука, нестабільний Wi‑Fi.
  • Виправлення: налаштуйте клієнтський ServerAliveInterval і серверний ClientAliveInterval. Якщо за балансувальником/бастіоном — вирівняйте таймаути простою.

5) Python‑додаток падає з BrokenPipeError під час запису в stdout

  • Симптом: додаток виходить під час логування або print; trace закінчується BrokenPipeError.
  • Корінна причина: stdout прив’язаний до процеса, що впав (log shipper crash, head або закритий pipe менеджером сервісів).
  • Виправлення: обробляйте SIGPIPE/EPIPE, використовуйте належні лог‑хендлери і робіть лог‑колектори більш стійкими. У systemd‑сервісах розгляньте логування в journald напряму замість крихких пайпів.

6) «Broken pipe» під час rsync/tar через ssh

  • Симптом: передача зупиняється; rsync повідомляє broken pipe; залишаються часткові файли.
  • Корінна причина: мережеве переривання, mismatch keepalive SSH, або віддалений диск завис і спричинив таймаут та відключення.
  • Виправлення: використовуйте keepalive, опції rsync для відновлення, перевіряйте затримку сховища на обох кінцях. Уникайте великих одиночних файлів без можливості resume.

7) Багато broken pipes під час деплою, але лише хвилину

  • Симптом: короткий шторм EPIPE навколо часу деплою.
  • Корінна причина: рестарт upstream закриває keepalive‑з’єднання посеред запиту; клієнти бачать disconnect.
  • Виправлення: робіть graceful reload, заливайте зливання з’єднань, налаштовуйте readiness checks і розбивайте рестарти. Нехай проксі знає про стан upstream.

8) Broken pipes збігаються з високим iowait

  • Симптом: EPIPE спайки, зростання 499, високий iowait, %util диска близько 100.
  • Корінна причина: затримки сховища затримують обробку запитів; клієнти таймаутять і відключаються; сервер після цього пише в закриті сокети.
  • Виправлення: зменшити синхронні записи в шляхах обробки запитів, перемістити логи на швидше сховище, налаштувати чекпойнти БД і усунути вузьке місце на диску.

Жарт №2: Якщо ви «виправляєте» broken pipe, заглушивши логи, ви не виправили трубу — ви просто забрали її можливість поскаржитися.

Чеклісти / поетапний план

План триажу крок за кроком (15–30 хвилин)

  1. Підтвердьте, що це реальна проблема: корелюйте EPIPE повідомлення з метриками для користувача (HTTP 5xx, латентність, таймаути). Якщо нічого не корелює — вважайте як шум і заплануйте прибирання логів.
  2. Визначте шар: Nginx/Apache vs app server vs SSH vs скрипти. Різні причини — різні виправлення.
  3. Класифікуйте напрямок: downstream (клієнт пішов) vs upstream (бекенд пішов). Логи Nginx зазвичай підкажуть.
  4. Перевірте вирівнювання таймаутів: таймаут клієнта < idle LB < proxy read/send < upstream. Невідповідності викликають churn і broken pipes.
  5. Перевірте ресурсний тиск: насичення CPU, iowait, OOM‑kill, ліміти FD, проблеми з backlog сокетів.
  6. Перевірте мережеві симптоми: скидання, повторні передачі, conntrack‑тиск, невідповідності MTU (якщо локалізовано по шляхах).
  7. Перевірте затримки сховища: iostat await/%util, iotop топ писарів, дискове використання journald, шторм чекпойнтів БД.
  8. Підтвердіть трасуванням: короткий strace на процесі або tcpdump у вузькому вікні, якщо потрібно (обережно у продакшні).
  9. Застосовуйте одну зміну за раз: таймаути, буферизація, обсяг логів або масштабування. Потім перезважуйте.

Жорсткі правила для продакшн‑систем

  • Ніколи не вважайте «клієнт закрив» автоматично «не наша проблема», поки не перевірите, чи клієнт не закрив через вашу латентність.
  • Не збільшуйте таймаути, щоб приховати симптоми. Довші таймаути часто збільшують конкуренцію і розширюють області відмов.
  • Логування — це навантаження. Якщо ви не можете дозволити обсяг логів у найгірший день — ви не можете дозволити їх взагалі.
  • Знайте свої бюджети. Якщо LB кидає idle зʼєднання на 60s, не дозволяйте upstream запитам тривати 75s і називати це «стійкістю».

FAQ

1) Чи «broken pipe» завжди помилка?

Ні. У пайплайнах це часто очікувана ситуація. У мережевих сервісах — це симптом того, що пір закрився; наскільки це погано — залежить від кореляції з помилками і латентністю.

2) У чому різниця між EPIPE і ECONNRESET?

EPIPE зазвичай означає, що ви писали після того, як пір закрився. ECONNRESET часто означає, що пір послав TCP RST, різко зупинивши зʼєднання. Обидва можуть зʼявлятися в подібних сценаріях; все вирішує таймінг.

3) Чому я бачу «broken pipe», коли користувачі скасовують завантаження?

Сервер продовжує писати відповідь, поки не помітить, що клієнт пішов. Наступний запис завершується помилкою EPIPE. Це нормально для великих завантажень і стрімів.

4) Nginx показує 499 і broken pipe. Хто винен?

499 означає, що клієнт закрив запит. Але клієнти закривають через причини: повільний upstream, таймаути LB, мобільні мережі. Якщо 499 ростуть разом з латентністю upstream — вважайте це проблемою на боці сервера.

5) Чи може затримка сховища справді спричинити broken pipe на веб‑сервері?

Так. Якщо обробники запитів блокуються на диску (логування, тимчасові файли, записи в БД), латентність відповідей зростає. Клієнти і проксі таймаутять і закривають з’єднання. Ваш наступний запис вдаряє по EPIPE.

6) Чому бачу broken pipe в SSH, хоча сервер нормальний?

SSH‑сесії вмирають, коли NAT/фаєрволи скидають стан простою або ноутбук спить. Сервер дізнається про це тільки при записі, коли отримує EPIPE. Keepalive допомагають, але не здатні обійти політики всіх middlebox‑ів.

7) Чи варто ігнорувати SIGPIPE у моєму додатку?

Часто так, але свідомо. Багато мережевих серверів вимикають SIGPIPE і обробляють EPIPE помилки. Правильна стратегія залежить від мови/рантайму і того, чи можна безпечно робити повторну спробу.

8) Ми змінили keepalive і тепер broken pipe збільшився. Чому?

Keepalive може збільшити повторне використання з’єднань, але також підвищує ймовірність того, що ви писатимете у з’єднання, яке middlebox мовчки вбив. Якщо пір зник без чистого закриття, ваш наступний запис виявить проблему.

9) Як зупинити заповнення логів broken pipe, не втративши корисні сигнали?

Зменшіть рівень логування для очікуваних відключень на краю (наприклад, часті відміни клієнтів), але зберігайте метрики і лічильники помилок. Мета — співвідношення сигнал/шум, а не сліпота.

10) Чи змінює Debian 13 поведінку broken pipe?

Основна семантика — на рівні ядра і довго існуюча. Те, що змінюється на практиці — це ваш стек: новіші systemd/journald, новіші ядра і дефолти у сервісах як OpenSSH та пакети Nginx.

Висновок: наступні реальні кроки

«Broken pipe» в Debian 13 — це ліхтарик, а не вирок. Іноді він освітлює банальну істину: клієнт пішов, пайп закінчився, життя триває.
Іноді це перша видима тріщина від глибшої проблеми: затримки сховища, невирівняні таймаути, падіння upstream або мережевий churn.

Наступні кроки, що приносять швидкий ефект:

  1. Вибудуйте звичку швидкої кореляції: спайки broken pipe завжди перевіряйте проти латентності та 499/5xx.
  2. Навмисно вирівняйте таймаути між клієнтом/LB/проксі/upstream і задокументуйте їх.
  3. Коли бачите broken pipe під навантаженням — виміряйте затримки сховища (await/%util); не припускайте, що це «мережа».
  4. Впровадьте логування як частину продуктивності: обмежуйте debug‑логи, слідкуйте за здоровʼям journald і уникайте синхронних записів у гарячих шляхах.
  5. Зберігайте один «зріз відомо‑хорошого» для основних сервісів. Він перетворює невизначену підозру на швидку діагностику.
← Попередня
Proxmox «failed to start pve-ha-lrm»: чому HA не запускається і що перевірити
Наступна →
Proxmox не завантажується після оновлення ядра: правильне відкатування через GRUB

Залишити коментар