Форма развёртывания

Что это такое

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

Почему это важно

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

Ключевые идеи

У независимого развёртывания есть цена

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

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

Монолит — защитимое значение по умолчанию

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

Отказы по умолчанию не уважают границы

Разделение системы на части не изолирует отказ; оно создаёт новые режимы отказа. Вызывающий, который вечно ждёт неотзывчивую зависимость, распространил отказ сам. Изоляцию нужно проектировать: явные тайм-ауты, ограниченные повторные попытки и определённое поведение в ухудшенном режиме для каждой зависимости. Поэтому «сервисы разделены» и «сервисы независимы» — разные утверждения.

зависимость недоступна, тайм-аута нет  -> вызывающий исчерпывает свои потоки
зависимость недоступна, тайм-аут есть -> вызывающий ухудшается, остальные продолжают

Окружения и конфигурация — часть формы

То, чем отличаются разработка, стейджинг и продакшен, решает, насколько безопасно вы можете тестировать. Чем меньше различий, тем точнее результат стейджинга предсказывает продакшен. Конфигурация, живущая в окружении, а не в коде, и сборка, которую продвигают, а не пересобирают, удерживают протестированный артефакт идентичным тому, который вы запускаете.

Радиус поражения — это проектный выбор

Спросите о каждой части: если она откажет или ошибётся, что ещё остановится или испортится? Одна база данных на всё означает, что одна плохая миграция оборачивается сбоем у всех. Граница, проведённая вокруг самого разрушительного отказа — платежи, аутентификация, конвейер данных, — ограничивает, как далеко может распространиться ошибка. И это часто более веская причина для разделения, чем производительность или размер команды.

Практика

Назовите ограничение

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

Готово, когда: список либо пуст, либо содержит хотя бы одно измеримое ограничение.

Изолируйте один отказ

Возьмите одну внешнюю зависимость, которую вызывает ваша система. Добавьте тайм-аут и определённый ухудшенный ответ, затем смоделируйте недоступность зависимости.

Готово, когда: остальная система по-прежнему обслуживает запросы, а ухудшенный путь наблюдаем в метрике или журнале.

Частые ошибки

  • Распределение до появления операционной причины — сложность оплачивается вперёд за выгоду, которую так и не получают; исправить: начните с одной развёртываемой единицы и извлекайте по потребности.
  • Разделение без владения данными — сервисы делят схему и не могут выпускаться независимо; исправить: считайте владение данными частью границы развёртывания.
  • Вызов зависимости без тайм-аута — потоки вызывающего заполняются, и отказ распространяется; исправить: задайте явные тайм-ауты и ограничьте повторные попытки у каждого исходящего вызова.
  • Расходящиеся окружения — результат стейджинга не предсказывает поведение продакшена; исправить: продвигайте одну сборку через окружения и держите различия в конфигурации.
  • Игнорирование радиуса поражения общего ресурса — одна ошибка выводит из строя несвязанные функции; исправить: проводите границы вокруг отказа, который важнее всего сдержать.

Что читать дальше