Модуль 3.8: Шлях даних мережі кластера
Складність:
[MEDIUM]— Ключова тема діагностикиЧас на виконання: ~35 хвилин
Передумови: Модуль 3.1 (Сервіси), Модуль 3.6 (Мережеві політики), Модуль 3.7 (CNI)
Що ви зможете робити
Розділ «Що ви зможете робити»Після цього модуля ви зможете:
- Простежити пакет від пода A до пода B між вузлами через весь мережевий стек
- Пояснити, як VXLAN, IP-in-IP та native routing працюють на рівні Linux
- Дебажити проблеми міжвузлового з’єднання, перевіряючи маршрути, мости та тунельні інтерфейси
- Порівняти overlay та native routing підходи та їх компроміси щодо продуктивності
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Ви можете створювати Сервіси та писати мережеві політики цілий день, але коли щось ламається у продакшені о 3 годині ночі, вам потрібно розуміти, куди насправді йдуть пакети. Цей модуль навчить вас ментальній моделі, яка перетворює мережеві загадки на розвʼязувані задачі.
Бойова історія: Мовчазне скидання через MTU
Платформна команда мігрувала кластер з Flannel із
host-gwна Flannel ізvxlan-інкапсуляцією. Все виглядало нормально — малі проби перевірки стану проходили, ping між Подами працював, Сервіси розвʼязувалися коректно. Але кожні кілька хвилин критичне пакетне завдання зависало та зрештою завершувалося таймаутом.Після двох днів безрезультатної діагностики (перезапуск Подів, перевірка DNS, звинувачення застосунку) молодший інженер запустив
tcpdumpна вузлі та помітив дещо особливе: TCP SYN-пакети проходили між вузлами нормально, але великі навантаження даних мовчазно скидалися. Причина? VXLAN додає 50-байтовий заголовок, зменшуючи ефективний MTU з 1500 до 1450. CNI був налаштований на MTU 1500, тому будь-який пакет, близький до ліміту, був занадто великий для тунелю, і бітDon't Fragmentзмушував ядро мовчазно скидати його замість фрагментації.Виправлення було однорядковою зміною конфігурації (
"MTU": 1450), але знайти його вимагало розуміння фактичного шляху даних — де пакети входять в ядро, як вони інкапсулюються та де вони виходять. Саме цьому і вчить цей модуль.
Що ви вивчите
Розділ «Що ви вивчите»Після завершення цього модуля ви зможете:
- Простежити пакет від клієнтського Поду через правила kube-proxy до Поду-бекенду
- Розрізняти відповідальність CNI та kube-proxy
- Пояснити, як працює розвʼязання імен CoreDNS від початку до кінця
- Використовувати
tcpdump,iptables-save,conntrackтаnslookupдля діагностики реальних мережевих проблем - Застосовувати систематичну ментальну модель діагностики мережі кластера
Чи знали ви?
Розділ «Чи знали ви?»-
kube-proxy насправді нічого не проксіює (попри свою назву). У режимі iptables він просто програмує правила DNAT в ядрі. Фактична пересилка пакетів повністю обробляється мережевим стеком Linux — kube-proxy ніколи не бачить самих пакетів даних.
-
Один Сервіс із 1000 бекендами генерує ~8000 правил iptables у режимі iptables. Ось чому великі кластери (5000+ Сервісів) часто переходять на режим IPVS або рішення на основі eBPF, як Cilium, які можуть обробляти сотні тисяч бекендів без лінійного сканування правил.
-
Kubernetes вимагає пласку мережу: кожен Під повинен мати можливість звʼязатися з кожним іншим Подом без NAT. Це єдине проєктне рішення (задокументоване в мережевій моделі Kubernetes) робить можливою всю абстракцію Сервісів — і саме тому існують плагіни CNI.
-
CoreDNS обробляє приблизно 10 000–50 000 запитів на секунду у типовому продакшен-кластері. Одне неправильно налаштоване значення
ndotsможе помножити це на 5, тому що кожен запит ініціює розширення пошукових доменів (наприклад,api.example.comперетворюється на 5 окремих DNS-запитів, перш ніж останній успішно завершиться).
Частина 1: Потік від Сервісу до Поду — прохід пакету
Розділ «Частина 1: Потік від Сервісу до Поду — прохід пакету»Розуміння точного шляху пакету — це основа всієї мережевої діагностики. Простежимо запит від Поду A до ClusterIP Сервісу, який маршрутизує до Поду B.
1.1 Прохід пакету ClusterIP
Розділ «1.1 Прохід пакету ClusterIP»┌─────────────────────────────────────────────────────────────────────────┐│ Прохід пакету ClusterIP (режим iptables) ││ ││ Під A (10.244.1.5) Під B (10.244.2.8) ││ ┌─────────────────┐ ┌─────────────────┐ ││ │ curl 10.96.0.50 │ │ nginx :80 │ ││ └────────┬────────┘ └────────▲────────┘ ││ │ │ ││ ▼ │ ││ ┌─────────────────┐ ┌────────┴────────┐ ││ │ 1. пара veth │ │ 7. пара veth │ ││ │ (під → вузол)│ │ (вузол → під)│ ││ └────────┬────────┘ └────────▲────────┘ ││ │ │ ││ ▼ │ ││ ┌─────────────────┐ │ ││ │ 2. iptables │ Ланцюжок PREROUTING │ ││ │ правило DNAT │ dst: 10.96.0.50:80 │ ││ │ перезаписує │ → 10.244.2.8:80 │ ││ │ dst │ │ ││ └────────┬────────┘ │ ││ │ │ ││ ▼ │ ││ ┌─────────────────┐ │ ││ │ 3. conntrack │ Записує NAT- │ ││ │ запис таблиці│ відображення для │ ││ └────────┬────────┘ зворотного трафіку │ ││ │ │ ││ ▼ │ ││ ┌─────────────────┐ ┌────────┴────────┐ ││ │ 4. Рішення │ │ 6. Рішення │ ││ │ маршрутизації│ │ маршрутизації│ ││ │ (той самий │──── той самий ────────►│ (доставити │ ││ │ вузол чи │ вузол? │ локально) │ ││ │ тунель?) │ │ │ ││ └────────┬────────┘ └─────────────────┘ ││ │ інший вузол ││ ▼ ││ ┌─────────────────┐ ┌─────────────────┐ ││ │ 5a. Інкапсуляція│ ═══ VXLAN/Geneve ═══►│ 5b. Декапсуляція│ ││ │ CNI │ тунель │ CNI │ ││ │ (якщо │ │ (на вузлі │ ││ │ оверлей) │ │ призначення)│ ││ └─────────────────┘ └────────┬────────┘ ││ Вузол 1 │ Вузол 2 ││ └──► крок 6 │└─────────────────────────────────────────────────────────────────────────┘Ось що відбувається на кожному кроці:
- Пара veth: Пакет залишає мережевий простір імен Поду A через віртуальну пару ethernet, яка зʼєднує його з мережевим простором імен хоста (вузла).
- iptables DNAT: kube-proxy запрограмував правила iptables. Ланцюжок PREROUTING знаходить збіг з адресою призначення
10.96.0.50(ClusterIP Сервісу) і перезаписує її на IP Поду-бекенду — скажімо,10.244.2.8. Якщо існує кілька бекендів, випадковий або циклічний вибір відбувається через правила ймовірності iptables. - conntrack: Модуль відстеження зʼєднань ядра записує це NAT-відображення. Коли Під B відповідає, conntrack автоматично обертає трансляцію, щоб Під A бачив відповідь від IP Сервісу, а не від IP Поду.
- Рішення маршрутизації: Ядро маршрутизує пакет на основі перезаписаної адреси призначення. Якщо Під B на тому ж вузлі, пакет іде безпосередньо до пари veth Поду B. Якщо Під B на іншому вузлі, він іде до CNI.
- Інкапсуляція CNI: Для оверлейних мереж (VXLAN, Geneve) CNI загортає пакет у зовнішній заголовок для тунелювання до вузла призначення. Для маршрутизованих мереж (BGP, host-gw) ядро пересилає його безпосередньо.
- Доставка: На вузлі призначення пакет декапсулюється (якщо потрібно) та маршрутизується до пари veth Поду B.
- Під отримує: Під B бачить пакет від IP Поду A, призначений для його власного IP на порті 80.
1.2 Прохід пакету NodePort
Розділ «1.2 Прохід пакету NodePort»NodePort додає додатковий крок на початку:
┌─────────────────────────────────────────────────────────────────────────┐│ Прохід пакету NodePort ││ ││ Зовнішній клієнт ││ ┌─────────────────┐ ││ │ curl │ ││ │ 192.168.1.10: │ ││ │ 30080 │ ││ └────────┬────────┘ ││ │ ││ ▼ ││ ┌─────────────────┐ Вузол 1 (192.168.1.10) ││ │ 1. eth0 вузла │ ││ │ приймає на │ ││ │ порті 30080 │ ││ └────────┬────────┘ ││ │ ││ ▼ ││ ┌─────────────────┐ ││ │ 2. iptables │ Ланцюжок KUBE-NODEPORTS: ││ │ DNAT │ dst-port 30080 → 10.96.0.50:80 (ClusterIP) ││ │ │ → потім обирає бекенд: 10.244.2.8:80 ││ └────────┬────────┘ ││ │ ││ ▼ ││ ┌─────────────────┐ ││ │ 3. SNAT (можливо)│ Якщо externalTrafficPolicy: Cluster (стандартно)││ │ │ джерело перезаписується на IP вузла, щоб ││ │ │ зворотний трафік повертався через цей вузол ││ └────────┬────────┘ ││ │ ││ ▼ ││ (далі як потік ClusterIP з кроку 4) ││ │└─────────────────────────────────────────────────────────────────────────┘Ключова деталь: З externalTrafficPolicy: Cluster (за замовчуванням) kube-proxy виконує SNAT вихідної адреси, що означає — Під-бекенд бачить IP вузла, а не реальний IP клієнта. Налаштування externalTrafficPolicy: Local зберігає IP клієнта, але маршрутизує лише до Подів на вузлі-отримувачі — якщо там їх немає, зʼєднання відхиляється.
1.3 Hairpin-трафік
Розділ «1.3 Hairpin-трафік»Коли Під викликає свій власний Сервіс (наприклад, Під за web-svc робить curl до web-svc), пакет може бути маршрутизований назад до нього самого. Це називається hairpin або hairpin NAT:
┌─────────────────────────────────────────────────────────────┐│ Потік hairpin ││ ││ Під A відправляє на IP Сервісу ││ │ ││ ▼ ││ iptables DNAT обирає... сам Під A! ││ │ ││ ▼ ││ Пакет повинен вийти з netns Поду A і знову увійти в нього ││ (потребує режиму hairpin на мості/veth) ││ ││ Якщо режим hairpin ВИМКНЕНО → пакет мовчазно скидається ││ Якщо режим hairpin УВІМКНЕНО → пакет коректно повертається ││ │└─────────────────────────────────────────────────────────────┘Більшість плагінів CNI вмикають режим hairpin за замовчуванням. Якщо ви бачите переривчасті збої, коли Під іноді не може звʼязатися зі своїм власним Сервісом, hairpin — найімовірніша причина. Перевірте за допомогою:
# Перевірити режим hairpin на інтерфейсі vethk exec <pod> -- cat /sys/class/net/eth0/brport/hairpin_mode# Або на вузлі:cat /sys/devices/virtual/net/<veth-name>/brport/hairpin_modeЧастина 2: Відповідальність CNI проти kube-proxy
Розділ «Частина 2: Відповідальність CNI проти kube-proxy»Це одне з найбільш неправильно зрозумілих розмежувань у мережі Kubernetes. Помилка призводить до діагностики неправильного компонента.
2.1 Розподіл відповідальності
Розділ «2.1 Розподіл відповідальності»┌─────────────────────────────────────────────────────────────────────────┐│ CNI проти kube-proxy: Хто що робить? ││ ││ ┌──────────────────────────────┐ ┌──────────────────────────────┐ ││ │ Плагін CNI │ │ kube-proxy │ ││ │ │ │ │ ││ │ ✓ Призначення IP Подам │ │ ✓ DNAT Сервіс → Під │ ││ │ ✓ Створення пар veth │ │ ✓ Маршрутизація ClusterIP │ ││ │ ✓ Налаштування маршрутів │ │ ✓ Правила NodePort │ ││ │ Подів │ │ ✓ Правила LoadBalancer │ ││ │ ✓ Міжвузлове тунелювання │ │ ✓ Привʼязка сесій │ ││ │ (VXLAN, Geneve, BGP) │ │ ✓ Вибір Endpoint │ ││ │ ✓ Застосування мережевих │ │ │ ││ │ політик (деякі CNI) │ │ ✗ НЕ призначає IP │ ││ │ ✓ Звʼязок Під-до-Поду │ │ ✗ НЕ створює тунелі │ ││ │ │ │ ✗ НЕ застосовує політики │ ││ │ ✗ НЕ обробляє Сервіси │ │ (окрім через правила DNAT) │ ││ │ (якщо eBPF не замінює │ │ │ ││ │ kube-proxy, напр. Cilium)│ │ │ ││ └──────────────────────────────┘ └──────────────────────────────┘ ││ ││ ┌──────────────────────────────────────────────────────────────┐ ││ │ Швидка діагностика │ ││ │ │ ││ │ "Під A не може звʼязатися з Подом B за IP Поду" │ ││ │ → Проблема в CNI (маршрутизація, інкапсуляція, MTU) │ ││ │ │ ││ │ "Під A не може звʼязатися із Сервісом, але МОЖЕ звʼязатися │ ││ │ з Подом B за IP Поду" │ ││ │ → Проблема в kube-proxy (правила DNAT, endpoints) │ ││ │ │ ││ │ "Під A не може розвʼязати імʼя сервісу" │ ││ │ → Проблема в CoreDNS (див. Частину 3) │ ││ │ │ ││ └──────────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────────────┘2.2 Швидка матриця CNI
Розділ «2.2 Швидка матриця CNI»Різні плагіни CNI використовують різні підходи. Ось порівняння, актуальне для діагностики:
| Функція | Calico | Flannel | Cilium |
|---|---|---|---|
| Площина даних | iptables або eBPF | VXLAN або host-gw | eBPF |
| Маршрутизація за замовчуванням | BGP (без інкапсуляції) | VXLAN-оверлей | eBPF пряма маршрутизація |
| Мережеві політики | Так (нативні) | Ні (потрібен додаток) | Так (L3–L7) |
| Може замінити kube-proxy | Так (режим eBPF) | Ні | Так (заміна kube-proxy) |
| Проблема MTU | Немає (без інкапсуляції) | Так (−50 байт для VXLAN) | Залежить від конфігурації |
| Інструмент діагностики | calicoctl node status | Перевірити інтерфейс VXLAN | cilium status |
2.3 Режими kube-proxy
Розділ «2.3 Режими kube-proxy»kube-proxy може працювати в різних режимах. Режим впливає на те, як Сервіси реалізовані в ядрі:
# Перевірити, який режим використовує kube-proxyk get configmap kube-proxy -n kube-system -o yaml | grep mode
# Або перевірити логи kube-proxyk logs -n kube-system -l k8s-app=kube-proxy | head -20| Режим | Як працює | Продуктивність | Коли використовувати |
|---|---|---|---|
| iptables | Правила DNAT на кожен Сервіс/Endpoint | Обчислення правил O(n) | За замовчуванням, підходить для < 5000 Сервісів |
| IPVS | Віртуальний сервер з реальними бекендами | Пошук O(1) через хеш-таблицю | Великі кластери (5000+ Сервісів) |
| nftables | Наступне покоління заміни iptables | Краще за iptables | Рекомендований шлях для K8s 1.31+ |
Частина 3: Шлях розвʼязання DNS (CoreDNS)
Розділ «Частина 3: Шлях розвʼязання DNS (CoreDNS)»DNS — це клей, який робить імена Сервісів функціональними. Коли Під викликає curl web-service, ось що насправді відбувається.
3.1 Повний шлях розвʼязання DNS
Розділ «3.1 Повний шлях розвʼязання DNS»┌─────────────────────────────────────────────────────────────────────────┐│ Шлях розвʼязання DNS ││ ││ Під (10.244.1.5) ││ ┌──────────────────────────────┐ ││ │ curl http://web-svc │ ││ │ │ ││ │ 1. glibc читає │ ││ │ /etc/resolv.conf: │ ││ │ nameserver 10.96.0.10 │ ◄── ClusterIP CoreDNS ││ │ search default.svc. │ ││ │ cluster.local │ ││ │ svc.cluster.local │ ││ │ cluster.local │ ││ │ options ndots:5 │ ││ └──────────┬───────────────────┘ ││ │ ││ │ 2. "web-svc" має 0 крапок, що < ndots (5) ││ │ Тому спочатку пробуються пошукові домени: ││ │ ││ │ Запит 1: web-svc.default.svc.cluster.local ← ЗНАЙДЕНО!││ │ (Якщо промах: web-svc.svc.cluster.local) ││ │ (Якщо промах: web-svc.cluster.local) ││ │ (Якщо промах: web-svc.) ← абсолютний запит останній ││ │ ││ ▼ ││ ┌──────────────────────────────┐ ││ │ 3. UDP-пакет до │ ││ │ 10.96.0.10:53 │ ││ │ (ClusterIP CoreDNS) │ ││ └──────────┬───────────────────┘ ││ │ ││ ▼ ││ ┌──────────────────────────────┐ ││ │ 4. DNAT kube-proxy │ ││ │ 10.96.0.10 → 10.244.0.3 │ ◄── Фактичний IP Поду CoreDNS ││ └──────────┬───────────────────┘ ││ │ ││ ▼ ││ ┌──────────────────────────────┐ ││ │ 5. Під CoreDNS │ ││ │ - Перевіряє плагін │ ││ │ kubernetes (записи │ ││ │ всередині кластера) │ ││ │ - Повертає A-запис: │ ││ │ 10.96.45.123 │ ◄── ClusterIP Сервісу ││ └──────────┬───────────────────┘ ││ │ ││ ▼ ││ Під отримує IP, зʼєднується з 10.96.45.123 ││ (далі вступає в дію потік Сервіс → Під з Частини 1) ││ │└─────────────────────────────────────────────────────────────────────────┘3.2 Пастка ndots
Розділ «3.2 Пастка ndots»Стандартне значення ndots:5 у Kubernetes означає, що будь-яке імʼя з менш ніж 5 крапками розглядається як відносне. Це запускає розширення пошукових доменів:
# Запит "api.example.com" (2 крапки, < 5) генерує такі запити:# 1. api.example.com.default.svc.cluster.local → NXDOMAIN# 2. api.example.com.svc.cluster.local → NXDOMAIN# 3. api.example.com.cluster.local → NXDOMAIN# 4. api.example.com. → SUCCESS
# Це 4 DNS-запити замість 1!Для Подів, що часто звертаються до зовнішніх доменів, зменште ndots або використовуйте завершальну крапку:
# Варіант 1: Встановити ndots у специфікації ПодуapiVersion: v1kind: Podmetadata: name: optimized-dnsspec: dnsConfig: options: - name: ndots value: "2" containers: - name: app image: nginx# Варіант 2: Використовуйте завершальну крапку (абсолютне імʼя, пропускає пошук)curl http://api.example.com.# ^ завершальна крапка = абсолютне, без розширення пошуку3.3 Типові режими збоїв DNS
Розділ «3.3 Типові режими збоїв DNS»| Симптом | Імовірна причина | Як перевірити |
|---|---|---|
| Весь DNS не працює | Поди CoreDNS не працюють | k get pods -n kube-system -l k8s-app=kube-dns |
| Переривчасті таймаути DNS | CoreDNS перевантажений або NetworkPolicy блокує UDP/53 | k top pods -n kube-system, перевірте політики |
| Зовнішні імена не працюють | CoreDNS не може звʼязатися з upstream DNS | Перевірте конфігурацію плагіна forward CoreDNS, DNS вузла |
| Міжпросторові запити не працюють | Неправильний FQDN або пошуковий домен | Використовуйте повний FQDN: svc.ns.svc.cluster.local |
| DNS працює, зʼєднання не працює | DNS в порядку, проблема в Сервісі/CNI | nslookup успішний, але curl не працює = не DNS |
# Швидка перевірка здоровʼя DNS з будь-якого Подуk run dns-check --rm -it --image=busybox:1.36 --restart=Never -- \ nslookup kubernetes.default
# Перевірити логи CoreDNS на помилкиk logs -n kube-system -l k8s-app=kube-dns --tail=50Частина 4: Ментальна модель діагностики
Розділ «Частина 4: Ментальна модель діагностики»Коли мережа ламається, вам потрібен систематичний підхід. Не вгадуйте — слідкуйте за пакетом.
4.1 Трирівнева діагностика
Розділ «4.1 Трирівнева діагностика»┌─────────────────────────────────────────────────────────────────────────┐│ Дерево рішень діагностики мережі ││ ││ "Під A не може звʼязатися із Сервісом X" ││ │ ││ ▼ ││ ┌─────────────────────────────────┐ ││ │ Рівень 1: DNS │ ││ │ Чи може Під A розвʼязати імʼя? │ ││ │ nslookup <service> │ ││ └──────┬──────────┬───────────────┘ ││ НІ │ │ ТАК ││ │ │ ││ ▼ ▼ ││ Перевірте CoreDNS │ ││ Поди, resolv.conf │ ││ NetworkPolicy │ ││ на UDP/53 │ ││ │ ││ ┌─────────────────▼───────────────┐ ││ │ Рівень 2: Сервіс (kube-proxy) │ ││ │ Чи має Сервіс endpoints? │ ││ │ k get endpoints <service> │ ││ └──────┬──────────┬───────────────┘ ││ НІ │ │ ТАК ││ │ │ ││ ▼ ▼ ││ Перевірте збіг │ ││ селекторів, проби │ ││ готовності Подів │ ││ │ ││ ┌─────────────────▼───────────────┐ ││ │ Рівень 3: Під-до-Поду (CNI) │ ││ │ Чи може Під A звʼязатися з IP │ ││ │ endpoint безпосередньо? │ ││ │ curl <endpoint-ip>:<port> │ ││ └──────┬──────────┬───────────────┘ ││ НІ │ │ ТАК ││ │ │ ││ ▼ ▼ ││ Проблема CNI: Під не слухає ││ маршрути, на порті або ││ інкапсуляція, застосунок ││ MTU, мережеві зламаний ││ політики ││ │└─────────────────────────────────────────────────────────────────────────┘4.2 Основні команди діагностики
Розділ «4.2 Основні команди діагностики»# === Рівень 1: DNS ===# Перевірити DNS зсередини Подуk run debug --rm -it --image=busybox:1.36 --restart=Never -- nslookup web-svc
# Перевірити resolv.conf всередині Подуk exec <pod> -- cat /etc/resolv.conf
# === Рівень 2: Сервіс / kube-proxy ===# Перевірити наявність endpointsk get endpoints <service-name>
# Перевірити правила iptables для конкретного Сервісу (на вузлі)iptables-save | grep <service-name>
# Перевірити таблицю conntrack на застарілі записиconntrack -L -d <service-clusterip>
# === Рівень 3: Під-до-Поду / CNI ===# Перевірити пряме зʼєднання Під-до-Подуk run debug --rm -it --image=busybox:1.36 --restart=Never -- \ wget -qO- --timeout=5 http://<pod-ip>:<port>
# Захопити пакети на вузлі (запускайте на вузлі, не в Поді)tcpdump -i any -nn host <pod-ip> and port <port>
# Перевірити MTUk exec <pod> -- ip link show eth0# Шукайте значення "mtu" — має відповідати конфігурації CNI
# Перевірити маршрути всередині Подуk exec <pod> -- ip route4.3 Conntrack: Прихований стан
Розділ «4.3 Conntrack: Прихований стан»Conntrack (відстеження зʼєднань) — це модуль ядра, який забезпечує роботу NAT. Він запамʼятовує, які зʼєднання відповідають яким трансляціям. Застарілі записи conntrack — поширене джерело загадкових збоїв:
# Список усіх записів conntrack для IP Сервісуconntrack -L -d 10.96.0.50
# Підрахувати записи (великі числа можуть вказувати на витік зʼєднань)conntrack -C
# Видалити застарілі записи (обережно у продакшені)conntrack -D -d 10.96.0.50 -p tcp --dport 80Коли conntrack вас підводить: Якщо Під видалений і створений заново з тим самим IP (рідко, але можливо), conntrack може все ще мати записи, що вказують на старий стан зʼєднання. Симптоми включають зʼєднання, які зависають або скидаються без видимої причини, але лише до конкретних Подів.
4.4 Чеклист скидання пакетів між вузлами
Розділ «4.4 Чеклист скидання пакетів між вузлами»Коли пакети скидаються між вузлами, пройдіть цей список:
-
Невідповідність MTU: Чи використовує CNI інкапсуляцію? Якщо так, чи зменшено MTU відповідно?
Terminal window # Перевірити MTU на інтерфейсі Поду проти тунельного інтерфейсу вузлаip link show vxlan.calico # або flannel.1, cilium_vxlan -
Правила файрвола: Чи дозволяють файрволи вузлів (iptables, firewalld, хмарні групи безпеки) протокол CNI?
- VXLAN: UDP порт 4789
- Geneve: UDP порт 6081
- BGP: TCP порт 179
- Wireguard: UDP порт 51820
-
Здоровʼя CNI: Чи працює демон CNI на всіх вузлах?
Terminal window k get pods -n kube-system -l k8s-app=calico-node # або flannel, cilium -
Вичерпання IP: Чи закінчилися IP у CIDR Подів на конкретному вузлі?
Terminal window k describe node <node> | grep -A5 "PodCIDR"
Типові помилки
Розділ «Типові помилки»| Помилка | Проблема | Рішення |
|---|---|---|
| Діагностика DNS, коли проблема в kube-proxy | Витрачений час на неправильний рівень | Дотримуйтесь трирівневої моделі: спочатку DNS, потім Сервіс, потім CNI |
| Ігнорування MTU після зміни режиму CNI | Великі пакети мовчазно скидаються | Завжди встановлюйте MTU = фізичний MTU мінус накладні витрати інкапсуляції (50 для VXLAN) |
| Не перевіряти conntrack | Застарілі записи NAT спричиняють переривчасті збої | Використовуйте conntrack -L для перевірки стану, коли зʼєднання зависають |
Забути про externalTrafficPolicy | Втрата вихідного IP клієнта або відсутність бекендів на вузлі | Розумійте Cluster (SNAT, усі бекенди) проти Local (зберігає IP, лише локальні) |
Встановлення занадто високого ndots | Множення DNS-запитів, повільні запити | Використовуйте ndots: 2 для Подів, що звертаються до зовнішніх сервісів, або використовуйте завершальні крапки |
| Тестування ClusterIP ззовні кластера | Таймаут зʼєднання | ClusterIP працює лише всередині кластера; використовуйте NodePort/port-forward для зовнішніх тестів |
| Запуск tcpdump на неправильному інтерфейсі | Захоплення нічого не показують | Використовуйте tcpdump -i any для захоплення на всіх інтерфейсах, потім звужуйте |
| Звинувачення застосунку перед перевіркою мережі | Години витрачені на діагностику коду застосунку | Завжди спочатку перевіряйте мережеве зʼєднання простими інструментами (wget, curl) |
Тест
Розділ «Тест»1. Під може звʼязатися з іншим Подом за його IP (10.244.2.8), але не може звʼязатися з ним через ClusterIP Сервісу (10.96.0.50). Який компонент найімовірніше несправний?
Відповідь
Найімовірніше несправний kube-proxy. Оскільки зʼєднання Під-до-Поду працює, CNI функціонує коректно. ClusterIP Сервісу обробляється правилами iptables/IPVS/nftables kube-proxy. Перевірте:
k get endpoints <service>— чи є endpoints?iptables-save | grep <service-name>— чи присутні правила DNAT?- Чи працює kube-proxy?
k get pods -n kube-system -l k8s-app=kube-proxy
2. Ви змінюєте CNI з Flannel з host-gw на Flannel з vxlan. Малі запити (перевірки стану, ping) працюють нормально, але великі HTTP-відповіді скидаються. Яка найімовірніша причина та виправлення?
Відповідь
Невідповідність MTU. Інкапсуляція VXLAN додає 50-байтовий заголовок. Якщо MTU Поду все ще 1500 (стандартне для host-gw), пакети близько 1500 байт перевищуватимуть ємність тунелю після інкапсуляції.
Виправлення: Встановіть MTU CNI на 1450 (1500 − 50 для накладних витрат VXLAN). У ConfigMap Flannel:
{ "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan", "MTU": 1450 }}Потім перезапустіть Поди Flannel, щоб усі вузли отримали новий MTU.
3. Розробник повідомляє, що curl api.external.com з Поду займає 2 секунди, але лише 50 мс з його ноутбука. DNS є вузьким місцем. Поясніть чому та як виправити.
Відповідь
Стандартне значення ndots:5 у Kubernetes означає, що api.external.com (2 крапки, що менше 5) розглядається як відносне імʼя. Резолвер пробує ці запити по черзі, перш ніж успішно завершитися:
api.external.com.default.svc.cluster.local— NXDOMAIN (~500 мс)api.external.com.svc.cluster.local— NXDOMAIN (~500 мс)api.external.com.cluster.local— NXDOMAIN (~500 мс)api.external.com.— SUCCESS (~50 мс)
Це 3 зайвих запити, що додають ~1,5 секунди затримки.
Виправлення (оберіть одне):
- Використовуйте завершальну крапку:
curl api.external.com. - Встановіть
dnsConfig.options.ndots: 2у специфікації Поду - Використовуйте FQDN із завершальною крапкою в конфігурації застосунку
4. Ви запускаєте k get endpoints my-service і бачите <none>. Поди працюють і мають правильні мітки. Що ще може спричинити порожні endpoints?
Відповідь
Навіть якщо Поди працюють з правильними мітками, endpoints будуть порожніми, якщо:
- Поди не Ready — проба готовності не проходить. Лише Поди, що проходять пробу готовності, додаються до обʼєкта Endpoints. Перевірте:
k get pods(шукайте0/1 READY). - Селектор Сервісу вимагає мітки, яких немає у Подів — перевірте
k describe svc my-serviceта порівняйте зk get pods --show-labels. - Поди знаходяться в іншому просторі імен, ніж Сервіс. Сервіси обирають Поди лише у своєму просторі імен.
- Контролер endpoint не працює — вкрай рідко, але перевірте Під kube-controller-manager у
kube-system.
Найчастіше відповідь — невдалі проби готовності.
5. Після видалення та повторного створення Поду-бекенду деякі існуючі зʼєднання до Сервісу зависають на 30+ секунд перед відновленням. Нові зʼєднання працюють нормально. Що відбувається?
Відповідь
Застарілі записи conntrack. Таблиця відстеження зʼєднань ядра все ще має записи, що відображають існуючі зʼєднання на IP старого Поду. Оскільки цей IP більше не існує (або належить іншому Поду), пакети відправляються в нікуди.
Записи зрештою закінчаться (таймаут TCP, зазвичай 120 секунд для встановлених зʼєднань), тому проблема зрештою зникає. Нові зʼєднання працюють, бо створюють свіжі записи conntrack, що вказують на дійсні бекенди.
Для негайного виправлення: conntrack -D -d <service-clusterip> -p tcp --dport <port>. Для запобігання: використовуйте graceful-завершення Поду (хуки preStop, зливання зʼєднань), щоб Під видалив себе з endpoints перед завершенням процесу.
Практична вправа: Виклик відстеження пакету
Розділ «Практична вправа: Виклик відстеження пакету»Мета: Простежити запит від початку до кінця — від клієнтського Поду через DNS, kube-proxy та CNI до Поду-бекенду. Ви використовуватимете tcpdump, nslookup та iptables-save для спостереження за кожним рівнем.
Середовище: кластер kind або minikube (одного вузла достатньо для цієї вправи).
Підготовка
Розділ «Підготовка»# Створити Deployment бекенду та Сервісk create deployment trace-backend --image=nginx --replicas=2k expose deployment trace-backend --port=80 --name=trace-svc
# Зачекати на готовність Подівk wait --for=condition=ready pod -l app=trace-backend --timeout=60s
# Створити Під для діагностики, який залишається працюватиk run trace-client --image=nicolaka/netshoot --restart=Never -- sleep 3600
# Зачекати на ньогоk wait --for=condition=ready pod/trace-client --timeout=60sКрок 1: Дослідити рівень DNS
Розділ «Крок 1: Дослідити рівень DNS»# Перевірити конфігурацію DNS клієнтського Подуk exec trace-client -- cat /etc/resolv.conf# Примітка: nameserver має бути ClusterIP CoreDNS
# Розвʼязати імʼя Сервісуk exec trace-client -- nslookup trace-svc# Має повернути ClusterIP trace-svc
# Спробувати FQDNk exec trace-client -- nslookup trace-svc.default.svc.cluster.local
# Порівняти: розвʼязати із завершальною крапкою (пропускає пошукові домени)k exec trace-client -- nslookup trace-svc.default.svc.cluster.local.Запишіть: Який IP повернув trace-svc? Це ClusterIP.
Крок 2: Дослідити рівень Сервісу
Розділ «Крок 2: Дослідити рівень Сервісу»# Отримати деталі Сервісуk get svc trace-svc -o wide
# Отримати endpoints (IP Подів-бекендів)k get endpoints trace-svc
# Переглянути правила iptables для цього Сервісу (потрібен доступ до вузла)# На minikube: minikube ssh# На kind: docker exec -it <node-container> bash# Потім запустіть:iptables-save | grep trace-svc# Ви побачите ланцюжки KUBE-SERVICES, KUBE-SVC-* та KUBE-SEP-*
# Ланцюжок KUBE-SVC містить правила ймовірності для балансування навантаження# Ланцюжки KUBE-SEP містять правила DNAT до конкретних IP ПодівЗапишіть: Скільки записів KUBE-SEP існує? Має відповідати кількості реплік (2).
Крок 3: Простежити пакет за допомогою tcpdump
Розділ «Крок 3: Простежити пакет за допомогою tcpdump»# В одному терміналі запустіть tcpdump на вузлі (потрібен доступ до вузла)# На kind: docker exec -it <node-container> bashtcpdump -i any -nn port 80 and host $(k get pod trace-client -o jsonpath='{.status.podIP}')
# В іншому терміналі зробіть запит з клієнтського Подуk exec trace-client -- curl -s http://trace-svc
# Спостерігайте за виводом tcpdump:# 1. Ви побачите початковий SYN від IP trace-client до ClusterIP# 2. Потім пакет після DNAT від IP trace-client до IP Поду-бекенду# 3. Відповідь від IP Поду-бекенду до IP trace-client# 4. Conntrack обертає NAT, тому trace-client бачить ClusterIPКрок 4: Дослідити conntrack
Розділ «Крок 4: Дослідити conntrack»# На вузлі перевірте записи conntrackconntrack -L -d $(k get svc trace-svc -o jsonpath='{.spec.clusterIP}') 2>/dev/null
# Ви побачите записи, що показують:# src=<client-pod-ip> dst=<clusterIP> dport=80# та зворотне відображення:# src=<backend-pod-ip> dst=<client-pod-ip>Крок 5: Перевірити вплив мережевої політики
Розділ «Крок 5: Перевірити вплив мережевої політики»# Застосувати політику, що блокує трафік до бекендуcat << 'EOF' | k apply -f -apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: deny-trace-backendspec: podSelector: matchLabels: app: trace-backend policyTypes: - Ingress ingress: [] # Порожній = заборонити весь ingressEOF
# Спробувати запит знову (має не спрацювати/таймаут)k exec trace-client -- curl -s --connect-timeout 5 http://trace-svc# Очікується: таймаут або відмова зʼєднання (залежить від CNI)
# Перевірити, що DNS все ще працює (так і повинно бути — DNS іде до CoreDNS, не до бекенду)k exec trace-client -- nslookup trace-svc
# Видалити політикуk delete networkpolicy deny-trace-backend
# Перевірити, що зʼєднання відновленоk exec trace-client -- curl -s --connect-timeout 5 http://trace-svcОчищення
Розділ «Очищення»k delete pod trace-client --forcek delete deployment trace-backendk delete svc trace-svcКритерії успіху:
- Вміння визначити ClusterIP через розвʼязання DNS
- Вміння знайти правила DNAT iptables для Сервісу
- Вміння спостерігати потік пакету в tcpdump (до DNAT та після DNAT)
- Вміння переглядати записи conntrack для активних зʼєднань
- Розуміння, що NetworkPolicy блокує трафік Під-до-Поду, але не DNS
- Вміння описати трирівневу модель діагностики (DNS, Сервіс, CNI)
Ключові висновки
Розділ «Ключові висновки»- Слідкуйте за пакетом, а не за припущеннями. Використовуйте
tcpdump,iptables-saveтаconntrack, щоб побачити, що насправді відбувається, замість вгадування. - Три рівні (DNS, kube-proxy/Сервіс, CNI/Під-до-Поду) незалежні. Ізолюйте, який рівень зламаний, перш ніж заглиблюватися.
- MTU має значення. Кожного разу, коли залучена інкапсуляція, ефективний MTU зменшується. Мовчазне скидання великих пакетів — класичний симптом.
- conntrack невидимий, але критичний. Він підтримує стан NAT для кожного зʼєднання через Сервіс. Застарілі записи спричиняють одні з найбільш заплутаних переривчастих збоїв.
- ndots:5 — це дорого. Для навантажень, що звертаються до зовнішніх сервісів, або зменште
ndots, або використовуйте завершальні крапки в іменах доменів.
Додаткове читання
Розділ «Додаткове читання»- Модуль 3.1: Глибоке занурення в Сервіси — Типи Сервісів, селектори та endpoints
- Модуль 3.6: Мережеві політики — Як політики взаємодіють зі шляхом даних
- Модуль 3.7: Плагіни CNI — Глибоке занурення в архітектуру та конфігурацію CNI
- Модуль 3.3: DNS у Kubernetes — Повна конфігурація CoreDNS та діагностика
Наступний модуль
Розділ «Наступний модуль»Модуль 3.3: DNS у Kubernetes — Глибоке занурення в конфігурацію CoreDNS, користувацькі DNS-політики та розширену діагностику.