Как делают софт и карьера Релиз и эксплуатация: деплой, мониторинг, дежурства, инциденты
0%

Релиз и эксплуатация: деплой, мониторинг, дежурства, инциденты

Релиз и эксплуатация: деплой, мониторинг, дежурства, инциденты

До этого момента в треке мы шли по фазам, где разработчик — главный герой: требования, проектирование, код, тесты. Всё это заканчивается моментом, когда PR влит в основную ветку. И вот тут у большинства новичков карта мира обрывается. «Дальше как-то само доедет до пользователей, наверное, этим занимаются DevOps».

Так вот, не само и не только они. Разница между «код лежит в main» и «код приносит пользу живым людям и не разрушает бизнес» — это отдельная большая инженерная дисциплина, и в 2020-х она перестала быть чужой зоной. В типичной продуктовой команде именно вы будете смотреть на дашборд после выкатки своей фичи, именно вам придёт алерт, и именно вас разбудят, если сервис лёг в три ночи.

Эта статья — про то, что происходит справа от мержа. Мы разберём путь артефакта через окружения, стратегии выкатки и их цену, миграции БД (главный источник «нельзя откатить»), наблюдаемость, дежурства и жизнь инцидента от первого алерта до постмортема.

Инструментальная сторона всего этого — CI/CD, Kubernetes, Terraform, Prometheus — подробно разобрана в треке DevOps; в частности, стратегии CD и наблюдаемость и дежурства. Здесь мы смотрим с другой стороны: не «как настроить», а «как это устроено как часть работы разработчика и что от вас будут ждать».

Три разных слова, которые путают: билд, релиз, деплой

Начнём с терминологии, потому что на неё в разговоре опираются, а объяснять никто не будет.

Билд (build) — превращение исходников в артефакт: бинарник, jar, docker-образ, набор статики. Артефакт иммутабельный: собран один раз, дальше только копируется. Это ключевая идея. Если вы собираете образ отдельно для стейджа и отдельно для прода, вы тестируете не то, что выкатываете — где-то поедет версия библиотеки или флаг компилятора, и вы будете это ловить неделю.

Релиз (release) — решение о том, что конкретная версия артефакта объявляется пригодной для пользователей. Это в первую очередь организационный акт: кто-то посмотрел на результаты тестов, на список изменений, на календарь и сказал «да». В стартапе этот кто-то — автор PR, и занимает это ноль секунд. В банке — релиз-менеджер, CAB (change advisory board) и подписи трёх человек.

Деплой (deploy) — техническое действие по размещению артефакта в окружении. Деплой на стейдж — не релиз. Деплой на прод с выключенным фича-флагом — тоже ещё не релиз для пользователя.

Это разделение — не педантизм. Оно даёт главный практический вывод: деплой и релиз можно и нужно разделять во времени. Код едет на прод часто, безопасно и незаметно; фича включается пользователям отдельным решением, когда бизнес готов. Именно на этом стоит вся современная практика непрерывной поставки.

Окружения и путь артефакта

Артефакт продвигается (промоутится) через цепочку окружений. Классический набор:

  • dev / local — машина разработчика или общее окружение для сборки-мусора, куда можно катить что угодно;
  • test / QA — стабильное окружение для ручного и автоматизированного тестирования;
  • staging / preprod — максимально похожее на прод: те же версии, та же топология, желательно те же объёмы данных (обезличенных);
  • production — живые пользователи.

В реальности этот набор всегда чем-то отличается. Бывают эфемерные окружения на каждый PR (review apps) — очень удобно, довольно дорого. Бывает всего два окружения, потому что третье никто не оплатил. Бывает окружение с честным именем staging2-new-real, в котором и живёт настоящая правда.

Два правила, которые отделяют работающий пайплайн от карго-культа.

Первое: артефакт один, конфигурация разная. Всё, что отличается между окружениями (адреса БД, ключи, лимиты, флаги), приходит извне — переменными окружения, конфиг-картами, секрет-менеджером. Классическая формулировка — третий пункт The Twelve-Factor App. Если в коде есть if env == "prod", вы уже сломали иммутабельность артефакта, просто ещё не заметили.

