awesome-everything EN
↑ Обратно к восхождению

Архитектура бэкенда

Деплой без простоя: graceful shutdown как свойство флота

Суть В rolling-деплое graceful shutdown становится свойством флота: дерегистрируй до завершения, доводи новые поды до ready раньше дренажа старых, размеряй задержку дерегистрации по p99 и джиттери закрытие соединений, чтобы флот клиентов не переподключился одним громовым всплеском.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 17 min

Каждый урок до сих пор был про то, как один процесс умирает хорошо. Но ты никогда не деплоишь один процесс — ты катишь флот. Rolling-деплой проходит по твоим репликам, заменяя каждый старый под новым, и видимое пользователю обещание — «ноль простоя»: ни один запрос не падает, ни один клиент не замечает, хотя каждый инстанс разбирается и пересобирается под ними. Вот неуютная часть: идеальное выключение каждого инстанса не даёт тебе этого обещания само по себе. У тебя может быть безупречный SIGTERM-обработчик, верная readiness, чистый дренаж, идемпотентные возвраты — и всё равно простой на деплое, потому что координация между инстансами неверна. Снеси старый под до того, как его замена готова, — и ты сжал мощность посреди деплоя. Снеси слишком много сразу — и ты в брауноуте. Дай балансировщику держать устаревший маршрут несколько секунд — и ты отвергаешь соединения. Пошли каждому клиенту Connection: close в один миг — и они все переподключатся одним синхронизированным громовым всплеском, что повалит те самые поды, что ты только поднял. Деплой без простоя — не сумма хороших выключений; это свойство того, как выключения скоординированы по флоту. Это капстоун юнита: всё, что ты выучил, поднятое с одного процесса до многих.

Мощность не должна проседать: новый готов до дренажа старого

Первый инвариант уровня флота — что общая обслуживающая мощность никогда не падает ниже спроса во время раската. Rolling-деплой управляется двумя ручками (Kubernetes называет их maxUnavailable и maxSurge, но концепт универсален): сколько старых подов может быть недоступно сразу и сколько лишних новых подов можно поднять сверх желаемого числа. Безопасный паттерн — сёрджи вверх, затем дренируй: доведи новый под полностью до ready — прохождение readiness-пробы, прогрет, подключён к хранилищам — до того, как начнёшь дренировать старый, что он заменяет. Если перевернуть это, завершая старый под до того, как новый обслуживает, у тебя есть окно, где флот на один инстанс короче, и под устойчивой нагрузкой эта недостающая мощность — потерянные или поставленные в очередь запросы. maxUnavailable: 0 с положительным maxSurge кодирует «никогда не падать ниже полной мощности»; это медленнее и стоит временных лишних инстансов, но это та настройка, что реально даёт ноль простоя под нагрузкой.

Дерегистрируй до завершения, размеряй по реальному распространению

Это гонка дерегистрации из урока три, но теперь как дисциплина флота. Каждый старый под должен быть убран из ротации балансировщика до того, как перестанет принимать — и поскольку дерегистрация в конечном счёте согласована, ты должен переждать распространение до закрытия слушателя. На масштабе флота числа становятся конкретными и большими. Задержка дерегистрации (таймаут дренажа соединений) типичного облачного балансировщика по умолчанию 30–60 секунд; для сервисов с долгоживущими соединениями или большими загрузками команды толкают её до 600 секунд или больше. Правило размера, что выживает в контакте с продом: ставь окно дренажа примерно в 3× твоей p99-длительности запроса, чтобы все, кроме самых патологических, запросы в полёте завершились, затем зажми его страховочным таймаутом. Ошибись в этом по сотне подов — и каждый деплой сбрасывает тонкий, коррелированный слой 502 — невидимый в одном запросе, кричащий в агрегированном проценте ошибок.

Громовое переподключение: джиттери закрытие

Последняя ловушка — та, что показывает только флот. Когда ты дренируешь, ты шлёшь Connection: close, чтобы клиенты переподключились к здоровым инстансам — верно. Но если каждый под закрывает свои keep-alive соединения в один миг, каждый клиент переподключается в один миг, и флот из тысяч клиентов приземляется одним синхронизированным громовым переподключением на выжившие (и только запущенные, холодные) поды — самоинфликтное громовое стадо, ровно тот режим отказа, что юниты про circuit breaker и идемпотентность кружили снова и снова. Фикс — тот же, что использовали те юниты: джиттер. Размажь закрытие соединений по рандомизированному окну вместо одного мига, чтобы переподключения смазались по времени, а не всплеснули. В сочетании с сёрдж-мощностью и правильно размеренной дерегистрацией джиттеренное закрытие — то, что превращает «каждый инстанс выключился корректно» в «флот ни разу не икнул». Ноль простоя — это graceful shutdown плюс координация: мощность, порядок и размазывание.

Почему это работает

