Привет, меня зовут Руслан Савченко, я руководитель службы разработки динамических таблиц в Yandex Infrastructure и преподаватель в Школе анализа данных. Сегодня я поделюсь актуальными инсайтами о том, как ломаются большие системы и как их траблшутить. Думаю, это будет полезно разработчикам и студентам, которые интересуются Site Reliability Engineering. Вообще, мало где сейчас обучают SRE, хотя в индустрии такие задачи очень востребованы (кстати, статья про сетевую аварию в Яндексе стала одной из самых популярных в этом блоге в прошлом году).

В основе статьи — материалы SRE Week, открытого интенсива ШАДа по работе с большими нагруженными системами. Он открывает небольшую часть контента семестрового курса, который я читаю на программе по инфраструктуре больших данных. Сам курс покрывает и другие важные темы, например, контейнеры, оркестрацию, работу сети и многое другое.

Из чего состоят распределённые системы

Прежде чем перейти к возможным поломкам и способам их починки, давайте разберёмся с тем, что же такое распределённая система.

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

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

Типичный сервис может включать балансировку нагрузки, API Proxy для реализации пользовательского API, бэкенд‑сервисы для логики, базы данных и кэши для оптимизации. Для отказоустойчивости между базами данных обычно делается репликация.

Наблюдение за сервисами начинается с проверки доступности. Это простой способ мониторинга, который показывает, доступен ли сервис. Проверки могут быть простыми, например, пинги по протоколам HTTP, RPC, ICMP, или более сложными, включая сценарии использования сервиса. Проверка доступности помогает собрать метрики о времени доступности сервиса — это одна из базовых метрик.

Для отладки используется логирование — оно позволяет фиксировать события и ошибки, что помогает в ретроспективе анализировать нештатные ситуации. Логи могут сохраняться в файлы (эффективно при большом объёме, но нет удобного поиска и отказоустойчивости) или отправляться в отдельные системы, скажем, в ClickHouse или InfluxDB, для быстрого поиска (это надёжно, но дороже по железу). Кроме того, облачная платформа, в которой разворачиваются сервисы, может предоставлять возможность хранения и просмотра логов. Важно учитывать нагрузку на систему от логирования и использовать сжатие логов для её снижения.

Мониторинг — основное средство наблюдения за состоянием системы. Он включает графики временных рядов, которые показывают различные метрики динамике. Методология USE (Usage, Saturation, Error) помогает отслеживать использование ресурсов, их насыщение и ошибки. Мониторинг охватывает такие параметры, как общее количество запросов, доля ошибочных запросов, время обработки и пропускная способность. Самый критичный параметр — отношение успешно выполненных запросов к их общему числу. Конечно, бывают ситуации, когда время обработки и пропускная способность не так важны, но чаще всего это принципиальные моменты.

Наконец, обнаружение аномалий — одна из важнейших частей мониторинга. Временные ряды позволяют выявлять отклонения, например, внезапное повышение нагрузки на CPU или увеличение числа ошибок. Система может автоматически детектировать аномалии и связывать ошибки с трассировкой и логами для быстрого анализа. В Яндексе, помимо автоматики, мы используем дашборды, где выведены десятки графиков, дежурные смотрят их, видят корреляции и исходя из собственного опыта выводят причинно‑следственную связь.

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

Как ломаются сервисы

Даже самые высокопроизводительные и надёжные системы не застрахованы от сбоев. Несколько лет назад авторы статьи Why Does the Cloud Stop Computing провели детальный анализ более тысячи аварий, чтобы выявить основные причины недоступности популярных сервисов, которыми каждый из нас пользуется каждый день. Они исследовали мессенджеры, соцсети, маркетплейсы, стриминги — в общем, всё, без чего нельзя представить современную жизнь. Оказалось, что даже в крупных сервисах бывают от одного до десяти случаев недоступности в год, и избежать этого практически невозможно.

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

Допустим, в систему идут запросы на доступ к данным, которые вы научились лимитировать. При этом есть ряд вспомогательных запросов, от которых обычно трафика нет. Здесь порой возникают проблемы. Если кто‑то пытается добиться отказа сервиса через множественную авторизацию, всплеск таких неожиданных запросов, которые обычно не видны на общей картине, может привести к поломке.

