Читай реальный конфиг балансировщика, шлюза и CDN, затем принимай решение: лгущий health-check, несовпадение алгоритма, шлюз в один инстанс и заголовок кэша, который течёт или устаревает.
SDSenior◷ 14 min
Уровень
ОсновыJuniorMiddleSenior
Баги трафика живут в конфиге, не в прозе: health-check, лишь открывающий сокет, алгоритм, балансирующий не то, шлюз без реплики, заголовок кэша, кэширующий не тот ответ. Читай каждый сниппет, рассуждай, что реально происходит под нагрузкой, и выбирай ответ, под которым подпишется старший инженер.
Практикуй цикл, который гоняешь на ревью дизайна или конфига: найди настройку балансировщика, шлюза или CDN, рассуждай, как она ведёт себя под нагрузкой и отказом, и выбери изменение, которое поведение реально поддерживает.
Сниппет 1 — health-check
upstream api { server 10.0.1.10:8080; server 10.0.1.11:8080;}# проверка доступностиlocation /healthz { return 200 "ok"; } # статичный 200, ничего не трогает# у узла 10.0.1.11 исчерпан пул БД; каждый реальный запрос отдаёт 500
Викторина
Completed
Узел .11 проходит /healthz и остаётся в ротации, отдавая пользователям 500-е. Почему и каков фикс?
Heads-up Перезапуск — разовый; чек продолжит проходить в следующий раз, когда зависимость откажет, потому что он ничего не тестирует. Почини чек, чтобы он пробил цепочку зависимостей и LB выкидывал нездоровые узлы автоматически.
Heads-up TCP-чек ещё мельче — он лишь доказывает, что порт принимает соединение. Фикс — более глубокий HTTP-чек, трогающий БД/кэш, а не более мелкий.
Heads-up Больше узлов не чинит чек, не умеющий обнаружить отказ; плохой узел остаётся в ротации, отдавая 500-е. Сделай health-check осмысленным и добавь пассивное выкидывание по доле ошибок.
Сниппет 2 — выбор алгоритма
# кэш-уровень: каждый узел держит горячее подмножество ключей в памятиupstream cache_tier { least_conn; # балансировать по наименьшему числу активных соединений server cache-1; server cache-2; server cache-3; # ... 12 узлов}# наблюдается: hit ratio ужасен (~10%); почти каждый запрос промахивается
Викторина
Completed
Кэш-уровень использует least_conn, а hit ratio ~10%. Что не так и какой алгоритм нужен?
Heads-up 10% катастрофичен для горячего кэша — значит ключи не липнут к узлам. Причина — балансировка по соединениям вместо ключа; консистентное хеширование чинит локальность.
Heads-up Больше узлов делает хуже при least_conn — ключи разлетаются ещё шире. Проблема в алгоритме, игнорирующем ключ, а не в числе узлов; перейди на консистентное хеширование.
Heads-up Round-robin тоже игнорирует ключ, поэтому проблема локальности остаётся и hit ratio низок. Нужна привязка ключа — консистентное хеширование — а не другой алгоритм, балансирующий по числу.
Сниппет 3 — деплой шлюза
# kubernetes deployment для API-шлюза (перед 15 сервисами)kind: Deploymentmetadata: { name: api-gateway }spec: replicas: 1 # один инстанс strategy: { type: Recreate } # убить старый под, затем стартовать новый# все 15 сервисов достижимы только через этот шлюз
Викторина
Completed
В чём риск этого деплоя шлюза и каков минимальный фикс?
Heads-up Даже быстрый перезапуск — полный сбой всех 15 сервисов, пока единственный под лежит, а Recreate гарантирует разрыв на каждом деплое. Одна реплика — учебный SPOF; гоняй несколько за LB.
Heads-up StatefulSet не адресует доступность здесь — шлюз stateless. Фикс — несколько реплик с rolling update, а не стабильные идентичности/хранилище.
Heads-up Больше ресурсов помогает пропускной способности, не доступности. Один бóльший под всё равно единая точка отказа; нужна не одна реплика с rolling-деплоем.
Сниппет 4 — заголовок кэша
# ответ для GET /account/orders (показывает заказы залогиненного пользователя)HTTP/1.1 200 OKCache-Control: public, max-age=600# отдаётся через разделяемый CDN; заголовка Vary нет
Викторина
Completed
Этот аутентифицированный ответ на пользователя несёт Cache-Control: public на разделяемом CDN. Каково последствие и фикс?
Heads-up Разделяемый кэш ключуется по URL, а не по пользователю. public + нет Vary значит он может отдать заказы одного пользователя другому по тому же пути. Персонализированные ответы никогда не public на разделяемом кэше.
Heads-up Короткий TTL всё равно отдаёт закэшированную копию любому, кто бьёт по URL в окне — включая другого пользователя. Проблема в public на разделяемом кэше, не в окне свежести; используй private/no-store.
Heads-up Частота изменений неважна; проблема в том, что ответ на пользователя. Даже неизменные данные текут, если Пользователь B получает закэшированные заказы Пользователя A. Помечай private/no-store и Vary по аутентификации.
Вспомните перед уходом
01
Почему статичный-200 health-check лжёт и что делает чек осмысленным?
02
Когда least-connections/round-robin дают ужасный hit ratio кэша и каков фикс?
03
Почему ответ на пользователя с Cache-Control: public на разделяемом CDN опасен и как починить?
Итог
Каждое решение по трафику в этом юните сводится к настройке, которую можно прочесть прямо из конфига. Health-check, возвращающий статичный 200 (или просто открывающий сокет), лжёт — он никогда не трогает цепочку зависимостей, поэтому узел, отдающий 500-е, остаётся в ротации; сделай его пробящим реальную БД/кэш и добавь пассивное выкидывание. Алгоритм должен совпадать с целью: least-connections/round-robin балансируют по числу и разрушают hit ratio кэш-уровня, где консистентное хеширование по ключу хранит локальность. Шлюз при replicas: 1 — учебная единая точка отказа с простоем на каждом деплое — гоняй несколько реплик за балансировщиком с rolling update. А ответ на пользователя с Cache-Control: public на разделяемом CDN — это утечка данных, ждущая случиться — используй private/no-store и Vary. Старший навык — найти настройку, рассудить, как она ведёт себя под нагрузкой и отказом, и выбрать фикс, который поведение поддерживает — а не тот, что его прячет.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.