Второе: чем ближе к проду, тем автоматичнее. Звучит контринтуитивно — кажется, что прод надо катить руками и осторожно. Наоборот: ручной деплой на прод в стрессе в пятницу вечером — это ровно та ситуация, где человек забудет шаг. Автоматизированный деплой делает одно и то же каждый раз, а осторожность выражается в стратегии выкатки и в возможности откатиться.

Стратегии выкатки и их честная цена

Вот главный набор способов заменить работающую версию на новую. Ни один из них не «лучший» — каждый покупает одно за счёт другого.

Распределение трафика между версиями при rolling, blue-green и canary

Recreate (остановили — подняли). Выключаем старую версию, поднимаем новую. Даунтайм равен времени запуска. Просто, дёшево, честно. Годится для внутренних систем, ночных батчей, админок. Для публичного сервиса с SLA — нет.

Rolling update. Экземпляры заменяются пачками: убили два пода, подняли два новых, дождались готовности, следующие два. Даунтайма нет, дополнительных ресурсов почти не нужно. Плата: какое-то время в проде живут обе версии одновременно. Это не мелочь. Значит, N и N+1 должны уметь работать с одной и той же схемой БД, с одним и тем же форматом сообщений в очереди, с одними и теми же ключами кеша. Откат тоже rolling, то есть небыстрый.

Blue-green. Разворачиваем полную вторую копию (green), прогоняем на ней смоук-тесты, переключаем балансировщик, старую (blue) держим наготове. Откат — одно переключение, секунды. Плата: двойная инфраструктура на время выкатки, а главное — общая база данных, которая никуда не задваивается, поэтому «мгновенный откат» справедлив только для кода, но не для миграций.

Canary. Пускаем на новую версию 1% трафика, смотрим на метрики ошибок и задержек, поднимаем до 5%, 25%, 100%. Ошибку видит малая доля пользователей, и решение об откате принимается на данных, а не на интуиции. Плата: нужен роутинг по долям, нужна метрика, по которой автоматика или человек примут решение, и нужна дисциплина не пропускать паузы наблюдения. Разработчику здесь важно понимать: канарейка ловит только то, что видно в метриках за окно наблюдения. Утечка памяти, которая проявится через шесть часов, канарейкой не ловится.

Feature flags (фича-флаги, toggles). Код едет на прод выключенным, включается конфигом — для внутренних сотрудников, потом для 5% пользователей, потом для всех. Это самый мощный инструмент из перечисленных, потому что он разводит деплой и релиз полностью, и «откат» становится изменением булева значения без пересборки.

# Простой пример: флаг с процентным раскатом и стабильным разбиением
# пользователей — один и тот же user_id всегда попадает в ту же группу.
import hashlib

def flag_enabled(flag: str, user_id: str, percent: int) -> bool:
    """percent — доля пользователей (0..100), для которых флаг включён."""
    # Хеш от пары «флаг + пользователь»: разные флаги дают независимые группы,
    # а один флаг для одного пользователя стабилен между запросами.
    digest = hashlib.sha256(f"{flag}:{user_id}".encode()).digest()
    bucket = int.from_bytes(digest[:4], "big") % 100
    return bucket < percent

# Использование в коде
if flag_enabled("new_checkout", user.id, percent=5):
    result = new_checkout(order)
else:
    result = legacy_checkout(order)

Стабильность разбиения — не деталь, а требование. Если пользователь при каждом запросе случайно попадает то в новую, то в старую ветку, вы получите неповторяемые баги и невозможность что-либо измерить.

У флагов есть своя цена, о которой умалчивают: каждый флаг — это ветвление, то есть удвоение числа путей в коде. Десяток живых флагов даёт комбинаторику, которую никто не тестирует. Поэтому здоровая практика — у флага с самого начала есть владелец и дата смерти, а в бэклоге лежит задача на его удаление. Флаг, забытый на два года, — это уже техдолг, к которому мы вернёмся в статье про поддержку и техдолг. Подробный разбор видов флагов — у Мартина Фаулера.

