Содержание статьи
Сервер потерял питание в 00:32. Мы узнали об этом в 08:18
Инцидент устранён. Он продолжался с 00:32 до 08:47 UTC 17 августа 2026 года и закрыт. Данные клиентов не были потеряны, повреждены или раскрыты. Если в этот промежуток ваш деплой завершился ошибкой, автоматически он не повторится – запустите его ещё раз, и всё должно пройти успешно. Ниже – полный разбор того, что произошло и что мы изменили после инцидента.
В воскресенье, 17 августа 2026 года, в 00:32 UTC один из наших серверов хранения потерял питание. Он не перезагрузился. Не упал. Просто выключился и оставался выключенным, пока в 08:22 UTC инженер не нажал кнопку питания вручную.
В течение восьми часов и пятнадцати минут были недоступны S3-совместимое объектное хранилище, реестр контейнеров, деплои serverless-приложений и статических сайтов. Уже запущенные приложения продолжали работать. Данные не были потеряны.
Наша статус-страница обнаружила сбой и открыла инцидент в 00:35 UTC – через три минуты после начала проблемы. Информация была верной, публичной, а сама страница оставалась доступна на протяжении всего сбоя.
Но этого никто не увидел. О проблеме мы узнали только в 08:18 UTC, когда инженер по другому поводу открыл статус-страницу и спросил, почему столько сервисов горят красным.
Именно этот разрыв – между моментом, когда о проблеме уже знает система, и моментом, когда о ней узнаёт человек, – и был настоящим инцидентом. Всё остальное – детали.
Что увидели наши клиенты
Причина у сбоя была одна, а симптомов – четыре. Поэтому на статус-странице всё выглядело гораздо хуже, чем можно было ожидать от отказа одного сервера.
-
Объектное хранилище возвращало connection refused, а не просто отвечало медленнее. S3 был полностью недоступен, а не работал с ограничениями.
-
Вместе с ним перестал работать и реестр контейнеров, поскольку его блобы (Blob, Binary Large Object — легкая оболочка для бинарных данных) хранятся в объектном хранилище.
-
Деплои serverless-приложений и статических сайтов завершались ошибкой, потому что при них образы отправляются в этот реестр и загружаются из него. Неудачные деплои так и оставались в состоянии ошибки: автоматически они не повторяются, поэтому после восстановления их нужно было запустить вручную ещё раз.
-
Уже запущенные приложения сбой не затронул. Всё это время они продолжали обслуживать запросы на текущей версии.
Одна деталь оказалась неожиданной для нескольких клиентов, поэтому её стоит проговорить отдельно: даже если в serverless-контейнере изменить только переменную окружения, система всё равно обращается к реестру. При создании каждой новой ревизии Knative преобразует тег образа в его дайджест. Поэтому, если реестр недоступен, завершится ошибкой даже деплой, который использует уже загруженный ранее образ. Если вы считали, что во время сбоя можно безопасно менять только переменные окружения, такое предположение было вполне логичным – но неверным.
Почему один сервер положил весь сервис
Вот здесь начинается самое важное – и совсем не то, о чём мы рассчитывали писать.
Пользовательские данные в объектном хранилище защищены с помощью избыточного кодирования (erasure coding): каждый объект разбивается на четыре фрагмента данных и два фрагмента чётности, всего шесть частей. Для восстановления объекта достаточно любых четырёх. На бумаге такая схема выдерживает потерю двух фрагментов.
Проблема была в том, где именно могли размещаться эти шесть частей.
Наш кластер распределяет фрагменты по отдельным дискам, но не требует, чтобы они находились на разных серверах. На каждом сервере хранения установлено много дисков. Поэтому, когда один сервер отключился, вместе с ним в среднем стали недоступны полтора из шести фрагментов каждого объекта, а у большого числа объектов – два или три.
-
Потерян один фрагмент из шести: объект по-прежнему доступен.
-
Потеряны два: фрагментов уже недостаточно для безопасного обслуживания запросов – чтение останавливается.
-
Потеряны три: остаётся меньше четырёх частей, необходимых для восстановления, – объект нельзя прочитать, пока диски не вернутся в строй.
Именно поэтому сбой оказался полным, а не частичным, и поэтому хранилище не могло автоматически восстановиться, пока сервер оставался выключенным. Данные никуда не исчезли. Они просто были недоступны до возвращения оборудования в строй.
Очевидное решение – размещать все шесть фрагментов на шести разных серверах. В тот день сделать это было невозможно: схема «четыре плюс два» требует шести доменов отказа, а у нас работало только четыре сервера хранения. Это нельзя было исправить одним переключением параметра конфигурации. За значением по умолчанию скрывалось инфраструктурное решение, которое мы незаметно для себя откладывали.
Расширение ёмкости хранилища уже было запланировано, а обеспечение избыточности объектного хранилища на уровне отдельных серверов стало одним из главных приоритетов после этого инцидента. Конкретную дату здесь называть не будем: лучше объявить об изменении, когда оно уже будет внедрено, чем пообещать определённую неделю, а потом переносить сроки.
Была и вторая, менее масштабная проблема, которая усугубила ситуацию. Помимо основного массива данных объектное хранилище поддерживает небольшой набор внутренних служебных метаданных. Они хранятся не с помощью избыточного кодирования, а в двух копиях. Эти копии тоже могли оказаться на одном сервере – и для нескольких записей так и произошло. В их числе была запись, которую шлюзы хранилища читают при запуске: обе её копии находились на отказавшем сервере. Именно эта крошечная запись определяла разницу между чтением с ограничениями и connection refused. Сотни терабайт пользовательских данных пережили сбой лучше, чем несколько килобайт конфигурации, которая их описывала.
Для решения этой части проблемы новое железо не требуется. Мы можем хранить такие служебные метаданные в трёх копиях на трёх разных серверах уже на существующей инфраструктуре. Именно это изменение позволит вместо полной недоступности получить лишь замедление работы. Оно стоит первым в очереди.
Ещё один инцидент, которого удалось избежать
Есть ещё одна вещь, которая не произошла, но мы считаем правильным о ней рассказать.
Когда один из серверов хранения исчезает из кластера, кластер начинает восстанавливать недостающие копии на оставшихся машинах. Обычно это правильное поведение. Но в нашем случае пришлось бы восстановить около 115 TiB данных на трёх серверах, у которых суммарно оставалось 140 TiB свободного места.
При таком сценарии заполненность кластера в итоге достигла бы примерно 97% – выше порога, после которого он полностью перестаёт принимать новые записи. То есть сбой чтения превратился бы в полную недоступность сервиса, причём без какого-либо нового отказа оборудования: это сделала бы наша собственная автоматика. При наблюдавшейся скорости восстановления до этого оставалось около пяти дней, так что в запасе были дни, а не часы. Но двигались мы явно не туда, а процесс восстановления при этом не остановили.
Вывод неприятный, но простой: при заполненности 71% кластер из четырёх узлов не способен пережить окончательную потерю одного из них. Для этого нужен либо значительно больший запас свободной ёмкости, либо больше серверов.
Почему пропало питание
Мы не знаем. И не собираемся делать вид, что знаем.
Все причины, которые можно было бы увидеть на уровне ПО, исключены. Не было ни kernel panic, ни логов падения, ни каких-либо ошибок памяти, ни проблем с температурой. Все десять дисков прошли проверку состояния без единой ошибки. До остановки сервер непрерывно работал 103 дня без единого предупреждения ядра.
Это была и не перезагрузка. Системный лог обрывается посреди строки, а следующая запись о запуске появляется только спустя восемь часов, когда сервер включили вручную. Подсистема хранения независимо подтвердила, что произошло резкое отключение питания, а не штатное завершение работы.
Поставщик оборудования сообщил, что в это время в дата-центре никаких проблем с электропитанием не было. Данных мониторинга по самому серверу у него тоже нет. Можно провести полную аппаратную диагностику, но для этого сервер хранения придётся вывести из работы примерно на шесть часов. Пока он работает нормально, такая остановка обойдётся нам дороже, чем ценность ответа на вопрос о причине.
Этот случай зафиксирован в истории сервера. Если такое повторится, мы сразу отправим его на полную диагностику, а данные об этом инциденте помогут заметно быстрее найти причину.
Что мы изменили в тот же день
Два изменения мы внедрили уже через несколько часов после восстановления сервиса.
1. Теперь дежурному инженеру звонят по телефону
До этого инцидента уведомление о проблеме на платформе приходило одному человеку – по электронной почте. В 00:35 воскресенья письмо – это не сигнал тревоги.
Теперь, когда на нашей публичной статус-странице появляется инцидент, дежурному инженеру поступает голосовой звонок и одновременно отправляется SMS. Здесь стоит отдельно упомянуть три архитектурных решения – именно благодаря им на эту систему можно положиться:
-
Она намеренно вынесена за пределы нашей обычной системы уведомлений. Та учитывает пользовательские настройки, дайджесты, ограничения частоты и периоды тишины. Для клиентских уведомлений всё это правильно, для аварийного оповещения – нет. Такое оповещение нельзя подавить настройками.
-
Текст голосового сообщения передаётся прямо в запросе на звонок, а не загружается со страницы в нашей инфраструктуре. Ведь причиной инцидента вполне может оказаться как раз сбой системы, которая должна была отдать эту страницу.
-
Оповещение нельзя случайно вызвать задним числом. Инциденты, добавленные постфактум, в том числе записи, которые мы внесли в историю специально для этого постмортема, игнорируются. Постмортем не должен заставлять телефон звонить в три часа ночи.
После инцидента важно не только устранить конкретную проблему, но и пересмотреть процессы мониторинга и реакции на сбои. На бесплатном уроке 14 октября в 20:00 можно посмотреть, как современные инструменты помогают находить проблемы в инфраструктуре и быстрее реагировать на них.
2. Watchdog, который может сам включить выключившийся сервер
У наших серверов есть API управления, через который можно узнать фактическое состояние питания и имитировать нажатие кнопки включения. В конечном счёте восьмичасовой сбой свёлся к тому, что нужно было нажать одну кнопку, но никто в это время не бодрствовал.
Теперь watchdog (сторожевой механизм) проверяет каждый сервер раз в минуту и при необходимости может нажать эту кнопку без участия человека. Ошибка здесь может дорого обойтись: если отправить команду на выключение исправному серверу хранения, watchdog сам создаст тот самый сбой, от которого должен защищать. Поэтому почти вся работа была посвящена тому, чтобы научить его как можно чаще отказываться от действий:
-
Watchdog работает не на той инфраструктуре, за которой следит. Если разместить его внутри контролируемой системы, при её отказе он отключится вместе с ней.
-
Прежде чем что-либо сделать, он должен получить одинаковый вывод из четырёх независимых сигналов: доступности по сети, состояния кластера, состояния питания и состояния рабочих нагрузок.
-
Если одновременно пропадают две или более машины, это считается проблемой мониторинга или сети, а не одновременным аппаратным отказом. При связанном сбое watchdog ничего предпринимать не будет.
-
Он не пытается угадывать состояние питания. Если оборудование не может подтвердить, что сервер действительно выключен, watchdog поднимает тревогу и останавливается.
-
Между неудачной проверкой и любым действием стоят ещё несколько защитных блокировок (interlocks): интервал подтверждения, период ожидания (cooldown) и запрет на действие, если рабочие нагрузки остаются исправными.
Вся логика принятия решения сведена к одной чистой функции без побочных эффектов. Поэтому каждый из этих предохранителей покрыт тестами, а не надеждой на то, что всё сработает как задумано.
Watchdog уже успел принести пользу, пусть и не самым эффектным способом. На следующее утро он отправил аварийное оповещение по совершенно исправному серверу – сработала единственная ветка кода, которая могла выполниться до истечения интервала подтверждения. Для этого хватило одного потерянного сетевого пакета. Мы уже исправили проблему. И лучше было обнаружить её именно так, чем столкнуться с обратной ситуацией.
Что мы пока не исправили
Мы предпочитаем опубликовать этот список, а не преподносить историю лучше, чем она есть на самом деле.
-
Схема размещения данных пока не исправлена. Пока фрагменты данных и копии служебных метаданных не гарантированно размещаются на разных серверах, потеря одной машины всё ещё будет приводить к недоступности объектного хранилища. Watchdog сократит время такого сбоя, но не предотвратит его. Проблему со служебными метаданными можно решить без нового оборудования. Для остального потребуется дополнительная ёмкость, которую мы уже запланировали, но пока не добавили.
-
Запас свободной ёмкости слишком мал, чтобы пережить окончательную потерю одного узла. Решение то же: расширение кластера в те же сроки.
-
У дежурного инженера один номер телефона. Единственный получатель – это единая точка отказа в системе, вся задача которой как раз состоит в том, чтобы таких точек не было.
-
Первопричина отключения питания остаётся неизвестной – это наше осознанное решение до тех пор, пока проблема не повторится.
Какие выводы мы сделали
Обнаружение проблемы и оповещение о ней – две разные системы, а мы построили только одну. Восемь часов наш мониторинг точно и быстро фиксировал проблему и показывал её публично, пока все спали. Мониторинг, который никого не будит, – это журнал событий, а не сигнал тревоги.
У избыточности есть своя топология, и именно она определяет реальные гарантии. «Шесть копий» и «шесть копий, которым разрешено находиться на одном сервере» – совершенно разные вещи. Мы настроили второе, считая, что получили первое.
Самый маленький компонент нанёс самый большой ущерб. Сотни терабайт пользовательских данных деградировали ровно так, как было задумано. А несколько килобайт внутренних служебных метаданных, к которым мы отнеслись менее строго просто потому, что их мало, превратили медленный сервис в полностью недоступный. Если у вас устроено что-то похожее, начинайте аудит с самых маленьких пулов.
Проверяйте восстановление там, где его действительно видит клиент. В какой-то момент мы ненадолго решили, что реестр уже восстановился, потому что он начал отвечать на запрос аутентификации. Но этот ответ приходит от слоя, который вообще не обращается к хранилищу. Если бы мы на этом основании объявили сервис восстановленным, то отправили бы трёх клиентов повторять запросы к системе, которая всё ещё не работала. Единственная надёжная проверка – запрос, который действительно читает реальные данные.
Ещё по теме:
-
«Kubernetes: архитектура и абстракции — полный гайд»
-
«Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise»
-
«Self-service деплой: как перестать ждать DevOps и ускорить команду»
Больше полезных материалов по инфраструктуре смотрите в дайджесте.
