Стройте ответ вокруг владения состоянием, лимитов, отказа и восстановления. Определение становится инженерным ответом только в конкретном production-сценарии.
Вопросы и ответы
10 подробных ответов
01Проследите полный путь DNS-резолвинга: от getaddrinfo в приложении до авторитативного сервера. Где по пути кэши и как отлаживать каждый хоп?
middle
Короткий ответ: Приложение вызывает getaddrinfo → libc смотрит nsswitch.conf (сначала /etc/hosts) → локальный stub-резолвер (systemd-resolved со своим кэшем) → рекурсивный резолвер (ISP/CoreDNS), кэширующий по TTL → root → TLD → авторитативный сервер зоны.
Подробно:
- nsswitch.conf и /etc/hosts — до всякого DNS: строка
hosts: files dnsозначает, что /etc/hosts выигрывает у сети. - Stub-резолвер — systemd-resolved (127.0.0.53) держит свой кэш; сбрасывается
resolvectl flush-caches. - Рекурсивный резолвер — кэширует ответы по TTL и, что часто забывают, NXDOMAIN тоже (negative caching, TTL берётся из SOA).
- Рекурсия — root → TLD (.com) → авторитативный сервер зоны.
Отладка по хопам: dig example.com (системный резолвер), dig @8.8.8.8 (конкретный резолвер), dig +trace (полная рекурсия от root, мимо всех кэшей), getent hosts (путь как у приложения, через nsswitch).
app → getaddrinfo → nsswitch.conf → /etc/hosts
→ stub (systemd-resolved, кэш)
→ рекурсивный резолвер (кэш по TTL)
→ root → TLD → авторитативный
⚠️ Частая ошибка: отлаживать одним dig'ом и пропустить nsswitch и /etc/hosts (dig ходит в DNS напрямую, мимо них) — и negative caching: «запись удалил, а NXDOMAIN ещё доживает свой TTL».
02curl по IP работает, а по hostname падает — но не каждый раз. Как диагностировать?
middle
Короткий ответ: «Иногда» — сигнатура DNS: один битый адрес в наборе A-записей, закэшированный отрицательный ответ, split-horizon (разные резолверы отдают разное) или экспансия search-доменов в Kubernetes (ndots). Метод: опросить dig'ом каждый резолвер в цепочке и сравнить ответы и TTL.
Подробно:
- Битый адрес в наборе — DNS отдаёт несколько A-записей round-robin; если один бэкенд мёртв, падает каждый N-й запрос.
- Negative caching — резолвер закэшировал NXDOMAIN/SERVFAIL и отдаёт его до конца TTL, хотя запись уже существует.
- Split-horizon — внутренний и внешний резолверы видят разные зоны; какой достался машине — вопрос resolv.conf.
- ndots в Kubernetes — имя с числом точек меньше 5 сначала прогоняется через search-домены (svc.cluster.local и т.д.): лишние запросы, тайм-ауты, изредка неожиданные совпадения имён.
dig +short api.example.com # системный резолвер
dig +short api.example.com @10.0.0.2 # каждый резолвер из resolv.conf
dig +trace api.example.com # что отдаёт авторитативный
dig api.example.com # сравнить ANSWER-набор и TTL
⚠️ Частая ошибка: решить «IP работает — значит, сеть в порядке» и уйти отлаживать приложение. Перемежающийся сбой по имени — это round-robin по битому набору или гонка кэшей, а не баг в коде.
03Опишите TLS-хендшейк. Зачем нужен SNI и как несколько сертификатов уживаются на одном IP?
senior
Короткий ответ: Клиент шлёт ClientHello (версии, шифры и hostname в SNI) → сервер отвечает сертификатом и параметрами обмена ключами → стороны выводят сессионные ключи, дальше трафик шифруется симметрично. SNI нужен потому, что TLS происходит ДО HTTP: Host-заголовок ещё не отправлен, и серверу нечем выбрать сертификат.
Подробно:
- ClientHello — версии TLS, список шифров, SNI открытым текстом; в TLS 1.3 сразу и key share.
- ServerHello + сертификат — сервер выбирает шифр и сертификат под пришедший SNI.
- Обмен ключами — (EC)DHE даёт forward secrecy; из общего секрета выводятся сессионные ключи.
- Почему именно SNI — hostname обязан ехать в хендшейке: Host-заголовок HTTP придёт уже внутри зашифрованного канала. Так десятки виртуальных хостов с разными сертификатами живут на одном IP:443.
| Где терминировать TLS | Плюсы | Минусы |
|---|---|---|
| На балансировщике | дёшево, центральное управление сертификатами | внутри периметра трафик открытым текстом |
| На поде / mTLS | шифрование end-to-end, взаимная аутентификация | CPU, раздача и ротация сертификатов |
⚠️ Частая ошибка: просроченный сертификат — классика аварий. «Продлим руками» — неправильный ответ: ACME/cert-manager для автообновления плюс алерт за 2–3 недели до истечения.
04L4- vs L7-балансировка: что видит и что умеет каждый уровень?
junior
Короткий ответ: L4-балансировщик видит только IP и порт — раскидывает TCP/UDP-потоки, быстрый и дешёвый, но для него протокол непрозрачен. L7 разбирает HTTP: маршрутизация по пути и заголовкам, ретраи, терминация TLS, sticky-сессии — ценой CPU на парсинг.
Подробно:
| L4 | L7 | |
|---|---|---|
| Видит | IP:порт, SYN | метод, путь, заголовки, куки |
| Маршрутизация | round-robin/хэш по потокам | /api → один пул, /static → другой |
| Ретраи и таймауты | нет — не знает, где кончается запрос | per-request ретраи, circuit breaking |
| TLS | пропускает насквозь (passthrough) | терминация, X-Forwarded-For |
| Цена | почти нулевая, миллионы pps | парсинг HTTP: CPU и латентность |
Примеры: L4 — IPVS, AWS NLB; L7 — nginx, Envoy, AWS ALB. Частый каскад: L4 снаружи → L7 внутри.
⚠️ Частая ошибка: ждать от L4 ретраев и маршрутизации по пути — он не видит HTTP и в принципе не знает, где закончился один запрос и начался следующий.
05В логах «nf_conntrack: table full, dropping packet». Что такое conntrack и как это чинить?
senior
Короткий ответ: conntrack — таблица состояний соединений в netfilter; на ней держатся NAT и stateful-файрвол. Каждый NAT-нутый поток занимает запись; таблица переполнилась — новые пакеты молча дропаются. Лечение: поднять лимит, убрать лишние NAT-хопы, укротить DNS-трафик.
Подробно:
- Зачем таблица — чтобы обратный пакет прошёл ту же трансляцию, ядро помнит каждый поток: адреса, порты, состояние, тайм-аут.
- UDP тоже считается — каждый DNS-запрос создаёт запись, живущую ~30 с после ответа. Микросервисы без DNS-кэша плодят тысячи записей — классический продовый инцидент в Kubernetes.
- Диагностика — сравнить счётчик с лимитом и посмотреть, кто ест таблицу:
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
conntrack -L -p udp --dport 53 | wc -l # сколько съел DNS
sysctl -w net.netfilter.nf_conntrack_max=1048576 # тактическое лечение
- Стратегически — меньше NAT-хопов, локальный DNS-кэш (NodeLocal DNSCache), eBPF-датаплейн (Cilium) вместо kube-proxy/iptables — потоки идут мимо conntrack.
⚠️ Частая ошибка: выкрутить лимит и закрыть тикет. Лимит — симптоматическое: причина обычно в DNS-шторме или лишнем NAT, и с ростом трафика она вернётся.
06Как iptables обрабатывает пакет: какие таблицы и цепочки, и где происходит DNAT?
middle
Короткий ответ: Пакет идёт по цепочкам в фиксированном порядке: PREROUTING (таблица nat — здесь DNAT) → решение маршрутизации → INPUT или FORWARD (filter) → POSTROUTING (nat — здесь SNAT/masquerade). Обратные пакеты правила заново не проходят: conntrack применяет обратную трансляцию автоматически.
Подробно:
- DNAT до маршрутизации — адрес назначения надо переписать до того, как ядро решит, куда пакет: себе (INPUT) или транзитом (FORWARD).
- filter в INPUT/FORWARD — здесь живут обычные правила файрвола.
- SNAT/masquerade на выходе — в POSTROUTING, когда исходящий интерфейс уже известен.
- conntrack — правило DNAT срабатывает только на первом пакете потока; остальные пакеты и обратный трафик транслируются по записи conntrack.
пакет ─► PREROUTING (nat: DNAT) ─► routing decision
│ локальный?
├─ да ─► INPUT (filter) ─► процесс
└─ нет ─► FORWARD (filter)
└─► POSTROUTING (nat: SNAT) ─► наружу
Публикация порта контейнера (-p 8080:80) — это ровно DNAT-правило в PREROUTING плюс masquerade для обратного пути.
⚠️ Частая ошибка: искать DNAT в таблице filter — и удивляться, что tcpdump за точкой трансляции показывает уже переписанные адреса: NAT случился раньше, чем вы смотрите.
07Через VPN мелкие запросы проходят, а большие аплоады виснут. Как диагностировать?
senior
Короткий ответ: Классический MTU-блэкхол: туннель съедает часть кадра (оверлей уменьшает эффективный MTU; VXLAN — минус 50 байт), а Path MTU Discovery не работает, потому что файрвол режет ICMP «fragmentation needed». Мелкие пакеты пролезают, большие молча теряются.
Подробно:
- Механика — приложение шлёт пакеты под MTU 1500, туннель добавляет заголовки, пакет не влезает; с флагом DF его отбрасывают и должны сообщить ICMP type 3 code 4 — но ICMP заблокирован, и отправитель ничего не узнаёт.
- Сигнатура — TCP-хендшейк и мелкие ответы ок (маленькие сегменты), передача данных виснет. Ровно «curl работает, upload висит».
- Тест — бинарный поиск размера с запретом фрагментации:
ping -M do -s 1472 host # 1472 + 28 = 1500: проходит?
ping -M do -s 1422 host # сужаем, ищем реальный path MTU
- Лечение — MSS clamping на границе туннеля (
--clamp-mss-to-pmtu), либо снизить MTU интерфейса, либо разрешить нужный ICMP на файрволе.
⚠️ Частая ошибка: не связать «зависит от размера» с MTU и уйти отлаживать приложение. Размерозависимый сбой через туннель — это MTU, пока не доказано обратное.
08На сервере тысячи сокетов в TIME_WAIT — это проблема?
middle
Короткий ответ: Само по себе — нет: TIME_WAIT — штатное состояние стороны, закрывшей соединение первой (ждёт 2×MSL, обычно 60 с, чтобы опоздавшие сегменты не попали в новое соединение). Болит только на стороне КЛИЕНТА с высоким churn'ом соединений: заканчиваются эфемерные порты.
Подробно:
- Зачем состояние — защита от «заблудших» сегментов старого соединения и надёжное завершение: последний ACK может потеряться.
- Когда болит — клиент (прокси, сервис с соединением на каждый запрос) плодит десятки тысяч TIME_WAIT и исчерпывает ~28k эфемерных портов на кортеж (dst IP, dst port) — новые connect'ы падают с EADDRNOTAVAIL.
- Правильное лечение — keep-alive и connection pooling: не открывать соединение на каждый запрос.
- Ручки —
tcp_tw_reuseбезопасен для исходящих (переиспользование по TCP timestamps); расширитьip_local_port_range.
ss -s # сводка: сколько timewait
ss -tan state time-wait | awk '{print $4}' | sort | uniq -c | sort -rn | head
⚠️ Частая ошибка: рекомендовать tcp_tw_recycle — он ломал клиентов за NAT (timestamps от разных машин перемешиваются) и удалён из ядра начиная с 4.12.
09Health-check балансировщика зелёный, а пользователи ловят 502/504. Как такое возможно?
middle
Короткий ответ: Health-check проверяет не то, чем живёт трафик: путь /healthz отвечает, а /api умирает на зависимости. Вторая классика — рассинхрон idle-таймаутов keep-alive: балансировщик держит соединение дольше приложения, переиспользует уже закрытый сокет и получает 502.
Подробно:
- Проверка ≠ боевой путь — check без зависимостей зелёный, а реальный запрос падает на БД или внешнем API. Нужен readiness, дёргающий реальные зависимости (аккуратно: не устроить каскад).
- Гонка keep-alive — приложение закрывает idle-соединение через 5 с, балансировщик считает его живым 60 с: следующий запрос уезжает в закрытый сокет → 502. Правило: idle-таймаут приложения больше таймаута балансировщика.
- 502 vs 504 — разные болезни: 502 — бэкенд ответил «неправильно», 504 — не ответил вовсе.
| Код | Что значит | Типовая причина |
|---|---|---|
| 502 | некорректный ответ бэкенда | гонка keep-alive, креш процесса, RST |
| 504 | не дождались ответа за таймаут | зависший воркер, таймаут БД, перегрузка |
⚠️ Частая ошибка: верить зелёному health-check'у. Он отвечает на вопрос «жив ли процесс», а не «обслуживает ли он реальные запросы».
10HTTP/1.1 vs HTTP/2 vs HTTP/3: какую проблему решает каждый и что такое head-of-line blocking?
middle
Короткий ответ: Каждая версия убирает свой уровень head-of-line blocking (HOL). В HTTP/1.1 запросы на соединении идут строго по очереди; HTTP/2 мультиплексирует стримы в одном TCP, но потеря одного пакета стопорит все стримы (HOL уровня TCP); HTTP/3 (QUIC поверх UDP) даёт независимые стримы и 0-RTT.
Подробно:
| HTTP/1.1 | HTTP/2 | HTTP/3 (QUIC) | |
|---|---|---|---|
| Транспорт | TCP | TCP | UDP |
| Параллелизм | 1 запрос на соединение (браузер открывает ~6) | мультиплексирование стримов | независимые стримы |
| HOL | на уровне HTTP: очередь запросов | ушёл из HTTP, остался в TCP | нет: потеря тормозит только свой стрим |
| Бонусы | простота, отладка глазами | HPACK, приоритеты | 0-RTT, миграция соединения при смене сети |
- HOL в 1.1 — медленный ответ держит всех за собой; pipelining на практике сломан.
- HOL TCP в h2 — TCP гарантирует порядок байтов: дыра в потоке блокирует доставку всех стримов, даже если их байты уже дошли.
- QUIC — надёжность на уровне стрима, хендшейк совмещён с TLS 1.3.
⚠️ Частая ошибка: «h2 решил HOL полностью». Он убрал HOL на уровне HTTP, но в сети с потерями h2 на одном TCP-соединении может проиграть даже 1.1 с шестью соединениями.
Источники
Источники и редакционная политика
Материалы RecallDeck сопоставлены с официальной документацией и открытыми публикациями компаний, когда первичный источник доступен. Мы не связаны с упомянутыми работодателями, не публикуем конфиденциальные задания и не продаём места в подборках. Формат найма может меняться — уточняйте его у рекрутера.
От чтения к воспроизведению
Отрепетируйте полный цикл интервью.
RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.