Миграции базы данных: место, где ломается «просто откатим»

Если вы запомните из статьи одну вещь, пусть это будет она. Код откатывается легко. Данные — нет.

Классическая ловушка: разработчик переименовывает колонку name в full_name одной миграцией и выкатывает код, который читает новое имя. Что происходит:

  1. При rolling-выкатке старые поды ещё живы и обращаются к name — её уже нет. Сыплются ошибки.
  2. Выкатка признана неудачной, принимается решение откатиться. Код откатили — но схема уже новая, и старый код снова не работает.
  3. Инцидент из десятиминутного превращается в часовой, потому что теперь надо катить «миграцию вниз», а она, скорее всего, не тестировалась никогда.

Решение — шаблон expand / contract (он же parallel change): любое несовместимое изменение схемы разбивается на серию совместимых шагов, каждый из которых выкатывается отдельно.

Фазы миграции по шаблону expand/contract

-- Фаза 1 (expand). Отдельный релиз. Добавляем колонку, ничего не ломая.
-- Обязательно NULL-able или с DEFAULT: старый код о ней не знает и не пишет.
ALTER TABLE users ADD COLUMN full_name text;

-- Фаза 2 (dual write). Приложение пишет в обе колонки, читает старую.
-- Параллельно — бэкфилл существующих строк ПАЧКАМИ, а не одним UPDATE:
-- один UPDATE на 50 млн строк держит долгую транзакцию, раздувает WAL
-- и вполне способен положить прод сам по себе.
UPDATE users
SET full_name = name
WHERE full_name IS NULL
  AND id BETWEEN :lo AND :hi;   -- цикл по диапазонам с паузами

-- Фаза 3 (switch read). Приложение читает full_name. Откат ещё возможен:
-- старая колонка на месте и наполняется.

-- Фаза 4 (contract). Через дни или недели, когда уверены, что откат
-- к старой версии больше не понадобится:
ALTER TABLE users DROP COLUMN name;

Ещё несколько практических правил про миграции, за которые вам скажут спасибо на ревью:

  • Миграция и деплой кода — разные шаги. Сначала едет совместимая миграция, потом код. Не наоборот и не одновременно.
  • Осторожно с блокировками. В PostgreSQL ALTER TABLE ... ADD COLUMN с константным DEFAULT дёшев начиная с 11-й версии, а вот создание индекса без CONCURRENTLY заблокирует запись в таблицу на всё время построения. В MySQL правила свои. Всегда проверяйте, какую блокировку берёт ваша конкретная СУБД конкретной версии — детали в треке Базы данных.
  • Тестируйте миграцию на объёме, близком к продовому. Миграция, отработавшая за 200 мс на локальной базе с тысячей строк, на проде с 80 миллионами может идти сорок минут с блокировкой.
  • Необратимые операции — отдельным релизом и с паузой. DROP COLUMN, DROP TABLE, удаление данных не должны ехать в одном релизе с новой функциональностью.

Релизный цикл: непрерывно или поездом

Есть два полюса, и оба нормальны.

Continuous deployment. Каждый смерженный в main коммит, прошедший пайплайн, автоматически едет на прод. Релизов в день — десятки. Работает, когда тесты и наблюдаемость реально надёжны, изменения мелкие, а откат дешёвый.

Release train (релизный поезд). Есть расписание: раз в две недели, раз в месяц, раз в квартал. За несколько дней до релиза объявляется feature freeze (в основную ветку принимаются только багфиксы), ветка релиза стабилизируется, QA гоняет регресс, готовятся release notes, оповещаются саппорт и продажи. Не успел к поезду — едешь следующим.