Почему флот индивидуально-идеальных graceful shutdown’ов не есть автоматически деплой без простоя — что флот добавляет, чего один процесс не видит? Потому что каждое свойство, что важно во время деплоя, — это отношение между инстансами, а у одного процесса нет вида на соседей. Мощность — яснейший пример: один под, дренирующий безупречно, корректен в изоляции, но вызовет ли этот дренаж простой, зависит целиком от того, обслуживает ли уже его замена — факт, что дренирующий под не может знать и не может контролировать. То же верно для задержки дерегистрации: под может переждать распространение идеально, но правильное количество ожидания — функция распределения длительности запросов флота и времени сходимости балансировщика, а не чего-то локального. И громовое переподключение чисто эмерджентно — ни одно отдельное закрытие не неверно; вред идёт от тысяч корректных закрытий, приземляющихся одновременно, паттерн, что буквально не существует на масштабе одного процесса и потому не может быть предотвращён там. Это тот же урок, что урок про распределённые режимы отказа вбивал для circuit breaker: локальная корректность не складывается в глобальную корректность бесплатно, потому что режимы отказа живут во взаимодействиях. Graceful shutdown даёт тебе хорошо ведущий себя инстанс; деплой без простоя — слой оркестрации, что расставляет эти инстансы во времени и пространстве — сёрдж до дренажа, дерегистрация до завершения, джиттер синхронизированных действий — чтобы их индивидуально-корректные поведения не столкнулись. Деплой — где сходятся все темы этого трека: жизненный цикл (последовательность выключения), лимиты ресурсов (запас мощности), идемпотентность (безопасный возврат под churn) и распределённые системы (конечная согласованность и стадность). Владеть выключением значит владеть всеми четырьмя сразу.

Забота флотаНеверноВерноПровал, если проигнорировать
МощностьДренировать старый до готовности новогоСёрдж нового до ready, затем дренаж старогоПросадка мощности посреди деплоя
Размер пачкиЗаменять все/многие сразуОграниченные maxUnavailable / maxSurgeБрауноут под устойчивым трафиком
МаршрутизацияЗакрыть слушатель на SIGTERMДерегистрация + переждать распространениеОкно connection-refused на под
Окно дренажаФиксированный крошечный таймаут~3× p99, зажать страховкойОборванные длинные запросы, 502
ПереподключениеВсе клиенты закрывают сразуДжиттеренный Connection: closeГромовое переподключение на холодные поды
Викторина

У сервиса безупречное выключение каждого инстанса, но он всё равно роняет запросы во время rolling-деплоев под нагрузкой. Какая настройка уровня флота прямее всего чинит просадку мощности посреди деплоя?

Викторина

Во время деплоя каждый под шлёт Connection: close в тот же миг, как начинает дренировать, и выжившие холодные поды немедленно валятся. Что произошло и каков фикс?

Расставь шаги по порядку

Расставь один шаг rolling-деплоя без простоя для одной реплики:

  1. 1 Запустить новый под и ждать, пока он пройдёт readiness (сёрдж до дренажа)
  2. 2 Дерегистрировать старый под из балансировщика и переждать распространение
  3. 3 Дренировать старый под: джиттеренный Connection: close, дозавершить в полёте до ~3× p99
  4. 4 Закрыть ресурсы в обратном порядке зависимостей и выйти до страховочного таймаута
Вспомните перед уходом
  1. 01
    Почему флот идеальных выключений каждого инстанса не гарантирует деплой без простоя?
  2. 02
    Каковы три инварианта уровня флота rolling-деплоя без простоя и их реальные числа?
Итог

Ты никогда не деплоишь один процесс, ты катишь флот, и идеальное выключение каждого инстанса само по себе не покупает ноль простоя — недостающая половина это координация между инстансами. Три инварианта флота закрывают юнит. Мощность не должна проседать ниже спроса: сёрджи новый под до ready до дренажа старого, что он заменяет, что maxUnavailable: 0 плюс положительный maxSurge кодирует ценой временных инстансов. Маршрутизация должна дерегистрировать до завершения: уйти из ротации балансировщика и переждать в конечном счёте согласованное распространение до закрытия слушателя, с задержками дерегистрации 30–60s (600s+ для длинных соединений) и окном дренажа около 3× p99, зажатым страховочным таймаутом. И синхронизированные действия должны быть джиттеренными: не-джиттеренный одновременный Connection: close производит громовое переподключение, что валит холодные поды, что ты только запустил, так что размажь закрытия по рандомизированному окну. Глубокий урок, общий с уроком про распределённые режимы отказа, — что локальная корректность не складывается в глобальную бесплатно: режимы отказа живут во взаимодействиях, и деплой — где жизненный цикл, лимиты ресурсов, идемпотентность и конечная согласованность распределённых систем все сходятся. Эта сходимость — ровно то, что берёт финальный юнит, складывая весь бэкенд-трек в одну связную картину устойчивого сервиса под churn.

Связанные уроки
Продолжить восхождение ↑Graceful shutdown: тест с выбором ответа
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.