Самое слабое звено: как цепочки зависимостей влияют на системный риск
При оценке сценариев системного риска очень легко упустить из виду «цепочечные» зависимости. Нас учат смотреть на риск на уровне «узла», спрашивая: «Насколько вероятен отказ именно этого элемента?» Но системный риск значительно сложнее этого.
В большинстве систем одни компоненты зависят от других. Чаще всего мы рассматриваем это при проектировании хранилища для серверов, но это актуально для любого системного проектирования. Другой хороший пример — то, как веб-приложениям для работы нужны как серверы приложений, так и серверы баз данных.
Проще всего объяснить цепочечные зависимости на примере. Рассмотрим стандартную конфигурацию виртуализации с хранилищем SAN, чтобы понять, где находятся границы доменов отказов, где существуют цепочечные зависимости и какую роль играет резервирование в снижении системного риска.
В стандартной конфигурации SAN (storage area network) для виртуализации у вас есть хосты виртуализации (которые мы для простоты назовём «серверами»), SAN-коммутаторы (коммутаторы, выделенные для сети хранения) и сами дисковые массивы. Каждый из этих трёх «уровней» зависит от других для функционирования системы в целом. Если взять простейший возможный набор — один сервер, один коммутатор и один дисковый массив — перед нами очевидно три устройства, представляющие три отдельные точки отказа. Отказ любого из трёх приводит к отказу всей системы. Ни один элемент не является полезным сам по себе. Это и есть цепочечная зависимость, и цепочка прочна ровно настолько, насколько прочно её слабейшее звено.
В нашем упрощённом примере каждое устройство представляет домен отказа. Мы можем снизить риск, повышая надёжность каждого домена. Мы можем добавить второй сервер и реализовать стратегию высокой доступности или отказоустойчивости на уровне виртуализации для снижения риска отказа сервера. Это повышает надёжность одного домена отказов, но оставляет два других нетронутыми и столь же рискованными, как прежде. Затем можно уделить внимание уровню коммутации, добавив резервный коммутатор и настроив стратегию мультипасинга для обработки потери одного коммутационного пути — снижая риск на этом уровне. Теперь два домена отказов проработаны. Наконец, необходимо разобраться с доменом отказа хранилища — аналогично добавив резервирование через второй дисковый массив, зеркально отражающий первый и способный прозрачно переключаться при сбое.
Теперь, когда мы укрепили нашу систему, у нас по-прежнему три домена отказов в цепочке зависимостей. То, что мы сделали, — это сделали каждое «звено» цепочки, каждый домен отказа, по отдельности более устойчивым. Но цепочка по-прежнему существует. Это означает, что система в целом значительно менее надёжна, чем любой отдельно взятый домен отказа в этой цепочке. Мы сделали систему значительно лучше, чем в исходной точке, но у нас по-прежнему множество доменов отказов. Эти риски суммируются.
Сложность определения общего риска в том, что мы должны оценить риск каждого элемента, затем определить новый риск после снижения (через добавление резервирования) и затем найти совокупный риск каждого из доменов отказов в цепочке для определения общего риска всей системы. Крайне трудно определить риск внутри каждого домена отказа, поскольку способ снижения риска играет значительную роль. Например, кластер дисковых массивов, переключающийся слишком медленно, может привести к общему отказу системы даже когда сам кластер хранения, по всей видимости, сработал правильно. Поэтому даже чёткое определение отказа может оказаться непростой задачей.
Нередко возникает соблазн оценивать риск «сверху вниз», что весьма опасно, но очень типично для людей, не являющихся постоянными практиками оценки рисков. Тенденция здесь — смотреть на риск, рассматривая только «наиболее верхний» домен отказа — как правило, серверы в таком случае, — и игнорировать любые риски, лежащие ниже этой точки, считая их находящимися «под капотом», а не частью оценки риска. Легко проигнорировать более технические, менее очевидные и более плохо понимаемые компоненты — такие как сеть и хранилище — и сосредоточиться на относительно легко понимаемых и активно продвигаемых аспектах надёжности верхнего уровня. Такой «взгляд сверху» означает, что риски под верхним уровнем затушёвываются и в целом игнорируются, что ведёт к высокому риску без ясного понимания причин.
Понимание концепции цепочечных зависимостей объясняет, почему сложные системы — даже со сложными стратегиями снижения рисков — нередко оказываются значительно более хрупкими, чем более простые системы. В нашем примере выше мы могли бы предпринять несколько действий для «схлопывания» цепочки, что привело бы к более надёжной системе в целом.
Наиболее очевидным компонентом, который можно схлопнуть, является домен отказа сети. Если бы мы полностью убрали коммутаторы и подключили хранилище напрямую к серверам (что не всегда возможно), мы фактически устранили бы один целый домен отказа и убрали бы одно звено из нашей цепочки. Теперь вместо трёх цепочек, каждая из которых имеет некоторый потенциал отказа, у нас только две. Чем проще — тем лучше, при прочих равных условиях.
Теоретически можно также схлопнуть домен отказа хранилища, перейдя от внешнего хранилища к хранилищу, локальному для самих серверов, — что фактически сократило бы нас с двух доменов отказов до единственного. Единственный оставшийся домен, конечно, несёт большую сложность, чем до схлопывания, но общая сложность системы значительно снижается. Опять же, это при прочих равных условиях.
Ещё один подход для рассмотрения — повышение надёжности отдельных узлов самих по себе. Сегодня модно смотреть на более крупные системы и подходить к снижению рисков таким образом — добавляя избыточные, недорогие узлы для повышения надёжности доменов отказов. Но исторически это был не основной путь к надёжности. В прошлом значительно чаще, как показывает прежняя распространённость мейнфреймов и систем аналогичного класса, высокая степень надёжности закладывалась в отдельный узел. Мейнфреймы и высокоуровневые системы хранения, например, по-прежнему делают это сегодня. Это может быть чрезвычайно эффективным подходом, но он не охватывает многих сценариев и, как правило, крайне дорог — нередко с необходимостью частичного или даже полного обслуживания систем вендором. Это, как правило, срабатывает только в особых нишевых обстоятельствах и не является практичным в более общем масштабе.
Таким образом, в любой системе подобного рода у нас есть три ключевые стратегии снижения рисков: повышение надёжности отдельного узла, повышение надёжности отдельного домена или сокращение числа доменов отказов (звеньев) в цепочке зависимостей. Разумное сочетание этих стратегий может помочь достичь уровня снижения риска, подходящего для нашего бизнес-сценария.
Истинная сложность, которая останется, заключается в сравнении различных стратегий снижения рисков. Риск отдельного узла, как правило, можно оценить с определённой долей уверенности. Стратегия резервирования в рамках отдельного домена значительно хуже поддаётся оценке — некоторые стратегии резервирования высокоэффективны и создают чрезвычайно надёжные домены отказов, тогда как другие могут дать обратный эффект и фактически снизить надёжность домена! Сложность, нередко сопровождающая стратегии резервирования, никогда не бывает без оговорок: хотя она, как правило, окупается, она редко несёт ту степень надёжности, которая ожидается изначально. Оценка риска цепочки зависимостей поэтому ещё сложнее, поскольку требует чёткого понимания рисков, связанных с каждым из доменов отказов по отдельности, а также понимания возможности отказа на границах доменов (например, ранее упомянутый сбой из-за задержки переключения хранилища).
Давайте рассмотрим проблемы определения риска в двух очень распространённых подходах к одному сценарию, опираясь на то, что мы обсудили выше.
Два крайних примера одной и той же ситуации, которую мы рассматривали, — это одиночный сервер с внутренним хранилищем для размещения виртуальных машин и «цепочка» из шести устройств: два сервера с решением высокой доступности на серверном уровне, два коммутатора с резервированием на уровне коммутации и два дисковых массива, обеспечивающие высокую доступность на уровне хранилища. Если мы изменим какой-либо крупный фактор, как правило, можем дать вполне чёткую оценку относительного риска — если, например, какому-либо из доменов отказов не хватает надёжного резервирования — мы можем достаточно чётко определить, что одиночный сервер является более надёжной общей системой, за исключением случаев, когда отдельному узлу приписывается экстремально высокая степень надёжности, — что обычно является финансово нецелесообразной стратегией. Но при наличии резервирования в каждом домене отказов мы вынуждены сравнивать относительные риски надёжности внутри доменов (избыточная цепочка) с надёжностью между доменами (схлопнутая цепочка, одиночный сервер).
При двух принципиально разных подходах нет разумного способа оценить сравнительные риски двух средств снижения риска. Общепринято считать, что подход с шестью (или более) узлами и обширным снижением риска внутри доменов является более надёжным из двух, и это почти наверняка верно в целом. Но это не всегда так, и редко этот подход превосходит стратегию одиночного узла по-настоящему значительно, при этом нередко стоя в четыре-десять раз дороже стратегии с одиночным сервером. Это потенциально очень высокая цена за то, что, вероятно, является небольшим выигрышем в надёжности и небольшим потенциальным риском потери в надёжности. Каждая дополнительная единица резервирования добавляет сложность, которую человек должен реализовать, отслеживать и поддерживать, а вместе со сложностью и человеческим взаимодействием приходит всё больший и больший риск. Предотвращение человеческих ошибок нередко важнее, чем предотвращение механических сбоев.
Мы также должны учитывать стоимость восстановления. В случае отказа, как правило, тривиально легко восстановиться после сбоя простой системы. Восстановление крайне сложной системы после сбоя может потребовать значительных усилий. Сложные системы также требуют значительно более широкого и глубокого уровня опыта и уверенности для обслуживания.
Не существует простого ответа на вопрос об определении надёжности систем. Современные системы доставки информации просто слишком велики и слишком сложны, с слишком многими неопределёнными факторами, чтобы быть оценёнными во всех случаях. Однако при хорошем понимании цепочечных зависимостей и стратегий снижения рисков мы можем предпринять практические шаги для приблизительного определения относительных уровней риска, увидеть, как схожие рисковые сценарии соотносятся по стоимости, выявить точки хрупкости, распознать домены отказов и цепочки зависимостей и оценить, как изменения в архитектуре системы чётко ведут нас к повышению или снижению надёжности.