Ключевые понятия из этой картинки, которые вы услышите на работе:

  • Code freeze / feature freeze — период, когда новую функциональность в релиз не берут. Классический источник конфликтов: «моя фича готова на 95%, давайте всё-таки возьмём». Ответ почти всегда «нет», и это правильно: смысл заморозки в том, что после неё тестируемое не меняется.
  • Release candidate — версия-кандидат, которую тестируют как финальную.
  • Go / No-go — короткая встреча, где представители разработки, QA, продукта и эксплуатации говорят «едем» или «не едем». Полезная штука: она делает решение явным и разделённым, а не «Петя молча выкатил».
  • Release notes / changelog — список изменений для людей. Пишется не для галочки: саппорт по нему отвечает пользователям, а на разборе инцидента это первое, куда смотрят.
  • Окно повышенного внимания (bake time) — время после выкатки, когда команда смотрит на метрики. Именно поэтому релизить в пятницу вечером плохо не из-за суеверия, а потому что окно наблюдения приходится на выходные, когда никого нет.

Как это выглядит по полюсам. В стартапе обычно continuous deployment или что-то близкое: релиз — это мерж, release notes — это git log, решение принимает автор. Плюс — обратная связь от пользователей приходит за часы. Минус — при слабых тестах вы просто быстрее катите баги, а без наблюдаемости узнаёте о них от пользователей в поддержке.

В энтерпрайзе — поезд, часто с формальным change management (ITIL-процесс): заявка на изменение, окно работ, план отката, согласование CAB, уведомление смежных систем. Плюс — предсказуемость, координация десятков команд, аудируемость (для банков и госсектора это требование регулятора, а не прихоть). Минус — цикл обратной связи в недели, огромные релизы, а значит трудная локализация причины при поломке, и почти всегда — «релиз ночью в субботу» как норма жизни.

Промежуточная и очень частая реальность: continuous delivery без continuous deployment. Пайплайн умеет катить в любой момент, артефакт всегда готов, но нажатие кнопки — человеческое решение. Для большинства команд это разумная точка.

Наблюдаемость: как вообще узнать, что что-то не так

Выкатили. Дальше начинается эксплуатация, и первый вопрос — откуда вы узнаете о проблеме. Плохой ответ: «пользователь напишет в поддержку». Хороший: «система сама скажет, и раньше, чем это заметят люди».

Три классических источника данных (их часто зовут «три столпа», хотя это упрощение):

  • Метрики — числовые ряды во времени: RPS, доля ошибок, задержки по перцентилям, длина очереди, использование памяти. Дёшевы, агрегированы, идеальны для алертов и дашбордов. Не отвечают на вопрос «что именно случилось с конкретным запросом».
  • Логи — записи о событиях. Отвечают на «что произошло с этим конкретным запросом». Дороги в хранении, бесполезны без структуры и без идентификатора корреляции.
  • Трейсы — путь одного запроса через все сервисы с таймингами по каждому шагу. Незаменимы в распределённой системе, где «медленно» может означать любой из пятнадцати сервисов.

Что от вас как разработчика ждут на практике:

# Плохо: строка, из которой ничего нельзя агрегировать и по которой
# невозможно найти связанные события.
log.info(f"Не удалось обработать платёж для {user.email}: {err}")

# Хорошо: структурная запись с идентификатором трассировки,
# без персональных данных, с полями, по которым можно фильтровать.
log.error(
    "payment_failed",
    extra={
        "trace_id": ctx.trace_id,        # связывает логи всех сервисов
        "user_id": user.id,              # id, а не email
        "order_id": order.id,
        "provider": provider.name,
        "error_code": err.code,          # машинно-читаемый код
        "attempt": attempt,
    },
)

Правила, которые стоят реальных денег и нервов: не пишите в логи персональные данные, номера карт и токены (это ещё и юридическая проблема); логируйте trace_id во всём, иначе распределённая отладка превращается в гадание; не логируйте в цикле по каждой строке пачки на миллион записей — вы утопите систему логирования и получите счёт.

Что измерять. Есть два хороших готовых набора. Для сервисов, обслуживающих запросы, — RED: Rate (запросов в секунду), Errors (доля ошибок), Duration (распределение задержек). Для ресурсов — USE: Utilization, Saturation, Errors. И есть «четыре золотых сигнала» из Google SRE Book: latency, traffic, errors, saturation. Это по сути одно и то же, сформулированное по-разному; берите любой набор и не изобретайте свой.