Другой потенциальный источник проблем — просроченные сертификаты, которые могут заблокировать доступ к сервисам. Классический пример: Microsoft столкнулась с проблемой, когда сертификаты, выданные 29 февраля, в високосный год, не обновились на следующий год, потому что такой даты не было. В итоге система валидации просто зарезала сертификаты с неправильной датой.

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

Казалось бы, бэкапы страхуют от любых неприятностей, но и с ними возникают проблемы. Вы делаете бэкапы и храните их отдельно, но в нужный момент они оказываются недоступны из‑за проблем с питанием, сетью или дисками. Иногда независимые друг от друга проблемы случаются одновременно: мы можем быть готовы к одному вызову, но не к двум‑трём сразу. Например, во время работ на одном дата‑центре у вас может отключиться второй. Хотя повторение такой редкой поломки маловероятно, в некоторых случаях есть смысл пересмотреть подход к отказам. Правда, и это не гарантирует, что не сломается что‑то третье. Поэтому в работе с нагруженными системами нужно быть готовыми к любым сценариям.

Почему происходят поломки

Авторы статьи Why Does the Cloud Stop Computing выделяют несколько причин, которые подтверждаются нашим опытом.

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

  • Сеть. Наиболее значимые аварии часто связаны с сетью: неправильно настроена DNS, не так работает таблица маршрутов. В результате сервисы становятся недоступны.

  • Баги. Это, пожалуй, самая понятная для программистов категория. К примеру, разработчики допустили ошибку в телеметрии, что привело к утечке памяти и падению сервиса.

  • Мисконфигурация. Если в конфигурационном файле сервиса допущена опечатка, у сервиса могут случится проблемы. Провалидировать неправильные конфиги крайне сложно.

  • Всплеск трафика. Резкое увеличение трафика может перегрузить систему. К примеру, во время «Чёрной пятницы» маркетплейс не справился с нагрузкой, что привело к сбою сервиса.

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

  • Проблемы с дисками. Диски могут выходить из строя, что приводит к повреждению баз данных и индексов. В нашей системе метаданных, основанной на консенсусе, использовалось несколько идентичных SSD. Они создавали одинаковый поток записи, что привело к одновременному выходу из строя нескольких накопителей из‑за исчерпания циклов перезаписи. У нас было пять реплик, и мы смогли пережить потерю двух. Повезло, что не вышли из строя все пять сразу.

  • Непонятные ошибки железа. Иногда проще заменить неисправную машину, чем разбираться в причинах сбоя контроллера SATA. Важно как можно быстрее решить проблему, а уже потом детально копаться в том, что произошло: привлекать вендоров, обновлять прошивку или BIOS и т. д.

  • Внешние факторы. Стихийные бедствия и человеческий фактор могут повлиять на доступность сервисов. Однажды в Москве пропал интернет, потому что экскаватор случайно повредил оптоволоконный кабель. Бывает и такое.

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

В командах с высокой SRE‑культурой проблемы сами собой происходят редко. Чтобы инженеры не потеряли хватку, нужно проводить учения — контролируемо вносить различные возмущения в систему.

Как чинить аварии

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

Примеры механизмов защиты:

  • Рейт‑лимитеры защищают от перегрузок, ограничивая количество запросов.

  • Дублирование данных на нескольких жестких дисках помогает пережить выход из строя одного из них.

  • Балансировщики нагрузки распределяют трафик между узлами, чтобы не перегружать отдельные компоненты.

  • Алгоритм консенсуса позволяет системе выдерживать внезапное отключение одной машины.

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

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

Система часто работает в слегка деградированном состоянии, когда несколько проблем уже есть. Например, один жёсткий диск уже сломан, и, если сломается ещё один, система может выйти из строя.

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

Если авария всё же произошла, есть несколько возможных решений. Во‑первых, можно просто откатить назад. Хорошо, когда есть возможность откатить новый билд. Потому что если вы каким‑то несовместимым образом меняете схему работы с данными, то откатиться назад уже не сможете.

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

А ещё бывает, что проблема уходит сама. На такие вещи надо обращать внимание, потому что это сигнал о том, что с системой что‑то не в порядке. Даже если ошибка сама быстро исчезла, это не означает, что у вас стабильная система. Хуже того, вы не понимаете, в чём именно нестабильность. В будущем ошибка может проявиться на гораздо большем масштабе. Лучше попытаться с ней разобраться заранее.

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


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

Если вы решитесь на обучение, то советую заранее изучить материалы для подготовки, там есть варианты экзаменов и ссылки на полезные ресурсы.

Комментарии (0)