ftops.space
83 subscribers
677 photos
98 videos
33 files
432 links
ftops.space
Сервера на linux, FreeBSD, сети на микротик и сиськах.
Download Telegram
03:00. Учебный постмортем: панель объявила OOM по exit 137, дежурный предложил поднять memory limit. Но 137 совместим с SIGKILL и сам по себе не устанавливает причину. Проверяем journalctl -k --since '20 min ago', журнал kubelet и прирост oomkill в memory.events именно cgroup пострадавшего контейнера за окно аварии. В сценарии OOM-событий нет, зато kubelet зафиксировал провал liveness и принудительное завершение после grace period. Семантика счётчиков: [документация ядра](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html). На клиенте и сервере одновременно запускаем tcpdump -ni eth0 -nn -vvv 'tcp port 443 or icmp', подставив рабочий интерфейс. Клиент отправляет SYN повторно, сервер не видит его вовсе. Это локализует потерю между точками захвата, но ещё не доказывает петлю. Снимаем дельту TcpRetransSegs через nstat -az TcpRetransSegs: ретрансляции подтверждают симптом, а не его причину. Дальше traceroute -n -T -p 443 SERVERIP с клиента и отдельная трассировка обратно. ...
03:00. Учебный постмортем: панель объявила OOM по exit 137, дежурный предложил поднять memory limit. Но 137 совместим с SIGKILL и сам по себе не устанавливает причину. Проверяем journalctl -k --since '20 min ago', журнал kubelet и прирост oom_kill в memory.events именно cgroup пострадавшего контейнера за окно аварии. В сценарии OOM-событий нет, зато kubelet зафиксировал провал liveness и принудительное завершение после grace period. Семантика счётчиков: [документация ядра](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html). На клиенте и сервере одновременно запускаем tcpdump -ni eth0 -nn -vvv 'tcp port 443 or icmp', подставив рабочий интерфейс. Клиент отправляет SYN повторно, сервер не видит его вовсе. Это локализует потерю между точками захвата, но ещё не доказывает петлю. Снимаем дельту TcpRetransSegs через nstat -az TcpRetransSegs: ретрансляции подтверждают симптом, а не его причину. Дальше traceroute -n -T -p 443 SERVER_IP с клиента и отдельная трассировка обратно. Повторяющиеся хопы — улика; на подозрительных маршрутизаторах ловим возврат одного IP-пакета с убывающим TTL. Проверяем ip route get SERVER_IP, нужную таблицу FIB и BGP next-hop на обоих узлах. Только сопоставление таблиц объясняет, почему пересылка замкнулась: слово BGP в тикете ничего не доказывает. Параметры TCP-проб: [traceroute](https://www.man7.org/linux/man-pages/man8/traceroute.8.html). Исправление в этом сценарии — устранить взаимную пересылку для проблемного префикса, затем подтвердить доставку SYN/SYN-ACK с обеих сторон и восстановление probes. Сетевая петля может довести сервис и до настоящего OOM через накопление очередей; тогда это отдельное подтверждённое звено отказа. Автомасштабирование до проверки маршрута просто размножает платные таймауты. Эксплуатационный принцип https://ftops.space: сначала доказательства, потом дополнительные гигабайты.
ZFS не тормозил. Его просто не измеряли

Мониторинг кричал о задержках диска на FreeBSD-шлюзе. Железо было исправно, ошибок в пуле не было. Настоящая причина сидела выше: ARC разросся под рабочей нагрузкой, полезные метаданные начали вытесняться, а jails синхронно получили паузы на повторном чтении с накопителей.

Криминалистика начинается не с покупки NVMe. Сначала смотрят zpool status -v, zpool iostat -v 1 и sysctl kstat.zfs.misc.arcstats. Если размер ARC, промахи и состав кэша не сопоставлены с реальным профилем I/O, любой график latency рассказывает только половину истории.

FreeBSD остается эталоном для сетевых шлюзов и надежного хранения не из-за UNIX-ностальгии. ZFS, jails и прозрачные системные счетчики дают оператору главное: предсказуемость отказа. Но стабильность появляется только там, где ARC ограничен осознанно, пул регулярно проверяется, а деградацию замечают до того, как она становится аварией.

Облако продает лишнюю память как лекарство от незнания. На своем железе диаг...
ZFS не тормозил. Его просто не измеряли

Мониторинг кричал о задержках диска на FreeBSD-шлюзе. Железо было исправно, ошибок в пуле не было. Настоящая причина сидела выше: ARC разросся под рабочей нагрузкой, полезные метаданные начали вытесняться, а jails синхронно получили паузы на повторном чтении с накопителей.

Криминалистика начинается не с покупки NVMe. Сначала смотрят zpool status -v, zpool iostat -v 1 и sysctl kstat.zfs.misc.arcstats. Если размер ARC, промахи и состав кэша не сопоставлены с реальным профилем I/O, любой график latency рассказывает только половину истории.

FreeBSD остается эталоном для сетевых шлюзов и надежного хранения не из-за UNIX-ностальгии. ZFS, jails и прозрачные системные счетчики дают оператору главное: предсказуемость отказа. Но стабильность появляется только там, где ARC ограничен осознанно, пул регулярно проверяется, а деградацию замечают до того, как она становится аварией.

Облако продает лишнюю память как лекарство от незнания. На своем железе диагноз дешевле: измерить кэш, проверить пул, отделить задержку ZFS от задержки устройства. Неизмеренная память не является запасом надежности.

https://ftops.space

#ftops #devops #linux #freebsd #sre #инфраструктура
Мониторинг кричал: apiserver недоступен, S3 отвечает медленно. Дежурный проверил не легенду, а железо: cat /proc/pressure/io, iostat -xz 1, dmesg -T \| tail -100, ss -lntp \| egrep ':2379\|:2380'. I/O pressure не рос, TCP не терялся. Сломался не транспорт — нарушили процедуру membership. На одной server-ноде K3s остановили и выполнили: k3s server --cluster-reset --cluster-reset-restore-path\=<snapshot-name> --etcd-s3 --etcd-s3-bucket\=<bucket> .... Для нового хоста нужен исходный --token: им расшифровываются bootstrap-данные. S3 Secret из Kubernetes при restore недоступен — API ещё мёртв, поэтому параметры S3 подаются через CLI или конфиг на диске. После сообщения о завершённом reset запустили только первый server и проверили curl -sf http://127.0.0.1:2381/metrics \| egrep 'etcdserverhasleader\|etcddiskwalfsyncdurationseconds'. На остальных server-нодах при остановленном K3s архивировали и удалили из рабочего пути /var/lib/rancher/k3s/server/db, затем присоединили их заново. Ст...
Мониторинг кричал: apiserver недоступен, S3 отвечает медленно. Дежурный проверил не легенду, а железо: cat /proc/pressure/io, iostat -xz 1, dmesg -T \| tail -100, ss -lntp \| egrep ':2379\|:2380'. I/O pressure не рос, TCP не терялся. Сломался не транспорт — нарушили процедуру membership. На одной server-ноде K3s остановили и выполнили: k3s server --cluster-reset --cluster-reset-restore-path\=<snapshot-name> --etcd-s3 --etcd-s3-bucket\=<bucket> .... Для нового хоста нужен исходный --token: им расшифровываются bootstrap-данные. S3 Secret из Kubernetes при restore недоступен — API ещё мёртв, поэтому параметры S3 подаются через CLI или конфиг на диске. После сообщения о завершённом reset запустили только первый server и проверили curl -sf http://127.0.0.1:2381/metrics \| egrep 'etcdserverhasleader\|etcddiskwalfsyncdurationseconds'. На остальных server-нодах при остановленном K3s архивировали и удалили из рабочего пути /var/lib/rancher/k3s/server/db, затем присоединили их заново. Старые member ID нельзя пускать в новый quorum. Снепшот в бакете — лишь сырьё. DR существует только после регулярного restore-test, отдельного хранения token и записанного порядка запуска узлов. Практика bare-metal эксплуатации: https://ftops.space
Разбор аварии: active, но мертв

В 14:07 мониторинг увидел живой frr.service и четыре BGP-сессии в состоянии Established. Пользователи в это время получали таймауты. Дежурный перезапустил FRR, затем сетевой стек. Ничего не изменилось: процесс был исправен, а маршрут — нет.

Один транзитный провайдер принял утечку более специфичного префикса, второй предпочел этот маршрут и вернул трафик первому. Получилась межоператорская петля. Пакеты ходили между двумя AS до обнуления TTL, пока systemctl is-active продолжал печатать active. Он не врал: он отвечал на бесполезный вопрос о состоянии PID.

Криминалистика началась с mtr и tcpdump, а не с рестарта. Повторяющиеся адреса транзитов показали петлю, ICMP Time Exceeded подтвердил смерть по TTL, а сравнение received-routes и advertised-routes нашло более специфичный префикс. Счетчики ip -s link были чистыми: ядро передавало пакеты ровно туда, куда велела таблица маршрутизации.

После инцидента проверку сервиса заменили проверкой функции: внешний ...
Разбор аварии: active, но мертв

В 14:07 мониторинг увидел живой frr.service и четыре BGP-сессии в состоянии Established. Пользователи в это время получали таймауты. Дежурный перезапустил FRR, затем сетевой стек. Ничего не изменилось: процесс был исправен, а маршрут — нет.

Один транзитный провайдер принял утечку более специфичного префикса, второй предпочел этот маршрут и вернул трафик первому. Получилась межоператорская петля. Пакеты ходили между двумя AS до обнуления TTL, пока systemctl is-active продолжал печатать active. Он не врал: он отвечал на бесполезный вопрос о состоянии PID.

Криминалистика началась с mtr и tcpdump, а не с рестарта. Повторяющиеся адреса транзитов показали петлю, ICMP Time Exceeded подтвердил смерть по TTL, а сравнение received-routes и advertised-routes нашло более специфичный префикс. Счетчики ip -s link были чистыми: ядро передавало пакеты ровно туда, куда велела таблица маршрутизации.

После инцидента проверку сервиса заменили проверкой функции: внешний probe до контрольных адресов, контроль AS_PATH и префиксов, лимиты max-prefix, RPKI-фильтрация и аварийное отключение дефолтного экспорта. systemctl проверяет процесс. Эксплуатация обязана проверять функцию.

https://ftops.space

#ftops #devops #linux #freebsd #sre #инфраструктура
В 03:00 мониторинг показал DiskPressure, pod eviction и провал rollout. Поверхностный диагноз — «маленький VPS». Реальность: /var/lib/rancher/k3s/agent/containerd разросся слоями образов, а ImageGC стартовал слишком поздно и не успел освободить место до eviction threshold. Проверяем не красивый процент в панели, а ноду: kubectl describe node NODE; df -hT; df -ih; sudo du -xhd2 /var/lib/rancher/k3s/agent/containerd \| sort -h; sudo k3s crictl images. Если df показывает дефицит, а du нет — ищем удалённые, но открытые файлы: sudo lsof \+L1. Смотрим, что происходило внизу: cat /proc/pressure/io; awk '\{print $3,$4,$6,$8,$10,$13\}' /proc/diskstats; journalctl -u k3s -u k3s-agent --since '-2h' \| grep -Ei 'imagegc\|diskpressure\|evict\|filesystem'. Настраиваем kubelet заранее: imageGCHighThresholdPercent и imageGCLowThresholdPercent должны оставлять реальный резерв до evictionHard, а не делить последние гигабайты. На малой ноде свободное место — эксплуатационный бюджет. Ограничивайте размер ...
В 03:00 мониторинг показал DiskPressure, pod eviction и провал rollout. Поверхностный диагноз — «маленький VPS». Реальность: /var/lib/rancher/k3s/agent/containerd разросся слоями образов, а ImageGC стартовал слишком поздно и не успел освободить место до eviction threshold. Проверяем не красивый процент в панели, а ноду: kubectl describe node NODE; df -hT; df -ih; sudo du -xhd2 /var/lib/rancher/k3s/agent/containerd \| sort -h; sudo k3s crictl images. Если df показывает дефицит, а du нет — ищем удалённые, но открытые файлы: sudo lsof \+L1. Смотрим, что происходило внизу: cat /proc/pressure/io; awk '\{print $3,$4,$6,$8,$10,$13\}' /proc/diskstats; journalctl -u k3s -u k3s-agent --since '-2h' \| grep -Ei 'imagegc\|diskpressure\|evict\|filesystem'. Настраиваем kubelet заранее: imageGCHighThresholdPercent и imageGCLowThresholdPercent должны оставлять реальный резерв до evictionHard, а не делить последние гигабайты. На малой ноде свободное место — эксплуатационный бюджет. Ограничивайте размер образов, удаляйте неиспользуемые теги, контролируйте inode и проверяйте GC после каждого обновления K3s. Практика bare-metal эксплуатации: https://ftops.space
В 03:00 S3 честно отдал snapshot, а K3s после восстановления получил зависшие контроллеры, старые объекты в informer cache и попытки прежних server-нод вернуться в quorum. Поверхностный мониторинг написал: «etcd backup corrupted». Нет. Оператор восстановил байты, но не восстановил согласованное состояние кластера. Начинать надо не с перезапуска всего подряд: k3s etcd-snapshot ls --s3, затем проверить объект и выполнить на единственном выбранном server: k3s server --cluster-reset --cluster-reset-restore-path\=<SNAPSHOT>. После успешного reset убрать этот флаг, запустить узел штатно и только потом последовательно присоединять остальные server-ноды с актуальным token. Старые каталоги /var/lib/rancher/k3s/server/db на бывших участниках нельзя считать безобидными. Во время восстановления смотреть не только journal: journalctl -u k3s -b, ss -lntp \| grep 2379, ss -s, nstat -az \| grep -E 'TcpRetransSegs\|TcpExtTCPTimeouts', cat /proc/sys/net/netfilter/nfconntrackcount и cat /proc/sys/net/n...
В 03:00 S3 честно отдал snapshot, а K3s после восстановления получил зависшие контроллеры, старые объекты в informer cache и попытки прежних server-нод вернуться в quorum. Поверхностный мониторинг написал: «etcd backup corrupted». Нет. Оператор восстановил байты, но не восстановил согласованное состояние кластера. Начинать надо не с перезапуска всего подряд: k3s etcd-snapshot ls --s3, затем проверить объект и выполнить на единственном выбранном server: k3s server --cluster-reset --cluster-reset-restore-path\=<SNAPSHOT>. После успешного reset убрать этот флаг, запустить узел штатно и только потом последовательно присоединять остальные server-ноды с актуальным token. Старые каталоги /var/lib/rancher/k3s/server/db на бывших участниках нельзя считать безобидными. Во время восстановления смотреть не только journal: journalctl -u k3s -b, ss -lntp \| grep 2379, ss -s, nstat -az \| grep -E 'TcpRetransSegs\|TcpExtTCPTimeouts', cat /proc/sys/net/netfilter/nfconntrackcount и cat /proc/sys/net/netfilter/nfconntrackmax. Для диска: iostat -xz 1, grep -E 'Dirty\|Writeback' /proc/meminfo. Иногда «etcd timeout» — это забитый conntrack, потери TCP или fsync, утонувший в writeback. DR считается рабочим только после отдельной проверки API, quorum, системных контроллеров, секретов и повторного подключения нод. Хранить snapshot в S3 умеет даже ленивый managed-сервис; доказать восстановление умеет только команда с регулярно исполняемым runbook. Практика bare-metal эксплуатации: https://ftops.space
Мониторинг кричал: S3 недоступен, restore упирается в timeout. Проверили ядро: ss -s, nstat -az | egrep 'TcpRetransSegs|IpFragFails', conntrack -C, conntrack -S. Retransmit, фрагментация и conntrack были в норме. Сеть оправдана. Настоящий отказ был выше: настройки S3 хранились в Secret внутри самого кластера. Во время аварийного восстановления kube-apiserver ещё не работает, поэтому --etcd-s3-config-secret бесполезен. Получился классический замок с ключом внутри сейфа. Восстановление запускается на одном остановленном server с явными S3-параметрами и исходным токеном: k3s server --cluster-reset --cluster-reset-restore-path=SNAPSHOT --etcd-s3 --etcd-s3-bucket=BUCKET --etcd-s3-access-key=KEY --etcd-s3-secret-key=SECRET --token=ORIGINAL_TOKEN. Затем обычный запуск K3s; на остальных etcd-server старую базу убирают только после отдельной копии и присоединяют узлы заново. В DR-комплект входят снепшот, /var/lib/rancher/k3s/server/token, CA для S3, автономные credentials и проверенный runbook....
Мониторинг кричал: S3 недоступен, restore упирается в timeout. Проверили ядро: ss -s, nstat -az | egrep 'TcpRetransSegs|IpFragFails', conntrack -C, conntrack -S. Retransmit, фрагментация и conntrack были в норме. Сеть оправдана. Настоящий отказ был выше: настройки S3 хранились в Secret внутри самого кластера. Во время аварийного восстановления kube-apiserver ещё не работает, поэтому --etcd-s3-config-secret бесполезен. Получился классический замок с ключом внутри сейфа. Восстановление запускается на одном остановленном server с явными S3-параметрами и исходным токеном: k3s server --cluster-reset --cluster-reset-restore-path=SNAPSHOT --etcd-s3 --etcd-s3-bucket=BUCKET --etcd-s3-access-key=KEY --etcd-s3-secret-key=SECRET --token=ORIGINAL_TOKEN. Затем обычный запуск K3s; на остальных etcd-server старую базу убирают только после отдельной копии и присоединяют узлы заново. В DR-комплект входят снепшот, /var/lib/rancher/k3s/server/token, CA для S3, автономные credentials и проверенный runbook. Если restore никогда не прогоняли на пустом железе, резервной копии у вас нет. Есть надежда с HTTP API. https://ftops.space
03:00. Учебный постмортем: прямой WireGuard блокируется, после переноса через wstunnel в WSS соединение устанавливается. Проверка TCP/443 зелёная, handshake обновлялся недавно, но SSH зависает под нагрузкой. Дежурный объявляет: «DPI научился резать и это». Проверка порта такого диагноза не даёт. В выбранном режиме UDP WireGuard едет внутри WebSocket поверх TLS/TCP. Потеря внешнего TCP-сегмента задерживает выдачу следующих байтов приложению, даже если они уже пришли. Внутренние TCP-сессии ждут, а при достаточной задержке запускают собственные повторные передачи. Проблемы производительности такой связки отмечены в трекере wstunnel. Это механизм возможного отказа, ещё не доказательство причины конкретной аварии. На узле wstunnel: ss -tinp '( dport \= :443 )'. Находим нужный сокет по адресу и процессу, наблюдаем rtt, rto, retrans и Send-Q во время зависания. nstat -az TcpRetransSegs TcpExtTCPTimeouts снимаем дважды: важен прирост, счётчики от...
03:00. Учебный постмортем: прямой WireGuard блокируется, после переноса через wstunnel в WSS соединение устанавливается. Проверка TCP/443 зелёная, handshake обновлялся недавно, но SSH зависает под нагрузкой. Дежурный объявляет: «DPI научился резать и это». Проверка порта такого диагноза не даёт. В выбранном режиме UDP WireGuard едет внутри WebSocket поверх TLS/TCP. Потеря внешнего TCP-сегмента задерживает выдачу следующих байтов приложению, даже если они уже пришли. Внутренние TCP-сессии ждут, а при достаточной задержке запускают собственные повторные передачи. Проблемы производительности такой связки отмечены в [трекере wstunnel](https://github.com/erebe/wstunnel/issues/439). Это механизм возможного отказа, ещё не доказательство причины конкретной аварии. На узле wstunnel: ss -tinp '( dport = :443 )'. Находим нужный сокет по адресу и процессу, наблюдаем rtt, rto, retrans и Send-Q во время зависания. nstat -az TcpRetransSegs TcpExtTCPTimeouts снимаем дважды: важен прирост, счётчики относятся ко всему сетевому namespace. Сопоставляем его с захватом tcpdump -ni eth0 'host SERVER_IP and tcp port 443', подставив внешний интерфейс и IP сервера. Повторные передачи подтверждают проблему доставки, но сами по себе не отличают DPI от обычных потерь. Критерий восстановления — полезный трафик и задержка под нагрузкой, включая проверку на потерях. Отдельно проверяем, что маршрут к WSS-серверу не завернулся в сам VPN. WSS не гарантирует обход любого DPI, а уменьшение MTU не отменяет упорядоченную доставку TCP. Ещё один ingress не исправит свойства транспорта. Эксплуатация bare metal: https://ftops.space.
ПОСТМОРТЕМ: Systemd drop-in и молчаливый сброс лимитов Мониторинг кричал: nginx возвращает 502, worker процессы падают массово. On-call видит CPU в норме, RAM свободна, диски живые. Классический ложный диагноз — «приложение». На деле ядро честно логировало EMFILE в /proc/$(pidof nginx)/fd, а systemd ещё раньше тихо применил конкурирующий параметр из system.conf. Как воспроизвести диагностику: cat /proc/$(pidof nginx)/limits \| grep 'open files' systemctl show nginx \| grep -i limit journalctl -u nginx --since '2h ago' \| grep 'Too many open' ls /proc/$(pidof nginx)/fd \| wc -l Что произошло в иерархии systemd: пакетный апдейт дистрибутива доставил новый /lib/systemd/system/nginx.service с ExecStartPre и секцией Service без LimitNOFILE. Drop-in /etc/systemd/system/nginx.service.d/limits.conf исправно содержал LimitNOFILE\=1048576 и пережил обновление. Но параллельно мейнтейнер системы добавил в /etc/systemd/system.conf строку DefaultLimitNOFILE\=65536. Согласно иерархии systemd: Defau...
ПОСТМОРТЕМ: Systemd drop-in и молчаливый сброс лимитов Мониторинг кричал: nginx возвращает 502, worker процессы падают массово. On-call видит CPU в норме, RAM свободна, диски живые. Классический ложный диагноз — «приложение». На деле ядро честно логировало EMFILE в /proc/$(pidof nginx)/fd, а systemd ещё раньше тихо применил конкурирующий параметр из system.conf. Как воспроизвести диагностику: cat /proc/$(pidof nginx)/limits \| grep 'open files' systemctl show nginx \| grep -i limit journalctl -u nginx --since '2h ago' \| grep 'Too many open' ls /proc/$(pidof nginx)/fd \| wc -l Что произошло в иерархии systemd: пакетный апдейт дистрибутива доставил новый /lib/systemd/system/nginx.service с ExecStartPre и секцией Service без LimitNOFILE. Drop-in /etc/systemd/system/nginx.service.d/limits.conf исправно содержал LimitNOFILE\=1048576 и пережил обновление. Но параллельно мейнтейнер системы добавил в /etc/systemd/system.conf строку DefaultLimitNOFILE\=65536. Согласно иерархии systemd: DefaultLimit в system.conf задаёт глобальный дефолт; drop-in перекрывает конкретный юнит-файл, но только если в юнит-файле нет явного значения; когда юнит-файл умалчивает LimitNOFILE — применяется DefaultLimit из system.conf, и drop-in с LimitNOFILE\=1048576 его перекрывает корректно. Ловушка: если drop-in написан как LimitNOFILE\= (пустое значение для сброса в дефолт) — он явно сбрасывает к DefaultLimitNOFILE. Именно это случилось: junior-инженер «почистил» drop-in полгода назад. Правильная фиксация: # /etc/systemd/system/nginx.service.d/limits.conf Service LimitNOFILE\=1048576 LimitNPROC\=65536 systemctl daemon-reload systemctl restart nginx systemctl show nginx \| grep -E 'LimitNOFILE\|LimitNPROC' Проверяй system.conf после каждого dist-upgrade: grep -E 'DefaultLimit' /etc/systemd/system.conf /etc/systemd/system.conf.d/.conf 2>/dev/null Дополнительно: Pedit COW (CVE, раскрытая Anti-Malware на этой неделе) — ещё одно напоминание, что ядро исполняет то, что написано, а не то, что ты имел в виду. systemd работает так же. Архитектурный вывод: drop-in — это патч к юнит-файлу, а не абсолютный оверрайд системного дефолта. Если значение в drop-in не явное и не финальное — system.conf выиграет без предупреждения. https://ftops.space
Плановый вывод узла в Longhorn: allowScheduling\=false не делает того, что вы думаете. Мониторинг кричал: PVC в состоянии Terminating, pod зависает на ContainerCreating. На самом деле в ядре и планировщике Longhorn произошло следующее: флаг allowScheduling\=false запрещает размещение новых реплик на узле, но не триггерит eviction существующих. Реплики на выводимом узле продолжают входить в quorum volume до тех пор, пока узел физически доступен. Как только нода ушла из кластера без предварительной эвакуации реплик, Longhorn потерял кворум — volume перешёл в degraded, а затем в faulted. CSI-драйвер вернул I/O error монтирующему поду. Время обнаружения: 8-12 минут после отключения ноды, когда node controller сменил статус на NotReady. Диагностика до начала вывода узла: longhornctl get replica --node\=<node-name> longhornctl get volume \| grep -v healthy kubectl get nodes -o wide kubectl describe node <node-name> \| grep Taint Правильная процедура эвакуации реплик перед drain: kubectl -n l...
Плановый вывод узла в Longhorn: allowScheduling\=false не делает того, что вы думаете. Мониторинг кричал: PVC в состоянии Terminating, pod зависает на ContainerCreating. На самом деле в ядре и планировщике Longhorn произошло следующее: флаг allowScheduling\=false запрещает размещение новых реплик на узле, но не триггерит eviction существующих. Реплики на выводимом узле продолжают входить в quorum volume до тех пор, пока узел физически доступен. Как только нода ушла из кластера без предварительной эвакуации реплик, Longhorn потерял кворум — volume перешёл в degraded, а затем в faulted. CSI-драйвер вернул I/O error монтирующему поду. Время обнаружения: 8-12 минут после отключения ноды, когда node controller сменил статус на NotReady. Диагностика до начала вывода узла: longhornctl get replica --node\=<node-name> longhornctl get volume \| grep -v healthy kubectl get nodes -o wide kubectl describe node <node-name> \| grep Taint Правильная процедура эвакуации реплик перед drain: kubectl -n longhorn-system patch node.longhorn.io/<node-name> \ --type\=merge \ -p '\{"spec":\{"allowScheduling":false,"evictionRequested":true\}\}' Ждать полной эвакуации: watch -n5 'kubectl -n longhorn-system get replica \ -l longhornnode\=<node-name> -o wide' Только после того, как все реплики перенесены и volume вернулся в статус healthy, выполнять kubectl drain. Метрика ядра, которую нужно смотреть во время эвакуации: iostat -xz 2 на узле-источнике — активный replication flush даёт устойчивый write throughput до нуля. Пока есть I/O — реплика не эвакуирована. Longhorn не Ceph. Он не абстрагирует вас от топологии узлов. Каждая реплика — это живое состояние, привязанное к ноде. Полная процедура планового обслуживания узлов Longhorn и разбор смежных отказов — на ftops.space.
Мониторинг кричал: OOM, приложение упало, нужно больше RAM. Дежурный заказал upgrade инстанса. Петля продолжалась. Постмортем. Ночь, BGP-сессия между двумя edge-узлами начала анонсировать маршрут через самого себя. traceroute показал TTL-expiry в цикле между двумя хопами. tcpdump на обоих концах — пакеты идут туда-обратно, TTL декрементируется до нуля, ICMP time-exceeded генерируется тысячами в секунду. Каждый новый поток через петлю открывает conntrack-запись. Диагностика, которая вскрыла правду: ip route show table all | grep -E 'nexthop|via' — немедленно показывает рекурсию cat /proc/sys/net/netfilter/nf_conntrack_count vs nf_conntrack_max — счётчик у потолка tcpdump -i eth0 -nn 'icmp and icmp[0] == 11' — ICMP time-exceeded сыпался сотнями в секунду dmesg | grep 'nf_conntrack: table full' — было в логах, но никто не смотрел ss -s — показал 80k+ ESTABLISHED при реальной нагрузке в 200 соединений Когда conntrack-таблица переполнилась, ядро начало дропать новые соединения. Приложение з...