Перцентили, а не среднее. Средняя задержка — почти бесполезная метрика. Если 95% запросов идут 50 мс, а 5% — 8 секунд, среднее покажет 450 мс, и это число не описывает опыт ни одного реального пользователя. Смотрите p50, p95, p99. p99 в 3 секунды означает, что каждый сотый запрос — плохой; при 10 миллионах запросов в сутки это сто тысяч недовольных обращений.

SLI, SLO, error budget. SLI (service level indicator) — измеряемый показатель качества: например, доля успешных ответов за окно. SLO (objective) — целевое значение: «99,9% успешных за 30 дней». Отсюда следует бюджет ошибок: 0,1% от 30 дней — это примерно 43 минуты допустимой недоступности в месяц. Это не бухгалтерия ради бухгалтерии, а инструмент разговора: пока бюджет не израсходован, команда катит фичи быстро; когда израсходован, приоритет автоматически смещается на надёжность. Такой договор снимает вечный спор «продукт давит фичами, а мы тонем в инцидентах» и переводит его в цифры. SLA (agreement) — это уже юридический документ с компенсациями клиенту, и его значение всегда мягче внутреннего SLO.

Алерты: как не оглохнуть

Мониторинг без алертов — это красивые дашборды, на которые никто не смотрит в три ночи. Алерты — то, что будит человека. И именно здесь команды чаще всего ошибаются.

# Prometheus alerting rules — иллюстрация принципа, а не готовый конфиг
groups:
  - name: checkout-service
    rules:
      # ПЛОХО: алерт на причину. Высокий CPU сам по себе не проблема —
      # если пользователям хорошо, будить никого не надо.
      - alert: HighCPU
        expr: rate(process_cpu_seconds_total[5m]) > 0.8
        for: 1m
        labels: { severity: page }

      # ХОРОШО: алерт на симптом, который чувствует пользователь,
      # с окном "for", отсекающим одиночные всплески.
      - alert: CheckoutErrorRateHigh
        expr: |
          sum(rate(http_requests_total{service="checkout",code=~"5.."}[5m]))
          / sum(rate(http_requests_total{service="checkout"}[5m])) > 0.02
        for: 5m
        labels:
          severity: page          # будим дежурного
        annotations:
          summary: "Более 2% ошибок оформления заказа в течение 5 минут"
          runbook: "https://wiki.internal/runbooks/checkout-errors"

      # Медленное выгорание бюджета ошибок — это тикет, а не звонок ночью.
      - alert: ErrorBudgetBurnSlow
        expr: slo:error_budget_consumed_ratio{service="checkout"} > 0.5
        for: 6h
        labels:
          severity: ticket        # в рабочее время

Принципы, за которыми стоит чужая боль:

  1. Алертить на симптомы, а не на причины. Пользователю всё равно, какой у вас CPU. Ему важно, что кнопка «Оплатить» работает. Алерты на причины плодят ложные срабатывания.
  2. Каждый алерт, который будит человека, должен требовать действия человека прямо сейчас. Если на алерт нормальная реакция «ну ладно, посмотрим утром» — это не page, это тикет. Смешение этих категорий — прямая дорога к alert fatigue: через месяц дежурный на автомате закрывает всё подряд, включая настоящий инцидент.
  3. К алерту прилагается runbook — короткая инструкция: что это значит, как проверить, что делать в первую очередь, кого звать. Разбуженный человек в три ночи не помнит архитектуру, он выполняет инструкцию.
  4. Ложные срабатывания чинят, а не терпят. Шумный алерт — это баг в мониторинге, и заводить на него тикет так же нормально, как на баг в коде.

Дежурства (on-call): как это устроено на самом деле

On-call — это когда конкретный человек в конкретный период обязан отвечать на алерты. Устройство обычно такое: ротация по неделе (реже — по сменам), первая линия и эскалация во вторую, если за N минут никто не подтвердил (acknowledge). Инструменты — PagerDuty, Opsgenie, Grafana OnCall, у кого-то самописный бот в мессенджере.

