Calico в Ready и без ошибок, а каждый новый под полминуты недоступен
После апгрейда Calico свежие поды 15–42 секунды сидели в чёрной дыре. Felix крутился в цикле XDP-resync, который видно только тестом «новый под ↔ новый под между нодами».
Во время многоходового апгрейда Kubernetes на bare-metal кластере в Optimacros Calico сначала нужно было поднять с 3.29.5 до 3.31.7. Сам апгрейд выглядел образцово: новые CRD и RBAC из апстрима, образы заменены, все calico-node в 1/1 Ready, ClusterInformation показывает новую версию, в логах Felix ни одного ERROR. Существующие поды общались, DNS резолвил, ingress работал.
И всё же что-то было не так.
Тест, который это поймал
После любого изменения CNI я гоняю одну конкретную проверку: создать свежий под на ноде A и ещё один на ноде B и замерить, через сколько первый запрос между ними пройдёт.
s=$(date +%s); until kubectl exec nt-src -- wget -qO- -T1 http://$IP:8080/ >/dev/null 2>&1; do :; done
echo "$(( $(date +%s)-s ))s"
Это занимало от 15 до 42 секунд после того, как целевой под уже был Ready. До апгрейда — 1–2 секунды. Health-check’и и смоук-тесты на существующих подах проходили: эндпоинты старых подов запрограммированы давно. Задержку показывает только новый эндпоинт.
Цикл без ERROR
В логах Felix на воркерах была строка, которую я сначала отмёл, потому что она на уровне warning:
Applying XDP BPF actions did not succeed, will retry with resync... error=failed to remove XDP program from lo
По нодам — 250–470 раз за пять минут на каждом воркере с Debian 13 и ноль раз на мастерах с другой ОС и ядром. Предыдущая версия Calico оставила xdpgeneric-программу на loopback, а новая не могла снять её на этом ядре. Каждая неудачная попытка ставила в очередь полный resync датаплейна. Новые эндпоинты программировались только в коротких паузах между resync’ами — и каждый новый под проводил десятки секунд в чёрной дыре.
Почему выключить XDP было безопасно
Calico использует XDP только для untracked- и prefilter-политик. В этом кластере NetworkPolicy не было вообще, так что XDP ничего полезного не делал, зато активно вредил. Починка — одна строка:
kubectl patch felixconfiguration default --type=merge -p '{"spec":{"xdpEnabled":false}}'
За секунды счётчик ошибок упал до нуля на всех нодах, а тест свежих подов вернулся к 1–2 секундам. Старая программа так и висит на lo — она была там и раньше и ничего не фильтрует. Откат — тот же патч с true, и на этих ядрах цикл сразу возвращается.
У решения есть триггер пересмотра: перед внедрением doNotTrack или host-endpoint политик, которым нужен XDP, и на следующем minor Calico, который всё равно перестраивает DaemonSet.
Выводы
- «calico-node Ready, ERROR нет» — это не здоровье сети. Felix может усердно падать на уровне warning.
- После любого изменения CNI тестируй свежий под ↔ свежий под между нодами, с таймером. Переиспользованные поды полностью прячут задержку программирования эндпоинтов.
- Считай warning’и по нодам. Цикл, который есть только на одной ОС или ядре, — сильная подсказка о причине.
- Неиспользуемая функция — всё равно работающий код. Если XDP не обслуживает ни одной политики, выключив его, ты бесплатно убираешь класс отказов.