Files
admin/.wiki/concepts/vds-kzntsv-dhcp-outage-2026-05-28.md
vitya 11554d45ac wiki(ingest): vds-kzntsv network-stack mismatch RCA + ifupdown anti-pattern
Incident 2026-05-28 ~05:45–13:52 MSK на vds-kzntsv. ~2.5ч активного outage
+ ~5.5ч на эфемерной статике до окончательного fix хостером.

Revised RCA: наш netplan+systemd-networkd конфликтовал с provider's expected
ifupdown stack. Их start/ipadd procedure ожидает чистый ifupdown и не может
auto-recover когда networkd «держит» eth0. 8 дней работало потому что
networkd сам тянул DHCP. Когда что-то на стороне Rusonyx разорвало
DHCP-binding — auto-recovery не сработала.

Fix: systemctl mask netplan + systemd-networkd* (на running system без stop —
IP и SSH сохранились), Rusonyx ребутнул VM и положил чистый
/etc/network/interfaces.d/ifcfg-eth0 через свой start/ipadd. Netmask /18,
gw 89.253.192.1, чистый ifupdown.

Wiki:
- NEW concepts/vds-kzntsv-dhcp-outage-2026-05-28 — full RCA + recovery
  runbook (эфемерная статика + permanent-fix via ifupdown) + diagnostic
  dot-graph + revised lessons-learned + anti-pattern
- NEW sources/vds-kzntsv-incident-2026-05-28 — timeline 05:25 backup OK →
  08:43 statics → 13:52 final reset; provider's ifcfg-eth0 content; ticket
  text reference
- UPDATE entities/vds-kzntsv — mask /18, ifupdown stack, kernel cmdline
  net.ifnames=0 объясняет eth0 naming, hypervisor hw80, pass-store путь,
  Known issues §
- UPDATE concepts/rusonyx-vps-onboarding-quirks — quirk #9 переписан про
  /18 layout + canonical ifcfg, quirk #10 NEW про ifupdown vs netplan
  stack choice + bootstrap mask commands
- UPDATE index.md + log.md

Tasks:
- STATUS header: incident RESOLVED summary
- NEXT_SESSION: следующая сессия — cleanup netplan-artifacts (optional),
  registry GC (~20G pending), board-viewer-build unhealthy разбор

Memory (out-of-repo): vds-kzntsv-rusonyx-network-recovery переписан с
revised RCA — canonical bootstrap step «mask netplan/networkd» для всех
Rusonyx Ubuntu VDS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:12:14 +03:00

20 KiB
Raw Blame History

title, type, tags, sources, updated
title type tags sources updated
vds-kzntsv network-stack mismatch 2026-05-28 — postmortem + recovery procedure concept
vds
rusonyx
network
dhcp
outage
postmortem
recovery
runbook
ifupdown
netplan
../sources/vds-kzntsv-incident-2026-05-28.md
2026-05-28

vds-kzntsv network-stack mismatch 2026-05-28

Постмортем + переиспользуемый runbook. Конкретный инцидент: ../entities/vds-kzntsv (89.253.255.94) был недоступен снаружи примерно с 28 мая 2026, ~05:4508:30 MSK (~2.5 часа активного outage), плюс ещё ~5.5 часов работы на эфемерной статике до окончательного fix хостером в ~13:52 MSK. Подробная хроника — в ../sources/vds-kzntsv-incident-2026-05-28.

Root cause (по итогу resolution хостером): наш Ubuntu 24.04 VDS с момента активации (2026-05-20) работал на двух параллельно активных сетевых стеках — netplan + systemd-networkd (от cloud-init + наши манипуляции) поверх ifupdown + networking (provider's expected stack). Их provisioning кладёт конфиг в /etc/network/interfaces.d/ifcfg-eth0 через start/ipadd/ipdel procedure, и ожидает что ifupdown подхватит при boot. networkd на eth0 этому мешает. 8 дней «работало», потому что networkd сам получал DHCP lease и обходил проблему. Когда DHCP-binding слетел на их стороне (28.05 ~05:45-08:00 MSK), их start/ipadd procedure не могла восстановить IP через ifupdown, потому что eth0 был «захвачен» systemd-networkd → VM осталась без IP. Окончательное решение: mask netplan + systemd-networkd, переход на чистый networking stack.

Симптомокартина

Симптом Где видно
SSH с любого хоста — No route to host / Destination Host Unreachable workstation, sibling-VPS ../entities/books-vds
Hoster gateway 89.253.192.40 отвечает Destination Host Unreachable на ARP внешний traceroute падает на 3-м hop
VM жива (через VNC), процессы и docker контейнеры работают Rusonyx panel → Консоль
ip -br link показывает eth0 UP LOWER_UP с MAC физический NIC ОК
ip a show eth0только inet6 fe80::*/64 scope link, нет IPv4 DHCP lease отсутствует
ip route — только docker-сети, нет default следствие отсутствия IPv4
networkctl status eth0State: degraded (configuring), Online state: online networkd ждёт ответ от DHCP
journalctl -u systemd-networkd -b — повторяющийся eth0: DHCPv6 lease lost, никаких DHCPv4 offer'ов DHCP-сервер хостера не отвечает
LLDP-сосед видим: Connected To: hw80.rusonyx.ru on port fe:54:00:9c:63:01 L2 со свитчем здоров → проблема выше

Анти-симптомы (что отметает наши конфиг-ошибки):

  • netplan не менялся днями (см. git log если бы был — у нас не versioned, но stat /etc/netplan/*.yaml).
  • Тот же шаблон netplan работает на соседнем VPS ../entities/books-vds (89.253.255.133) у того же хостера.
  • Reboot VM не помогает (uptime после ребута 1 час, IP так и не получен).
  • Daily backup завершился успешно за ~3 часа до обнаружения отказа → не «давно сломалось», именно острый отказ.

Диагностика — алгоритм

digraph diag {
    "Внешний SSH/ping не доходит" [shape=diamond];
    "Открыть VNC → ip -br link" [shape=box];
    "Link UP?" [shape=diamond];
    "Без NIC — driver/hardware issue" [shape=box];
    "ip a show <iface>" [shape=box];
    "Есть IPv4?" [shape=diamond];
    "Сеть работает иначе" [shape=box];
    "networkctl status <iface>" [shape=box];
    "LLDP видит свитч?" [shape=diamond];
    "Cable/L2 issue — тикет в DC" [shape=box];
    "Соседний VPS у того же хостера работает?" [shape=diamond];
    "Глобальный outage — ждать или тикет" [shape=box];
    "DHCP server проблема — статика + тикет" [shape=doublecircle];

    "Внешний SSH/ping не доходит" -> "Открыть VNC → ip -br link";
    "Открыть VNC → ip -br link" -> "Link UP?";
    "Link UP?" -> "Без NIC — driver/hardware issue" [label="нет"];
    "Link UP?" -> "ip a show <iface>" [label="да"];
    "ip a show <iface>" -> "Есть IPv4?";
    "Есть IPv4?" -> "Сеть работает иначе" [label="да"];
    "Есть IPv4?" -> "networkctl status <iface>" [label="нет"];
    "networkctl status <iface>" -> "LLDP видит свитч?";
    "LLDP видит свитч?" -> "Cable/L2 issue — тикет в DC" [label="нет"];
    "LLDP видит свитч?" -> "Соседний VPS у того же хостера работает?" [label="да"];
    "Соседний VPS у того же хостера работает?" -> "Глобальный outage — ждать или тикет" [label="нет"];
    "Соседний VPS у того же хостера работает?" -> "DHCP server проблема — статика + тикет" [label="да"];
}

Ключевые команды для каждого узла:

ip -br link                                 # NIC + state одной строкой на интерфейс
ip a show eth0                              # есть ли IPv4
sudo networkctl status eth0                 # State + LLDP-сосед (Connected To: ...)
sudo journalctl -u systemd-networkd -b | grep -iE "dhcp|discover|offer|nack" | tail -20
sudo ls /run/systemd/netif/leases/          # есть ли свежий lease (пусто — нет ни одного)

Recovery procedure (эфемерная статика — для немедленного восстановления)

Используется когда сеть лежит прямо сейчас и нужно срочно поднять доступ. Маска /18 (как прописывает хостер) — это самая чистая форма; gw в той же подсети, дополнительный link-route не требуется:

sudo ip addr add 89.253.255.94/18 dev eth0
sudo ip route add default via 89.253.192.1 dev eth0

DNS (если /etc/resolv.conf не сохранился):

sudo bash -c 'echo -e "nameserver 89.253.252.30\nnameserver 89.253.252.31" > /etc/resolv.conf'

Альтернативная форма с /24 (если по какой-то причине /18 не приемлемо — например провайдер изменил routing) — тогда нужны 3 команды с link-route к gw:

sudo ip addr add 89.253.255.94/24 dev eth0
sudo ip route add 89.253.192.1 dev eth0
sudo ip route add default via 89.253.192.1 dev eth0

Проверка:

ip a show eth0          # inet 89.253.255.94/...
ip route                # default via 89.253.192.1 dev eth0
curl -s -m 5 https://api.ipify.org   # должно вернуть 89.253.255.94

Эфемерность: прописано через ip — на reboot стирается. Для persistence см. ниже § «Permanent fix через ifupdown».

Permanent fix — через ifupdown stack (то что прописывает хостер)

Это и есть financial state после resolution 2026-05-28. Это конфигурация которую хостер ставит/восстанавливает через свой start/ipadd procedure — наш ifcfg-eth0 это просто слепок того что они кладут.

/etc/network/interfaces.d/ifcfg-eth0

# Autoconfigured by Provider's start/ipadd/ipdel procedure.
# !!! DO NOT MANUALLY EDIT OR DELETE THIS FILE !!!

auto eth0
allow-hotplug eth0
iface eth0 inet static
        address 89.253.255.94
        netmask 255.255.192.0
        post-up ip ro add 169.254.0.0/16 dev eth0 metric 400
        post-up ip ro add 89.253.192.1 dev eth0 metric 400
        post-up ip ro add default via 89.253.192.1 dev eth0 metric 400
        post-up ip ro add 89.253.192.0/18 via 89.253.192.1 dev eth0 src 89.253.255.94 metric 400
        post-up ip ro del 89.253.192.0/18 dev eth0 proto kernel scope link src 89.253.255.94

Не редактировать руками — хостер перезапишет через start/ipadd при следующих manipulations с VM (resize, network reset). Если нужны user-specific routes — отдельный файл рядом, не редактировать ifcfg-eth0.

Прибить netplan + systemd-networkd

sudo systemctl disable systemd-networkd.service systemd-networkd.socket systemd-networkd-wait-online.service
sudo systemctl mask    systemd-networkd.service systemd-networkd.socket systemd-networkd-wait-online.service
sudo systemctl mask    netplan        # preventive — netplan.service unit not exist в Ubuntu, но mask не повредит

# verify
systemctl is-enabled netplan systemd-networkd networking
# expected: masked, masked, enabled

Безопасно на running systemdisable/mask блокируют только автозапуск на следующий boot. Текущий networkd-процесс продолжает работать до явного stop или reboot. SSH-сессии и L3-конфиг (IP/маршруты) переживают.

Запросить provider-side reset

Если IP по DHCP не приходит и /etc/network/interfaces.d/ifcfg-eth0 отсутствует или пустой — тикет в Rusonyx с запросом «настроить дефолтную конфигурацию сети». Они через свою start/ipadd procedure пропишут ifcfg-eth0 и ребутнут VM. После ребута сеть поднимается на ifupdown.

Kernel cmdline (для понимания почему eth0, а не ens3)

Rusonyx прописывает в GRUB net.ifnames=0 biosdevname=0 — это выключает systemd predictable interface naming. Поэтому интерфейс всегда eth0, не ens3/enp0s3 (хотя udev знает их как altnames для ip a show). Менять эти флаги в cmdline нельзя — провайдерский ifcfg-eth0 рассчитан именно на eth0.

Где взять gateway если он неизвестен

У Rusonyx и многих российских VDS-хостингов используется proxy-ARP / unnumbered routing — gateway сидит в /18 или /19 надсети, а наш /24 гостирует через неё. Не угадывать .1 в своей /24 — это не сработает.

Источники:

  1. Соседний VPS у того же хостера — самый надёжный. ssh sibling 'ip route' покажет default via X.X.X.X dev eth0. Так мы и сделали в этот раз — взяли gw из ../entities/books-vds (89.253.192.1).
  2. Cached lease на самой VM (если уцелел): cat /run/systemd/netif/leases/*, cat /var/lib/dhcp/dhclient.leases — поля ROUTER=, DNS=. В этот раз не уцелело.
  3. Тикет в support — последний resort, тратит часы.
  4. Активационное письмо при выдаче VPS — обычно содержит netmask + gw. Но если VPS активирован годы назад — письмо может быть утеряно.

Root cause (по итогам resolution)

Корректировка относительно нашей первоначальной гипотезы (которую мы вынесли в тикет на основе наших observations):

Первоначально мы атрибутировали отказ полностью инфраструктуре хостера (DHCP server / MAC binding). Это было частично верно — что-то в их инфре действительно потеряло binding нашей VM 28.05 в окне ~05:45-08:15 MSK, и их start/ipadd procedure пыталась его восстановить. Но их procedure не смогла справиться потому что наш сетевой стек был сконфигурирован неправильно:

  • Поверх их ожидаемого ifupdown + networking (под который рассчитан их start/ipadd) у нас активны параллельно netplan + systemd-networkd (от cloud-init).
  • systemd-networkd «захватывал» eth0 на уровне routing и DHCP, не давая ifupdown'у нормально применить provider's static config.
  • 8 дней с этим conflict'ом всё работало, потому что networkd сам получал DHCP lease (там где их ipadd не успевал) и сеть жила.
  • Когда что-то на стороне хостера разорвало DHCP binding — networkd не смог получить новый lease (потому что binding с их стороны был broken), а ifupdown с готовой статикой не смог взять интерфейс (потому что networkd ещё держал его в configuring state).

Окончательное решение хостера (после нашего тикета): mask netplan + systemd-networkd, reboot — после boot ifupdown подхватил ifcfg-eth0 сразу, конфликта нет, сеть работает.

Атрибуция ответственности:

  • Их сторона: что-то в их сетевой инфре спровоцировало DHCP-binding loss 28.05 (точная причина не указана в ответе — отписка через сутки). Это trigger инцидента.
  • Наша сторона: мы наколхозили netplan + systemd-networkd поверх их ifupdown-stack (отчасти от cloud-init, отчасти при попытках восстановить сеть через VNC). Эта конфигурация усугубила инцидент: их auto-recovery не смогла сработать. Это prolonging factor.

Без conflict'а — инцидент длился бы минуты до auto-recovery. С conflict'ом — длился до нашего тикета + ручного fix хостером.

Lessons learned (revised)

  1. Уважать provider's expected stack. Rusonyx — ifupdown. Не накатывать netplan поверх, даже если он «по умолчанию в Ubuntu 24.04». Чистый netplan был бы менее проблемным чем mixed setup, но безопаснее всего — то что хостер использует.
  2. Hostprovider auto-recovery — реальная вещь. У Rusonyx есть start/ipadd procedure которая инжектит IP при reboot/start. Если она работает корректно — DHCP lease loss восстанавливается прозрачно для клиента. Не мешать её работе.
  3. Соsed (sibling VPS) — главный диагностический инструмент. ../entities/books-vds подтвердил отсутствие глобального outage и дал референс на сетевые параметры (gw, DNS resolvers).
  4. Готовый тикет-шаблон важен. Хороший initial-ticket с конкретикой (LLDP, journalctl, ping/traceroute) сократил процесс. Хостер увидел что мы не зовём «оно не работает» а уже сделали половину RCA.
  5. Когда хостер просит configuration change — оценить trade-off. Их framing «у вас неправильный стек» был корректен (по последствиям), но мы первоначально подозревали что они переводят разговор от своего RCA. Reality — и то и другое одновременно: их инфра glitched, наш стек не дал auto-recovery, оба фактора важны.
  6. disable + mask без stop — безопасно на running system. Не падает SSH, не падает IP. Изменение вступает только на reboot. Это полезный паттерн для «приготовить состояние к чистому reboot, не трогая текущее».

Anti-pattern

Не делать: persistent статику через netplan для VDS у Rusonyx. Это противоречит их provisioning. Использовать только ifupdown (/etc/network/interfaces.d/*), а лучше — попросить их start/ipadd procedure сделать это (тикет).

Lessons learned

  1. DHCP — единая точка отказа на стороне хостера. Если у них падает DHCP-привязка нашей VM, и lease истёк — мы выпадаем независимо от состояния нашего стека. Mitigation: статика в netplan, но это требует договорённости с хостером (они могут autosocket pool — статический IP перестанет работать когда они ребалансируют).
  2. Soсед — главный диагностический инструмент. ../entities/books-vds был ключом: подтвердил что L2/глобальный outage отсутствует, дал точный gw для статики. Иметь второй VPS у того же хостера — это бесплатный мониторинг сетевой инфры хостера.
  3. VNC console — обязательный backup-канал. Без rusonyx-vps-onboarding-quirks §1-2 и доступа в VNC мы бы не смогли ни диагностировать, ни поднять статику.
  4. Activation-email с netmask+gw — критичный артефакт. Хранить (в pass-store / pinned email folder).
  5. Cached lease file — мог бы сократить recovery на 2-3 шага. Не уцелел. Можно периодически бэкапить /run/systemd/netif/leases/ куда-то persistent? — minor follow-up, vs стоимость не оправдан.
  6. Daily backup-лог = upper-bound оценки downtime. Если backup прошёл успешно в 05:45 — VDS был жив. Это поможет хостеру в их RCA найти точное окно.

Cross-refs