Честные вещи, которые редко проговаривают вслух.

Дежурство — это труд, и он должен оплачиваться или компенсироваться. Отгулами, доплатой за дежурную неделю, отдельно за ночные подъёмы — механика бывает разная. Компания, где дежурство «просто входит в обязанности разработчика» бесплатно и без ограничений, — это красный флаг, а не норма индустрии. При этом в российских компаниях практика разнится очень сильно, и вопрос про on-call — один из тех, что обязательно надо задать на собеседовании; мы вернёмся к этому в статьях про собеседования со стороны кандидата и вознаграждение.

Ротация должна быть достаточно широкой. Дежурство вдвоём на команду означает неделя через неделю — это выгорание за полгода, гарантированно.

Дежурный не обязан чинить всё сам. Его задача — снизить влияние на пользователей и, если не хватает знаний, эскалировать. «Разбудить старшего в три ночи» — это не поражение, это штатная процедура, которая должна быть описана.

Работает принцип «вы построили — вы и дежурите» (you build it, you run it). Он придуман не для наказания разработчиков, а потому что это самая быстрая петля обратной связи из существующих. Человек, которого дважды разбудила его собственная неинформативная ошибка, начинает писать логи иначе.

Что спросить на собеседовании, если хотите понимать, во что ввязываетесь: есть ли дежурства и у кого; как компенсируются; сколько человек в ротации; сколько ночных подъёмов было за последние три месяца (это лучший вопрос — он про факты, а не про политику); есть ли runbook’и; что происходит с задачами дежурного на его неделю. Ответ «у нас почти не будят, статистики нет» обычно означает «статистики нет».

Инцидент: что происходит на самом деле

Инцидент — это незапланированное событие, ухудшающее сервис для пользователей. Не всякий баг — инцидент, и не всякий инцидент — авария: сюда попадает и деградация, и частичный отказ, и «работает, но в пять раз медленнее».

Severity (уровень серьёзности). У большинства компаний есть шкала вроде SEV1–SEV4 или P1–P4: полный отказ платежей для всех — один уровень, сломанная иконка в админке — совсем другой. Шкала нужна, чтобы автоматически определять, кого будить и как быстро сообщать бизнесу. Её главный враг — инфляция: когда всё объявляется SEV1, шкала перестаёт работать.

Самое важное правило инцидента: сначала митигация, потом диагностика. Естественный инстинкт инженера — понять, почему сломалось. Это правильный инстинкт в неправильный момент. Пока пользователи страдают, задача одна — прекратить страдание: откатить релиз, выключить флаг, включить деградированный режим, добавить реплик, включить rate limit. Понимать причину будете потом, спокойно, по логам и трейсам, которые никуда не денутся.

Роли. В сколько-нибудь серьёзном инциденте команда быстро упирается в проблему координации: пять человек параллельно чинят одно и то же, никто не пишет бизнесу, а руководитель отдела каждые три минуты спрашивает в чате «ну что там». Поэтому вводят роли — идея пришла из систем реагирования на ЧС и описана в главе Managing Incidents книги Google SRE.

  • Incident Commander (IC) — координатор. Держит картину целиком, распределяет работу, принимает решения. Критично: IC не чинит руками. Как только он погружается в логи, координация исчезает.
  • Operations / responder — тот, кто выполняет действия в системе.
  • Communications Lead — общается с внешним миром: поддержка, менеджмент, статус-страница. Одна из главных функций — защитить чинящих от потока вопросов.
  • Scribe — ведёт таймлайн: что и во сколько сделали. Кажется бюрократией, но без таймлайна постмортем превращается в коллективное припоминание.

В маленькой команде все эти роли — один-два человека, и это нормально. Важна не структура, а то, что функции кем-то закрыты явно.

Постмортем: единственная часть, которая делает систему лучше

После инцидента пишется разбор. Ключевое слово — blameless, безобвинительный.

