Подборки вопросов: DevOps · SRE · DevSecOps · MLOps · AIOps
Блок 1. Основы SRE
1. Что такое SRE и чем отличается от DevOps?
SRE — инженерный подход к надёжности: берёшь задачи эксплуатации и решаешь их как софтверные — кодом, автоматизацией, измеримыми целями. Формула Google: SRE — это то, что получается, когда эксплуатацию поручают программистам. DevOps — культура и набор практик про скорость доставки: убрать стену между разработкой и эксплуатацией, автоматизировать путь от коммита до прода. SRE можно считать конкретной реализацией DevOps-идей с жёсткой количественной рамкой: SLO, error budget, лимит на toil. Разница видна в вопросах на собесах: DevOps-инженера спросят «как задеплоить безопасно», SRE — «что делаешь, когда error budget кончился». DevOps оптимизирует поток изменений, SRE ставит этому потоку измеримую границу: катись сколько хочешь, пока не сжёг бюджет ошибок. На практике роли пересекаются процентов на семьдесят — те же Kubernetes, Terraform, CI/CD, мониторинг, — но центр тяжести разный: у DevOps — пайплайн, у SRE — SLO и инциденты. Сильный ответ называет и общее, и различие, а не противопоставляет их как враждующие лагеря: в половине компаний это вообще одна вакансия с разными названиями.
Отвечают «SRE — это DevOps в Google» и всё. Без конкретики — SLO, error budget, 50% на разработку, отказ от toil — ответ пустой. Второй провал: рассказывать, что SRE «главнее» или «глубже» DevOps; интервьюер слышит человека, который спорит об аббревиатурах вместо работы.
Follow-up интервьюера: у нас нет выделенной SRE-команды — что из SRE-практик внедрять первым? — SLO на ключевые пользовательские пути и blameless postmortem: они дают эффект без реорганизации, остальное наслаивается.
2. SLI, SLO, SLA — в чём точная разница и кто за что отвечает?
SLI — индикатор, измеримая величина: доля успешных запросов, доля запросов быстрее 300 мс, свежесть данных. SLO — цель по этому индикатору: «99.9% запросов успешны за скользящие 30 дней». SLA — договор с внешними последствиями: «если доступность ниже 99.5%, возвращаем деньги». Иерархия жёсткая: SLI — что меряем, SLO — что обещаем себе, SLA — что обещаем клиенту за деньги. SLO всегда строже SLA — внутренняя цель должна срабатывать раньше штрафов: если SLA 99.5%, внутренний SLO будет 99.9%, и у команды есть буфер на реакцию. Ответственность разнесена: SLI и SLO — территория инженеров и продукта (они решают, что важно пользователю и какой уровень достижим), SLA — территория бизнеса и юристов, инженеры дают им фактуру — чего реально можем добиться и почём. Классическая ошибка формулировок: «у нас SLA 99.99%» про внутренний сервис без договора — это SLO, а не SLA; слова путают все, и интервьюер проверяет именно точность. Хорошо добавить: SLO без консенсуса с продактом — просто число в Grafana; сила SLO в том, что все стороны договорились, что ниже него — уже проблема.
Употребляют SLA и SLO как синонимы — мгновенный маркер. И не могут ответить, почему SLO строже SLA: без понимания буфера между внутренней целью и штрафом вся конструкция — заученные аббревиатуры.
Follow-up интервьюера: у внутренней платформы нет внешних клиентов — нужен ли ей SLA? — нужен SLO, согласованный с командами-потребителями: без формальной цели каждый инцидент превращается в спор «а нам обещали».
3. Как выбирать SLI для сервиса?
От пути пользователя, а не от того, что легко померить. Правило: SLI должен деградировать тогда и только тогда, когда страдает пользователь. CPU 90% — не SLI: пользователю всё равно, пока запросы быстрые; это причина, а не симптом. Стандартный набор для request-driven сервиса: availability — доля успешных запросов (не-5xx), latency — доля запросов быстрее порога (именно доля быстрее порога, а не средняя: так метрика сама становится процентом «хороших событий»), quality — доля ответов без деградации (полная выдача против урезанной). Для пайплайнов данных — freshness и coverage: насколько свежие данные и какая доля обработана. Для хранилищ — durability. Техника выбора: садишься с продактом, выписываешь критичные пользовательские пути (логин, поиск, оплата), для каждого формулируешь «что значит хорошо» — и получаешь SLI в форме «good events / total events». Точка измерения имеет значение: меряй как можно ближе к пользователю — на балансировщике честнее, чем внутри приложения, потому что упавший под из метрик приложения исчезает вместе со своими ошибками. Количество — единицы на сервис: три-пять SLI, покрывающих реальные пути, лучше тридцати, покрывающих внутренности.
Называют SLI всё подряд: CPU, память, число подов. Вопрос «пользователю от этого больно?» валит половину списка. Вторая грабля — средняя latency вместо доли запросов быстрее порога: среднее маскирует хвост, где живут реальные страдания.
Follow-up интервьюера: где мерить availability — на клиенте, балансировщике или в приложении? — максимально близко к пользователю, докуда дотягиваешься: балансировщик — рабочий компромисс, клиентские метрики — идеал, но шумный и дорогой.
4. Error budget — как считается и что происходит, когда он сгорел?
Error budget — разрешённая доля плохих событий: 1 минус SLO. При SLO 99.9% бюджет — 0.1% запросов или времени за окно. В минутах для месячного окна: 30 дней × 24 часа × 60 минут = 43 200 минут, 0.1% от них — 43.2 минуты даунтайма в месяц. Считается и в запросах: при 100 млн запросов в месяц можно «потратить» 100 тысяч ошибок:
# SLO 99.9%, окно 30 дней
budget = 1 - 0.999 # 0.001
minutes = 30*24*60 * budget # 43.2 минуты даунтайма
requests = 100_000_000 * budget # 100 000 ошибок
# осталось бюджета
spent = bad_events / total_events
remaining = 1 - spent / budget # доля бюджета в запасе
Но расчёт — простая часть. Главное на собесе — политика: error budget работает, только если сгоревший бюджет меняет поведение команды, и это записано и подписано заранее. Классика: бюджет исчерпан — фичевые релизы стоп, катятся только фиксы надёжности и security; приоритет спринта — устранение причин, съевших бюджет; агрессивные эксперименты и рискованные миграции замораживаются до восстановления. Смысл конструкции — общий язык разработки и эксплуатации: не «SRE опять запрещает релизы», а «мы вместе договорились о цене надёжности, и цена наступила». Бюджет ещё и легализует риск: пока он есть, можно катить смелее — несгораемый бюджет означает, что вы слишком осторожны.
Считают минуты, но не знают про политику — а вопрос именно про неё: error budget без последствий — это декоративная метрика. И путают направление: бюджет не «лимит на ошибки SRE», а разрешение на риск для всей команды, включая разработку.
Follow-up интервьюера: разработка отказывается останавливать фичи, бюджет сгорел — твои действия? — эскалация по заранее подписанной политике: если политику не подписывали, это провал внедрения SLO, и начинать надо с договорённости, а не с войны.
5. Девятки доступности — сколько это в минутах простоя?
Таблицу надо знать наизусть — она мгновенно переводит разговор о надёжности в деньги и усилия. За месяц (30 дней, 43 200 минут): 99% — 432 минуты, около 7.2 часа; 99.9% — 43.2 минуты; 99.95% — 21.6 минуты; 99.99% — 4.3 минуты; 99.999% — 26 секунд. За год: 99% — 3.65 дня, 99.9% — 8.8 часа, 99.99% — 52.6 минуты, 99.999% — 5.3 минуты. Зачем это на собесе: каждая девятка стоит на порядок дороже предыдущей, и таблица делает это осязаемым. 99.9% достижимы дисциплиной: нормальные деплои, алертинг, дежурства. 99.99% — это 4 минуты в месяц: человек не успевает даже проснуться и открыть ноутбук, значит, обязательна автоматика — автофейловер, автооткат, мультизона; ручной он-колл на этом уровне уже не работает. 99.999% — территория, где каждый компонент задублирован, деплой полностью автоматизирован и большинству бизнесов это не нужно. Отсюда правильный вопрос при проектировании: не «сколько девяток мы хотим», а «сколько минут простоя в месяц бизнес реально готов терпеть и сколько готов платить за следующую девятку».
Не могут перевести девятки в минуты — и тогда рассуждения о надёжности повисают в воздухе: кандидат, обещающий 99.99% с ручным фейловером «минут за пятнадцать», сам себе противоречит и не замечает этого.
Follow-up интервьюера: бизнес требует «пять девяток» — что ответишь? — покажу цену: 26 секунд в месяц означают полную автоматизацию восстановления, мультирегион и удвоение затрат; обычно после цифр запрос превращается в 99.9% для всего и 99.99% для платёжного пути.
6. Toil — что это по определению Google и зачем правило 50%?
Toil — не любая неприятная работа, а операционная работа с конкретными признаками: ручная, повторяющаяся, автоматизируемая, тактическая, без долгосрочной ценности и растущая линейно с ростом сервиса. Ручной рестарт зависшего воркера — toil: делается руками, повторяется, скрипт справился бы, а после выполнения система не стала лучше — завтра то же самое. Разбор сложного инцидента — не toil: он требует головы и оставляет после себя знание и фиксы. Написание автоматизации — тем более не toil, это его лекарство. Правило Google: toil не должен съедать больше ~50% времени SRE, остальное — инженерная работа, которая уменьшает будущий toil: автоматизация, улучшение надёжности, инструменты. Логика прямая: если команда на 100% занята ручной текучкой, она деградирует в классический ops — сервис растёт, toil растёт линейно, людей нанимают вслед за нагрузкой, и это не масштабируется. 50% — механизм самосохранения: половина времени тратится на то, чтобы вторая половина уменьшалась. Практика: toil надо мерить (трекать время по категориям хотя бы грубо), выносить в бэклог автоматизации и защищать инженерное время так же жёстко, как он-колл.
Называют toil'ом всё скучное — «митинги тоже toil». Нет: критерии конкретные, и интервьюер попросит классифицировать примеры. Второй провал — не знать, зачем 50%: без объяснения про линейный рост и деградацию в ops правило звучит как случайное число.
Follow-up интервьюера: toil стабильно выше 70% — что делаешь? — меряю и показываю: список топ-источников, стоимость в часах, план автоматизации топ-3. Если менеджмент не даёт времени — это уже разговор о том, что команду наняли не как SRE, а как кнопконажимателей.
7. Почему 100% — неправильная цель надёжности?
Три аргумента, и на собесе стоит выдать все. Первый — пользователь не заметит: он приходит через сеть с её потерями, Wi-Fi, мобильный оператор, его собственный телефон дают свой фон отказов. Если твой сервис надёжнее, чем путь до него, дополнительные девятки растворяются в чужих ошибках — пользователь не отличит 99.999% от 100%. Второй — цена растёт нелинейно: каждая девятка дороже предыдущей на порядок, а последние проценты пути к 100% стоят бесконечно — полное дублирование всего, отказ от любых изменений, потому что любое изменение — риск. Третий, главный для SRE, — 100% означает нулевой error budget, то есть запрет на риск: нельзя катить релизы, нельзя проводить эксперименты, нельзя делать миграции. Система со 100%-ной целью замирает — а бизнесу нужны фичи. Надёжность — не самоцель, а бюджет, который тратится на скорость развития: правильная цель — «достаточно надёжно, чтобы пользователь был доволен, и достаточно свободно, чтобы продукт развивался». Отсюда и вся конструкция SLO: она не про максимум надёжности, а про осознанно выбранный уровень, за которым дополнительные усилия не приносят ценности.
Отвечают только про «дорого». Дорого — половина ответа; вторая половина — 100% лишает права на изменения (нулевой бюджет ошибок) и не отличима для пользователя от 99.99% из-за фона отказов на пути к нему. Кто не называет error budget, тот не связал вопрос с ядром SRE.
Follow-up интервьюера: а есть системы, где цель всё-таки близка к 100%? — durability хранилищ, safety-критичные системы: там теряемое невосстановимо. Но и там это отдельные свойства, а не availability всего сервиса.
Блок 2. Надёжность и деградация
8. SPOF — как искать и устранять единые точки отказа?
SPOF — компонент, отказ которого кладёт систему целиком. Искать методично, а не вспоминать: рисуешь путь запроса от пользователя до данных и на каждом шаге спрашиваешь «что будет, если это умрёт». DNS, балансировщик, единственный инстанс приложения, мастер базы, кеш, очередь, NAT-гейтвей, зона облака — и дальше вглубь: единственный сетевой линк, один инженер, который знает, как это чинить (человек — тоже SPOF), сертификат с ручным продлением. Проверка честнее анализа: устроить контролируемый отказ и посмотреть — про это блок про chaos engineering. Устранение — избыточность плюс автоматический failover: N+1 (нагрузка держится при отказе одного узла), реплики базы с автопереключением, мультизонное размещение, пара балансировщиков с VRRP или DNS-фейловером. Ключевой нюанс, который отличает зрелый ответ: избыточность без автоматического и регулярно проверяемого переключения — не избыточность, а декорация. Резервная реплика, на которую ни разу не переключались, при инциденте не переключится — это правило подтверждается с печальной регулярностью. И второй нюанс: устранение SPOF стоит денег, поэтому приоритизируй по пути критичности — платёжный путь дублируем всюду, админка переживёт.
Перечисляют железо и забывают уровни выше: одна зона облака, один регион, одна очередь, один человек со знанием, один вендор. И почти все забывают сказать, что failover надо регулярно испытывать — резерв без учений это чекбокс, а не надёжность.
Follow-up интервьюера: балансировщиков два, но конфиг им раскатывает один Ansible-плейбук — есть SPOF? — да, общий канал изменений: кривой конфиг убьёт оба одновременно. Избыточность экземпляров не защищает от коррелированного отказа через общую зависимость.
9. Graceful degradation — что резать первым, когда система захлёбывается?
Graceful degradation — способность системы терять функциональность по частям, а не целиком: перегрузка или отказ зависимости приводит к урезанному сервису, а не к белому экрану. Проектируется заранее: функциональность делится на критичное ядро и необязательную периферию, и у периферии есть выключатели. Классический пример — интернет-магазин: платёжка и оформление заказа — ядро, деградировать не имеют права; лента рекомендаций, отзывы, «с этим товаром покупают», персонализация — периферия, которую режут первой. Пользователь без рекомендаций купит; пользователь без кнопки «оплатить» — нет. Механика: feature flags и рубильники деградации (отключить тяжёлые фичи одним переключением, без деплоя), фолбэки на дефолт (рекомендации недоступны — показать бестселлеры из кеша), таймауты на некритичные вызовы агрессивнее, чем на критичные — пусть виджет отвалится за 200 мс, а не тянет страницу три секунды. Важно, что деградация — решение продуктовое, а не только техническое: список «что резать первым» согласовывается с бизнесом заранее, в спокойное время. Во время инцидента спорить о приоритетах поздно — рубильники и порядок их применения должны лежать в ранбуке.
Говорят «отключим лишнее», но не могут ответить, что именно лишнее в конкретной системе и кто это решил. Отсутствие заранее согласованного порядка деградации означает, что резать будут наугад в три ночи — и однажды отрежут платёжку, спасая рекомендации.
Follow-up интервьюера: как убедиться, что деградированный режим вообще работает? — включать его регулярно: рубильник, который год не трогали, при инциденте не сработает. Часть команд гоняет деградацию в учениях, часть — планово на проценте трафика.
10. Load shedding — когда отвечать 503 лучше, чем обслуживать всех?
Load shedding — осознанный сброс части нагрузки: система, которая не вывозит, быстро отвечает части клиентов 503, чтобы остальных обслужить нормально. Контринтуитивная часть, которую проверяет вопрос: отказ части запросов — акт защиты, а не поражение. Перегруженный сервис без шеддинга деградирует для всех сразу: очереди растут, latency ползёт вверх, клиенты начинают ретраить по таймауту, ретраи добавляют нагрузку — и система сваливается в метастабильное состояние, из которого не выходит даже после спада трафика, потому что обслуживает уже только ретраи. Быстрый 503 дешевле медленного 200: клиент получает честный сигнал «отвали и приди позже» (в идеале — с Retry-After), а сервер тратит микросекунды вместо секунд. Реализация: лимит на конкурентные запросы или глубину очереди — всё сверх лимита отбивается сразу; приоритизация — шеддить сначала дешёвое и некритичное (боты, прогрев, аналитика), потом дорогое, платёжные запросы — последними; сброс по возрасту — запрос, просидевший в очереди дольше клиентского таймаута, обслуживать бессмысленно, клиент уже ушёл. Признак зрелости в ответе — связка с graceful degradation: shedding режет количество, деградация режет качество, работают вместе.
Кандидату психологически трудно сказать «отвечу ошибкой» — и он рассказывает про автоскейлинг. Но скейлинг занимает минуты, а перегрузка убивает за секунды; кто не понимает, что медленный ответ хуже быстрого отказа, тот не видел систему в метастабильном штопоре.
Follow-up интервьюера: как выбрать порог, после которого начинаем шеддить? — из нагрузочного тестирования: точка, где latency начинает нелинейно расти, и есть предел здоровой ёмкости; порог ставится с запасом до неё, а не по максимуму железа.
11. Backpressure — как система должна сопротивляться перегрузу?
Backpressure — механизм, которым перегруженный потребитель сообщает производителю «медленнее», и давление распространяется вверх по цепочке вместо того, чтобы копиться в буферах. Противоположность — принять всё и молча складывать: очереди растут, память кончается, latency уходит в минуты, а отправители продолжают слать, потому что никто им не сказал «стоп». Формы backpressure по слоям: TCP делает это из коробки — окно приёмника сужается, отправитель замедляется; в HTTP — 429/503 с Retry-After; в gRPC и HTTP/2 — flow control на уровне протокола; в очередях — ограниченные буферы и блокировка продюсера (bounded queue: если некуда класть, продюсер ждёт или получает отказ — это фича, а не баг); в стриминге — реактивные протоколы, где потребитель явно запрашивает N элементов. Ключевое проектное решение — ограниченность всех буферов: неограниченная очередь не решает перегруз, а прячет его до OOM, превращая честный отказ в внезапную смерть. Плюс связка с load shedding: backpressure — «замедлись», shedding — «этих не обслуживаем»; первая стратегия сохраняет всех ценой скорости, вторая сохраняет скорость ценой части клиентов. Выбор зависит от того, что дороже — latency или отказ.
«Поставим очередь, она сгладит нагрузку» — и на этом всё. Очередь без ограничения размера и без реакции продюсера на заполнение — отложенный OOM: кандидат, не проговоривший ограниченность буферов, предлагает спрятать проблему, а не решить.
Follow-up интервьюера: Kafka между сервисами — это backpressure? — частично: брокер разгружает продюсера от медленного консюмера, но лаг консюмера растёт. Нужен мониторинг лага и стратегия: скейлить консюмеров, шеддить или мириться с задержкой.
12. Thundering herd и cache stampede — что это и как гасить?
Thundering herd — толпа клиентов, одновременно бьющая в одну точку: после рестарта сервиса все переподключаются разом, после сбоя все ретраят синхронно, по крону в 00:00 стартуют тысячи джобов. Cache stampede — частный и самый частый случай: горячий ключ кеша истёк, и сотни запросов одновременно промахиваются и идут в базу за одним и тем же — база, привыкшая к 1% промахов, получает шквал одинаковых тяжёлых запросов и ложится, укладывая всех. Лечится тремя приёмами, и называть стоит все. Замок (request coalescing): на промахе только один запрос идёт пересчитывать значение, остальные ждут результата или получают протухшее значение (stale-while-revalidate) — в базу уходит один запрос вместо сотни. Раннее вероятностное обновление: ключ обновляется не по факту истечения, а с растущей вероятностью незадолго до — толпа не собирается, потому что истечения не происходит. Jitter TTL: к времени жизни добавляется случайная добавка, чтобы ключи, созданные одновременно (после деплоя, прогрева), не истекали одновременно. Тот же jitter — универсальное лекарство от любых синхронных толп: рандомизируй ретраи, кроны, переподключения. Синхронность — враг: всё, что может случиться одновременно у многих, однажды случится одновременно у всех.
Знают слово, но не приёмы: «поднимем TTL» не отвечает на вопрос — ключ всё равно когда-нибудь истечёт, и толпа соберётся. Замок, раннее обновление, jitter — конкретика, которую ждут; без неё ответ на уровне «постараемся, чтобы не».
Follow-up интервьюера: кеш целиком умер (рестарт Redis) — прогрев с нуля под полной нагрузкой, действия? — это уже не stampede, а cold cache: rate limit к базе, постепенный впуск трафика, прогрев горячих ключей до открытия шлюзов — иначе база ляжет, и инцидент удвоится.
13. Идемпотентность — почему она обязательна при ретраях?
Идемпотентная операция при повторе даёт тот же результат, что при однократном выполнении: DELETE по id, установка статуса, PUT с полным состоянием. Неидемпотентная — создаёт эффект на каждый вызов: списание денег, отправка письма, INSERT без ключа. Почему это фундамент надёжности: в распределённой системе таймаут не означает, что операция не выполнилась. Клиент отправил «списать 500 рублей», ответ не пришёл — сервер мог не получить запрос, мог получить и упасть до обработки, мог обработать и упасть до ответа. Клиент не знает, в каком из миров живёт, и единственная безопасная стратегия — повторить. Но повторять списание можно только если сервер умеет распознать дубль. Стандартное решение — idempotency key: клиент генерирует уникальный ключ операции (UUID), сервер хранит результат первого выполнения по этому ключу и на повтор с тем же ключом возвращает сохранённый результат, не выполняя операцию заново. Так работают платёжные API — Stripe с заголовком Idempotency-Key самый известный пример. Детали реализации, которые ценят: ключ хранится с TTL, проверка и запись — атомарно (иначе гонка двух одновременных дублей), а ключ привязан к содержимому операции — тот же ключ с другой суммой должен вернуть ошибку, а не старый результат.
Определение дают, а связку с ретраями — нет. Цепочка «таймаут не значит не выполнилось → повторять обязательно → повтор без идемпотентности = двойное списание» — и есть ответ; кто её не проговаривает, тот знает слово, но не проектировал ретраи вокруг денег.
Follow-up интервьюера: где хранить idempotency-ключи при нескольких инстансах сервиса? — в общем хранилище с атомарным set-if-absent: Redis SET NX с TTL или уникальный индекс в базе. Локальная память инстанса не работает — ретрай придёт на соседа.
14. Bulkhead и cell-based архитектура — как изолировать отказ?
Метафора из кораблестроения: переборки (bulkheads) делят корпус на отсеки, пробоина топит отсек, а не корабль. В системах — то же: ресурсы делятся на изолированные пулы, чтобы отказ одного потребителя не съел всё. Уровень приложения: отдельные пулы соединений и потоков на каждую внешнюю зависимость — если рекомендательный сервис завис, он исчерпает свой пул из 10 соединений, а не общий из 100, и платёжные вызовы продолжат ходить. Без переборки один медленный даунстрим вешает весь сервис — классика продовых инцидентов. Уровень инфраструктуры: отдельные node pools под критичные и фоновые нагрузки, лимиты ресурсов, выделенные очереди под тяжёлых клиентов. Cell-based архитектура — та же идея, доведённая до масштаба системы: вся система нарезается на самодостаточные ячейки (cell), каждая — полный стек со своими инстансами, базой и очередями, обслуживающий свой кусок пользователей; роутинг наверху раскидывает пользователей по ячейкам. Отказ ячейки бьёт по её доле пользователей — 5%, а не 100%: blast radius ограничен архитектурно. Бонусы: канареечные релизы на одну ячейку, предсказуемое масштабирование добавлением ячеек. Цена: дублирование инфраструктуры, сложность роутинга и миграции пользователей между ячейками — поэтому cells оправданы на больших масштабах, а bulkhead-пулы обязательны почти всегда.
Рассказывают только про circuit breaker — это другой паттерн: breaker реагирует на отказ, bulkhead ограничивает его радиус заранее. И не могут привести пример из жизни: «медленная зависимость исчерпала общий пул и повесила всё» — сценарий, который узнаёт любой, кто дебажил такой инцидент.
Follow-up интервьюера: чем cell отличается от зоны доступности? — зона — граница инфраструктурного отказа, cell — граница логического: кривой релиз или отравленные данные убьют все зоны сразу, но останутся в одной ячейке. Зрелые системы используют обе оси.
15. Почему твой SLO не может быть выше SLO критичной зависимости?
Арифметика надёжности безжалостна: если твой сервис на каждый запрос синхронно ходит в зависимость с доступностью 99.9%, твоя доступность не превысит 99.9% — сверху добавляются ещё и твои собственные отказы. Последовательные зависимости перемножаются: сервис с доступностью 99.95%, зовущий базу 99.95% и auth 99.9%, в сумме даёт примерно 99.8% — каждый хоп съедает часть бюджета. Отсюда практическое правило Google: критичная зависимость должна быть на порядок (на девятку) надёжнее, чем твоя цель, иначе она съедает весь твой error budget своими легальными отказами. На собесе от тебя ждут два хода. Первый — аудит: выпиши синхронные зависимости критического пути и их SLO; если у зависимости SLO ниже твоей цели или SLO вовсе нет — твоя цель фикция. Второй — что делать: поднять надёжность зависимости (не всегда в твоей власти), убрать её с критического пути (кеш, асинхронность, очередь — падение зависимости перестаёт быть твоим падением), добавить fallback (деградированный ответ вместо ошибки) или честно снизить свой SLO. Кеш поверх зависимости — самый частый ход: доступность связки «зависимость + кеш» выше, чем у зависимости в одиночку, ценой свежести данных.
Обещают 99.99%, стоя на базе с 99.9%, и не видят противоречия. Вопрос «перечисли зависимости критического пути и их SLO» вскрывает: композицию надёжности не считал — значит, SLO выбирался методом «красивое число».
Follow-up интервьюера: зависимость — внешний API без SLA, выкинуть нельзя. Как жить? — мерить его доступность самому, спроектировать fallback и таймауты под его реальное поведение и заложить его отказы в свой error budget: чужая ненадёжность — твоя задача проектирования.
Блок 3. Таймауты, ретраи, circuit breaker
16. Почему таймаут обязателен на каждый внешний вызов?
Потому что вызов без таймаута — это разрешение зависнуть навсегда, и однажды оно будет использовано. Дефолты библиотек коварны: у многих HTTP-клиентов таймаут по умолчанию — бесконечность или десятки минут. Сценарий инцидента типовой: даунстрим начал отвечать медленно, твои запросы к нему висят, каждый висящий запрос держит поток или соединение из пула, пул исчерпывается за секунды — и твой сервис перестаёт отвечать всем, включая запросы, которым этот даунстрим вообще не нужен. Медленная зависимость превращается в твой полный отказ — каскад через исчерпание ресурсов. Правила выбора значения: таймаут ставится от пользователя вниз — если пользователь ждёт максимум 2 секунды, нет смысла ждать базу 10; ориентир — недалеко от p99 latency зависимости в здоровом состоянии: слишком щедрый таймаут держит ресурсы на запросах, которые уже никому не нужны, слишком жадный — рубит легитимные медленные ответы и порождает ложные ретраи. Различай connect timeout (короткий, сотни миллисекунд — если TCP не установился быстро, дальше хуже не станет) и request timeout (по p99 операции). И помни: таймаут — не обработка ошибки, а её создание; за ним обязан стоять план — ретрай, fallback или честная ошибка пользователю.
«Поставим таймаут побольше, чтобы не рубить лишнего» — ответ человека, не терявшего пул соединений. И мало кто знает дефолты своих клиентов: вопрос «какой таймаут у вашего HTTP-клиента из коробки» — почти всегда молчание, а там бесконечность.
Follow-up интервьюера: таймаут сработал — запрос на сервере продолжает выполняться? — как правило да: клиент ушёл, сервер молотит дальше впустую. Поэтому нужны deadline propagation и отмена на сервере — иначе таймауты клиента не снижают нагрузку.
17. Retry с exponential backoff и jitter — зачем каждая из частей?
Ретрай — правильная реакция на транзиентную ошибку, но голый ретрай опасен, и каждая добавка в формуле лечит конкретную болезнь. Немедленный повтор бьёт в сервис, который ещё не оправился, — exponential backoff разносит попытки: 1с, 2с, 4с, 8с — даёт время на восстановление и снижает нагрузку от ретраев экспоненциально. Но backoff без jitter сохраняет синхронность: тысяча клиентов, отвалившихся одновременно, повторит одновременно — волнами через 1, 2, 4 секунды, и каждая волна добивает сервис в момент попытки встать. Jitter — случайность в паузе — размазывает волну в ровный поток:
import random, time
def call_with_retry(fn, max_attempts=4, base=1.0, cap=30.0):
for attempt in range(max_attempts):
try:
return fn()
except TransientError:
if attempt == max_attempts - 1:
raise
# full jitter: пауза от 0 до экспоненциального потолка
delay = random.uniform(0, min(cap, base * 2 ** attempt))
time.sleep(delay)
Обязательные оговорки, которые отличают продовый ответ: ретраить только транзиентное (таймаут, 503, обрыв соединения) — повторять 400 или 401 бессмысленно, а неидемпотентные операции без idempotency key — опасно; число попыток ограничено (3–4, не «пока не получится»); общий дедлайн важнее числа попыток — ретраи не должны вылезать за время, которое готов ждать вызывающий; и cap на паузу, чтобы backoff не улетал в минуты.
Пишут ретрай без jitter — и на вопрос «что будет, когда 10 тысяч клиентов отвалятся одновременно» не видят синхронных волн. Jitter кажется косметикой, а это главная защита от thundering herd собственного производства.
Follow-up интервьюера: почему full jitter (от нуля), а не небольшой разброс вокруг экспоненты? — полная случайность размазывает нагрузку ровнее всего; разброс ±10% оставляет волны почти нетронутыми. Это подтверждено симуляциями AWS — full jitter даёт минимум конкуренции.
18. Retry storm — как ретраи умножают нагрузку и зачем retry budget?
Ретраи в глубокой цепочке перемножаются. Пользователь → API → сервис A → сервис B, на каждом уровне по 3 попытки: когда B деградирует, каждый вызов A делает к нему 3 запроса, каждый вызов API — 3 вызова A, то есть 9 запросов к B, а с клиентскими ретраями — 27. Система, у которой заболел нижний слой, самоорганизуется в DDoS против собственного больного: нагрузка на отказавший компонент растёт в разы ровно в момент, когда он меньше всего может её принять. Это retry storm, и он превращает деградацию в полный отказ, а короткий сбой — в затяжной. Защиты. Первая — ретраить на одном уровне, а не на каждом: обычно у точки входа, где есть контекст про пользователя; глубокие слои отдают ошибку быстро и наверх. Вторая — retry budget: ретраи не больше фиксированной доли трафика (например, 10%); пока ошибки редки, каждый неудачный запрос повторяется, но при массовом отказе ретраи упираются в бюджет — система не умножает нагрузку в худший момент. Так работает Envoy и описанный в Google SRE Book подход с per-client квотами. Третья — circuit breaker, который вообще прекращает попытки при массовых отказах. И сигнал зрелости: сервер может явно запретить повтор (заголовок, код ошибки) — «мне плохо, не ретрай».
Арифметику 3×3×3 многие видят впервые прямо на собесе — и это честный маркер, что ретраи в их системах настроены на каждом слое по дефолту. Слов «retry budget» не знает большинство; кто знает — почти наверняка тушил такой шторм.
Follow-up интервьюера: на каком уровне цепочки оставить ретраи, если выбрать один? — ближе к пользователю: там известно, сколько он готов ждать, и один ретрай оттуда не умножается. Внизу — быстрые таймауты и честные ошибки.
19. Circuit breaker — состояния и когда размыкать
Circuit breaker — предохранитель между тобой и зависимостью: когда зависимость массово отказывает, он перестаёт её вызывать вообще, отдавая ошибку или fallback мгновенно. Три состояния. Closed — норма: запросы идут, breaker считает ошибки в скользящем окне. Порог превышен (например, больше 50% ошибок при минимум N запросов за окно) — переход в open: все вызовы отбиваются сразу, без сетевого похода; зависимость получает передышку вместо потока запросов и ретраев, а твои пулы не заняты ожиданием заведомых таймаутов. Через выдержку (десятки секунд) — half-open: пропускается пробная порция запросов; успех — breaker замыкается обратно в closed, провал — снова open и новая выдержка. Смысл half-open — проверить выздоровление малой кровью, не обрушив на встающий сервис полный трафик. Тонкости настройки, которые ждут в ответе: порог по доле ошибок, а не по абсолюту, и с минимальным объёмом запросов — иначе два таймаута в тихий час разомкнут цепь; breaker — на каждую зависимость отдельно (и в идеале на каждый инстанс/эндпоинт — деградировать может одна нода); таймауты и медленные ответы считаются отказами наравне с ошибками; открытие и закрытие breaker'а — событие в мониторинг: разомкнувшийся предохранитель — это инцидент зависимости, о котором надо знать.
Пересказывают три состояния по учебнику, но плывут на «а какой порог поставишь и почему»: без доли ошибок, минимального объёма и окна breaker в проде либо хлопает от шума, либо не срабатывает никогда. Состояния — теория, пороги — опыт.
Follow-up интервьюера: чем circuit breaker отличается от ретраев с backoff — зачем оба? — ретрай — оптимизм по поводу единичной ошибки, breaker — пессимизм по поводу массовой: ретраи лечат случайные сбои, breaker останавливает бессмысленные попытки при системном отказе. Вместе: ретраи внутри замкнутой цепи, breaker сверху.
20. Deadline propagation — зачем передавать дедлайн через цепочку сервисов?
Дедлайн — абсолютное время, после которого ответ никому не нужен. Пользовательский запрос имеет бюджет — скажем, 2 секунды; deadline propagation — передача остатка этого бюджета через всю цепочку вызовов: API потратил 300 мс и зовёт сервис A с дедлайном «осталось 1.7 с», тот потратил своё и зовёт B с остатком. Каждый сервис знает, сколько времени реально есть, и может отказаться сразу: если осталось 50 мс, а операция стоит 200, честнее вернуть ошибку немедленно, чем сделать работу, результат которой выбросят. Без пропагации живёт классический абсурд: пользователь ушёл по таймауту 2 секунды, а в глубине цепочки сервис B ещё 30 секунд честно молотит запрос к базе — ресурсы горят на обслуживание мертвецов, и под нагрузкой эта мёртвая работа вытесняет живую. Технически: в gRPC дедлайны — родная механика, передаются автоматически, у клиента context с отменой; в HTTP — заголовок с остатком бюджета и дисциплина руками; плюс отмена вниз — если клиент отвалился, вызовы к базе и даунстримам надо отменять, а не доделывать. Правило настройки: таймаут каждого уровня меньше таймаута вызывающего — иначе верх отвалится раньше, чем низ узнает, и смысл цепочки теряется.
Настраивают таймауты на каждом сервисе изолированно: у API 2 секунды, у сервиса в глубине — 10, и никто не видит, что нижний таймаут никогда не сработает до верхнего. Вопрос «кто и когда отменит запрос к базе, если пользователь уже ушёл» — обычно тишина.
Follow-up интервьюера: дедлайн истёк на середине записи в базу — отменять? — запись, в отличие от чтения, отменять опасно: результат станет неопределённым. Писать быстро и идемпотентно, отмену применять к чтениям и вычислениям — а для записей дедлайн проверять до старта.
21. Fallback-стратегии — что отдавать, когда зависимость лежит?
Fallback — заранее спроектированный ответ второго сорта вместо ошибки. Арсенал по убыванию качества. Кеш: отдать последнее известное значение — протухшие на десять минут рекомендации лучше их отсутствия; паттерн stale-while-revalidate и serve-stale-on-error должны быть настроены заранее. Дефолт: нет персонализации — покажи общий топ; не считается скоринг — примени консервативное правило. Очередь: операция, которую не обязательно выполнять синхронно, откладывается — «заказ принят, подтверждение придёт позже»; асинхронность превращает отказ зависимости в задержку вместо ошибки. Урезанный ответ: выдача без обогащения, страница без виджета. И честная ошибка — тоже стратегия, причём для части операций единственно верная: показывать пользователю «оплата прошла» по дефолту, когда платёжка недоступна, — катастрофа; для операций с деньгами и записью состояния fallback часто невозможен, и это надо сказать явно. Правила инженерии фолбэков: fallback-путь должен быть проще и надёжнее основного (fallback, зовущий ещё один сервис, — новая точка отказа); он должен быть постоянно прогрет и протестирован — код, который исполняется только в катастрофу, в катастрофу и сломается; и его срабатывание — метрика с алертом: молчаливый fallback маскирует деградацию, система «работает», пока не выясняется, что неделю все жили на кеше.
Предлагают fallback для всего подряд, включая платежи и запись данных, — не видя, что местами деградированный ответ хуже ошибки. И почти никто не говорит про мониторинг срабатываний: fallback без алерта — способ не заметить инцидент, а не пережить его.
Follow-up интервьюера: как тестировать fallback-пути? — инжектить отказы зависимостей регулярно: в тестах — моками, в проде — chaos-экспериментами на доле трафика. Fallback, не сработавший ни разу за год, считается несуществующим.
22. Почему ретраить неидемпотентный POST опасно — и что делать?
POST «создать заказ» ушёл, ответ не пришёл. Таймаут не говорит, где умер запрос: до сервера, на сервере до коммита или после коммита на обратном пути. В последнем случае заказ уже существует — ретрай создаст второй. Дубль заказа, двойное списание, два письма пользователю — стандартные последствия наивного ретрая. При этом не ретраить — тоже плохо: транзиентные ошибки реальны, и без повторов надёжность падает. Разрешение дилеммы — сделать операцию безопасной для повтора, а не отказаться от повтора. Первый путь — idempotency key: клиент передаёт уникальный ключ операции, сервер дедуплицирует и на повтор возвращает результат первого выполнения; ретраить становится безопасно на любом уровне. Второй — перевести операцию в естественно идемпотентную форму: PUT с клиентским id ресурса вместо POST с серверной генерацией — повтор перезапишет то же самое. Третий — сверка перед повтором: прежде чем ретраить, спросить «а не создался ли заказ» — работает, но добавляет запрос и гонку. Четвёртый — асинхронность: писать команду в очередь с exactly-once-семантикой на стороне консюмера (то есть та же дедупликация, но в одном месте). Отдельно проговори: автоматические ретраи в инфраслое — nginx, service mesh, клиентские библиотеки — по умолчанию должны ретраить только идемпотентные методы; включённый бездумно retry на POST в прокси — мина, которую находят по двойным заказам.
«POST не ретраим» — и всё. Это не ответ, а капитуляция: интервьюер ждёт способов сделать повтор безопасным. Обратная грабля — не знать, что mesh или nginx с retry-политикой могут ретраить POST сами, без ведома приложения.
Follow-up интервьюера: сервер сгенерировал idempotency key сам — работает? — нет: ключ должен переживать потерю ответа, то есть создаваться на стороне, которая повторяет, — у клиента. Серверный ключ умирает вместе с потерянным ответом.
Блок 4. Балансировка и трафик
23. L4 vs L7 балансировка — что видит каждая и когда что брать?
L4 работает на уровне TCP/UDP: видит IP и порты, не заглядывая в содержимое, — решение о маршруте принимается по соединению, дальше пакеты летят по установленному пути. Быстро, дёшево по CPU, миллионы соединений — но никакой логики про HTTP: нельзя маршрутизировать по URL, нельзя видеть код ответа, одно долгоживущее соединение прибито к одному бэкенду. L7 разбирает протокол приложения: видит путь, заголовки, куки — умеет маршрутизацию по /api на одну группу, по хосту на другую, канареечные проценты, ретраи, терминацию TLS, сжатие; балансирует запросы, а не соединения — критично для gRPC, где одно HTTP/2-соединение несёт тысячи запросов, и L4 приклеит их все к одному поду. Цена — CPU на парсинг и лишний хоп. Практика: обычно слоями — L4 на входе как дешёвый распределитель (в облаках NLB, в Kubernetes — Service), L7 за ним как умный маршрутизатор (Ingress, Envoy, nginx). Выбор по потребности: нужен просто раскидать TCP — L4 и не переплачивай; нужна маршрутизация, канарейки, работа с HTTP-семантикой — L7. Здоровый ответ упоминает и наблюдаемость: L7 видит коды ответов и latency запросов — то, из чего строятся SLI; L4 может предложить только счётчики байтов и соединений.
Не могут объяснить проблему gRPC через L4 — а это любимый продовый пример: сервис отскейлили, а нагрузка осталась на старых подах, потому что HTTP/2-соединения живут часами и L4 балансирует соединения, а не запросы.
Follow-up интервьюера: клиентская балансировка вместо центральной — когда оправдана? — во внутренней сетке с service discovery: клиент знает список бэкендов и балансирует сам, минус лишний хоп; в этом и суть sidecar-подхода service mesh.
24. Round robin, least connections, consistent hashing — какой алгоритм когда?
Round robin — по кругу: простой, предсказуемый, хорош, когда запросы примерно одинаковы по стоимости и бэкенды одинаковы по мощности; версия с весами закрывает неравные машины. Слабость — слепота к фактической нагрузке: тяжёлые запросы могут случайно скучковаться на одном бэкенде, и RR продолжит грузить его наравне со свободными. Least connections отдаёт запрос бэкенду с наименьшим числом активных соединений — прокси для «кто свободнее»; хорош при разнородных по длительности запросах: медленный бэкенд копит соединения и автоматически получает меньше нового. Ловушка — при холодном старте или после рестарта у ноды ноль соединений, и на неё обрушивается всё; лечится slow start. Consistent hashing маршрутизирует по ключу (user_id, session, URL): один и тот же ключ всегда попадает на тот же бэкенд. Зачем он кешам: если кеширующие ноды выбираются хешем по ключу, каждая нода держит свой кусок ключей — hit rate высокий; обычный хеш key % N при изменении N перетасовывает почти все ключи, и добавление одной ноды обнуляет кеш всего кластера. Consistent hashing при изменении числа нод перемещает только ~1/N ключей — кластер переживает скейлинг без лавины промахов. Плюс sticky-маршрутизация для stateful-случаев. Итог для собеса: RR — дефолт, least conn — разнородные запросы, hashing — когда важна привязка ключа к ноде.
Про consistent hashing знают название, но не могут ответить «что случится с кешем при добавлении ноды с обычным модулем» — а вся ценность алгоритма именно в этом сценарии. Кто не проговаривает «переедет 1/N ключей вместо всех», знает слова, не механику.
Follow-up интервьюера: hot key: один пользователь генерит 30% трафика, hashing шлёт всё на одну ноду — что делать? — реплицировать горячие ключи на несколько нод (bounded loads, key salting) или выносить топ-ключи в отдельный слой: консистентность хеша не отменяет неравномерности ключей.
25. Health checks — active vs passive и какой глубины должна быть проверка?
Active — балансировщик сам опрашивает бэкенды по интервалу; passive — следит за реальными ответами (outlier detection: нода с всплеском ошибок или таймаутов выкидывается из ротации). Active даёт предсказуемость и снятие ноды до пользовательских ошибок, passive ловит то, чего синтетика не видит, — деградацию под реальной нагрузкой. Зрелые системы используют оба. Конфиг active-проверки в nginx выглядит так:
upstream backend {
server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
}
# active-проверки (nginx plus / angie);
# в open source nginx — passive через max_fails
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
}
Глубина проверки — главный трейдофф. Мелкая (порт открыт, /ping отвечает 200) не заметит, что сервис жив, но бесполезен — база отвалилась. Глубокая (проверяем базу, кеш, зависимости) честнее, но опасна: отказ общей зависимости провалит проверку на всех нодах разом, балансировщик выкинет всех — и самодиагностика превратит частичную деградацию в полный отказ. Правило: health check проверяет сам инстанс (процесс, локальные ресурсы), а не транзитивную доступность зависимостей; разница между liveness и readiness и их грабли разобраны в DevOps-подборке — здесь та же логика на уровне балансировщика.
Делают «честный» health check с проверкой базы — и при икоте базы балансировщик синхронно выкидывает все ноды: классический самострел, превращающий деградацию зависимости в полный даунтайм. Вопрос «что будет при отказе общей зависимости» обязан прозвучать в твоём ответе раньше, чем его задаст интервьюер.
Follow-up интервьюера: все ноды провалили health check — что должен сделать балансировщик? — режим fail-open: если здоровых нет, слать трафик во все, как будто здоровы все. Часть запросов пройдёт — это лучше гарантированного нуля; так ведут себя зрелые балансировщики и DNS-фейловеры.
26. Rate limiting — token bucket vs sliding window и где ставить лимиты?
Token bucket: в ведро капают токены с постоянной скоростью, запрос забирает токен, пустое ведро — 429. Ключевое свойство — допускает бёрсты в размер ведра: клиент, молчавший минуту, может выстрелить пачкой, средняя скорость при этом ограничена скоростью пополнения. Это обычно то, что нужно: реальный трафик бёрстовый, и жёсткое «не чаще 10 в секунду ровно» бьёт по легитимным клиентам. Sliding window считает запросы в скользящем окне: честнее фиксированных окон (у тех на границе минуты пролезает двойной лимит — 100 запросов в 59-ю секунду и 100 в 61-ю), без бёрстов сверх лимита; вариант sliding window counter — компромисс по памяти через взвешивание двух соседних окон. Выбор: bucket — когда бёрсты допустимы и важна средняя скорость, window — когда нужен жёсткий потолок в любом интервале. Где ставить: на краю (edge/API gateway) — против абьюза и ботов, до того как трафик потратил твои ресурсы; на границах между сервисами — чтобы один внутренний потребитель не съел общую зависимость (это защита ёмкости, родня load shedding); и на дорогих операциях точечно — логин (брутфорс), поиск, экспорт. Лимиты — по ключу: IP, токен, тенант; и с честным ответом — 429, Retry-After, чтобы клиент отступил, а не молча ретраил.
Знают один алгоритм и не могут объяснить разницу поведений: «token bucket пропустит бёрст, sliding window — нет» — та фраза, которой не хватает большинству ответов. И ставят лимит только на краю, забывая внутренние границы: наружный лимит не спасёт базу от собственного взбесившегося крона.
Follow-up интервьюера: rate limiter распределённый — счётчики в Redis, Redis упал. Пропускать или блокировать? — fail-open для пользовательского трафика (лимитер — защита, а не критичная функция) и fail-closed для операций с риском денег или безопасности; решение фиксируется заранее, а не импровизируется.
27. CDN и кеширование на краю — что кешировать и как инвалидировать?
CDN ставит кеш географически близко к пользователю: статика раздаётся с edge-ноды за десятки миллисекунд вместо похода на origin через полмира, а origin разгружается на порядки. Что кешировать — по убыванию очевидности: статика с версионированными именами (app.3f2a1c.js) — кешируется навечно, Cache-Control: immutable, инвалидация не нужна вообще, потому что новая версия — это новое имя; картинки и медиа — долго; API-ответы — уже аккуратно: публичные и одинаковые для всех (каталог, курсы валют) кешируются с коротким TTL, персонализированные — нельзя (или через нормализацию: кешируется общая часть, персональное дотягивается отдельно). Классика провала — кешануть ответ с чужим Set-Cookie или персональными данными и раздать его всем: заголовки Vary и дисциплина «что кешируемо» — не бюрократия, а защита от утечки. Инвалидация: лучшая — версионирование имён (проблема исчезает по построению); TTL — базовый механизм, выбирается по допустимой протухлости; purge по событию — принудительная чистка при изменении контента, но у purge есть задержки и лимиты, строить на нём консистентность нельзя; stale-while-revalidate — отдавать протухшее, пока фоново обновляется. Плюс SRE-бонус: CDN — амортизатор инцидентов, при падении origin может отдавать stale-контент (serve-stale-on-error) — сайт «жив» на кеше, пока чинится бэкенд.
Говорят «кешируем статику», и всё — без версионирования, без Vary, без стратегии инвалидации. Вопрос «как обновить лежащий в CDN файл» с ответом «сделаем purge» вместо «у нас версионированные имена, purge не нужен» показывает, что с CDN человек работал как с чёрным ящиком.
Follow-up интервьюера: cache hit ratio упал с 95% до 60% — куда смотреть? — что изменилось в ключе кеша: новый query-параметр, разросшийся Vary, укороченный TTL, деплой с массовой сменой имён — или трафик-паттерн (лонгтейл, боты). Каждый процент промахов — это множитель нагрузки на origin.
28. DNS в отказоустойчивости — TTL, GeoDNS, anycast
DNS — первый рубеж маршрутизации и последний, о котором вспоминают при проектировании отказоустойчивости. TTL — время кеширования ответа резолверами: он определяет скорость DNS-фейловера. TTL 24 часа означает, что после переключения записи часть пользователей сутки ходит на труп; TTL 60 секунд даёт быстрое переключение ценой роста запросов к авторитативным серверам. Практика: для записей, участвующих в фейловере, TTL держат коротким постоянно (30–300 с), а перед плановыми миграциями снижают заранее — за время старого TTL до переключения. Оговорка, которую ждут: TTL — обещание, а не гарантия; часть резолверов и приложений кеширует дольше, поэтому DNS-фейловер — минуты и «почти все», а не секунды и «все». GeoDNS отвечает разным пользователям разными адресами по геолокации резолвера: европейцев — в европейский регион, азиатов — в азиатский; это и latency, и основа мультирегионального фейловера (health check у DNS-провайдера убирает мёртвый регион из ответов). Anycast — иной механизм: один и тот же IP анонсируется из многих точек мира по BGP, сеть сама везёт пакет к ближайшей; переключение при отказе точки происходит на уровне маршрутизации — быстрее DNS и без TTL-хвостов. Так работают корневые DNS-серверы, публичные резолверы и входные слои CDN. В сумме: anycast — вход и мгновенный обход отказа точки, GeoDNS — распределение по регионам, короткий TTL — управляемость всего этого.
«Переключим DNS при аварии» без слова про TTL — значит, фейловер займёт столько, каков TTL, плюс непослушные кеши. И путают GeoDNS с anycast: первое — разные ответы DNS, второе — один IP из многих мест; механизмы разные и отказы обходят по-разному.
Follow-up интервьюера: почему не поставить TTL 5 секунд на всё? — нагрузка на авторитативные серверы и латентность резолва у пользователей вырастут, а часть резолверов всё равно проигнорирует. Короткий TTL — инструмент для записей фейловера, а не дефолт.
29. Канарейка и постепенная раскатка трафика — как SRE снижает риск релиза?
Идея одна: новая версия получает малую долю трафика, метрики сравниваются с базовой, и только после подтверждения здоровья доля растёт — 1% → 5% → 25% → 100%. Радиус поражения плохого релиза ограничен процентом канарейки, а откат — это вернуть трафик, секунды. Механика раскатки и сравнение с blue-green разобраны в DevOps-подборке; на SRE-собесе центр тяжести другой — критерии и автоматика. Что сравниваем: SLI канарейки против контроля — error rate, latency (p99, не среднее), насыщение ресурсов, бизнес-метрики (конверсия, если доступна быстро); сравнение честное только при одинаковом трафике — канарейка и baseline должны получать одинаково перемешанных пользователей, иначе разница в метриках — артефакт раскладки. Автоматический откат обязателен: канарейка, за которой «следят глазами», ночью не работает; порог деградации — триггер отката без человека. Время выдержки на каждом шаге — не секунды: часть проблем (утечки памяти, деградация кешей, редкие пути) проявляется за десятки минут. И честное ограничение: канарейка ловит то, что проявляется на её проценте трафика и за время выдержки; редкие баги и проблемы масштаба (пул соединений, шардированные данные) она пропустит — поэтому канарейка дополняет, а не отменяет мониторинг после полной раскатки.
Рассказывают процесс, но не критерии: «посмотрим, что всё ок» — а что именно, какими метриками, против чего сравниваем, кто откатывает в четыре утра? Канарейка без автоматического отката и без честного baseline — ритуал, а не контроль риска.
Follow-up интервьюера: метрики канарейки чуть хуже контроля — статистически это шум или сигнал? — на 1% трафика выборка мала: нужны минимальные объёмы и грубые пороги на малых долях, тонкие сравнения — на больших; иначе канарейка либо дёргает откаты от шума, либо ей всё «ок».
Блок 5. Распределённые системы
30. CAP-теорема без искажений — и почему PACELC честнее
Точная формулировка: при сетевом разделении (partition) система вынуждена выбирать между консистентностью (C — все видят одни и те же данные) и доступностью (A — каждый запрос получает ответ). Не «выбери два из трёх»: partition — не опция, а данность, сеть рвётся независимо от твоих желаний, поэтому реальный выбор всегда «что делаем, когда сеть порвалась». CP-система при партиции отказывает части запросов, но не отдаёт устаревшее и не принимает конфликтующие записи — так ведёт себя etcd: потерявшее кворум меньшинство перестаёт обслуживать. AP-система продолжает отвечать всеми частями, принимая расхождение данных с последующей сверкой — так живут Cassandra и DynamoDB в соответствующих режимах. Честное продолжение — PACELC: при партиции (P) выбор между A и C, а иначе (E, else) — между latency (L) и консистентностью (C). Это добивает главный самообман: платить за консистентность приходится не только в редкие разрывы сети, а на каждом запросе — синхронная репликация и кворумные чтения стоят миллисекунд всегда. Поэтому системы описываются парой: etcd — PC/EC (консистентность всегда, ценой доступности и latency), Dynamo-подобные — PA/EL (доступность и скорость, ценой расхождений). На собесе главное — говорить о выборе per-операция: одна система может давать разные гарантии разным запросам.
«Выбираем два из трёх» — искажение, с которого начинается провал: CA-систем в распределённом мире нет, партицию не выбирают. И почти никто не знает PACELC — а именно он объясняет, почему консистентность стоит latency даже в идеально здоровой сети.
Follow-up интервьюера: банк — это точно CP? — не весь: проведение платежа — CP, показ баланса на морде — часто AP с оговоркой «может отставать», уведомления — тем более. Гарантии выбираются на операцию, а не на компанию.
31. Strong vs eventual consistency — где какая допустима?
Strong consistency: после подтверждённой записи любое чтение видит её — система ведёт себя как одна машина. Eventual: реплики сходятся к одному состоянию со временем; прочитать можно устаревшее, но расхождение временно. Выбор — продуктовый, и на собесе его проверяют примерами. Баланс счёта при списании — strong: решение «хватает ли денег» по устаревшему значению — прямой путь к овердрафту; уникальность логина, остаток товара при оформлении, права доступа — туда же: везде, где на прочитанном значении принимается необратимое решение. Счётчик лайков — eventual: никто не пострадает, увидев 1041 вместо 1043; лента, счётчики просмотров, аналитика, кеши профилей — терпят устаревание легко. Между полюсами — градации, которые стоит назвать: read-your-writes (пользователь видит собственную запись сразу — иначе «я сохранил, а оно исчезло»), monotonic reads (время не идёт назад между запросами), causal consistency (следствия не видны раньше причин — ответ не раньше комментария). Часто нужна не strong везде, а правильная слабая гарантия в правильном месте: профиль можно читать с реплики, но свою только что отредактированную анкету пользователь должен видеть немедленно — это read-your-writes через чтение с мастера или session stickiness, а не глобальная строгость. Цена strong — latency и доступность (см. PACELC), поэтому дефолт зрелой системы: strong там, где решения, eventual — где отображение.
Делят мир на два лагеря и не знают промежуточных гарантий: read-your-writes и monotonic reads — ровно те слова, которых не хватает, когда кандидата спрашивают «пользователь сохранил профиль и не видит изменений — что сломано?».
Follow-up интервьюера: чтение с реплик MySQL с лагом — какую гарантию потеряли? — read-your-writes: свежая запись ещё не доехала. Чинится маршрутизацией «после записи читаем с мастера N секунд» или сессионной привязкой — и это надо делать явно, лаг сам себя не объявит.
32. Кворум — почему N/2+1 и что даёт правило R+W>N?
Кворум — минимум узлов, чьё согласие нужно для операции. Магия N/2+1 — в пересечении: два любых большинства из N узлов имеют общий узел, поэтому не могут существовать два независимых кворума с противоречащими решениями — это математическая защита от split-brain. Кластер из 5 узлов терпит отказ двух (кворум 3); из 4 — только одного (кворум 3), поэтому чётное число узлов не добавляет отказоустойчивости, только расход — отсюда нечётные размеры кластеров etcd и ZooKeeper. Партиция 3/2: большинство работает, меньшинство останавливается — грустно для тех, кто в меньшинстве, но безопасно для данных. Вторая часть — кворумные чтения и записи в Dynamo-подобных системах: N реплик, запись подтверждается W из них, чтение опрашивает R; при R+W>N множества чтения и записи гарантированно пересекаются — чтение застанет хотя бы одну реплику с последней записью (различить поможет версия). Классика: N=3, W=2, R=2. Ручки настраиваются под операцию: W=N, R=1 — дорогая запись, дешёвое чтение; W=1, R=1 — быстро и без гарантий (R+W≤N — пересечения нет, можно читать старьё). Оговорка для честности: R+W>N в Dynamo-стиле — не строгая линеаризуемость (sloppy quorum, конфликтующие версии, ремонт при чтении), а практическая гарантия «читаем свежее» в нормальных условиях.
Заучили «N/2+1», но не могут объяснить, почему именно большинство: слово «пересечение» — ключ, без него формула — заклинание. И на «кластер из 4 узлов лучше, чем из 3?» отвечают «да, узлов же больше» — нет: кворум 3, терпим один отказ, как и при трёх узлах.
Follow-up интервьюера: N=3, W=1, R=1 — что выигрываешь и чем платишь? — минимальной latency обеих операций; платишь окном потери данных (запись жила на одном узле) и чтением устаревшего. Для кешей и метрик — норм, для денег — нет.
33. Raft на пальцах — лидер, выборы, репликация лога
Raft — алгоритм консенсуса: несколько узлов договариваются о едином порядке операций и переживают отказы меньшинства. Три роли: лидер (один), фолловеры, кандидаты (переходное состояние выборов). Все записи идут через лидера: он добавляет операцию в свой лог, рассылает фолловерам, и когда большинство подтвердило запись — операция закоммичена и применяется к state machine; фолловеры узнают о коммите со следующими сообщениями. Лидер держит власть периодическими heartbeat'ами. Умер лидер — фолловер, не услышавший heartbeat за случайный election timeout, становится кандидатом, поднимает номер терма и просит голоса; получил большинство — новый лидер. Случайность таймаутов — защита от бесконечных ничьих: кто-то просыпается первым. Ключевые гарантии, которые стоит проговорить: голос отдаётся только кандидату с логом не короче своего — лидером не станет узел, потерявший закоммиченные записи; терм — логические часы, отсекающие старых лидеров (вернувшийся из партиции экс-лидер видит больший терм и разжалуется); большинство — то самое пересечение кворумов: два лидера в одном терме невозможны. На практике Raft — это etcd, а значит и весь Kubernetes: почему узлов нечётное число и что случится при потере кворума — разобрано в DevOps-подборке; Consul и большинство современных реплицируемых систем — та же механика.
Плавают на границах: что при партиции с экс-лидером в меньшинстве (он принимает записи? — нет: не соберёт большинство подтверждений, записи не коммитятся), зачем случайный таймаут, почему терм важен. Слова «лидер, выборы» знают все — гарантии знают единицы.
Follow-up интервьюера: клиент прочитал с фолловера — может получить старьё? — да, фолловер может отставать; линеаризуемые чтения идут через лидера (с подтверждением кворума или lease). Поэтому «читать с реплик etcd для скорости» — размен строгости на latency, который надо делать осознанно.
34. Шардирование БД — по какому ключу, что такое hot shard и как решардить?
Шардирование — горизонтальная нарезка данных по узлам, когда один узел уже не вмещает объём или нагрузку. Ключ шардирования — главное решение, и оно почти необратимо. Критерии: равномерность распределения (hash от user_id раскидывает ровно; диапазоны по дате — плохо: вся запись сегодняшнего дня бьёт в один шард) и локальность запросов (типовой запрос должен попадать в один шард: все данные пользователя вместе — запросы «всё по юзеру» дёшевы; зато запросы поперёк ключа — «все заказы за вчера» — превращаются в scatter-gather по всем шардам). Hot shard — узел, получающий непропорциональную нагрузку: мегазнаменитость в соцсети, тенант-кит в B2B, «дефолтное» значение ключа. Хеш не спасает — он размазывает ключи, а не нагрузку одного ключа. Лечение: salting горячих ключей (дробить на подключи), выносить китов на выделенные шарды, кешировать горячее выше. Решардинг — вторая боль: key % N при смене N перемещает почти всё. Решения: consistent hashing (движется ~1/N), заранее много виртуальных шардов, приклеенных к малому числу физических (решардинг — переезд виртуальных шардов, а не пересчёт ключей), directory-based маппинг. Живой решардинг — та же дисциплина, что миграции без даунтайма: двойная запись, фоновая доливка, переключение чтений, откат наготове.
Называют шардирование ответом на всё, но не могут выбрать ключ под конкретные запросы — а кросс-шардовые джойны и транзакции, которые они только что потеряли, обнаруживают уже на встречном вопросе. Hot shard для многих новость: «хеш же распределяет равномерно» — ключи, но не нагрузку.
Follow-up интервьюера: шардировали по user_id — прилетела задача «топ товаров за час по всем пользователям». Как? — не гонять scatter-gather на каждый запрос: асинхронная агрегация в отдельное хранилище (стрим, matview). Аналитика поперёк ключа — отдельный контур, а не запрос в OLTP-шарды.
35. Распределённые транзакции: 2PC vs saga
Задача: операция затрагивает несколько сервисов или баз — заказ, списание, резерв на складе — и нужна атомарность «всё или ничего». 2PC (two-phase commit): координатор спрашивает всех участников «готовы?» (prepare — каждый блокирует ресурсы и обещает закоммитить), после единогласия командует commit. Гарантия атомарности честная, но цена продовая: синхронная блокировка ресурсов у всех участников на время транзакции, а главное — координатор является точкой отказа: умер между prepare и commit — участники висят с залоченными ресурсами, не зная исхода; это блокирующий протокол. Плюс каждый участник обязан уметь prepare — HTTP-сервисы этого не умеют. Поэтому в микросервисах дефолт — saga: длинная операция нарезается на локальные транзакции, каждая коммитится сразу, а атомарность заменяется компенсациями — на отказ шага N выполняются компенсирующие действия для шагов 1..N-1: списали деньги, резерв не удался — делаем возврат. Две механики: оркестрация (центральный координатор-стейт-машина гонит шаги — прозрачно, видно состояние) и хореография (сервисы реагируют на события друг друга — слабее связность, труднее отследить). Цена saga, которую обязательно проговорить: между шагами система в промежуточном состоянии, изоляции нет — параллельные операции видят «деньги списаны, заказа нет»; компенсации — не магический rollback, а бизнес-действия, которые тоже могут падать и обязаны быть идемпотентными и ретраябельными.
Рассказывают saga как «просто откатим», не понимая, что компенсация — прямое действие с собственными отказами, а изоляция потеряна by design. Про 2PC не могут назвать главный грех — блокирующего координатора; «медленный» — не тот ответ, «блокирующий при отказе» — тот.
Follow-up интервьюера: компенсация невозможна — письмо уже отправлено, внешний платёж проведён. Что делать? — переставить необратимые шаги в конец саги, а до них — только резервирования; где нельзя — семантические компенсации (письмо-отмена, возврат платежа) и сверка (reconciliation) как последняя линия.
36. Clock skew — почему нельзя доверять времени в распределённой системе?
У каждой машины свои часы, и они врут по-разному: кварц дрейфует, NTP подтягивает время с точностью в лучшем случае миллисекунды, а при сбоях синхронизации — секунды и больше; поверх этого время умеет прыгать назад (коррекция NTP, миграция VM). Следствие: сравнивать таймстампы с разных машин для установления порядка событий — ошибка. «Запись A новее записи B, потому что таймстамп больше» — на расхождении часов last-write-wins тихо выбрасывает более позднюю по смыслу запись; лог-строки двух сервисов, сшитые по времени, показывают следствие раньше причины; TTL и лизы, выданные одними часами и проверенные другими, истекают не тогда. Инструменты вместо доверия к стенным часам. Монотонные часы — счётчик, который никогда не идёт назад: все измерения длительностей (таймауты, latency) — только по ним, не по wall clock. Логические часы: Lamport clock — счётчик, инкрементится локально, при обмене сообщениями берётся max — даёт порядок, согласованный с причинностью (если A могло повлиять на B, счётчик A меньше); vector clocks дополнительно отличают причинность от конкурентности. Централизованные последовательности — id из одного источника (лог, база) как арбитр порядка. И честная экзотика: Google Spanner с TrueTime покупает глобальный порядок атомными часами и ожиданием окна неопределённости — дорого, зато честно. Практический минимум SRE: NTP/chrony под мониторингом, алерт на дрейф, паранойя к любому коду, сравнивающему время разных машин.
«Синхронизируем NTP, и порядок будет» — NTP уменьшает расхождение, но не устраняет: миллисекунды дрейфа — вечность для системы с тысячами событий в секунду. Про монотонные часы не знают: измеряют таймауты wall clock'ом, который однажды прыгнет назад — и таймер уйдёт в минус.
Follow-up интервьюера: чем Lamport clock отличается от vector clock — одним предложением? — Lamport даёт порядок, но по счётчикам не отличишь причинную связь от совпадения; vector хранит счётчик на узел и умеет сказать «эти события конкурентны» — за это платишь размером вектора.
37. Split-brain и fencing — как не дать двум «мастерам» испортить данные?
Split-brain — состояние, когда после разрыва сети обе половины кластера считают себя главными: два мастера принимают записи независимо, и после восстановления сети у тебя два расходящихся набора данных, которые часто невозможно слить без потерь. Возникает из наивного фейловера: «мастер не отвечает — поднимем нового». Но «не отвечает» не значит «умер»: старый мастер может быть жив и обслуживать своих клиентов за партицией — недоступность неотличима от смерти, это фундаментальное ограничение, а не недоработка. Защиты. Кворум: право быть мастером даёт только большинство (см. вопрос про N/2+1) — меньшинство обязано сложить полномочия; половина 2/2 кворума не имеет вообще, поэтому двухузловые кластеры без арбитра — конструкция для выстрела в ногу. Fencing — гарантированное отстранение старого мастера до назначения нового: STONITH (выключить питание через IPMI — грубо и надёжно), отзыв доступа на уровне хранилища или сети (старый мастер физически не может писать). Fencing token — элегантный вариант: каждое новое лидерство получает монотонно растущий номер, хранилище отвергает записи со старым номером — даже проснувшийся зомби-мастер безвреден, его токен протух. Лизы с оговоркой: лидерство арендуется на время и продлевается; истекла лиза — сложи полномочия сам, но это опирается на локальные часы и паузы процесса (GC-пауза длиннее лизы — и зомби живёт), поэтому лиза без fencing token — защита с дыркой.
Фейловер описывают, а fencing пропускают: «поднимем нового мастера» без ответа «а кто гарантирует, что старый перестал писать?» — это и есть рецепт split-brain. Вопрос «мастер завис в GC на 40 секунд и очнулся — что произойдёт?» разделяет читавших Kleppmann и остальных.
Follow-up интервьюера: почему автофейловер баз часто выключают, несмотря на риск ручной задержки? — цена ошибки асимметрична: лишние минуты даунтайма при ручном переключении дешевле split-brain с расхождением данных; автомат включают, когда есть кворум, fencing и отрепетированность.
Блок 6. Инциденты и он-колл
38. Роли в инциденте — зачем разделять командира и коммуникацию?
Крупный инцидент — это не только техническая задача, но и координационная, и роли существуют, чтобы эти задачи не смешивались в одной перегретой голове. Incident commander — командир инцидента: владеет процессом, а не клавиатурой; решает, какие гипотезы проверяются, кого привлечь, когда эскалировать, фиксирует решения. Критично: IC не дебажит сам — командир, ушедший с головой в терминал, перестаёт быть командиром, и инцидент разваливается на несогласованные действия. Operations lead / исполнители — руки: диагностика и изменения, строго через IC, чтобы два человека одновременно не крутили одну систему в разные стороны. Communications lead — коммуникация: статусы стейкхолдерам, ответы бизнесу, обновления статус-пейджа. Зачем выделять отдельно: во время инцидента вопросы «ну что там?» прилетают каждые пять минут, и если на них отвечает тот, кто чинит, — не происходит ни ответов, ни починки; comms lead — щит, за которым инженеры работают. Плюс scribe — хронология событий и действий (или бот, собирающий таймлайн). Важная оговорка про масштаб: в мелком инциденте все роли живут в одном человеке, и это нормально; суть не в штатном расписании, а в том, что при росте инцидента роли явно раздаются и передаются — «теперь IC — Маша» произносится вслух и пишется в канал.
Пересказывают роли по книжке, но не могут объяснить зачем: ключевые фразы — «IC не дебажит» и «comms lead защищает инженеров от вопросов». Кто их не говорит, вероятно, не был в инциденте, где всё чинят все и никто не знает, что происходит.
Follow-up интервьюера: дежурный один, ночь, инцидент крупный — как роли? — все на нём, пока не эскалирует: первая обязанность соло-дежурного при крупном инциденте — позвать людей, а не геройствовать; протокол эскалации и есть замена штату ролей.
39. Severity-уровни — кто и как объявляет инцидент?
Severity — шкала тяжести, привязанная к пользовательскому и бизнес-влиянию, а не к внутренней драме. Типовая: SEV1 — критичный путь лежит или массовая потеря данных, все руки, статус-пейдж, эскалация к менеджменту; SEV2 — заметная деградация или отказ важной функции у части пользователей — активная работа дежурных, но без будильника для всей компании; SEV3 — ограниченное влияние, обходной путь есть — рабочие часы; SEV4/тикет — не инцидент. Критерии должны быть написаны заранее и в терминах наблюдаемого: «ошибки платежей выше X%», «SLO горит быстрее Y» — потому что в три ночи никто не должен философствовать о тяжести. Два процессных правила, которые проверяет вопрос. Первое: объявить инцидент может любой — без разрешения менеджера, без комитета; порог объявления должен быть низким. Второе: сомневаешься — объявляй и завышай severity: цена ложного SEV2 — несколько разбуженных людей и запись в журнале; цена прозеванного — час немой деградации, пока каждый думал «наверное, не так страшно». Понизить severity легко и приятно, повышать с опозданием — больно. И механика: объявление — это не эмоция в чате, а действие с эффектами: создаётся канал, назначается IC, стартует таймлайн, зовутся нужные люди. Инцидент — режим работы, в который система входит по кнопке, а не по настроению.
Уровни называют, а процессные принципы — нет: «кто имеет право объявить» и «что делать при сомнении» — суть вопроса. Команды, где инцидент объявляется только после согласования с лидом, платят за это часами MTTR — и интервьюер ищет, понимает ли кандидат почему.
Follow-up интервьюера: половина «инцидентов» после разбора оказывается SEV3-шумом — снижать порог объявления? — нет: ложные объявления — здоровая цена ранней реакции. Снижать надо трение (объявить — одна команда бота), а калибровку criteria подкручивать постмортемами.
40. Прод горит — что делаешь в первые пять минут?
Порядок жёсткий, и он противоречит инстинктам инженера. Первое — оценить масштаб, а не искать причину: какие SLI горят, какая доля пользователей задета, растёт ли — от этого зависит severity и состав привлечённых. Второе — объявить инцидент и коммуникацию: канал, IC, первая запись «знаем, работаем» — тридцать секунд, которые окупаются отсутствием паники и дублирующих расследований. Третье — стабилизировать самым дешёвым известным способом, и вопрос номер один: что менялось? Релиз, конфиг, флаг, миграция за последние часы — откатить, не выясняя, «точно ли дело в нём»: откат дёшев и обратим, а корреляция с изменением — сильнейшая из первых гипотез. Полный чек-лист действий при упавшем после релиза проде — в DevOps-подборке. Дальше рубильники деградации, скейлинг, переключение трафика — восстановить сервис, зафиксировав улики (логи, дампы, метрики) для разбора. Причины — потом: диагностика на лежащем проде оплачивается минутами пользовательского даунтайма, mitigation first — root cause later. Типовая ошибка, которую вопрос и ловит: инженер 40 минут молча копает «почему», вместо того чтобы за 4 минуты вернуть сервис откатом и копать уже на здоровом проде.
Начинают с «смотрю логи и ищу причину» — неверный порядок: сначала масштаб, коммуникация и стабилизация. И забывают «что менялось?» — вопрос, который решает большинство инцидентов быстрее любого профайлера.
Follow-up интервьюера: откат сделали — не помогло. Следующий шаг? — гипотеза честно закрыта: возвращаюсь к ленте изменений (чужие деплои, инфраструктура, внешние зависимости) и симптомам по слоям; параллельно — деградация и трафик-менеджмент, чтобы сервис жил, пока ищем.
41. Коммуникация во время инцидента — как держать всех в курсе и не мешать чинить?
Коммуникация — не побочка, а часть тушения: без неё вокруг инцидента мгновенно вырастает второй — из паники, слухов и параллельных расследований. Механика. Один канал инцидента — вся работа там: гипотезы, действия, решения; личка и звонки без следа в канале запрещены, потому что таймлайн потом собирается из канала. Регулярные статусы — каждые 15–30 минут для SEV1/2, по расписанию, даже если новостей нет: «работаем, изменений нет, следующий статус в 14:30» — легитимная и важная новость; молчание стейкхолдеры заполняют худшими сценариями и приходят дёргать инженеров. Формат статуса: влияние (что и у кого не работает), что сделано, что делаем, когда следующее обновление — без внутренних гипотез и обвинений, которые потом разлетятся скриншотами. Разделение аудиторий: технический канал — для тушащих, статусы для бизнеса — отдельно и переведённые с инженерного («недоступна оплата картами, около 20% заказов» вместо «5xx на payment-gateway»); внешний статус-пейдж — своя дисциплина формулировок. Ответственный — communications lead, чтобы инженеры не отвлекались. И правило передачи: смена IC или дежурного — явный handoff с резюме состояния в канале, а не растворение в воздухе. После инцидента канал — сырьё постмортема: хорошая коммуникация во время пожара делает разбор после пожара вдвое дешевле.
Считают коммуникацию бюрократией, отвлекающей от починки, — пока не увидят инцидент, где три команды параллельно чинят одно и то же тремя способами. Пункт «статус по расписанию, даже когда нечего сказать» — ровно то, что отличает работавших в больших инцидентах.
Follow-up интервьюера: менеджмент требует прогноз «когда починим», а ты не знаешь. Что говоришь? — честную рамку: «диагноз не подтверждён, следующий статус через 20 минут; если гипотеза X подтвердится — восстановление около часа». Выдуманный ETA хуже честной неопределённости: его придётся опровергать.
42. Blameless postmortem — структура и почему без виноватых
Постмортем — письменный разбор инцидента: таймлайн (обнаружение, эскалации, действия — с точным временем), влияние (сколько минут, какая доля пользователей, деньги, сгоревший error budget), причины, что сработало и что нет, и action items — конкретные, с владельцами и сроками, а не «быть внимательнее». Blameless — принцип: разбираются системы и процессы, а не персоналии; вопрос ставится не «кто удалил базу», а «почему система позволила удалить базу одной командой без подтверждения и почему это заметили через час». Это не мягкосердечие, а инженерный расчёт: человек ошибается гарантированно — это константа, проектировать надо систему, в которой ошибка не превращается в катастрофу. И прагматика: в культуре наказаний люди скрывают детали, сглаживают таймлайны, не сообщают о near-miss — организация теряет главный источник знаний о собственных слабостях, и следующий инцидент приходит по тем же рельсам. Blameless не значит «без ответственности»: действия называются (обезличенно — «инженер выполнил миграцию на проде»), процессные и умышленные нарушения — отдельная тема вне постмортема. Признаки зрелого процесса: постмортем обязателен по критерию (SEV1/2, потеря данных, сгоревший бюджет), публичен внутри компании, action items трекаются до закрытия — постмортемы, чьи выводы никогда не выполняются, хуже отсутствия постмортемов: команда учится тому, что разборы — театр.
Рассказывают структуру, но проваливают «почему blameless»: ответ «чтобы не обидеть» — мимо; правильный — «наказание учит скрывать, скрытое повторяется». И не говорят про трекинг action items — а без него весь процесс декоративен.
Follow-up интервьюера: инженер повторно сделал то же самое, что уже было в постмортеме, — всё ещё blameless? — вопрос к системе остаётся первым: почему action item, запрещающий это техническими средствами, не был сделан? Повторяемость ошибки — дефект системы сильнее, чем дефект человека.
43. MTTD, MTTR и burn rate алерты — как алертить по SLO?
MTTD — среднее время обнаружения, MTTR — восстановления; обе метрики уменьшаются правильным алертингом. Наивный алерт «error rate > 1% пять минут» плох в обе стороны: будит при коротком всплеске, который SLO не угрожает, и молчит при медленной утечке бюджета. Правильная рамка — burn rate: скорость сжигания error budget относительно «ровного» расхода. Burn rate 1 — бюджет сгорит ровно за окно SLO (30 дней); 14.4 — за двое суток. Multi-window multi-burn-rate из SRE Workbook: быстрое сжигание ловится коротким окном и пейджит, медленное — длинным окном и заводит тикет; второе короткое окно в условии гасит алерт сразу после конца всплеска:
# SLO 99.9%: page при burn rate 14.4 (1h + 5m окна)
- alert: ErrorBudgetBurnFast
expr: |
( sum(rate(http_requests_total{code=~"5.."}[1h]))
/ sum(rate(http_requests_total[1h])) ) > 14.4 * 0.001
and
( sum(rate(http_requests_total{code=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) ) > 14.4 * 0.001
labels: {severity: page}
# ticket при burn rate 3 (24h + 2h окна) — медленная утечка
- alert: ErrorBudgetBurnSlow
expr: |
(...[24h]...) > 3 * 0.001 and (...[2h]...) > 3 * 0.001
labels: {severity: ticket}
Смысл конструкции: пейдж приходит только когда бюджет реально под угрозой, тикет — когда угроза медленная, а всё остальное не будит никого. Это и снижает MTTD (медленные деградации перестают быть невидимыми), и лечит алертную усталость, которая раздувает MTTR.
Знают слова MTTD/MTTR, но алертинг у них — пороги на метриках, не связанные с SLO. И не могут объяснить цифру 14.4: это скорость, при которой месячный бюджет сгорает за 2 дня, — числа выводятся из окна и SLO, а не берутся с потолка.
Follow-up интервьюера: зачем в условии второе, короткое окно? — чтобы алерт погас сразу после конца всплеска: длинное окно ещё «помнит» ошибки часами, и без короткого условия дежурный смотрит на алерт по уже здоровой системе.
44. Он-колл гигиена — ротация, компенсация, handoff, предел нагрузки
Он-колл — производственная система, и у неё есть инженерные параметры. Размер ротации: минимум 5–6 человек на очередь; меньше — дежурства чаще раза в месяц-полтора, накапливается усталость, отпуск одного ломает график, и очередь живёт в предвыгорании. Нагрузка: Google формулирует предел как не больше двух инцидентов за 12-часовую смену — не потому что цифра священна, а потому что каждый инцидент требует не только тушения, но и разбора, постмортема, фиксов; дежурный, разгребающий шесть инцидентов за смену, не делает ничего из этого, и качество реакции падает вместе с надёжностью. Стабильно перегруженный он-колл — сигнал чинить систему (шумные алерты, несделанные фиксы), а не терпеть людей. Компенсация: дежурство — работа, даже тихое: ограничение свободы (быть трезвым, с ноутбуком, в зоне связи) оплачивается деньгами или отгулами; бесплатный он-колл — скрытый налог, который платят увольнениями лучших. Handoff: передача смены — явный ритуал, а не смена таймстампа в PagerDuty: что горело, что тлеет, какие алерты зашумлены, какие работы запланированы — письменно, в канале. И правило эскалации без вины: дежурный обязан звать второго и старшего, когда не вывозит; культура «сам справлюсь, неудобно будить» стоит дороже любых разбуженных — час одинокого героизма против десяти минут вдвоём.
Говорят о он-колле как о неизбежном зле, без параметров: размер ротации, предел инцидентов на смену, компенсация, handoff — конкретика, по которой видно, строил ли человек дежурство или только страдал в нём. Ответ «у нас дежурит вся команда из трёх человек, бесплатно» — красный флаг в обе стороны собеседования.
Follow-up интервьюера: ночные пейджи каждую ночь, но всё «по делу» — что чинить? — систему, а не людей: каждый ночной пейдж — вопрос «почему это не чинится автоматикой или не ждёт утра?»; правило «ночью будит только то, что не может ждать до утра» пересобирает алертинг сильнее любых threshold'ов.
45. Алерты на симптомы vs на причины — почему пейджить надо по симптомам?
Симптом — то, что видит пользователь: ошибки, latency, недоступность — деградация SLI. Причина — внутреннее состояние: CPU 95%, диск заполняется, под рестартует. Пейджить надо по симптомам, и аргументов три. Полнота: причин отказа бесконечно много, всех не предусмотришь, а симптом один — пользователю плохо; алерт на SLI ловит и те причины, о которых ты не подумал. Точность: высокий CPU без деградации пользовательского опыта — не инцидент, а повод для тикета; алерт на причину будит людей ради состояния, которое система, возможно, переживёт сама. Приоритет: симптом сразу говорит о масштабе влияния, причина — нет. Причинные метрики при этом не выбрасываются — они переезжают из пейджей в диагностику: дежурный, разбуженный симптомом, смотрит дашборд причин, чтобы понять откуда; и в тикеты — «диск заполнится за неделю» не будит, а планируется. Исключение честности ради: несколько ведущих индикаторов неминуемой катастрофы (диск базы на 98%, сертификат истекает завтра) пейджить можно и нужно — это симптомы будущего с гарантией наступления. Как строить алертинг, чтобы не сойти с ума, разобрано в DevOps-подборке, а когда алертов всё равно слишком много — способы гасить шторм и корреляцию смотри в AIOps-подборке: там та же философия, доведённая до автоматики.
Соглашаются «алертить на симптомы», а потом на просьбу спроектировать алерты для сервиса выдают CPU, память и рестарты подов — рефлексы сильнее принципов. И теряют вторую половину: причинные метрики не удаляются, а меняют роль — с будильника на диагностику.
Follow-up интервьюера: симптомный алерт сработал, а дашборды причин чистые — что это значит? — что наблюдаемость дырявая: пользователь страдает способом, который вы не мониторите. Такие инциденты — самые ценные для постмортема: они показывают слепые зоны телеметрии.
Блок 7. Capacity и производительность
46. Capacity planning — трафик растёт 15% в месяц, когда закупать?
Capacity planning — арифметика из четырёх величин: текущая утилизация, скорость роста, lead time (сколько занимает добавление ёмкости) и headroom (запас, ниже которого опускаться нельзя). Метод: берёшь органический рост из истории (те самые 15%/мес), добавляешь запланированные события (запуски, маркетинг, сезонные пики — их история не предскажет, их надо спрашивать у бизнеса), проецируешь вперёд и находишь дату, когда прогноз упрётся в порог. Заказывать надо за lead time до этой даты: в облаке lead time — минуты для VM, но недели для квот, зарезервированных мощностей и редких GPU; в железе — месяцы. Пример: утилизация пиковая 55%, порог с учётом запаса на отказ зоны — 70%, рост 15%/мес — потолок через ~1.7 месяца ((70/55) ≈ 1.27, ln(1.27)/ln(1.15) ≈ 1.7); если квота согласуется три недели — заявка уходит сейчас, а не «когда упрёмся». Важные оговорки: планируется по пикам, а не по средним — среднесуточная утилизация 40% с вечерним пиком 85% означает, что запаса нет; ёмкость меряется в ограничивающем ресурсе (не «CPU вообще», а узкое место: соединения к базе, IOPS, память конкретного пула); и прогноз — процесс, а не документ: пересчитывается регулярно, потому что и рост, и профиль нагрузки дрейфуют.
Отвечают «мы в облаке, отскейлимся» — и забывают про квоты, лимиты аккаунта, редкие типы инстансов и stateful-слои (базу за минуты не отскейлишь). Второй провал — планирование по средней утилизации: инциденты случаются на пиках, средняя — самообман.
Follow-up интервьюера: маркетинг обещает кампанию с «примерно тройным трафиком» — как готовишься? — нагрузочный тест до целевого уровня заранее, прескейлинг перед стартом (не надежда на автоскейл), план деградации и load shedding на случай, если «тройной» окажется десятикратным.
47. Headroom и N+1 по зонам — сколько запаса держать?
Headroom — разница между располагаемой ёмкостью и пиковой нагрузкой, и его размер — не вкусовщина, а следствие модели отказов. Главное правило: система обязана переживать отказ самой крупной единицы отказа без деградации — для мультизонной системы это зона. Арифметика: три зоны, отказ одной — выжившие две принимают всю нагрузку, то есть каждая должна уметь взять в 1.5 раза больше своей доли; значит, утилизация зон в норме не выше ~66%. Для двух зон — не выше 50%: половина мощности простаивает всегда, поэтому три зоны экономичнее двух при том же уровне защиты, а четыре — ещё чуть лучше (75%). Это N+1 на уровне зон; для критичных систем считают N+2 (переживаем отказ зоны во время выката или обслуживания второй). Тот же счёт применяется к нодам в пуле и репликам сервиса. Тонкости, которые ценят: считать надо не только CPU — при отказе зоны трафик через выживших вырастет и по соединениям, и по пропускной способности, и по нагрузке на локальные кеши (их hit rate просядет от чужого трафика); автоскейлинг не заменяет headroom — новые ноды поднимаются минуты, а зона умирает мгновенно, запас на отказ должен быть уже поднят; и headroom надо защищать — он невидимо съедается ростом трафика, пока его кто-то явно не мониторит как метрику с порогом.
Называют «держим 20% запаса» как универсальную цифру — а она выводится: из числа зон и модели отказа. Кандидат, у которого две зоны и утилизация 80%, обещает пережить отказ зоны — арифметика говорит, что нет, и на собесе её попросят вслух.
Follow-up интервьюера: бизнес давит: «66% утилизации — это 34% выброшенных денег». Что отвечаешь? — это страховка с известной ценой: показываю стоимость часа даунтайма против стоимости запаса; сокращать можно осознанно — авто-деградацией и шедингом некритичного при отказе зоны вместо полного дублирования.
48. Vertical vs horizontal scaling — критерии выбора
Вертикальное — растим машину: больше CPU, памяти, диска. Просто, не требует менять архитектуру, нет накладных на координацию — и для многих нагрузок это правильный первый ход: базы данных, монолиты, всё, что плохо делится. Пределы: потолок железа существует и приближается нелинейно по цене; апгрейд обычно означает рестарт (даунтайм или фейловер); и главное — одна большая машина остаётся одной точкой отказа: скейл вверх не добавляет надёжности вообще. Горизонтальное — добавляем машины: потолок практически снят, отказоустойчивость встроена (N инстансов переживают смерть одного), можно скейлить эластично под нагрузку. Цена — архитектурные требования: сервис должен быть stateless или уметь шардировать состояние, нужна балансировка, появляется координация и вся физика распределённых систем — консистентность, частичные отказы. Критерии выбора на собесе: где живёт состояние (stateless-сервисы — горизонтально почти всегда; stateful — вертикально до упора, потом шардирование как отдельный дорогой проект); характер нагрузки (пилообразная — горизонталь с автоскейлом; ровная — можно и вертикально); требования к надёжности (нужно переживать отказ инстанса — горизонталь обязательна независимо от производительности); и стоимость инженерии — переписать монолит ради горизонтали дороже, чем купить машину вдвое больше, до определённого масштаба. Честный ответ: почти каждая реальная система использует оба — горизонтальный stateless-слой поверх вертикально раскормленной базы с репликами.
Идеологический ответ «горизонтальное всегда лучше» — без оговорки, что базе или JVM с огромным хипом вертикаль часто дешевле и проще, а надёжность горизонтали не бесплатна: её оплачивают распределённой сложностью, которую потом дебажат ночами.
Follow-up интервьюера: сервис stateless, но горизонтальный скейл не помогает — latency растёт с числом инстансов. Куда смотреть? — вниз: общий ресурс за сервисом — база, кеш, внешний API. Скейлил фронт узкого места, а не само узкое место — новые инстансы только сильнее его душат.
49. Нагрузочное тестирование — типы и что каждый ловит
Типов четыре, и каждый отвечает на свой вопрос — путать их значит получать неверные ответы. Load test — целевая нагрузка (текущий пик или прогнозный) с проверкой SLI: держим ли мы ожидаемое с нужной latency; отвечает «готовы ли к завтрашнему дню». Stress test — рост нагрузки до разрушения: где предел ёмкости, какой компонент падает первым и — важнее — как падает: деградирует плавно с 503 или умирает целиком с каскадом; отвечает «где потолок и что за ним». Soak (endurance) — умеренная нагрузка часами и сутками: ловит то, чего не видно за десять минут — утечки памяти, рост дескрипторов, деградацию пулов, распухание диска от логов, медленное съезжание latency; отвечает «доживём ли до конца недели». Spike — резкий скачок и сброс: как система переживает мгновенный наплыв — успевает ли автоскейл, срабатывает ли shedding, восстанавливается ли после спада или застревает в деградации; отвечает «что будет при телерекламе». Общие требования к честности: профиль нагрузки — как в проде (реальный микс запросов, а не один эндпоинт; открытая модель нагрузки, где запросы прилетают по расписанию независимо от ответов, — закрытая модель с фиксированными воркерами маскирует деградацию, замедляясь вместе с сервисом); окружение и данные — прод-подобные; и заранее записанные критерии прохождения, иначе результат теста — «ну, что-то показало».
Знают только «нагрузили и посмотрели» — без различения типов: утечку памяти ловит soak, а не load, поведение при наплыве — spike, а не stress. И почти никто не знает про открытую vs закрытую модель нагрузки — а закрытая модель систематически завышает результаты.
Follow-up интервьюера: стресс-тест упёрся в 12k RPS — что делаешь с этим числом? — во-первых, узнаю, кто упёрся (профиль узкого места), во-вторых, ставлю пороги load shedding и автоскейла с запасом до этой точки, в-третьих, фиксирую как baseline: после значимых изменений тест повторяется и деградация ёмкости ловится как регрессия.
50. USE-метод Грегга — как методично искать проблему производительности
USE — чек-лист Брендана Грегга: для каждого ресурса проверь Utilization (насколько занят), Saturation (сколько работы ждёт в очереди) и Errors. Сила метода — в переборе по ресурсам, а не по догадкам: CPU, память, диски (I/O), сеть, плюс программные ресурсы — пулы потоков и соединений, файловые дескрипторы, лимиты cgroups. Вместо «посмотрю в top и поищу что-нибудь странное» — таблица ресурсов и три вопроса к каждому; проблема, которая прячется от интуиции, не спрячется от перебора. Ключевое различие, которое проверяют: утилизация и сатурация — разные вещи. CPU 100% — это утилизация, само по себе не беда: процессор для того и куплен; беда — run queue длиннее числа ядер (сатурация: работа стоит в очереди), и latency растёт именно от неё. Диск занят на 90% — норм для батча; очередь запросов к диску и растущий await — сигнал. Память: утилизация — занятые гигабайты, сатурация — активный своппинг и работа OOM-киллера, ошибки — отказы аллокации. Инструменты по слоям: vmstat, iostat, sar, ss, node_exporter для тех же величин в Prometheus. Для сервисов USE дополняется методом RED (Rate, Errors, Duration — про запросы, а не ресурсы): RED говорит «пользователю плохо», USE — «какой ресурс виноват»; вместе они дают полный маршрут от симптома к причине.
Смотрят только утилизацию — и пропускают сатурацию, где живёт настоящая боль: CPU 70% с гигантским run queue (троттлинг cgroups!) выглядит «здоровым» для того, кто не знает разницы. Слова «очередь» и «ожидание» в ответе — маркер понимания.
Follow-up интервьюера: контейнер: CPU по метрикам 60%, приложение тормозит — что за ресурс проверишь первым? — CPU-троттлинг по лимитам cgroups: container_cpu_cfs_throttled_periods_total. Лимит душит пики при скромной средней утилизации — классика Kubernetes-перформанса.
51. Автоскейлинг и его ловушки — почему он не спасает от всего?
Автоскейлинг — добавление и удаление ёмкости по метрике; в Kubernetes это HPA, его механика и настройки разобраны в DevOps-подборке. Здесь — ловушки, которые проверяют на SRE-собесах. Запаздывание: цепочка «метрика собралась → скейлер решил → под создан → нода поднялась → приложение прогрелось» занимает минуты, а всплеск трафика — секунды; автоскейл спасает от плавного роста, но не от спайка — от спайка спасают преднабранный headroom и load shedding. Cold start: новый инстанс с холодными кешами и пустыми пулами медленнее старых — влитый под полной нагрузкой, он может ухудшить latency, а не улучшить; нужны прогрев и slow start на балансировке. Флаппинг: нагрузка колеблется вокруг порога — скейлер дёргает реплики вверх-вниз, каждый цикл платит cold start'ом; лечится гистерезисом, стабилизационными окнами, разными порогами вверх и вниз. Неправильная метрика: скейл по CPU бесполезен, когда узкое место — соединения к базе или внешний API: новые поды умножают давление на то, что и так захлёбывается; метрика скейла должна отражать реальный ограничитель (глубина очереди, RPS на под, custom-метрики). Каскад при инциденте: деградация зависимости роняет throughput, утилизация падает — скейлер сокращает ёмкость ровно перед восстановлением трафика. И потолки: квоты, лимиты пулов нод, максимумы HPA — автоскейл упирается в них молча, если не мониторить остаток до потолка.
Автоскейлинг у кандидатов — магическая кнопка «надёжность»: спайк, cold start, флаппинг и скейл-по-не-той-метрике всплывают только на встречных вопросах. Особенно показателен сценарий «зависимость затормозила, что сделает HPA по CPU?» — сократит реплики, и мало кто это предвидит.
Follow-up интервьюера: как защититься от scale-down во время инцидента? — консервативная политика вниз: длинное стабилизационное окно, минимум реплик с запасом, а у зрелых команд — «замок» на скейл-даун при активном инциденте или горящем SLO.
52. Система тормозит — как методично найти узкое место?
Главное слово — методично: от симптома по слоям, сужая круг измерениями, а не перебором догадок. Шаг один — определи симптом количественно: latency или throughput, какой перцентиль, какие эндпоинты, все пользователи или сегмент, когда началось — и что менялось в этот момент (релиз, конфиг, рост трафика): корреляция с изменением сокращает поиск на порядок. Шаг два — раздели путь запроса: где время? Трейсинг отвечает сразу (спан покажет, что 800 из 900 мс — база); без трейсинга — по метрикам каждого хопа: балансировщик, сервис, зависимости. Найденный слой — сужай дальше: если сервис — профилируй (CPU-профиль, flame graph, лок-контеншн), если база — топ медленных запросов, планы, ожидания. Шаг три — на подозреваемом хосте пройди USE: утилизация, сатурация, ошибки по ресурсам; помни про невидимое — троттлинг cgroups, соседей на ноде, лимиты пулов. Классические находки: исчерпанный пул соединений (в коде «база медленная», а база скучает — время уходит на ожидание коннекта из пула), N+1 запросы, GC-паузы, один горячий шард. Правила дисциплины: меняй по одной вещи и измеряй после каждой; фиксируй гипотезы письменно; и не хватайся за оптимизацию до локализации — ускорять код, который занимает 5% времени, можно бесконечно и бесполезно: закон Амдала работает и в дебаге.
Хаотичный перебор: «посмотрю логи... добавлю индексов... подниму реплики» — действия без измерений. Интервьюер ждёт схему «симптом → слой → ресурс → фикс → проверка»; отдельный маркер зрелости — вопрос «что менялось?» в первой же фразе.
Follow-up интервьюера: p99 вырос, p50 на месте — о чём это говорит? — страдает хвост: часть запросов бьётся о редкое ограничение — GC-паузы, лок, ретраи, медленный шард, cold cache. Ищи, что общего у медленных запросов, — трейсы с фильтром по длительности отвечают быстрее всего.
Блок 8. Практика SRE
53. Chaos engineering — что инжектить в GameDay и почему именно это?
Определение chaos engineering спрашивают редко — спрашивают, что конкретно ты будешь ломать и зачем. Правильная рамка: эксперимент начинается с гипотезы устойчивого состояния (steady state hypothesis) — «при убийстве одного пода error rate не превысит X, latency Y»; инжектим отказ, сравниваем метрики с гипотезой; расхождение — находка, ради которой всё затевалось. Набор инжекций по нарастанию: убить под — проверяет рестарты, реплики, ретраи клиентов, поведение балансировки; убить ноду — плюс перескейлинг и привязку stateful-нагрузок; отключить зону — проверка того самого N+1 headroom и мультизонной маршрутизации, самый честный тест архитектурных обещаний; добавить латентность к базе или зависимости (не убить, а замедлить!) — самый недооценённый эксперимент: полные отказы системы обычно переживают, а вот медленная зависимость вскрывает отсутствие таймаутов, исчерпание пулов, каскады — то, чем реально болеют проды; отдельно — отказ внешнего API, DNS, истечение сертификата. Правила безопасности: начинать со стейджа, в прод — с минимальным blast radius (один под, доля трафика) и кнопкой мгновенной остановки; эксперимент во время инцидента — отменяется. GameDay — командное учение вокруг таких инжекций: проверяется не только система, но и люди — алерты сработали? ранбук нашёлся? эскалация прошла?
Рассказывают про «обезьяну, убивающую поды» без гипотезы — а без steady state hypothesis это не эксперимент, а вандализм со спецэффектами. И почти никто не называет инжекцию латентности — хотя именно slow, а не down, ломает проды чаще всего.
Follow-up интервьюера: менеджмент боится «ломать прод специально» — как продаёшь практику? — отказ случится всё равно; вопрос лишь, произойдёт он в 3 ночи без подготовки или во вторник в 11 утра с командой наготове и кнопкой отмены. Плюс старт со стейджа и минимального радиуса — это переговорная позиция, а не капитуляция.
54. Учения по отказам в стиле DiRT — зачем проверять людей, а не только системы?
DiRT (Disaster Recovery Testing) — подход Google к регулярным учениям: масштабные, заранее спланированные испытания, в которых отказ реален или реалистично симулирован, а проверяются не только системы, но и люди с процессами. Логика: техническая избыточность без процессной — половина защиты. Резервный дата-центр есть, а инструкция переключения устарела на два года; бэкапы делаются, но их ни разу не разворачивали целиком; эскалация прописана, но телефон в ней — уволившегося сотрудника; ключевой сервис умеет чинить один человек, и он в отпуске. Ни одна из этих дыр не видна в мониторинге — они обнаруживаются только исполнением. Что проверяют учения помимо систем: срабатывание алертов (узнали от мониторинга или от «жертвы»?), полноту ранбуков (можно ли пройти по шагам без автора?), работу эскалаций и ролей, коммуникацию, и — важный жанр — недоступность людей: классика DiRT-сценариев, когда «главный по системе» объявляется недоступным, и команда обязана справиться без него; фактор автобуса проверяется только так. Практика внедрения без гугловского размаха: регулярные табличные учения (проговорить сценарий по ролям за столом), одно «живое» учение в квартал — восстановление из бэкапа на чистое окружение, фейловер базы, отключение зоны на стейдже; каждая находка — тикет с владельцем, как из настоящего постмортема. Учение без последующих фиксов — театр.
Сводят учения к технике — «протестируем фейловер», забывая людей и процессы: устаревший ранбук и недоступный ключевой человек роняют восстановление так же надёжно, как невставшая реплика. Сценарий «а теперь без Пети» в ответах почти не звучит — а это самая дешёвая и самая полезная инжекция.
Follow-up интервьюера: как часто и с каким размахом проводить, чтобы не парализовать работу? — ритм важнее размаха: табличные — ежемесячно, живые ограниченные — ежеквартально, большие — раз в год. Редкие грандиозные учения хуже частых маленьких: навык восстановления — скоропортящийся.
55. Миграция без даунтайма — двойная запись, переключение, откат
Каноничная последовательность переезда данных (новая база, новая схема, новый сервис) без остановки мира. Фаза 1 — двойная запись: приложение пишет в старое и новое хранилище, читает по-прежнему из старого; новое наполняется живым потоком. Фаза 2 — бэкфилл: фоновая доливка исторических данных в новое; после — сверка (reconciliation): счётчики, чексуммы, сэмплирование записей — расхождения чинятся, пока чтения всё ещё идут из старого, то есть ошибки миграции пока никому не больно. Фаза 3 — переключение чтений: постепенно (канареечно — по проценту трафика или сегментам пользователей), с двойным чтением для верификации в идеале: читаем из обоих, отдаём старое, сравниваем и логируем расхождения. Фаза 4 — старое хранилище остаётся на приёме записей ещё период отката: если новое повело себя плохо, чтения возвращаются на старое одним переключением — данные там не отставали благодаря продолжающейся двойной записи. Фаза 5 — только убедившись, отключаем двойную запись и хороним старое. Ключевые свойства: на каждом шаге есть дешёвый откат, и точка невозврата отодвинута максимально далеко. Схемные миграции в той же философии — expand-contract: подробнее в DevOps-подборке. Грабли двойной записи, которые стоит назвать: она не атомарна — запись в старое прошла, в новое нет; нужны идемпотентный ретрай доливки и финальная сверка, иначе «мигрировали» — понятие оптимистическое.
Предлагают «остановим запись, перельём, переключим» — это даунтайм, о недопустимости которого был вопрос. Или знают фазы, но не могут ответить «а что, если двойная запись наполовину упала» — неатомарность двойной записи и обязательность сверки отличают делавших от читавших.
Follow-up интервьюера: когда можно наконец удалить старое хранилище? — после полного цикла на новом: пиковые нагрузки, месячные батчи, восстановление из бэкапа нового хранилища проверено. И не удалить, а сначала закрыть на запись и подержать в read-only — дешёвая страховка от «забыли одного читателя».
56. Runbook — что отличает хороший от бесполезного?
Runbook — инструкция дежурному для конкретного алерта или сценария, и его качество проверяется одним критерием: может ли уставший человек в три ночи, видящий систему второй раз в жизни, пройти по нему и не сделать хуже. Признаки хорошего. Привязка к алерту: из алерта — прямая ссылка на ранбук; ранбук начинается с «что это значит и какое влияние», чтобы дежурный за десять секунд понял тяжесть. Команды copy-paste: не «проверьте состояние очереди», а конкретная команда с реальными хостами и неймспейсами, и рядом — как выглядит нормальный и ненормальный вывод; каждая «переведи с общего на конкретное» операция, оставленная читателю, — минуты MTTR и шанс ошибки. Действия с предупреждениями: какие шаги безопасны, какие необратимы, что нужно зафиксировать до рестарта (логи, дампы — форензика испаряется вместе с перезапуском). Критерии эскалации: «если после X не помогло или ошибок больше Y — буди такую-то команду» — явные, чтобы дежурный не тянул из вежливости. Свежесть: ранбук — код; ревью при изменениях системы, дата последней проверки, а лучший тест — учения: новичок проходит по ранбуку на стейдже, всё, обо что он споткнулся, — баги документа. И финальная мысль для собеса: ранбук, который сводится к «выполни эти 5 команд», — кандидат на автоматизацию; зрелый цикл — ранбук → скрипт → кнопка → автоматика, и ранбуки постепенно умирают, рождая автоматизацию.
Описывают ранбук как «документацию по системе» — нет: это не архитектурный обзор, а маршрут действий под конкретный сигнал. Второй провал — не назвать критерии эскалации и пометки необратимости: ранбук без них подталкивает героизм и уничтожение улик рестартом.
Follow-up интервьюера: как заставить ранбуки не протухать? — встроить в процесс: алерт без ранбука не проходит ревью; постмортем обновляет ранбуки задетых сценариев; и периодический прогон на учениях — протухание обнаруживается исполнением, а не аудитом.
57. Автоматизация toil — когда скрипт, когда сервис, а когда оставить руками?
Решение — экономическое, и считать надо честно. Формула проста: стоимость автоматизации против частота × время × цена ошибки ручного исполнения, с горизонтом в год-два. Отсюда лестница. Оставить руками: редкое (раз в квартал), быстрое, требующее суждения по месту — автоматизация не окупится, а протухший автомат для редкой задачи опаснее свежей головы; но даже тут минимум — чек-лист, потому что память врёт. Скрипт: повторяемое, механическое, исполняемое человеком по решению человека — рестарты, ротации, сборы диагностики; скрипт в репозитории с ревью, а не в домашней папке дежурного. Порог перехода к сервису: когда скрипту нужны расписание, реакция на события, состояние, права, аудит, или когда его гоняют так часто, что человек-запускатель сам стал toil'ом — тогда это оператор, контроллер, джоба с мониторингом; сервис дороже на порядок (у него есть свой мониторинг, свои инциденты, свой владелец), и это надо проговорить. Ловушки автоматизации: автомат без владельца деградирует в источник инцидентов; половинчатая автоматизация («скрипт делает, человек проверяет глазами») иногда хуже ручной — усыпляет внимание, оставляя ответственность; и автоматизировать процесс, который не отлажен руками, — консервировать хаос. Правило Google здесь честное: сначала процесс становится скучным и повторяемым, потом он автоматизируется — автоматизация суждений не заменяет.
Два полюса провала: «автоматизируем всё» без экономики (год пилить оператора для задачи на 10 минут в месяц) и «руки надёжнее» без учёта цены ошибок и линейного роста toil. Ответ без слов «частота, время, цена ошибки, владелец» — вкусовщина, а не инженерия.
Follow-up интервьюера: автоматизация сама стала источником инцидентов — сворачивать? — чинить как сервис: владелец, тесты, стейджинг, лимиты и стоп-условия, аудит действий. Автомат, устраивающий инциденты, — это сервис без SRE-практик, и лечится он ими же, а не возвратом к рукам.
58. Надёжность релизов глазами SRE — freeze, прогрессивные раскатки, feature flags
Релизы — главный источник инцидентов, поэтому SRE смотрит на процесс релиза как на систему управления риском. Инструменты. Прогрессивная раскатка — дефолт для всего: канарейка → проценты → 100%, с автоматическим сравнением метрик и автооткатом (см. вопрос про канарейку); скорость раскатки пропорциональна уверенности и обратна размеру blast radius. Feature flags — разделение деплоя и релиза: код едет в прод выключенным, включается флагом постепенно и по сегментам; главный выигрыш — откат без деплоя за секунды, и мгновенный рубильник для деградации; цена — дисциплина уборки флагов, иначе конфигурационное пространство комбинаций флагов само становится источником инцидентов. Release freeze перед пиками — Черная пятница, Новый год, крупные события: в окна максимальной цены отказа не катится ничего, кроме критических фиксов, потому что error budget в пик стоит дороже номинала — та же минута даунтайма бьёт по большему числу пользователей и денег. Freeze — не «остановка разработки», а сдвиг рисковых действий из дорогого окна. Плюс гигиена: никаких пятничных вечерних релизов не потому что примета, а потому что время до обнаружения и число доступных рук в выходные хуже; релизные окна в часы, когда команда в строю; и метрики самого процесса — частота релизов, доля откатов, время отката: медленный откат — это несуществующий откат.
Знают слова, но не связку с error budget: freeze и скорость раскатки — это управление расходом бюджета, а не ритуалы. И про флаги забывают тёмную сторону — неубранные флаги и непротестированные комбинации, которые однажды встречаются в проде впервые.
Follow-up интервьюера: разработка жалуется, что freeze тормозит фичи, — чем аргументируешь окно заморозки? — ценой минуты в пик против цены недельной задержки фичи, цифрами прошлых пиковых инцидентов; и компромиссами: freeze только на критичный путь, экспресс-полоса для low-risk изменений за фича-флагами.
59. Production readiness review — что проверить до того, как сервис поедет в прод?
PRR — ворота между «работает у нас на стейдже» и «за это отвечает он-колл»: структурированная проверка готовности сервиса к продовой жизни, у Google — обязательный этап перед принятием сервиса на SRE-поддержку. Чек-лист по областям. Наблюдаемость: SLI определены и измеряются, дашборд есть, алерты симптомные и с ранбуками, логи структурированы. Надёжность: таймауты и ретраи на всех внешних вызовах выставлены осознанно, зависимости критического пути перечислены с их SLO, поведение при отказе каждой — задокументировано и проверено, graceful shutdown работает (сервис доживает запросы при выселении пода). Ёмкость: нагрузочный тест пройден, пределы известны, автоскейлинг настроен и проверен, requests/limits обоснованы измерениями, а не скопированы у соседа. Эксплуатация: деплой и откат автоматизированы и отрепетированы, миграции совместимы на шаг назад, конфигурация и секреты — по стандарту платформы, бэкапы делаются и восстановление проверено. Процесс: владелец определён, очередь он-колла назначена, эскалации прописаны. Смысл ритуала двойной: во-первых, дыры находятся до первого инцидента, а не первым инцидентом; во-вторых, PRR — договор между командой разработки и теми, кто будет просыпаться: SRE вправе не брать сервис на поддержку, пока список не закрыт, — и это право превращает чек-лист из пожелания в стандарт.
Сводят готовность к «мониторинг навесили» — без нагрузочного теста, репетиции отката и проверки поведения при отказе зависимостей. И упускают процессную силу PRR: без права SRE сказать «не берём» ревью превращается в формальность, которую проходят копипастом.
Follow-up интервьюера: бизнес требует запустить до закрытия чек-листа — что делаешь? — торгуюсь рисками, а не принципами: запуск за фича-флагом на процент трафика, известные дыры — в явный список рисков с владельцами и дедлайнами, и договорённость, чей пейджер звонит до закрытия списка.
60. Как SRE взаимодействует с продуктовыми командами — embedded, платформа и error budget как общий язык
Модели три, и у каждой своя экономика. Центральная SRE-команда, владеющая продом: глубокая экспертиза и стандарты, но плохо масштабируется людьми и рискует стать «операторами чужого кода» — разработка теряет контакт с последствиями своих решений. Embedded: SRE сидит внутри продуктовой команды — максимальный контекст и влияние на архитектуру до того, как она поехала в прод; цена — изоляция (один SRE в команде разработчиков без цеха коллег профессионально дичает) и разнобой практик между командами. Платформенная модель — мейнстрим последних лет: SRE строят платформу и golden paths — стандартные рельсы деплоя, наблюдаемости, алертинга, PRR-шаблоны, — а продуктовые команды сами эксплуатируют свои сервисы на этих рельсах; SRE масштабируются кодом, а не телами. Реальные компании миксуют: платформа для всех, embedded — для критичных систем. Связующий механизм в любой модели — error budget как общий язык: без него разговор «надёжность против фич» — спор вкусов и авторитетов, с ним — арифметика, под которой подписались обе стороны и менеджмент; SRE перестаёт быть «отделом нет», а становится стороной договора. На собесе этот вопрос — проверка зрелости: младший рассказывает про инструменты, старший — про то, как устроены стимулы, чтобы надёжность не держалась на героизме. И это честный разговор в обе стороны — спрашивать на собесе, по какой модели живёт SRE в компании и подписан ли error budget, стоит и тебе: ответ расскажет о будущей работе больше, чем описание вакансии.
Знают только одну модель — свою — и не могут назвать её слабости. Вторая грабля — error budget упоминают как метрику, а не как договор: вся его сила в подписи менеджмента под последствиями, без неё это просто график в Grafana.
Follow-up интервьюера: продуктовая команда систематически игнорирует надёжность — рычаги без административного ресурса? — сделать цену видимой: отчёт по сгоревшему бюджету и инцидент-часам команды перед менеджментом, PRR-ворота на он-колл поддержку, и платформенные рельсы, на которых надёжный путь — самый лёгкий путь.
Подготовка к собеседованию
Помогаю готовиться лично: мок-собеседования, разбор ответов, стратегия под конкретную компанию. Пишите в Telegram.
Мок-собесы и разбор ваших ответов — в менторстве и закрытом чате.