Смысл не в гуманизме, а в инженерии: если за ошибку наказывают, люди начинают скрывать детали, и вы теряете именно ту информацию, ради которой всё затевалось. Формулировка «Вася выкатил без тестов» бесполезна. Формулировка «пайплайн позволял выкатить с красными тестами при наличии одного апрува» — это описание дефекта системы, и его можно починить.

Хороший постмортем содержит:

  • краткое резюме и влияние в измеримых величинах: «42 минуты, около 18 тысяч неудачных попыток оплаты, примерно N потерянных заказов»;
  • таймлайн с точным временем: когда началось, когда обнаружили (важнейший разрыв — time to detect), когда митигировали, когда закрыли;
  • что сработало хорошо — не для позитива, а чтобы это сохранить;
  • анализ причин без остановки на первой найденной: техника «пяти почему» полезна, но помните, что у сложного отказа обычно не одна причина, а совпадение нескольких — об этом хорошо пишет Джон Оллспоу и вся школа Learning from Incidents;
  • action items — конкретные задачи с владельцем и сроком, заведённые в трекер. Без них постмортем — сочинение.

Хороший источник для чтения — публичные постмортемы: сборник на GitHub, разборы инцидентов Cloudflare и GitLab (последний — легендарная история про удалённую продовую базу и шесть неработающих способов бэкапа). Читать чужие постмортемы — один из самых дешёвых способов набрать опыт, за который другие заплатили бессонными ночами.

Как измеряют качество процесса: метрики DORA

Чтобы разговор «у нас хорошо или плохо с поставкой» не был вкусовым, есть исследование DORA (программа DORA / Accelerate State of DevOps), выделившее четыре метрики:

Метрика Что означает Почему важна
Deployment frequency Как часто вы катите в прод Частые релизы = мелкие изменения = дешёвая локализация поломки
Lead time for changes От коммита до прода Скорость обратной связи от реальности
Change failure rate Доля релизов, вызвавших сбой Качество процесса, а не героизм
Time to restore service Как быстро восстанавливаетесь Устойчивость важнее безошибочности

Главный контринтуитивный вывод исследования: скорость и надёжность не противоречат друг другу, а коррелируют. Команды, которые катят чаще, ломают прод реже — потому что маленькое изменение проще проверить, проще откатить и проще связать с поломкой. «Мы релизим раз в квартал, зато надёжно» — обычно означает «мы релизим раз в квартал, и каждый релиз — это ночь ужаса».

И сразу предупреждение: как только метрика становится целью, её начинают накручивать (закон Гудхарта). Deployment frequency прекрасно растёт от пустых коммитов, а MTTR — от переклассификации инцидентов в «плановые работы». Метрики DORA — термометр для команды, а не KPI для премии. Про подводные камни метрик в управлении есть материал в треке проектного менеджмента.

Что из этого реально делает джун

Чтобы снять тревогу: никто не ждёт, что вчерашний студент будет чинить кластер. Но вот что действительно входит в вашу зону ответственности с первого дня.

  • Смотреть на свою фичу после выкатки. Открыть дашборд, посмотреть на ошибки и задержки своего эндпоинта, а не закрыть тикет и уйти. Это самый недооценённый навык из всех.
  • Писать код, пригодный к эксплуатации. Структурные логи с trace_id, метрики на ключевые операции, понятные сообщения об ошибках, health-check, который проверяет реальную готовность, а не отвечает 200 OK всегда.
  • Проверять обратную совместимость. Прежде чем менять формат API, схему БД или структуру сообщения в очереди, спросить себя: что будет, если старая и новая версия поработают вместе полчаса.
  • Уметь откатить. Знать команду отката своего сервиса и то, где выключается ваш фича-флаг. До того, как это понадобится, а не во время.
  • Читать постмортемы своей команды. Это концентрированный опыт про то, как именно ломается ваша конкретная система.
  • Задавать вопросы про дежурства до выхода на работу. См. выше.
# Минимальный набор, который стоит знать про свой сервис ДО первого инцидента

# Что сейчас на проде и какая версия
kubectl -n prod get deploy checkout -o jsonpath='{.spec.template.spec.containers[0].image}'

# История выкаток — что менялось и когда
kubectl -n prod rollout history deploy/checkout

# Откат на предыдущую ревизию (знать заранее, а не гуглить в 3 ночи)
kubectl -n prod rollout undo deploy/checkout

# Логи всех подов сервиса за последние 15 минут
kubectl -n prod logs -l app=checkout --since=15m --tail=200

# Кто и что перезапускал: события неймспейса по времени
kubectl -n prod get events --sort-by=.lastTimestamp | tail -30

Типичные ошибки

Считать, что после мержа задача закончена. Definition of Done в живой команде обычно включает «работает на проде и подтверждено метриками». Задача, висящая в «Done», пока её фича сыплет пятисотками, — это ложь в трекере.

Катить большими редкими релизами «чтобы надёжнее». Пятьдесят изменений разом означают, что при поломке вы не знаете, какое из них виновато. Размер релиза — главный множитель сложности разбора.

Релизить в пятницу вечером и перед отпуском. Не суеверие: окно наблюдения не должно приходиться на время, когда людей нет.

Начинать инцидент с поиска причины. Сначала остановить боль.

Полагаться на бэкапы, которые никогда не восстанавливали. Непроверенный бэкап — это не бэкап, а надежда. История GitLab по ссылке выше — ровно про это.

Игнорировать шумные алерты. Через месяц шум съедает внимание к настоящим.

Считать 200 OK от health-check доказательством работоспособности. Классика: liveness-проба отвечает из HTTP-хендлера, а пул соединений к БД мёртв — оркестратор считает под здоровым и продолжает слать на него трафик.

Обвинять человека в постмортеме. Гарантированный способ получить в следующий раз неполный таймлайн.

Тестировать миграции только на пустой локальной базе. Объём меняет всё.

Мини-итог

  • Билд, релиз и деплой — три разных вещи; разведение деплоя и релиза (через фича-флаги) — главный рычаг безопасной поставки.
  • Артефакт собирается один раз и промоутится по окружениям; различия живут в конфигурации, а не в коде.
  • Стратегии выкатки покупают безопасность за ресурсы и сложность: rolling дёшев, но требует совместимости версий; blue-green даёт мгновенный откат за двойную инфраструктуру; canary даёт решение на данных за сложность роутинга.
  • Данные не откатываются вместе с кодом. Expand / contract — обязательный к освоению шаблон.
  • Наблюдаемость — это не дашборд для красоты, а способность ответить на вопрос, которого вы не предвидели. Алерты — на симптомы, с runbook, и только те, что требуют действия сейчас.
  • Дежурства — оплачиваемый труд с широкой ротацией и правом эскалировать. Вопросы про них на собеседовании — норма.
  • В инциденте: сначала митигация, потом диагностика; роли распределены явно; постмортем безобвинительный и заканчивается задачами в трекере.
  • Частые мелкие релизы надёжнее редких больших — это не мнение, а данные DORA.

Источники

  • Google SRE Book — бесплатно онлайн; главы про мониторинг, управление инцидентами, постмортемы и error budget.
  • Google SRE Workbook — практическая часть: как считать SLO и внедрять их.
  • Nicole Forsgren, Jez Humble, Gene Kim, «Accelerate» — исследовательская база метрик DORA; актуальные отчёты — на dora.dev.
  • Jez Humble, David Farley, «Continuous Delivery» — канон про пайплайн поставки и промоушен артефактов.
  • Feature Toggles и ParallelChange — Мартин Фаулер про флаги и expand/contract.
  • The Twelve-Factor App — конфигурация, логи, паритет окружений.
  • danluu/post-mortems — коллекция публичных разборов инцидентов.
  • Learning from Incidents — современный взгляд на разбор отказов в сложных системах.

Что дальше

Мы довели изменение до пользователей и научились узнавать, что оно сломалось. Но жизнь системы на этом только начинается: приходят обращения от пользователей, накапливается техдолг, код, который вчера был отличным решением, сегодня мешает. Об этом — в следующей статье: Поддержка, технический долг и эволюция продукта.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов