Русский
Русский
English
Статистика
Реклама

Отказоустойчивость

Обеспечение сетевой безопасности совместно с брокерами сетевых пакетов. Часть вторая. Активные средства безопасности

13.04.2021 20:10:28 | Автор: admin

В первой части мы рассказали про наиболее популярные пассивные средства ИБ, которые применяются для мониторинга и анализа трафика в сети. Возникает логичный вопрос: если системы умеют выявлять угрозы, то почему бы не блокировать их? Сегодня предлагаем Вам поговорить про активные средства ИБ, их применение и проблемы внедрения в современную сетевую инфраструктуру.

Активные средства безопасности

Средства активной безопасности, подключаемые в разрыв (in-line), не только обнаруживают угрозы, но и блокируют их в режиме реального времени. При внедрении активных средств специалисты сталкиваются с проблемами обеспечения отказоустойчивости сети, дальнейшей масштабируемости средств безопасности, а также с необходимостью уменьшения задержек передачи пакетов при увеличении объёма трафика в сети.

Среди наиболее популярных активных средств информационной безопасности остановимся на IPS, NGFW, SWG, AMP, DLP и Anti-DDoS, а также рассмотрим способы их развёртывания на базе Bypass и брокеров сетевых пакетов для обеспечения гарантированной безопасности сети.

Системы предотвращения вторжений (IPS)

Системы предотвращения вторжений (Intrusion Prevention Systems - IPS) это программные и аппаратные средства, предназначенные для обнаружения и предотвращения попыток несанкционированного доступа к конфиденциальным данным, повышения привилегий, использования уязвимостей программного обеспечения и вывода из строя компьютерных систем. Такие попытки вторжений осуществляются главным образом через Интернет или локальную сеть, могут иметь форму атак хакеров/инсайдеров или же быть результатом действий вредоносных программ.

IPS это логическая эволюция IDS. Однако если IPS заблокировала хороший трафик, который, как она подозревала, был плохим (ложное срабатывание), или же физически вышла из строя, нарушив целостность сети, то важные бизнес-процессы компании нарушатся. Таким образом, специалисты по ИБ должны тщательно выбирать и развёртывать IPS с большой осторожностью.

В настоящее время системы IPS активно развиваются в части сокращения числа ложных срабатываний и увеличения эффективности решения. Результатом усовершенствования можно считать так называемые IPS-системы нового поколения - NGIPS, которые выполняют все функции в режиме реального времени, никак не влияя на сетевую активность организации, и, помимо всего прочего, предоставляют возможности мониторинга приложений и использования информации из сторонних источников (например, баз уязвимостей).

В России требования к системам обнаружения вторжений (под понятием СОВ подразумеваются как пассивные IDS системы, так и активные IPS системы) появились еще в 2011 году, тогда ФСТЭК России поделила СОВ на 6 классов защиты. Отличия между классами заключаются в уровне информационных систем и непосредственно информации, которая подлежит обработке (персональные данные, конфиденциальная информация, государственная тайна). Соответствие требованиям регулятора является важным фактором при выборе СОВ, в то же время это положительно сказывается на развитии отечественного рынка IPS/IDS решений.

В пространстве IPS решений представлены продукты следующих производителей: Positive Technologies, Код Безопасности, Smart-Soft, Info Watch, Инфотекс, Stonesoft, Trend Micro, Fortinet, Cisco, HP, IBM, Juniper, McAfee, Sourcefire, Stonesoft, Trend Micro, Check Point, Palo Аlto Networks.

Межсетевые экраны нового поколения (NGFW)

Межсетевые экраны нового поколения (Next-Generation Firewall - NGFW) это эволюция типовых межсетевых экранов с возможностью отслеживания состояния соединений. Поскольку всё большее число компаний сейчас используют онлайн-приложения и службы SaaS, то классический контроль портов и протоколов уже недостаточен для обеспечения эффективной сетевой безопасности. В отличие от предыдущего поколения, в новых устройствах добавлена тесная интеграция дополнительных возможностей, таких как встроенная глубокая проверка пакетов (DPI), предотвращение вторжений (IPS) и проверка трафика на уровне приложений (Web Application Firewall). Некоторые NGFW также включают проверку зашифрованного трафика TLS/SSL, фильтрацию веб-сайтов, управление пропускной способностью, QoS, антивирусную проверку и интеграцию со сторонними системами управления идентификацией (LDAP, RADIUS и Active Directory). Решения NGFW в скором времени заменят традиционные межсетевые экраны, предотвращая вторжения и контролируя приложения как по периметру, так и внутри сети.

Производители NGFW решений: UserGate, Континент, Huawei, Check Point, Сisco, Fortinet, McAfee, Palo Alto Networks и Sourcefire.

Шлюзы информационной безопасности (SWG)

Прокси-серверы с функциями информационной безопасности (Security Web Gateway SWG), также известные как веб-фильтры это программно-аппаратные комплексы (ПО + сервер), разработанные и оптимизированные для соблюдения политик веб-безопасности компании и контроля доступа пользователей к веб-сайтам. Веб-сайты, которые содержат вредоносное ПО или неприемлемый контент (например, порнографию или азартные игры), блокируются на SWG, тем самым повышая производительность труда сотрудников, ограничивая ответственность компании и защищая компьютерные устройства пользователей от вреда.

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

Производители SWG решений: Ростелеком-Солар, Smart-Soft, UserGate, ESET, Kaspersky, Sophos, TRENDmicro, Huawei, Blue Coat, Cisco, McAfee, Trustwave и Websense.

Advanced malware protection (AMP)

Традиционные решения безопасности, такие как системы предотвращения вторжений, антивирусные продукты и шлюзы информационной безопасности предназначены для обнаружения известных угроз и эксплойтов, нацеленных на определённые уязвимости операционных систем и приложений. Однако сегодня уязвимости нулевого дня (Zero-Day Exploit - атаки, нацеленные на уязвимости, для которых ещё не разработана защита) вызывают наибольшую озабоченность компаний и правительственных учреждений.

Для защиты от этих угроз существует категория решений сетевой безопасности - AMP (Advanced Malware Protection). Основная задача AMP проверка файла, пересылаемого через сетевое устройство и/или записываемого на конечное оборудование, на наличие вредоносного кода. Система AMP осуществляет ретроспективный анализ и обеспечивает защиту не только до момента атаки или во время атаки, но и после того, как атака прошла. Кроме того, это решение позволяет отследить все пути распространения файла и может заблокировать файл на уровне сети.

Производители AMP решений: Kaspersky, Malwarebytes, Cisco, Damballa, FireEye и Palo Alto Networks.

Системы предотвращения утечки данных (DLP)

Системы предотвращения утечки данных (Data Loss Prevention или Data Leakage Prevention - DLP) это программно-аппаратные комплексы (ПО + сервер), которые предназначены для обнаружения и предотвращения потенциальных нарушений конфиденциальности данных и личной информации (номера кредитных карт, номера телефонов, данные паспорта и т. д.) путём мониторинга данных в нескольких состояниях:

  • при использовании (Data-inUse) на рабочем месте пользователя

  • при передаче (Data-inMotion) в сети компании

  • при хранении (Data-atRest) на серверах и рабочих станциях компании

Производители DLP решений: InfoWatch, Инфосистемы Джет, SmartLine Inc, Гарда Технологии, Zecurion, Ростелеком-Солар, Falcongaze, Атом Безопастность, ESET, SearchInform, CoSoSys, Blue Coat, Check Point, Cisco (IronPort), Fidelis, McAfee, RSA, Verdasys, Symantec, Websense.

Системы предотвращения распределённого отказа в обслуживании (DDoS Protection, Anti-DDoS)

Системы предотвращения распределённого отказа в обслуживании (Distributed Denial of Service (DDoS) Protection или Anti-DDoS) это специализированные программно-аппаратные и программные средства, предназначенные для защиты веб-серверов/сайтов компании от распределённых атак типа Отказ в обслуживании.

Атака типа отказ в обслуживании (DoS) - это попытка одного компьютера сделать другой компьютер недоступным для его предполагаемых пользователей путём забивания его полосы пропускания и/или вычислительных ресурсов паразитным трафиком, часто через поток пакетов SYN или ICMP. Распределённый отказ в обслуживании (DDoS) это DoS-атака, инициируемая ботнетом (совокупностью компьютеров, называемых ботами, которые заражены зомби-агентами или троянами), обычно используется для атак на целевые веб-сайты. Все боты в данном ботнете запрограммированы на выполнение действий в точно согласованное время, как это предписано центральной системой управления и контроля (Сommand and Сontrol - С&C), управляемой преступником.

На предприятиях системы Anti-DDoS помогают выявлять и предотвращать DDoS-атаки путём использования проприетарных алгоритмов и оценки механизмов защиты.

Средства безопасности в этой области производят компании: DDOS-GUARD, СТОРМ СИСТЕМС, Variti, Гарда Технологии, Kaspersky, Inoventica Technologies, Qrator Labs, Akamai Technologies, CloudFlare, Imperva, Sucuri, F5 Networks, Arbor Networks, Cisco, Corero и VeriSign.

Типичные проблемы развёртывания систем сетевой безопасности

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

Кейс 1 Развёртывание нескольких копий активных средств безопасности для обработки трафика в современных высоконагруженных сетях 40G/100G

В настоящее время широко распространяются стандарты 40G/100G. Производители активных средств безопасности стараются не отставать и предлагают высокопроизводительные инструменты для обеспечения защиты сетей данных стандартов. К сожалению, далеко не все предлагаемые современные инструменты могут обработать трафик высоконагруженных сетей стандартов 40G/100G. А те немногие, которые способны, имеют высокую стоимость и делают это с ограничениями.

Задача: Сеть компании построена по стандарту 40G. Существующая система NGFW имеет интерфейсы 40G, но её производительности хватает только при нагрузке в сети до 30%. При повышении нагрузки в сети установленная NGFW уже не справляется и начинает терять трафик. Необходимо внедрить решение, которое будет обеспечивать работоспособность сети с использованием существующей активной системы NGFW и минимально возможными инвестициями.

Решение: Для решения данной задачи необходимы: брокер сетевых пакетов и дополнительная единица NGFW (в некоторых случаях даже менее производительная). Существующую и вновь приобретённую NGFW нужно подключить к брокеру сетевых пакетов, который, в свою очередь, подключить через Bypass к сети компании. Пакетный брокер будет балансировать получаемый из cети трафик на входы NGFW с сохранением целостности сессий и агрегировать трафик с выходов NGFW для дальнейшей передачи. Таким образом, нагрузка будет равномерно распределяться на две системы NGFW, что позволит поддерживать необходимую пропускную способность и быстродействие сети компании.

Кейс 2 Безопасное развёртывание нескольких активных средств безопасности последовательно

Многие компании для достижения более глубокой защиты корпоративной сети последовательно развёртывают несколько активных средств безопасности. Таким образом, прежде чем трафик из интернета попадёт в корпоративную сеть, он должен пройти через системы IPS, Firewall, SWG и др. и получить разрешение на дальнейшее распространение по сети. Для минимизации рисков простоя сети компании нуждаются в эффективном и безопасном способе последовательного развёртывания активных средств ИБ.

Задача: В компании, в сетевой инфраструктуре которой развёрнута система межсетевого экрана, планируется внедрить активные системы IPS и Anti-DDoS. При этом необходимо обеспечить бесперебойность работы сети при выходе из строя средств ИБ.

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

Кейс 3 Поддержка отказоустойчивых сетевых конфигураций и активных систем информационной безопасности

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

Отказоустойчивые сетевые архитектуры требуют избыточных компонентов сетевой инфраструктуры и дополнительных единиц активных средств ИБ. Чтобы избыточность свести к минимуму и в то же время обеспечить максимальную отказоустойчивость, необходимо проектировать сеть с использованием брокера сетевых пакетов и Bypass.

Задача: В компании поставлена задача по организации отказоустойчивой конфигурации сети с активными средствами ИБ. Необходимо обеспечить бесперебойное функционирование системы ИБ в случае отказа группы систем и/или одного из элементов группы. Построенный сегмент активных средств ИБ не должен влиять на работоспособность общей сетевой инфраструктуры компании. Также требуется предусмотреть возможность дальнейшей масштабируемости и повышения производительности активных систем ИБ без изменения общей конфигурации сети.

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


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

Резюмируя, хочется выделить преимущества применения брокеров сетевых пакетов:

  • Простота интеграции, надёжность и отказоустойчивость сети

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

  • Постоянный мониторинг доступности каждого активного устройства ИБ и различные сценарии реагирования на аварии

  • Один Bypass для нескольких активных средств

  • Вариативность пропускания трафика через активные системы ИБ

  • Простота внедрения новых решений в инфраструктуру

Подробнее..

Отказоустойчивость между 5 дата-центрами как мы разгребаем зоопарк

30.06.2020 10:05:46 | Автор: admin
Сейчас мы стоим в 4 физически разных ЦОДах, соединённых кольцом тёмной оптики, размещая там 5 независимых пула ресурсов. И так получилось, что если в одну из кроссовых попадёт метеорит, то у нас тут же отвалится 3 этих пула, а оставшиеся два не потянут нагрузку. Поэтому мы занялись полной ребалансировкой, чтобы навести порядок. Вообще, оглядываясь назад, могу сказать, что все действия по размещению это вынужденные ходы. И вот только сейчас на 15-й год мы можем настраивать инфраструктуру так, как нужно нам.



По дороге нам пришлось научиться готовить ProxySql балансировщики MySQL и немного закопаться в сетевой стек. Но, пожалуй, начну с самого начала. А начиналось всё с shared hosting и VPS в Мастехосте, на которых крутилось наше расписание электричек, которое многие из вас видели. И эти сервера получали тройной трафик на майские, отчего сразу ложились. Точнее, мы не знаем, какой трафик они получали, потому что ложились именно на тройном от обычного.


Первый ЦОД


Сначала ЦОДа вообще не было. Был старый системник в общаге МГУ. Потом, почти сразу виртуальный хостинг у Мастерхоста (они ещё живы, чертяки). Посещаемость сайта с расписанием электричек удваивалась каждые 4 недели, поэтому очень скоро мы перешли на KVM-VPS, это случилось примерно в 2005 году. В какой-то момент мы упёрлись в ограничения по трафику, поскольку тогда надо было соблюдать баланс между входящим и исходящим. У нас было две инсталляции, и мы перекладывали с одной на другую пару увесистых файлов каждую ночь, чтобы соблюсти требуемые пропорции.

В марте 2009 были только VPS. Это дело хорошее, решили переходить на colocation. Купили пару физических железных серверов (один из них тот самый со стены, тело которого мы храним как память). Поставили в ЦОД Фиорд (и они ещё живы, чертяки). Почему? Потому что было недалеко от тогдашнего офиса, порекомендовал знакомый, и вставать надо было быстро. Плюс было сравнительно недорого.

Разделение нагрузки между серверами было простым: на каждом был бэк, MySQL с master-slave репликацией, фронт был там же, где и реплика. Ну т.е. почти без разделения по типу нагрузки. Довольно скоро их тоже начало не хватать, купили третий.

Примерно 1 октября 2009 мы поняли, что серверов уже больше, но на новый год ляжем. Прогнозы по трафику показывали, что возможная мощность будет перекрыта с запасом. Причём упирались мы в производительность БД. Был месяц полтора на подготовку перед ростом трафика. Это было время первых оптимизаций. Купили пару серверов чисто под БД. Там делали акцент на быстрые диски со скоростью вращения 15krps (не помню точную причину, почему мы не использовали SSD, но скорее всего они имели невысокий лимит по количеству операций записи, и при этом стоили, как самолет). Разделили фронт, бэк, базы, подкрутили настройки nginx, MySQL, провели ресеч по оптимизации SQL запросов. Пережили.

Сейчас-то мы стоим в паре Tier-III ЦОДов и в Tier-II по UI (с замахом на T3, но без сертификатов). А вот Фиорд был ни разу даже не T-II. У них были проблемы по живучести, бывали ситуации из разряда все провода питания в одном коллекторе, а там пожар, а генератор ехал три часа. В общем, решили переезжать.

Выбрали ещё один ЦОД, Караван. Задача: как переехать серверами без даунтайма? Решили пожить на два ЦОДа какое-то время. Благо трафика внутри системы на тот момент было не столько, как сейчас, можно было гонять трафик по VPN между локациями какое-то время (особенно вне сезона). Сделали балансировку трафика. Постепенно увеличивали долю Каравана, через некоторое время полностью переехали туда. И вот у нас остался один ЦОД. А нужно два, мы это уже понимали, спасибо сбоям у Фиорда. Оглядываясь на те времена, могу сказать, что TIER III тоже не панацея, живучесть-то будет 99.95, но вот доступность это другое. Так что одного ЦОДа для доступности 99.95 и выше точно не хватит.

Вторым выбрали Стордату, и там уже была возможность оптического линка с площадкой Каравана. Успели протянуть первую жилу. Только начали загружать новый ЦОД, как Караван объявил, что у них наступила задница. Им надо было покинуть площадку, потому что здание сносят. Уже. Сюрприз! Новая площадка есть, предлагают потушить все, кранами поднять стойки с оборудованием (тогда у нас уже было 2.5 стойки железа), перевести, включить, и все заработает 4 часа на все сказки я уж молчу, что нам даже час простоя не подходил, а тут история на сутки минимум затянулась бы. Причём подавалось всё это в духе Всё пропало, гипс снимают, клиент уезжает!. 29 сентября первый звонок, а числа 10-го октября они хотели забрать всё и везти. За 3-5 дней нам пришлось разработать план переезда, и в 3 этапа, выключая по 1/3 оборудования за раз с полным сохранением сервиса и аптайма перевезти машины в Стордату. В итоге простой был 15 минут в одном не самом критичном сервисе.

Так мы опять остались с одним ЦОДом.

В этот момент нам надоело таскаться с серверами под мышкой и играть в грузчиков. Плюс надоело заниматься самим железом в ЦОДе. Стали смотреть в сторону публичных облаков.

От 2 до 5 (почти) ЦОДов


Начали искать варианты с облаками. Вышли на Крок, попробовали, протестировали, договорились по условиям. Мы встали в облако, которое в ЦОДе Компрессор. Сделали кольцо тёмной оптики между Стордатой, Компрессором и офисом. Везде свой аплинк и два плеча оптики. Перерубание любого из лучей не рушит сеть. Потеря аплинка не рушит сеть. Получили статус LIR, есть своя подсеть, BGP анонсы, сеть резервируем, красота. Как именно заходили в облако с точки зрения сети здесь описывать не буду, но были нюансы.

Так у нас стало 2 ЦОДа.

У Крока есть еще ЦОД на Волочаевской, они расширили свое облако и туда, предлагали перенести часть ресурсов наших туда. Но помня историю с Караваном, который, по сути, так и не оправился после сноса ЦОДа, захотелось облачные ресурсы брать у разных провайдеров, чтобы уменьшить риск, что компания перестанет существовать (страна такая, что игнорировать такой риск нельзя). Поэтому с Волочаевской не стали связываться на тот момент. Ну и ещё второй вендор делает магию с ценами. Потому что, когда вы можете взять и эластично уехать, это даёт сильную переговорную позицию по ценам.

Смотрели разные варианты, но выбор пал на МТС.Cloud. Тому было несколько причин: облако на тестах показало себя хорошо, с сетью ребята тоже умеют работать (телеком оператор все-таки), и очень агрессивная маркетинговая политика захвата рынка, как следствие, интересные цены.

Итого 3 ЦОДа.

Дальше все-таки мы подключили и Волочаевскую нужны были дополнительные ресурсы, а в Компрессоре уже было тесновато. В общем, перераспределили нагрузку между тремя облаками и своим оборудованием в Стордате.

4 ЦОДа. Причём уже по живучести везде T3. Сертификаты, кажется, не у всех есть, но не буду утверждать точно.

У МТС был нюанс. Ничего кроме МГТС последней милей туда зайти не могло. При этом тянуть темную оптику МГТС целиком от ЦОДа до ЦОДа не было возможности (долго, дорого, и, если я не путаю, они такую услугу и не предоставляют). Пришлось делать со стыком, выводить два луча из ЦОДа до ближайших колодцев, где есть наш провайдер темной оптики Мастертел. У них разветвлённая сеть оптики по всему городу, и, если что, они просто сваривают нужный маршрут и дают вам жилу. А в это время Чемпионат мира по футболу пришел в город, неожиданно, как снег зимой, и доступы в колодцы в Москве закрыли. Мы ждали, пока это чудо закончится, и мы сможем прокинуть свой линк. Казалось бы, нужно было выйти из ЦОДа МТС с оптикой в руках, посвистывая дойти до нужного люка и опустить её туда. Условно. Делали три с половиной месяца. Точнее первый луч сделали довольно быстро, к началу августа (напомню, что ЧМ закончился 15 июля). А вот со вторым плечом пришлось повозиться первый вариант подразумевал, что надо перекопать Каширское шоссе, для чего перекрыть его на недельку (там при реконструкции завалило какой-то туннель, где лежат коммуникации, надо откапывать). К счастью, нашли альтернативу: другой маршрут, такой же геонезависимый. Получилось две жилы от этого дата-центра до разных точек нашего присутствия. Кольцо оптики превратилось в кольцо с ручкой.

Чуть забегая вперёд, скажу, что всё равно нам его положили. К счастью, в самом начале эксплуатации, когда еще мало всего перенесли. В одном колодце случился пожар, и пока монтажники матерились в пене, во втором колодце кто-то вытащил посмотреть коннектор (какой-то он был новой конструкции, интересно же). Математически вероятность одновременного сбоя была ничтожна. Практически мы его поймали. Собственно, нам и во Фиорде везло там рубанулось основное питание, и вместо включения его обратно, кто-то перепутал рубильник и выключил резервную линию.

Были не только технические требования по распределению нагрузки между локациями: чудес не бывает, и агрессивная маркетинговая политика с хорошими ценами подразумевает определенные темпы роста потребления ресурсов. Так что мы все время держали в голове, какой процент ресурсов надо отправить в МТС обязательно. Всё остальное мы перераспределяли между другими ЦОДами более-менее равномерно.

Снова своё железо


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

Искали решение, которое бы позволило получить облако на своем железе относительно легко. На тот момент мы никогда не работали с серверами Cisco, только с сетевым стеком, в этом был риск. На Деллах же простое хорошо знакомое железо, надёжное как автомат Калашникова. У нас такое стояло годами, и до сих пор где-то есть. Но идея Hyperflex в том, что он из коробки поддерживает гиперконвергентность итогового решения. А у Делла всё живёт на обычных маршрутизаторах, и там есть нюансы. В частности, производительность по факту не такая прикольная как в презентациях из-за оверхеда. В смысле, их можно правильно настроить и будет супер, но мы решили, что это не наш бизнес, и пусть Делл готовят те, кто находит в этом призвание. В итоге выбрали Cisco Hyperflex. Этот вариант победил по совокупности как самый интересный: меньше геморроя в настройке и эксплуатации, и во время тестов все было хорошо. Летом 2019 запустили кластер в бой. У нас была полупустая Стойка в Компрессоре, занятая по большей части только сетевым оборудованием, там и разместили. Таким образом получили пятый ЦОД физически-то четыре, но по пулам ресурсов получилось пять.

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

Окупаемость проекта железного проекта по средним ценам наших облаков за год.

Вы находитесь здесь


В этот момент у нас закончились вынужденные ходы. Как видите, у нас не было особо вариантов экономически, и постоянно мы нагружали то, куда должны были встать по каким-то причинам. Это привело к странной ситуации, что нагрузка неравномерная. Отказ любого сегмента (а сегмент с ЦОДами Крока держится на двух Нексусах в узком месте) это потеря пользовательского опыта. То есть сайт сохранится, но будут явные сложности с доступностью.

Был сбой в МТС со всем ЦОДом. Было ещё два в других. Периодически отваливались облака, либо контроллеры облаков, либо возникала какая-то сложная сетевая проблема. Короче, мы время от времени теряем ЦОДы. Да, кратковременно, но все равно неприятно. В какой-то момент приняли за данность, что ЦОДы отваливаются.

Решили идти на отказоустойчивость уровня дата-центров.

Сейчас мы не ляжем, если откажет один из 5 ЦОДов. Но вот если потеряем плечо Крока будут очень серьёзные просадки. Так и родился проект отказоустойчивости дата-центров. Цель такая если ДЦ умрёт, сеть до него умрёт или оборудование умрёт, сайт должен работать без вмешательства руками. Плюс после аварии мы должны штатно восстановиться.

В чём подводные камни



Сейчас:


Нужно:


Сейчас:


Нужно:


Эластик устойчив к потере одной ноды:


MySQL базы (много небольших) управляются достаточно сложно:




Про это лучше детальнее напишет мой коллега, который делал балансировку. Важно то, что до того, как мы навесили это, если мы теряли мастера, то надо было руками зайти на резерв и там поставить флажок r/o=0, перестроить на этот новый мастер ансиблом все реплики, а их в основной гирлянде более двух десятков, поменять конфиги приложения, потом раскатать конфиги и дождаться обновления. Сейчас приложение ходит по одному anycast-ip, который смотрит на LVS балансировщике. Постоянный конфиг не меняется. Вся топология баз на оркестраторе.

Сейчас между нашими ЦОДами протянута тёмная оптика, которая позволяет обращаться к любому ресурсу в пределах нашего кольца как к локальному. Время ответа между ЦОДами и время внутри плюс-минус одинаковое. Это важное отличие от других компаний, которые строят геокластеры. Мы очень сильно завязаны на своё железо и свою сеть, и мы не пытаемся локализовать запросы внутри ЦОДа. Это с одной стороны круто, а с другой если захотим в Европу или в Китай, то свою тёмную оптику не вытащим.

Это означает ребалансировку почти всего, в первую очередь баз данных. Много схем, когда активный мастер и на чтение, и на запись держит всю нагрузку, а рядом реплика синхронная для быстрого переключения (мы не пишем в два сразу, а именно реплицируем, иначе работает не очень хорошо). Основная база при этом в одном ЦОДе, а реплика в другом. Ещё частичные копии могут быть в третьем для отдельных приложений. Таких инстансов от 10 до 15 в зависимости от времени года. Оркестратор растянутый кластер между цодами 3 ЦОДами. Тут мы детальнее расскажем ещё, когда найдутся силы описать, как вся эта музыка играет.

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

В общем, есть ещё чем заняться, но план понятный.
Подробнее..

Дата-центры высшего уровня отвечаем на часто задаваемые вопросы про Tier IV

06.08.2020 12:10:31 | Автор: admin
Неделю назад мы рассказали о планах строительства нового дата-центра Tier IV и сразу получили несколько вопросов про этот уровень в классификации Uptime Institute. Из обсуждений в чатах получился полноценный FAQ. Так что сегодня развею самые живучие слухи про Tier IV и немного расскажу, какие требования Uptime Institute мы учитываем в проекте нового дата-центра.



Что значит максимально возможный уровень, придумали что-то новенькое?

Стандартам от Uptime Institute уже больше 25 лет. Столько времени существует система классификации Tier.

Сертификация дата-центров на уровни Tier проходит по нескольким программам:
  • Сертификация проектной документации (Design Documents) аудиторы проверяют пакет проектных документов по основным инженерным системам: кондиционирование, энергоснабжение. Также изучают документы по смежным системам, например, топливоснабжению.
  • Сертификация построенного ЦОД (Constructed Facility) здесь смотрят на соответствие построенного дата-центра сертифицированному проекту и проверяют инженерные системы при полной проектной нагрузке. Когда клиентского ИТ-оборудования еще нет, нагрузку имитируем тепловыми пушками.
    Этот уровень сдают только после Design.
  • Сертификация эксплуатационной устойчивости (Operational Sustainability) тут идет комплексная оценка эксплуатационных практик. Как именно это происходит, мы уже подробно рассказывали.
    Для сертификации по этой программе нужно сначала сдать Design и Facility.

Еще есть программа Management&Operations для проверки эксплуатации. Но это несертификация, а аудит дата-центра, так что подробно останавливаться не будем.

Уровень дата-центра закладывается еще на этапе концепции и проектирования. Поэтому мы начинаем готовиться к сертификации на Tier IV на этапе проектирования здания, еще до проектирования инженерных систем.

Почему мы так много говорим про стандарты Tier?

Система Tier содержит список требований к дата-центрам разных уровней. Но там нет конкретных объяснений, как это сделать, только требования к надежности инфраструктуры. Uptime Institute пишет:
стандарты Tiers приветствуют инновационные инженерные решения и признают, что все центры обработки данных непохожи друг на друга

А значит, есть несколько вариантов, как соблюсти требования.

Мы в DataLine занимаемся практической стороной: честно смотрим на лучшие европейские ЦОДы, берем лучшие практики, с осторожностью пробуем новое и применяем это в проектировании своих дата-центров. Делимся опытом, в том числе в наших Университетах.

Вот такой опыт сертификации по стандартам Uptime Institute у нас накопился:
  • 2014 год прошли аудит Management&Operations.
  • 2015 год дата-центр NORD-4 получил сертификат Design.
  • 2016 год сертифицировали NORD-4 на Facility.
  • 2018 год у NORD-4 появился сертификат Operational Sustainability.
  • 2020 год NORD-4 подтвердил сертификат Operational Sustainability.

Что дальше:
  • 2020 год совместно с Ростелеком-ЦОД начали строительство дата-центра в Остаповском проезде и его подготовку к сертификации на Tier IV.
  • 2020 год во втором полугодии планируем сдать в Uptime Institute проект NORD-5.
  • 2021 год планируем сертифицировать NORD-5 на Tier III по программе Facility.

Так что в практике применения стандартов всегда есть что обсудить, будем рады поговорить в нашей Салатовой телеге.

В чем основное отличие уровней?

Я уже немного рассказывал про схемы резервирования, характерные для разных Tier.

Посмотрим на сравнительную таблицу в стандарте:


Вот так уровни отличаются по минимальному числу активных компонентов, поддерживающих нагрузку (их обозначают той самой буквой N):
  • Tier I используется N минимальное количество оборудования для работы ЦОД, то есть резерва нет.
  • Tier II инженерное оборудование резервируется по схеме N+1.
  • Tier III по схеме N+1 резервируется инженерное оборудование и пути дистрибуции: кабели питания, трассы, трубопроводы.
  • Tier IV если случается единичный отказ любого оборудования, все равно остается N активных компонентов.

Но дело не только в энках, особенно в случае с Tier IV. Главное отличие Tier IV это единственный уровень с отказоустойчивостью. Он так и называется: Fault tolerant infrastructure. Также для него обязательны секционирование (или компартментализация, очень уж мне нравится это слово) и непрерывное охлаждение. Ниже посмотрим, что это значит.

Tier IV отличается от Tier III схемой резервирования оборудования 2(N+1)?

Как мы видим, никакая конкретная схема резервирования для Tier IV не указана. Как добиться N после любого отказа, каждый ЦОД решает сам. Раньше многие понимали требования Tier IV слишком буквально и предлагали сложные схемы наподобие 2N+1 или 2(N+1), чтобы уж наверняка избежать отказов. Но на практике это не обязательно.

Что такое отказоустойчивость в Tier IV? Чем отличается от Tier III?

В дата-центре Tier III мы допускаем ситуации отказа, где сотрудники должны вмешаться и переключиться вручную между резервными элементами.
В Tier IV такие переключения отсутствуют или происходят автоматически.

Что такое непрерывное охлаждение в Tier IV?

Tier III формально допускает несильное повышение температуры в машзале, когда происходит отказ оборудования и мы переключаемся между основным и резервным кондиционером или чиллером.

В Tier IV за этим следят гораздо жестче. Уже на этапе проектирования нужно обязательно предоставить расчеты скорости повышения температуры и доказать, что даже теоретически в машзале не станет жарче.

Что значит в Tier IV системы не только зарезервированы, но и защищены от физического воздействия? В чем отличие от Tier III?

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

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

А если случится пожар?

Все инженерное оборудование и все трассы у нас расположены в отдельных помещениях или защищенных коробах. Значит, при возгорании одной трассы теряется только резерв, без нарушения работы ИТ-оборудования.

А если упадет метеорит?

Наша страна настолько сурова, что иногда случается и такое. Но все-таки ЦОД не военный объект, чтобы защищаться от подобных аварий. Очень жаль, но система Tier не рассматривает защиту от всемирного потопа, вторжения инопланетян или захвата власти рептилоидами.

Tier IV это в 2 раза дороже?

Да, где-то может получиться и дороже. Но при должном подходе можно сэкономить без потери качества.

Для нас это первый опыт с Tier IV. Немного страшно, но мы движемся в этом направлении. Как только появятся новости, будем рады рассказать подробнее.
Подробнее..

Network-as-a-Service для крупного предприятия нестандартный кейс

30.09.2020 16:04:54 | Автор: admin

Как обновить сетевое оборудование на крупном предприятии без остановки производства? О масштабном проекте в режиме операции на открытом сердце рассказывает менеджер по управлению проектами Linxdatacenter Олег Федоров.

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

Диапазон запросов от обеспечения отказоустойчивости сети до создания и управления клиентской автономной системой с приобретением блока IP-адресов, настройкой протоколов маршрутизации и управлением трафиком согласно политикам организаций.

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

Данный тренд по времени совпал с периодом развития и усложнения собственной сетевой инфраструктуры Linxdatacenter. Мы расширили географию своего присутствия в Европе за счет подключения к удаленным площадкам, что в свою очередь потребовало и совершенствования инфраструктуры сети.

Компания запустила новый сервис для клиентов, Network-as-a-Service: решение всех сетевых задач клиентов мы берем на себя, позволяя им сосредоточиться на основном бизнесе.

Летом 2020 года завершился первый большой проект в этом направлении, о котором хотелось бы рассказать.

На старте

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

Последняя модернизация оборудования на предприятии проходила около 10 лет назад. Новое руководство предприятия решило улучшить связность, начав с обновления инфраструктуры на самом базовом, физическом уровне.

Проект был разделен на две части: апгрейд серверного парка и сетевого оборудования. Мы отвечали за вторую часть.

Базовые требования к работам включали в себя минимизирование простоев производственных линий предприятия во время выполнения работ (а на некоторых участках и полное исключение простоев). Любая остановка прямые денежные потери клиента, чего не должно было произойти ни при каких обстоятельствах. В связи с режимом работы объекта 24х7х365, а также с учетом полного отсутствия периодов плановых простоев в практике предприятия, перед нами была поставлена задача, по сути, выполнить операцию на открытом сердце. Это и стало главной отличительной чертой проекта.

Поехали

Работы планировались по принципу движения от удаленных от ядра узлов сети к более близким, а также от менее влияющих на работу производственных линий к влияющим на эту работу непосредственно.

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

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

Мы определили зоны, не влияющие на производственный процесс, а также критические участки цеха, погрузочно-разгрузочный блок, склады и т. д. На ключевых участках с клиентом был согласовано допустимое время простоя для каждого узла сети в отдельности: от 1 до 15 минут. Полностью избежать отключения отдельных узлов сети было невозможно, так как кабель должен быть физически переключен из старого оборудования в новое, а в процессе переключения необходимо также распутать бороду проводов, которая сформировалась в процессе нескольких лет эксплуатации без должного ухода (одно из последствий аутсорсинга работ по монтажу кабельных линий).

Работы были разделены на несколько этапов.

Этап 1 Аудит. Подготовка и согласование подхода к планированию работ и оценка готовности команд: клиента, подрядчика, выполняющего монтаж, и нашей команды.

Этап 2 Разработка форматадля проведения работ, с глубоким детальным анализом и планированием. Выбрали формат чек-листа с точным указанием порядка и последовательности действий, вплоть до последовательности переключения патч-кордов по портам.

Этап 3 Проведение работ в шкафах, не влияющих на производство. Оценка и корректировкавремени простоядля последующих этапов работ.

Этап 4 Проведение работ в шкафах, напрямую влияющих на производство. Оценка и корректировкавремени простоядля финального этапа работ.

Этап 5 Проведение работ в серверной по переключению оставшегося оборудования. Запуск на маршрутизации на новом ядре.

Этап 6 Последовательное переключение ядра системы со старых сетевых конфигураций на новые для плавного перехода всего комплекса системы (VLAN, маршрутизация и т. д.). На данном этапе мы подключили всех пользователей и перевели все сервисы на новое оборудование, проверили правильность подключения, удостоверились, что никакие из сервисов предприятия не остановились, гарантировали, что в случае возникновения каких-либо проблем они будут связаны непосредственно с ядром, что облегчало устранение возможных неполадок и финальную настройку.

Прическа бороды проводов

Проект оказался непростым еще и из-за сложных исходных условий.

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

Выглядело это примерно так:


так:


или так:


Во-вторых, для каждой подобной задачи необходимо было подготовить файл с описанием процесса. Берем провод Х из порта 1 старого оборудования, втыкаем его в порт 18 нового оборудования. Звучит просто, но когда у тебя в исходных данных 48 полностью забитых портов, а также отсутствует опция простоя (мы помним про 24х7х365), единственный выход работать по блокам. Чем больше можно вытащить проводов из старого оборудования за один раз, тем быстрее можно их причесать и вставить в новое сетевое железо, избежав сбоев и простоев в работе сети.

Поэтому на подготовительном этапе мы провели разбивку сети по блокам каждый из них относился к определенному VLAN. Каждый порт (или их подмножество) на старом оборудовании это какой-то из VLAN в новой топологии сети. Мы сгруппировали их так: в первых портах коммутатора разместились пользовательские сети, в середине производственные сети, а в последних точки доступа и аплинки.

Такой подход позволил за один прием вытаскивать и причесывать из старого оборудования не 1 провод, а 10-15. Это в несколько раз ускорило рабочий процесс.

Кстати, вот как выглядят провода в шкафах после причесывания:


или, например, так:


После завершения 2-го этапа мы взяли паузу на анализ ошибок и динамики проекта. Например, сразу вылезли мелкие недочеты из-за неточностей в предоставленных нам схемах сети (неверный коннектор на схеме неверный купленный патч-корд и необходимость его замены).

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

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

Вызов времени проект под COVID-ом

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

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

В ходе работ возникали неучтенные проблемы, и приходилось вносить корректировки на лету. Так удалось быстро предотвращать влияние человеческого фактора (ошибки в схеме, ошибки в определении статуса активности интерфейса и т. д.).

Хотя дистанционный формат работы и казался непривычным в начале проекта, мы довольно быстро приспособились к новым условиям и вышли на финальный этап работ.

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

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

А в результате

Технические итоги проекта

Прежде всего, было создано новое ядро новой сети предприятия, для чего мы построили физические/логические кольца. Сделано это таким образом, чтобы у каждого коммутатора в сети появилось второе плечо. В старой сети многие коммутаторы подсоединялись к ядру по одной трассе, одним плечом (аплинком). Если он рвался, коммутатор становился полностью недоступен. А если через один аплинк подключалось несколько коммутаторов, то авария выводила из строя целый отдел или производственную линию на предприятии.

В новой сети даже довольно серьезной сетевой инцидент ни при каком сценарии не сможет положить всю сеть или значимый ее участок.

90% всего сетевого оборудования обновлено, выведены из эксплуатации медиаконвертеры (преобразователи среды распространения сигнала), а также упразднена необходимость в выделенных силовых линиях для запитки оборудования за счет подключения к PoE-коммутаторам, где электропитание осуществляется по Ethernet-проводам.

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

Схема сети

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

Бизнес-итоги проекта

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

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

Подтверждение качества работы запрос от клиента на продолжение оказания услуг по модернизации сети на его остальных площадках в России.
Подробнее..

Почему важно проверить ПО на вашей СХД высокой доступности (99,9999)

02.10.2020 10:21:39 | Автор: admin

Какая версия прошивки самая правильная и рабочая? Если СХД гарантирует отказоустойчивость на 99,9999%, то значит ли, что и работать она будет бесперебойно даже без обновления ПО? Или наоборот для получения максимальной отказоустойчивости нужно всегда ставить самую последнюю прошивку? Постараемся ответить на эти вопросы, опираясь на наш опыт.

Небольшое введение

Все мы понимаем, что в каждой версии программного обеспечения, будь то операционная система или драйвер для какого-то устройства, зачастую содержатся недоработки/баги и прочие особенности, которые могут, как не "проявиться" до конца службы оборудования, так и вскрыться только при определенных условиях. Количество и значимость таких нюансов зависит от сложности (функциональности) ПО и от качества тестирования при его разработке.

Зачастую пользователи остаются на прошивке с завода (знаменитое "работает, значит не лезь") или всегда ставят самую последнюю версию (в их понимании последняя значит самая рабочая). Мы же используем другой подход смотрим релиз ноты для всего используемого в облаке mClouds оборудования и внимательно выбираем подходящую прошивку для каждой единицы оборудования.

К такому выводу мы пришли, что называется, с опытом. На своем примере эксплуатации расскажем, почему обещанные 99,9999 % надежности СХД ничего не значат, если вы не будете своевременно следить за обновлением и описанием ПО. Наш кейс подойдет для пользователей СХД любого вендора, так как подобная ситуация может произойти с железом любого производителя.

Выбор новой системы хранения данных

В конце прошлого года, к нашей инфраструктуре добавилась интересная система хранения данных: младшая модель из линейки IBM FlashSystem 5000, которая на момент приобретения именовалась Storwize V5010e. Сейчас она продается под названием FlashSystem 5010, но фактически это та же аппаратная база с тем же самым Spectrum Virtualize внутри.

Наличие единой системы управления - это, кстати, и есть основное отличие IBM FlashSystem. У моделей младшей серииона практически не отличается от моделей более производительных. Выбор определённой модели лишь даёт соответствующую аппаратную базу, характеристики которой дают возможность использовать тот или иной функционал или обеспечить более высокий уровень масштабируемости. ПО при этом идентифицирует аппаратную часть и предоставляет необходимый и достаточный функционал для этой платформы.

IBM FlashSystem 5010IBM FlashSystem 5010

Кратко про нашу модель 5010. Это двухконтроллерная блочная система хранения данных начального уровня. Она умеет размещать в себе диски NLSAS, SAS, SSD. Размещение NVMe в ней недоступно,так как эта модель СХД позиционируется для решения задач, которым не требуется производительность NVMe дисков.

СХД приобреталась для размещения архивной информации или данных, к которым не происходит частого обращения. Поэтому нам было достаточно стандартного набора её функционала: тиринг (Easy Tier), Thin Provision. Производительность на NLSAS дисках на уровне 1000-2000 IOPS нас тоже вполне устраивала.

Наш опыт как мы не обновили прошивку вовремя

Теперь собственно про само обновление ПО. На момент приобретения система имела уже немного устаревшую версию ПО Spectrum Virtualize, а именно, 8.2.1.3.

Мы изучили описание прошивок изапланировали обновление до 8.2.1.9. Если бы мы были чуть расторопнее, то этой статьи бы не было на более свежей прошивке баг бы не произошел. Однако, по определённым причинам обновление этой системы было отложено.

В результате небольшая задержка обновления привела к крайне неприятной картине, как в описании по ссылке: https://www.ibm.com/support/pages/node/6172341.

Да, в прошивке той версии как раз был актуален так называемый APAR (Authorized Program Analysis Report) HU02104. Проявляется он следующим образом. Под нагрузкой при определённых обстоятельствах начинает переполняться кэш, далее система уходит в защитный режим, в котором отключает ввод-вывод для пула (Pool). В нашем случае выглядело как отключение 3-х дисков для RAID-группы в режиме RAID 6. Отключение происходит на 6 минут. Далее, доступ к Томам в Пуле восстанавливается.

Если кто не знаком со структурой и именованием логических сущностей в контексте IBM Spectrum Virtualize, я сейчас кратко расскажу.

Структура логических элементов СХДСтруктура логических элементов СХД

Диски собираются в группы, которые называются MDisk (Managed Disk). MDisk может представлять собой классический RAID (0,1,10,5,6) или виртуализованный DRAID (Distributed RAID). Использование DRAID позволяет увеличить производительность массива, т.к. будут использоваться все диски группы, и уменьшить время ребилда, благодаря тому, что нужно будет восстанавливать только определённые блоки, а не все данные с вышедшего из строя диска.

Распределение блоков данных по дискам при использовании Distributed RAID (DRAID) в режиме RAID-5.Распределение блоков данных по дискам при использовании Distributed RAID (DRAID) в режиме RAID-5.

А эта схема показывает логику работы ребилда DRAID в случае выхода из строя одного диска:

Логика работы ребилда DRAID при выходе из строя одного дискаЛогика работы ребилда DRAID при выходе из строя одного диска

Далее, один или несколько MDisk образуют так называемый Pool. В пределах одного пула не рекомендуется использовать MDisk с разным уровнем RAID/DRAID на дисках одного типа. Не будем в это сильно углубляться, т.к. мы планируем рассказать это в рамках одной из следующих статей. Ну и, собственно, Pool делится на Тома (Volumes), которые презентуются по тому или иному протоколу блочного доступа в сторону хостов.

Так вот, у нас, в результате возникновения ситуации, описанной в APAR HU02104, из-за логического отказа трёх дисков, перестал быть работоспособным MDisk, который, в свою очередь, повлёк отказ в работе Pool и соответствующих Томов.

Так как эти системы довольно умные, их можно подключить к облачной системе мониторинга IBM Storage Insights, которая автоматически, при возникновении неисправности, отправляет запрос на обслуживание в службу поддержки IBM. Создаётся заявка и специалисты IBM в удалённом режиме проводят диагностику и связываются с пользователем системы.

Благодаря этому вопрос решился довольно оперативно и от службы поддержки была получена оперативная рекомендация по обновлению нашей системы на уже ранее выбранную нами прошивку 8.2.1.9, в которой на тот момент этот момент был уже исправлен. Это подтверждает соответствующий Release Note.

Итоги и наши рекомендации

Как говорится: "хорошо то, что хорошо заканчивается". Баг в прошивке не обернулся серьезными проблемами работа серверов была восстановлена в кратчайшие сроки и без потери данных. У некоторых клиентов пришлось перезапускать виртуальные машины, но в целом мы были готовы к более негативным последствиям, так как ежедневно делаем резервные копии всех элементов инфраструктуры и клиентских машин.

Мы получили подтверждение, что даже надежные системы с 99,9999% обещанной доступности требуют внимания и своевременного обслуживания. Исходя из ситуации мы сделали для себя ряд выводов и делимся нашими рекомендациями:

  • Нужно обязательно следить за выходом обновлений, изучать Release Notes на предмет исправления потенциально критичных моментов и своевременно выполнять запланированные обновления.

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

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

    Например, IBM для своих систем хранения данных держит в актуальном состоянии как минимум два релиза ПО. На момент написания этой статьи это 8.2 и 8.3. Обновления для 8.2 выходят раньше. Следом с небольшой задержкой обычно выходит аналогичное обновление для 8.3.

    Релиз 8.3 имеет ряд функциональных преимуществ, например, возможность расширения MDisk (в режиме DRAID) добавлением одного или более новых дисков (такая возможность появилась начиная с версии 8.3.1). Это довольно базовый функционал, но в 8.2 такой возможности, к сожалению, нет.

  • Если обновиться по каким-либо причинам нет возможности, то для версий ПО Spectrum Virtualize, предшествующих версиям 8.2.1.9 и 8.3.1.0 (где описанный выше баг актуален), для снижения риска его появления техническая поддержка IBM рекомендует ограничить производительность системы на уровне пула, как это показано на рисунке ниже (снимок сделан в русифицированном варианте GUI). Значение 10000 IOPS показано для примера и подбирается согласно характеристикам вашей системы.

Ограничение производительности СХД IBMОграничение производительности СХД IBM
  • Необходимо правильно рассчитывать нагрузку на системы хранения и не допускать перегрузки. Для этого можно воспользоваться либо сайзером IBM (если есть к нему доступ), либо помощью партнеров, либо сторонними ресурсами. Обязательно при этом понимать профиль нагрузки на систему хранения, т.к. производительность в МБ/с и IOPS сильно разнится в зависимости как минимум от следующих параметров:

    • тип операции: чтение или запись,

    • размер блока операции,

    • процентное соотношение операций чтения и записи в общем потоке ввода-вывода.

    Также, на скорость выполнения операций влияет как считываются блоки данных: последовательно или в случайном порядке. При выполнении нескольких операций доступа к данным на стороне приложения есть понятие зависимых операций. Это тоже желательно учитывать. Всё это может помочь увидеть совокупность данных со счётчиков производительности ОС, системы хранения, серверов/гипервизоров, а также понимание особенностей работы приложений, СУБД и прочих "потребителей" дисковых ресурсов.

  • И напоследок, обязательно иметь резервные копии в актуальном и рабочем состоянии. Расписание резервного копирования нужно настраивать исходя из приемлемых для бизнеса значений RPO и периодически обязательно проверять целостность резервных копий (довольно много производителей ПО для резервного копирования реализовали в своих продуктах автоматизированную проверку) для обеспечения приемлемого значения RTO.

Благодарим, что дочитали до конца.
Готовы ответить на ваши вопросы и замечания в комментариях. Также приглашаем подписаться на наш телеграмм-канал, в котором мы проводим регулярные акции (скидки на IaaS и розыгрыши промокодов до 100% на VPS), пишем интересные новости и анонсируем новые статьи в блоге Хабра.

Подробнее..

Гиперконвергентная система AERODISK vAIR v2. Часть 1. Система виртуализации АИСТ

14.04.2021 06:04:04 | Автор: admin


Всем привет. Этой статьей мы начинаем знакомить вас с новой версией российской гиперконвергентной системы AERODISK vAIR v2, в частности, со встроенным гипервизором АИСТ, который сейчас получил возможность работать автономно от vAIR, используя внешние СХД.


Изначально мы хотели рассказать о функциональности новой версии vAIR в одной статье, но материала получилось очень много, и мы встали перед выбором: либо сократить материал, либо разбить его на три части. Мы выбрали второй вариант и разбили материал следующим образом:


  • Управление кластером и гипервизор АИСТ
  • Файловая система ARDFS
  • Аппаратные платформы, лицензирование и поддержка

Соответственно, в первой части мы расскажем о функциях управления vAIR v2 и более подробно о подсистеме виртуализации. Но сначала хотелось бы сказать пару слов об архитектурных изменениях в версии vAIR v2.


Коротко об архитектуре. Основные отличия между первой и второй версией


С момента выхода первой версии в 2019 году архитектура vAIR претерпела ряд изменений. Связано это в первую очередь с борьбой за стабильность, ресурсоемкость и производительность. Однако обо всем по порядку.


Описывать полностью архитектуру с нуля мы рамках этой статьи не будем. Коснёмся лишь тех отличий, которые существуют в настоящий момент между первой и второй версией. Для более глубокого знакомства с изначальной архитектурой vAIR v1 рекомендуем ознакомиться с нашими предыдущими статьями на эту тему:



На уровне большой картинки на данный момент архитектура vAIR v2 выглядит следующим образом:



Ну а теперь переходим к деталям.


Косметические изменения


Незаметное внешнему глазу, но при этом важное и трудоемкое событие произошло в распределенной базе конфигураций (ConfigDB), которая отвечает за одновременное хранение конфигураций всех компонентов решения на всех нодах кластера. Мы полностью её переработали и, соответственно, оптимизировали. В итоге ConfigDB стала значительно стабильнее и минимизировала пожирание бесценных аппаратных ресурсов. Если говорить о цифрах, то за счёт переработки полезную производительность решения удалось увеличить примерно на 30%, что, очевидно, хорошо.


Стандартный блок данных, которым оперирует ARDFS, изменился с 4МБ до 64 МБ. Сделано это было также с целью увеличения производительности ввода-вывода.


Ещё одним маленьким, но приятным бонусом, который получился в результате оптимизации ARDFS и ConfigDB, стало снижение минимальных системных требований по количеству нод в кластере. Первая версия vAIR требовала не менее четырех нод, во второй-же версии начинать можно с трёх нод. Мелочь, но тоже приятно.


АИСТ покинул гнездо


Теперь перейдем к главному архитектурному изменению, с которого и начнем рассказ про подсистему виртуализации. Гипервизор АИСТ, который раньше умел работать только внутри vAIR, научился летать работать автономно и, соответственно, может поставляться отдельным продуктом по отдельной лицензии.


Для справки: и АИСТ, и vAIR как два отдельных продукта прошли всю необходимую экспертизу регуляторов и, соответственно, добавлены во всех необходимые гос. реестры Минцифры и Роспатента, чтобы по-честному считаться российским ПО.


Чтобы не было путаницы, поясним. По факту АИСТ и vAIR не являются разными продуктами. Гипервизор АИСТ это составная и обязательная часть гиперконвергентной системы vAIR, при этом АИСТ может использоваться в качестве самостоятельного решения, а также АИСТ может всегда быть обновлен до полноценного vAIR-а.


Данное архитектурное изменение не только позволяет импортозаместить зарубежную виртуализацию на российских предприятиях, но и открывает ряд крайне полезных сценариев использования vAIR. Разберем их:


Сценарий 1. Просто гиперконвергент


Тут все просто. АИСТ используется как составная часть vAIR и работает с хранилищем ARDFS. Это то, что было в первой версии, и остается сейчас.



Виртуальные машины, сеть и хранилище работают в рамках одной отказоустойчивой аппаратной платформы (3 ноды+).


Сценарий 2. Просто виртуализация


Классическая серверная виртуализация. На локальные диски физических серверов устанавливается АИСТ, к АИСТу пригоняются сторонние СХД по файловым или блочным протоколам, и на базе этих СХД хранятся виртуальные машины.



При этом в этой схеме всегда остается возможность добавить локальных дисков во все физические серверы, объединить их быстрым (от 10 Гбит/сек) интерконнектом и обновить лицензию АИСТа до vAIR, получив в итоге гибридный сценарий (см. ниже).


Сценарий 3. Гибридный сценарий


Самый интересный сценарий. Мы одновременно с гиперконвергентом используем в качестве хранилища виртуальных машин сторонние СХД (например ENGINE или ВОСТОК :-)). Полезным является то, что к любой виртуалке, которая хранится на ARDFS, мы можем легко прицепить дополнительные виртуальные диски с СХД. И наоборот, к любой виртуалке, которая лежит на СХД, мы можем прицепить виртуальные диски с ARDFS. Это открывает очень много возможностей, начиная с задач постепенной и бесшовной миграции инфраструктуры между разными хранилищами, заканчивая полезным использованием старых СХД и серверов хранения, которые и выкинуть жалко, и подарить некому.



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



Обзор функционала. Что нового и для чего


Функции управления


Управление системой осуществляется при помощи web-консоли на русском языке (поддерживаются любые браузеры) или командной строки. Важной и полезной плюшкой является то, что за счёт распределённого хранения конфигураций для осуществления управления всем кластером можно подключиться к любой ноде по IP или DNS-имени. Специальных серверов управления разворачивать не нужно. При этом это не запрещено.


Предусмотрен сценарий развертывания управляющей виртуальной машины (УВМ), которая через RestfulAPI может осуществлять полноценное управление всем кластером.


Кстати RestfulAPI, как понятно выше, есть, он описан и работает (по нему будет отдельная статья). Его спокойно можно использовать для автоматизации операций и интеграции со смежными системами. К примеру, уже сейчас есть интеграция (и, кстати, есть внедрение в продуктив) с российским VDI-решением Термидеск, как раз реализованная на базе нашего API. Плюс ещё несколько продуктов вендоров-партнеров на подходе.


Для управления виртуальными машинами изнутри гостевой ОС используется на выбор два протокола: VNC (по умолчанию) или Spice. На уровне отдельных ВМ администратор может задавать разные варианты подключений.



Сам интерфейс разбит на несколько логических частей.


1) Основная область управления, в которой выполняются почти все операции



2) Основное меню, которое выдвигается наведением курсора



3) Панель действий, на которой отображаются доступные для выбранного объекта действия.



4) Панель задач, которая показывает, какие задачи выполняются или были выполнены над выбранным объектом, вызывается выбором объекта и последующим кликом по кнопке задачи.



5) Ну и, наконец, информационная панель, которая показывает количество актуальных ошибок (красные) и выполняемых в данный момент действий (синие).



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



Лирическое отступление: когда я эту функцию показал моему старому товарищу, который является тру-админом (то есть он админил системы в те славные времена, когда систем ещё не существовало) он воскликнул:
Вы нормальные там??!!! Нельзя админить серьезные системы через мобилу!!!
Хочу отметить, что во втором своём высказывании он, безусловно прав, лазить по серьезным кластерам через мобилку опасно, можно ткнуть не туда и всё как обычно упадёт, но всегда есть НО
Я напомнил ему ситуацию, в которую он попал несколько лет назад, когда потратил примерно 40 минут времени и 10 тонн мата на то, чтобы перезагрузить пару зависших виртуалок на известном гипервизоре, используя свой смартфон. Ноутбука у него с собой не было, а его заказчик с паром из ушей требовал устранить проблему здесь и сейчас.
Вспомнив об этом случае, мой товарищ тру-админ перестал сомневаться в нашей нормальности :-).

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


Гипервизор АИСТ


Не секрет, что в основе АИСТа лежит старый добрый KVM с libvirt-овой обвязкой. При этом наша реализация очень сильно доработана. Про годный веб-интерфейс, который управляет не только виртуализацией, но и сетью с хранилищем и который доступен, как с любой ноды, так и с управляющей ВМ, мы писали выше. Но это далеко не все доработки. Вот ещё несколько крайне достойных функций, которых в штатном KVM-е нет.


Отказоустойчивость виртуальных машин (HAVM) реализована классическим и понятным образом. Она может быть активна или неактивна для каждой виртуалки, а включается на этапе создания ВМ или в процессе её редактирования.



Если параметр HAVM активен, то в случае отказа ноды кластера, ВМ автоматически перезапуститься на другой ноде.


Для отдельных ВМ или для групп ВМ предусмотрены приоритеты обслуживания (QOS) из libvirt-а, где в свою очередь предусмотрены шаблоны популярных конфигураций.



Для защиты данных на уровне ВМ, а также для большей гибкости администрирования предусмотрены мгновенные снимки и клоны (которые можно превратить в шаблоны ВМ соответственно). Важной доработкой и одновременно крайне большой радостью является то, что снэпшоты делаются на горячую (при работающей ВМ) и полностью поддерживают консистентность файловых систем гостевых ОС (Linux, Solaris, Windows, BSD) и ряда поддерживаемых СУБД (пока только PostgreSQL и MySQL). При этом с помощью RestfulAPI никто не мешает реализовать консистентные снимки для других систем внутри гостевой ОС самостоятельно.


Для внешнего хранения из коробки поддерживается NFS, то есть хранить виртуалки можно на распределенном хранилище ARDFS (доступно в vAIR) или на внешней СХД по протоколу NFS. Опционально предусмотрена поддержка блочных внешних СХД по протоколам iSCSI и FC.


Миграция виртуальных машин со сторонних гипервизоров


Миграция, причем неважно откуда и куда, всегда стоит особняком во всей ИТ-жизни. За время полутора лет эксплуатации нашими заказчиками первой версии vAIR они (и мы автоматически) регулярно сталкивались с проблемами миграции виртуальных машин со сторонних гипервизоров в АИСТ. Штатный конвертер KVM штука хорошая, но крайне капризная. Поэтому в vAIR v2 (и в АИСТе соответственно) мы предусмотрели человеческий конвертер ВМ из VMware/Hyper-V прямо в интерфейсе vAIR/АИСТ.



Для конвертации администратор выбирает шару NFS (пока только NFS), где лежат файлы виртуальных машин VMware или Hyper-V. Далее vAIR сам сканирует шару на наличие нужных ему файлов и формирует доступный список для миграции. Далее выбираем целевой пул ARDFS (или внешнюю СХД), то есть куда будем конвертировать, выбираем нужные файлы ВМ (можно несколько, они будут конвертироваться по очереди) запускаем и идём пить пиво.



Когда пиво выпито, новые, уже сконвертированные, виртуалки ждут нас уже внутри vAIR-а в выключенном состоянии.


Мониторинг и логирование


Функции мониторинга реализованы как локально, так и удаленно. Администратор может работать со счетчиками утилизации ресурсов CPU, RAM, сетевых интерфейсов и подсистемой ввода-вывода (IOPS, MB/s, latency), как на уровне отдельных нод, так и на уровне кластера в целом.



Всё то же самое доступно и для удаленной системы мониторинга на базе Grafana.



Для логирования и алертинга предусмотрен журнал событий (ноды, порты, физические диски (SMARTCTL), сенсоры, температура и т.п.) с разбивкой по категориям и возможностью оповещения по электронной почте. Опционально поддерживается SNMP.




Кроме описанных выше возможностей гипервизор АИСТ позволяет выполнять функционал, который мы считаем must have, поэтому сильно его разрисовывать не будем, а просто перечислим:


  • Обновление ПО без остановки и миграции виртуальных машин
  • Живая миграция ВМ, а в ближайшем будущем с возможностью динамичного распределения ресурсов (а-ля DRS)
  • Распределённые виртуальные коммутаторы с поддержкой VLAN-ов
  • Расширение кластера без остановки виртуальных машин
  • Автоподдержка (автоматическое оповещение производителя и заведение тикетов в тех. поддержку, при согласии заказчика, само собой)
  • Метрокластер (отдельная большая функция, которой мы посветим позже отдельную статью)

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


https://aerodisk.ru/upload/Datasheet_AIST_final_11042021.pdf


В завершение первой части


В процессе разработки vAIR и АИСТ собственных решений в области виртуализации многие наши доверенные партнеры (которые допущены к раннему доступу), глядя на это, утверждали, что это плохая идея, потому что ВМварь и Нутаникс не догнать, они слишком крутые и великие, у них тысячи программистов по всей планете, бороды длиннее и свитера в два раза толще.


На подобные утверждения мы всегда задавали вопрос.


А эти компании сразу появились на свет с тысячей бородатых разрабов в толстых свитерах?

ИЛИ другой вариант


А вы когда родились вам сразу было 35 лет, у вас была машина, семья, дача, работа и образование? Это в комплекте вам врачи в роддоме выдавали?

В продолжении этой мысли позволим себе процитировать нашу же старую статью:


притча на эту тему.


Однажды странник попал в город, где шло грандиозное строительство. Мужчины ворочали большие камни под палящим солнцем. Что ты делаешь? спросил наш герой у одного из рабочих, который медленно тащил булыжник. Ты что, не видишь камни таскаю! зло ответил тот. Тут странник заметил другого рабочего, который волок телегу с большими камнями, и спросил: Что ты делаешь? Я зарабатываю на еду для своей семьи, получил он ответ. Странник подошел к третьему рабочему, который занимался тем же, но работал энергичнее и быстрее. Что делаешь ты? Я строю храм, улыбнулся тот.

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


В жизни это совсем не так. Практически всегда (за редким исключением), новый серьезный продукт создается небольшим коллективом до 10 человек (а обычно 2-3). При этом на этапе создания закладывается 80% всего функционала продукта. Далее продукт силами этого маленького коллектива выходит на рынок (или как-то еще громко заявляет о себе), и там его уже подхватывают инвесторы (фонды, холдинги или крупные производители).


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


На этом мы завершаем первую часть цикла статей про vAIR v2. В следующей статье подробно расскажем о функционале файловой системы ARDFS.


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


Голосование доступно тут, на Хабре, а также в нашем телеграм-чате https://t.me/aerodisk


Всем спасибо за внимание, как обычно ждем конструктивной критики и интересных вопросов.

Подробнее..

Как новость про 4 выходных дня уронила нам базу данных

30.04.2021 14:04:59 | Автор: admin
Этот день яркий пример того, как несколько вещей, которые сами по себе не приводят к отказу, могут удачно совпасть. Итак, 23 апреля было совершенно обычным днём, с обычным трафиком и обычной загрузкой ресурсов. Как обычно, с запасом больше трети, чтобы при потере любого из ЦОДов пережить это без проблем. Никто не думал, что к серверному мониторингу нужно прикручивать ещё мониторинг того, что говорит президент на прямой линии, поэтому дальше случилось вот что:



Примерно в 13:30 у нас резко подскочила нагрузка на поиск по авиации и по железнодорожным билетам. Где-то в этот момент РЖД сообщила о перебоях на сайте и в приложении, а мы начали экстренно наливать дополнительные инстансы бекендов во всех ЦОДах.

Но на самом деле проблемы начались раньше. Примерно в 8 утра мониторинг прислал алерт про то, что на одной из реплик базы данных у нас что-то подозрительно много долгоживущих процессов. Но мы это прошляпили, сочли не очень важным.


Вводная


Наша инфраструктура за почти двадцать лет развития существенно разрослась. Приложения живут на трех платформах старый код на php в монолите, первая версия микросервисов на платформе с самодельной оркестрацией, вторая, стратегически правильная, это OKD, в котором живут сервисы на go, php и nodejs. Вокруг всего этого десятки баз на mysql с обвязкой для HA основная гирлянда, обслуживающая монолит, и много пар master-hotstandby для микросервисов. Помимо них мемкеши, кафки, монги, редисы, эластики, тоже далеко не в единственном экземпляре. Nginx и envoy в роли frontproxy. Все это дело живет в трех сетевых локациях и мы исходим из того, что потеря любой из них не отражается на пользователях.

АБ-тесты: mysql против эластика


У нас есть три нагруженных продукта. Расписание электричек, где просто очень много входного трафика. Расписание железной дороги по поездам дальнего следования и покупка-бронирование ж/д билетов там и трафика много и поиск посложнее. Авиация с очень тяжелыми поисками, многоступенчатым кэшом, большим количеством вариантов из-за пересадок и вилок плюс-минус 3 дня. Давным-давно все три продукта жили только в монолите, а потом мы начали потихоньку выносить отдельные части в микросервисы. Первыми разобрали электрички, и, несмотря на то, что обычно пик на майских приходится именно на них, новая архитектура очень удобно и просто позволяет масштабироваться под рост нагрузки. В случае с авиацией растащили большую часть монолита, и прямо в момент дня П уже неделю шло А/Б тестирование саджеста географии. Сравнивали две версии реализации новую, на elasticsearch, и старую, исторически ходившую в основную гирлянду mysql. В момент ее запуска, 15 апреля, уже поймали пачку проблем, но тогда их быстро зачинили, поправили код и решили, что больше оно не выстрелит.

Выстрелило. Надо заметить, что старая версия это своя реализация полнотекстового поиска и ранжирования на mysql. Не самое лучше решение, но проверенное временем и в основном работающее. Проблемы начинаются, когда любая из таблиц сильно фрагментируется, тогда все запросы с ее участием начинают тормозить и сильно грузить систему. И, очевидно, в 8 утра мы этот порог фрагментации переступили, о чем и сообщил алерт. Стандартная реакция на такую редкую, но всё-таки ожидаемую ситуацию, вывести затупившую реплику из-под нагрузки (с нашим проксирующим слоем из proxysql это делается элементарно), дальше запустить optimize + analyze и потом вернуть обратно. С учетом запаса по мощности в обычное время при обычной нагрузке это не приводит к каким-либо проблемам. Но тут в спокойное время мы этот алерт не обработали.

13:20


Примерно в это время звучит новость про майские праздники и нерабочие дни.

Пик трафика около 13:30


Как мы потом выяснили, буквально спустя несколько минут после того, как было объявлено про дополнительные выходные (которые не выходные, но выходные) начался рост трафика. Нагрузка пошла резко. В пике было 2,5 3 раза от нормы и так продолжалось несколько часов.

Нам почти сразу же посыпались оповещения о ЧП алерты уровня критичности просыпайся и чини. В первую очередь это был алерт о росте 50*-х ошибок, которые мы отдаем клиентам с наших frontproxy. Уровнем ниже сработал алерт на ошибки подключения к БД и в логах мы видели примерно такое: DB: Max connect timeout reached while reaching hostgroup 102 after 3162ms. Плюс алерты о нехватке мощностей на трех группах серверов приложений старой монолитной платформы. Alert storm в чистом виде.

Мысль о причинах пришла почти моментально, еще до захода на график с входящими запросами новость о каникулах уже успела промелькнуть во внутренней переписке в чатах.

Немного придя в себя в ситуации почти полного ахтунга, начали реагировать. Масштабировать сервера приложений, разбираться с ошибками на стыке приложение база. Быстро вспомнили про горевший с утра алерт и в processlist болеющей реплики нашли наших старых знакомых из саджеста географии. Списались с командой авиа, они подтвердили, что рост трафика в последние числа мая, которого даже близко не было прошлые 15 лет, реален. И это не атака, не какая-то проблема в балансировке, а натуральные живые пользователи. И под их живыми запросами нашей уже и без того перегруженной реплике стало совсем нехорошо.

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

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

Манипуляции с базой на какое-то время помогли. Примерно с 13:50 и до 14:30 все было спокойно.

Второй пик около 14:30


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


Нагрузка, похоже, связанная с перебоями на сайте РЖД

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

Ожидание не было скучным. Где-то за 5 минут узкое место системы каким-то до сих пор не совсем понятным образом пропихнулось с слоя приложения на слой БД, то ли на саму базу, то ли на proxysql. И к 14:40 у нас полностью остановилось запись в основной кластер mysql. Что именно там произошло, мы пока не разобрались, но смена мастера на hotstandby резерв помог. И минут через 10 запись мы вернули. Примерно в это же время решили принудительно перекинуть всю нагрузку от саджеста на эластик, пожертвовав результатами АБ кампании. Насколько это помогло, пока тоже не осознали, но хуже точно не стало.

15:00


Запись ожила, вроде все должно быть хорошо, и на репликах и на proxysql перед ними нагрузка в норме. Но почему-то ошибки при запросах на чтение из приложения к базе не заканчиваются. Примерно за 15-20 минут втыкания в графики по разным слоям и поиска хоть каких-то закономерностей осознали, что ошибки идут только с одного proxysql. Рестартанули его и ошибки ушли. Корневую причину раскопали уже существенно позже, при детальном анализе сбоя. Оказалось, что во время прошлого ЧП, за неделю до, во время старта АБ кампании про саджест, на proxysql не закрылись корректно соединения к одной из реплик гирлянды, с которой тогда производили манипуляции. И на этом экземпляре proxysql мы тупо уперлись в нехватку портов для исходящего трафика. Метрика эта, понятное дело, собирается, но вот вешать на неё алерт нам в голову не приходило. Теперь он уже есть.

15:20


Восстановили все продукты, кроме поездов.

15:50


Доналились последние бекенды поездов. Обычно это занимает не два часа, а час, но тут сами слегка накосячили в стрессовой ситуации.

Как часто бывает починили в одном месте, сломалось в другом. Бекенды стали принимать больше соединений, фронтпрокси стали меньше отбрасывать клиентские запросы из-за переполнения апстримов, как следствие вырос внутренний межсервисный трафик. И сложился сервис авторизации. Это микросервис, но не в OKD, а на старой платформе. Там масштабирование попроще, чем в монолите, но хуже, чем в OKD. Поднимали минут 15, несколько раз выкручивая параметры и добавляя мощностей, но в итоге тоже заработало.

16:10


Ура, все работает, можно сходить пообедать.

Красивые картинки


Красивые они потому, что не до конца информативные, но оси не прошли проверку СБ.

График 500-х:



Общая картина по нагрузке за 2 дня:



Выводы от капитана


  1. Спасибо, что не вечером.
  2. С алертами нужно что-то делать. Их уже очень много, но, с одной стороны, всё равно иногда не хватает, а с другой некоторые продалбываются, в том числе и из-за количества. Да и стоимость сопровождения с каждым новым алертом увеличивается. В общем тут пока есть понимание проблемы, но нет стратегического решения. Оно прячется где-то на стыке процессов и инструментов, ищем. Но пару алертов тактически мы уже запилили.
  3. Стоит внимательнее следить даже за минорными апдейтами софта. Багу, из-за которой proxysql не закрыл сокеты, скорее всего уже починили. Но это был минор и не про безопасность, а такое мы катим не слишком оперативно.
  4. Микросервисы лучше монолита, а OKD лучше нашей самодельной платформы. По крайней мере с точки зрения простоты масштабирования.
  5. Мы молодцы. Подготовленная инфраструктура дала хорошую основу, а команда, несмотря на пару факапов, отработала очень круто для такой стрессовой ситуации.
Подробнее..

Выбор архитектурного стиля (часть 2)

17.09.2020 12:16:31 | Автор: admin
Привет, хабр. Сегодня я продолжаю серию публикаций, которую написал специально к старту нового потока курса Software Architect.



Введение


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

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

В этот раз я предлагаю поговорить о возможностях организации системы в виде набора модулей/библиотек (компонентно-ориентированная архитектура) или сервисов (сервис-ориентированная архитектура).

Компонентно-ориентированная архитектура


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

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

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

Самая главная проблема такого монолита заключается в том, что разделение на модули является чисто логическим и может быть легко нарушено разработчиками. Может появиться модуль core, который постепенно превращается в помойку, может расти граф зависимостей между модулями и так далее. Для избежания таких проблем разработка должна вестить либо очень зрелой командой, либо под руководством архитектора, который на full time занимается code review и бьет нарушающих логическую структуру разработчиков по рукам.

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

Сервис-ориентированная архитектура


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

Сервис-ориентированная архитектура (SOA = service oriented architecture) решает все обозначенные проблемы монолита: при изменении затрагивается только одна служба, а четко определенный API поддерживает хорошую инкапсуляцию компонент.

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

Кстати говоря, возможность независимого развертывания это очень важная особенность сервиса. Если сервисы должны развертываться совместно или, тем более, в определенной последовательности, то система не может считаться сервис-ориентированной. В таком случае говорят о распределенном монолите (считается антипаттерном не только с точки зрения SOA, но и микросервисной архитектуры).

Сервис-ориентированная архитектура неплохо поддерживается архитектурным коммьюнити и вендорами. Отсюда следует наличие множества курсов и сертификаций, хорошо проработанных паттернов. К последним относится, например, не безызвестная сервисная шина предприятия (ESB = enterprise service bus). При этом ESB это багаж от вендоров, она не обязательно должна использоваться в SOA.

Пик популярности сервис-ориентированной архитектуры приходился примерно на 2008 год, после чего она пошла на спад, который стал существенно более резким после появления микросервисов (~2015 год).

Заключение


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

Подробнее..

Выбор архитектурного стиля (часть 3)

28.09.2020 02:15:18 | Автор: admin
Привет, Хабр. Сегодня я продолжаю серию публикаций, которую написал специально к старту нового потока курса Software Architect.



Введение


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

В прошлый раз мы поговорили о различных видах монолитов и об использовании компонентов для их построения, причем как компонентов сборки, так и компонентов развертывания. Мы разобрались с сервис-ориентированной архитектурой.

Сейчас мы наконец определим основные характеристики микросервисной архитектуры.

Отношение архитектур


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

Характеристики микросервисной архитектуры


Основными характеристиками микросервисной архитектуры являются:

Организация в соответствии с бизнес-возможностями
(Organized around Business Capabilities)
Продукты, а не проекты (Products not Projects)
Умные точки входа и глупые каналы (Smart endpoints and
dumb pipes)
Децентрализованное управление (Decentralized Governance)
Децентрализованное управление данными (Decentralized
Data Management)
Автоматизация инфраструктуры (Infrastructure Automation)
Страховка от сбоев (Design for failure)
Архитектура с эволюционным развитием (Evolutionary
Design)

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

Организация в соответствии с бизнес-возможностями (Organized around Business Capabilities)


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

Если мы говорим про монолиты и микросервисы, то в том случае, если разработка организована по функциональным отделам (бэкенд, фронтенд, администраторы баз данных), то получается классический монолит.

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

Продукты, а не проекты (Products not Projects)


Проектный подход при котором команда передает разработанную функциональность другим командам в случае микросервисной архитектуры совершенно не подходит. Команда должна поддерживать систему на протяжении всего ее жизненного цикла. Компания Amazon, один из флагманов внедрения микросервисов, заявляла: вы создаете продукт, и вы же запускаете его (you build, you run it). Продуктовый подход позволяет команде почувствовать потребности бизнеса.

Умные точки входа и глупые каналы (Smart endpoints and dumb pipes)


SOA архитектура большое внимание уделяла каналам связи, в частности Enterprise Service Bus (сервисная шина предприятия). Что зачастую приводит к Erroneous Spaghetti Box, то есть сложность монолита переходит в сложность связей между сервисами. В микросевисной архитектуре используются только простые способы взаимодействия.

Децентрализованное управление (Decentralized Governance)


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

Децентрализованное управление данными (Decentralized Data Management)


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

Автоматизация инфраструктуры (Infrastructure Automation)


MSA поддерживает процессы непрерывного развертывания и поставки. Осуществить это возможно только путем автоматизации процессов. При этом, развертывание большого количества сервисов уже не выглядит чем-то страшным. Процесс развертывания должен стать скучным. Второй аспект связан с управлением сервисами в продуктовой среде. Без автоматизации, управление процессами, запущенными в разных операционных средах, становится невозможным.

Страховка от сбоев (Design for failure)


Многочисленные сервисы MSA подвержены сбоям. При этом, обработка ошибок в распределенной системе весьма не тривиальная задача. Архитектура приложений должна быть устойчива к таким сбоям. Ребекка Парсонс считает очень важным, что мы больше не используем даже внутрипроцессное взаимодействие между сервисами, вместо этого для связи мы прибегаем к HTTP, который и близко не бывает столь же надёжен.

Архитектура с эволюционным развитием (Evolutionary Design)


Архитектура MSA системы должна развиваться эволюционно. Желательно ограничить необходимые изменения границами одного сервиса. Необходимо так же учитывать влияние на другие сервисы. Традиционный подход состоит в том, чтобы попытаться решить эту проблему с помощью управления версиями, но MSA предполагает использовать управление версиями в
качестве крайней меры.

Заключение


После всего вышесказанного можно сформулировать что такое микросервисы. Микросервисная архитектура это подход к разработке отдельного приложения в виде набора небольших сервисов, каждый из которых работает в своем собственном процессе и взаимодействует посредством облегченных механизмов, часто API-интерфейсом HTTP-ресурсов. Эти сервисы построены на бизнес-возможностях и могут быть развернуты независимо с помощью полностью
автоматизированного механизма развертывания. Существует минимальный уровень централизованного управления этими сервисами, которые могут быть написаны на разных языках программирования и использовать разные технологии хранения данных.



Читать часть 2
Подробнее..

Выбор архитектурного стиля. Часть 4

12.10.2020 16:11:28 | Автор: admin
В конце октября запускаем новую группу курса Архитектура и шаблоны проектирования и приглашаем всех специалистов на бесплатный Demo-урок Шаблон адаптер, который проведёт Матвей Калинин главный разработчик в одном из крупнейших банков страны.




Введение


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

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

Напомню: микросервисная архитектура это подход к разработке отдельного приложения в виде набора небольших сервисов, каждый из которых работает в своем собственном процессе и взаимодействует посредством облегченных механизмов, часто API-интерфейсом HTTP-ресурсов. Эти сервисы построены на бизнес-возможностях и могут быть развернуты независимо с помощью полностью автоматизированного механизма развертывания. Существует минимальный уровень централизованного управления этими сервисами, которые могут быть написаны на разных языках программирования и использовать разные технологии хранения данных.

Проблема выбора


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

Независимое развертывание


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

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

Улучшение отказоустойчивости


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

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

Гибкость и ясность


Жесткие физические границы сервиса обеспечивают низкую связанность, хорошо управляемые части, их независимую разработку. Разработчики хорошо понимают свою часть системы.

Но, независимость разработчиков приводит к эффекту колодца, когда никто не видит картины целиком.

Варьирование технологического стека


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

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

Техническая сложность


Исходя из всего вышесказанного можно сделать вывод, что разработка микросервисов является достаточно сложной технической задачей, требующей особых навыков разработчиков, технических специалистов (DevOps) и тестировщиков. А так же, что самое главное, определенной организационной перестройки и смены парадигмы мышления у отдельных индивидуумов.

Распространенные заблуждения


С микросервисной архитектурой связан ряд заблуждений. Вот некоторые из них:
  1. Код будет чище. Код не будет чище, если не будут предприняты усилия, чтобы код стал чище.
  2. Писать модули, решающие одну задачу легче.
    Не легче, поскольку у таких модулей будет очень много интеграций.
  3. Это работает быстрее, чем монолит.
    Монолит работает быстрее из-за меньшего количества сетевых вызовов.
  4. Инженерам проще, если не нужно работать с единой кодовой
    базой.
  5. Это самый простой способ обеспечить автоматическое
    масштабирование.
  6. И тут где-то замешан Докер.


Заключение


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

image
Подробнее..

Cortex и не только распределённый Prometheus

04.03.2021 10:11:49 | Автор: admin

В последнее время Prometheus стал де-факто стандартом для сбора и хранения метрик. Он удобен для разработчиков ПО - экспорт метрик можно реализовать в несколько строк кода. Для DevOps/SRE, в свою очередь, есть простой язык PromQL для получения метрик из хранилища и их визуализации в той же Grafana.

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

Недостатки

  • Отсутствие отказоустойчивости

    Prometheus работает только в единственном экземпляре, никакого HA.

  • Отсутствие распределения нагрузки

    В принципе, он хорошо скейлится вверх с ростом количества ядер. Так что это проблема только для тех у кого действительно много метрик.

  • Нет поддержки multi-tenancy

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

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

В принципе, все эти проблемы можно решить настроив несколько HA-пар Prometheus и распределив по ним свои приложения. Если перед каждой парой повесить прокси, то можно получить что-то вроде отказоустойчивости.

Но есть и минусы:

  • После того как один хост из пары упадёт/перезагрузится/whatever - у них случится рассинхронизация. В метриках будут пропуски.

  • Все метрики приложения должны умещаться на один хост

  • Управлять таким зоопарком будет сложнее - какие-то из Prometheus могут быть недогружены, какие-то перегружены. В случае запуска в каком-нибудь Kubernetes это не так важно.

Давайте рассмотрим какими ещё способами можно решить это.

PromQL прокси

Например promxy, который размещается перед 2 или более инстансами Prometheus и делает fan-out входящих запросов на все из них. Затем дедуплицирует полученные метрики и, таким образом, закрывает пропуски в метриках (если, конечно, они не попали на один и тот же временной интервал).

Минусы подобного решения на поверхности:

  • Один запрос нагружает сразу все инстансы за прокси

  • Прокси решает только проблему с пропусками в метриках.

Но для тех, у кого нагрузка укладывается в возможности одного Prometheus (либо ее можно грамотно раскидать по нескольким HA-парам) и кому не нужен multi-tenancy - это очень хороший вариант.

Thanos

Thanos - это уже более продвинутое решение.

Он устанавливает рядом с каждым инстансом Prometheus так называемый Sidecar - отдельный демон, который подглядывает за блоками данных, которые генерирует Prometheus. Как только блок закрывается - Sidecar загружает его в объектное хранилище (S3/GCS/Azure). Длина блоков в Prometheus прибита гвоздями и равна 2 часам.

Также он является прокси между GRPC Thanos StoreAPI и Prometheus для получения метрик, которые еще не были загружены в объектное хранилище.

Отдельный компонент Querier реализует PromQL: в зависимости от временного интервала запроса и настроек глубины хранения данных в Prometheus он может направить его в объектное хранилище, в Sidecar или в разбить на два подзапроса - для свежих данных запрос пойдёт через Sidecar в Prometheus, а для более старых - в объектное хранилище.

Отказоустойчивость свежих данных в Thanos реализуется примерно так же как и в promxy - делается fan-out запросов на все причастные сервера, результаты накладываются друг на друга и дедуплицируются. Задача по защите исторических данных лежит на объектном хранилище.

Multi-tenancy есть в некотором зачаточном состоянии, но в эту сторону проект, судя по всему, не развивается особо.

Cortex

Это наиболее сложный и функциональный проект. Его начали разрабатывать в Grafana Labs для своего SaaS решения по хранению метрик и несколько лет назад выложили в open source, с тех пор разработка идёт на гитхабе.

Как можно видеть на диаграмме выше - в нём очень много компонентов. Но бояться не стоит - большую часть из них можно не использовать, либо запускать в рамках одного процесса - single binary mode.

Так как Cortex изначально разрабатывался как SaaS решение - в нём поддерживается end-to-end multi-tenancy.

Хранение метрик

На данный момент в Cortex есть два движка. Оба они хранят данные в объектном хранилище, среди которых поддерживаются:

  • S3

  • GCS

  • Azure

  • OpenStack Swift (экспериментально)

  • Любая примонтированная ФС (например - NFS или GlusterFS). Хранить блоки на локальной ФС смысла нет т.к. они должны быть доступны всему кластеру.

Далее я буду для краткости называть объектное хранилище просто S3.

Chunks Storage

Изначальный движок в Cortex - он хранит каждую timeseries в отдельном чанке в S3 или в NoSQL (Cassandra/Amazon DynamoDB/Google BigTable), а метаданные (индексы) хранятся в NoSQL.

Chunks Storage, думается, со временем совсем выпилят - насколько я слышал, Grafana Labs свои метрики уже мигрировали в Blocks Storage.

Blocks Storage

Новый, более простой и быстрый движок, основанный на Thanos. Который, в свою очередь, использует формат блоков самого Prometheus. С ним нет нужды в NoSQL и модуле Table Manager (но нужен другой - Store Gateway).

Thanos, в данном, случае является внешней vendored зависимостью в коде Cortex. Есть разговоры о том, чтобы объединить два проекта в один, но когда это будет неизвестно (и будет ли вообще).

Архитектура

Далее я буду рассматривать работу с Blocks Storage.

Упрощённо принцип работы следующий:

  • Prometheus собирает метрики с endpoint-ов и периодически отправляет их в Cortex используя Remote Write протокол. По сути это HTTP POST с телом в виде сериализованных в Protocol Buffers метрик сжатый потом Snappy. В самом Prometheus, при этом, можно поставить минимальный retention period - например 1 день или меньше- читаться из него ничего не будет.

  • Модуль Distributor внутри Cortex принимает, валидирует, проверяет per-tenant и глобальные лимиты и опционально шардит пришедшие метрики. Далее он передает их одному или нескольким Ingester (в зависимости от того применяется ли шардинг).

    Также в рамках этого модуля работает HA Tracker (о нём ниже).

  • Ingester ответственен за запись метрик в долговременное хранилище и выдачу их для выполнения запросов. Изначально метрики записываются в локальную ФС в виде блоков длиной 2 часа. Затем, по истечении некоторого времени, они загружаются в S3 и удаляются с локальной ФС.

    Также поддерживается репликация и zone awareness для дублирования блоков по различным availability domain (стойки, ДЦ, AWS AZ и так далее)

  • Store-Gateway служит для отдачи блоков из S3.

    Он периодически сканирует бакет, находит там новые блоки, синхронизирует их заголовки в локальную ФС (чтобы после перезапуска не скачивать опять) и индексирует.

  • Querier реализует PromQL.

    При получении запроса анализирует его и, если необходимо, разбивает на два - одна часть пойдёт в Store Gateway (для более старых данных), а другая - в Ingester для свежих.

    По факту параллельных запросов может быть больше если запрашиваемый период большой и настроено разбиение по интервалам (об этом дальше в конфиге)

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

    Старые блоки не удаляются сразу, а маркируются и удаляются на следующих итерациях чтобы дать время Store-Gateway обнаружить новые, которые уже сжаты.

Отказоустойчивость

Помимо репликации данных между Ingester-ами нам необходимо обеспечить отказоустойчивость самих Prometheus. В Cortex это реализовано просто и элегантно:

  • Два (или более) Prometheus настраиваются на сбор метрик с одних и тех же endpoint-ов

  • В каждом из них настраиваются специальные внешние метки, которые показывают к какой HA-группе принадлежит данный Prometheus и какой у него идентификатор внутри группы.

    Например так:

  external_labels:    __ha_group__: group_1    __ha_replica__: replica_2
  • При приёме метрик Cortex из каждой группы выбирает один Prometheus и сохраняет метрики только от него

  • Остальным отвечает HTTP 202 Accepted и отправляет в /dev/null всё что они прислали

  • Если же активный инстанс перестал присылать метрики (сработал таймаут) - Cortex переключается на приём от кого-то из оставшихся в живых.

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

Авторизация

Каждый запрос на запись метрик из Prometheus должен содержать HTTP-заголовок X-Scope-OrgId равный идентификатору клиента (далее я буду называть их просто tenant, хорошего перевода не придумал). Метрики каждого tenant-а полностью изолированны друг от друга - они хранятся в разных директориях в S3 бакете и на локальной ФС

Таким же образом происходит и чтение метрик - в PromQL запросах тоже нужно тоже указывать этот заголовок.

При этом никакой реальной авторизации Cortex не проводит - он слепо доверяет этому заголовку. Auth Gateway есть в роадмапе, но когда он будет готов неизвестно. Даже просто добавить этот заголовок напрямую в Prometheus нельзя, только используя промежуточный HTTP прокси.

Для более гибкой интеграции Prometheus & Cortex я набросал простенький Remote Write прокси - cortex-tenant, который может вытаскивать ID клиента из меток Prometheus. Это позволяет использовать один инстанс (или HA-группу) Prometheus для отправки метрик нескольким разным клиентам. Мы используем этот функционал для разграничения данных разных команд, приложений и сред.

Авторизацию можно отключить, тогда Cortex не будет проверять наличие заголовка в запросах и будет подразумевать что он всегда равен fake - то есть multi-tenancy будет отключен, все метрики будут падать в один котёл.

При необходимости данные одного клиента можно полностью удалить из кластера - пока это API экспериментально, но работает.

Настройка Cortex

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

Для всех остальных гораздо проще установить несколько HA-пар Prometheus (например на каждую команду или каждый проект) и поверх них натянуть promxy

Так как документация имеет некоторое количество белых пятен - я хочу рассмотреть настройку простого кластера Cortex в режиме single binary - все модули у нас будут работать в рамках одного и того же процесса.

Danger Zone! Дальше много конфигов!

Зависимости

Нам понадобится ряд внешних сервисов для работы.

  • etcd для согласования кластера и хранения Hash Ring

    Cortex также поддерживает Consul и Gossip-протокол, которому не нужно внешнее KV-хранилище. Но для HA-трекера Gossip не поддерживается из-за больших задержек при сходимости. Так что будем юзать etcd

  • memcached для кеширования всего и вся.

    Cortex поддерживает его в нескольких своих модулях. Можно поднять несколько инстансов и указать их все в Cortex - он будет шардить по всем равномерно. В принципе, он не обязателен, но крайне рекомендован с точки зрения производительности.

    Также есть DNS-based discovery через SRV-записи, если не хочется указывать вручную.

  • minio для реализации распределённого S3 хранилища.

    Несколько странный проект, который часто в процессе разработки ломает обратную совместимость и требует полного перезапуска кластера для апгрейда. Также SemVer это для слабаков, поэтому у них версионирование по датам. Его разрабатывают, в основном, индусы - возможно в этом причина...

    Но других вариантов особо нет, можно поднять Ceph с S3 шлюзом, но это еще более громоздко.

    minio поддерживает Erasure Coding для отказоустойчивости, что есть хорошо.

  • HAProxy для связывания компонентов воедино

  • cortex-tenant для распределения метрик по tenant-ам

  • Prometheus собственно для сбора метрик

Общие вводные

  • Кластер мы будем строить плоский из 4 хостов - все они будут идентичны, с одинаковым набором сервисов. Это хорошо для небольших инсталляций, упрощает структуру.

    3 страйпа не поддерживает minio c Erasure Coding - он нарезает от 4 до 16 дисков в один EC-набор. В реальном проекте лучше использовать 5 или какое-либо большее нечетное число чтобы не было Split Brain.

    Также, если у вас много хостов, то некоторые компоненты, такие как etcd, лучше вынести отдельно. etcd основан на Raft и реплицирует данные на все ноды кластера, смысла в их большом количестве нет - это только увеличит среднюю нагрузку лишними репликами.

  • Все данные будем хранить в /data

  • Конфиги я буду приводить для одного хоста, для остальных обычно достаточно поменять адреса и\или хостнеймы

  • В качестве ОС используем RHEL7, но различия с другими дистрибутивами минимальны

  • У нас всё это дело, конечно, раскатывается через Ansible, но плейбук довольно сильно завязан на нашу инфраструктуру. Я постараюсь потом его подчистить и выложить

  • Некоторые RPM пакеты я собираю вручную (etcd, HAProxy и т.п.) с помощью FPM т.к. в репозиториях древние версии.

/etc/hosts
10.0.0.1 ctx110.0.0.2 ctx210.0.0.3 ctx310.0.0.4 ctx4

etcd

Как и Zookeeper, с настройками по умолчанию etcd - бомба замедленного действия. Он не удаляет ненужные снапшоты и разрастается до бесконечности. Зачем так сделано - мне не понятно.

Поэтому настроим его соответственно:

/etc/etcd/etcd.conf
ETCD_NAME="ctx1"ETCD_LOGGER="zap"ETCD_LOG_LEVEL="warn"ETCD_DATA_DIR="/data/etcd/ctx1.etcd"ETCD_LISTEN_CLIENT_URLS="http://personeltest.ru/away/10.0.0.1:2379,http://127.0.0.1:2379"ETCD_LISTEN_PEER_URLS="http://personeltest.ru/away/10.0.0.1:2380"ETCD_ADVERTISE_CLIENT_URLS="http://personeltest.ru/away/10.0.0.1:2379"ETCD_INITIAL_CLUSTER_TOKEN="cortex"ETCD_INITIAL_ADVERTISE_PEER_URLS="http://personeltest.ru/away/10.0.0.1:2380"ETCD_AUTO_COMPACTION_RETENTION="30m"ETCD_AUTO_COMPACTION_MODE="periodic"ETCD_SNAPSHOT_COUNT="10000"ETCD_MAX_SNAPSHOTS="5"ETCD_INITIAL_CLUSTER="ctx1=http://ctx1:2380,ctx2=http://ctx2:2380,ctx3=http://ctx3:2380,ctx4=http://ctx4:2380"

memcached

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

/etc/sysconfig/memcached
PORT="11211"USER="memcached"MAXCONN="512"CACHESIZE="2048"OPTIONS="--lock-memory --threads=8 --max-item-size=64m"

Minio

Тут минимум настроек.

По сути мы просто перечисляем хосты, которые будут использоваться для хранения данных (+ путь до директории где данные хранить - /data/minio) и указываем ключи S3. В моем случае это были ВМ с одним диском, если у вас их несколько - то формат URL несколько меняется.

По умолчанию используется странное распределение дисков под данные и под коды Рида-Соломона: половина сырого объема уходит под redundancy. Так как у нас всего 4 хоста - это не особо важно. Но на большем по размеру кластере лучше использовать Storage Classes для снижения доли Parity-дисков.

/etc/minio/minio.env
MINIO_ACCESS_KEY="foo"MINIO_SECRET_KEY="bar"MINIO_PROMETHEUS_AUTH_TYPE="public"LISTEN="0.0.0.0:9000"ARGS="http://personeltest.ru/away/ctx{1...4}/data/minio"

Также нужно будет создать бакет с помощью minio-client - в нашем случае пусть называется cortex

HAProxy

Он у нас будет служить для равномерного распределения нагрузки по кластеру и отказоустойчивости. Все сервисы обращаются к локальному HAProxy, который в свою очередь проксирует запросы куда нужно.

Таким образом мы имеем что-то вроде Full Mesh топологии и отказ или перезапуск любого из сервисов или хостов целиком не влияет на функциональность кластера.

На больших кластерах (сотни-тысячи хостов) такая схема может быть узким местом, но если вы работаете с такими, то и сами это знаете :)

/etc/haproxy/haproxy.cfg
global    daemon    maxconn 10000    log 127.0.0.1 local2    chroot /var/emptydefaults    mode http    http-reuse safe    hash-type map-based sdbm avalanche    balance roundrobin    retries 3    retry-on all-retryable-errors    timeout connect 2s    timeout client 300s    timeout server 300s    timeout http-request 300s    option splice-auto    option dontlog-normal    option dontlognull    option forwardfor    option http-ignore-probes    option http-keep-alive    option redispatch 1    option srvtcpka    option tcp-smart-accept    option tcp-smart-connect    option allbackupslisten stats    bind 0.0.0.0:6666    http-request use-service prometheus-exporter if { path /metrics }    stats enable    stats refresh 30s    stats show-node    stats uri /frontend fe_cortex    bind 0.0.0.0:8090 tfo    default_backend be_cortexfrontend fe_cortex_tenant    bind 0.0.0.0:8009 tfo    default_backend be_cortex_tenantfrontend fe_minio    bind 0.0.0.0:9001 tfo    default_backend be_miniobackend be_cortex    option httpchk GET /ready    http-check expect rstring ^ready    server ctx1 10.0.0.1:9009 check observe layer7 inter 5s    server ctx2 10.0.0.2:9009 check observe layer7 inter 5s    server ctx3 10.0.0.3:9009 check observe layer7 inter 5s    server ctx4 10.0.0.4:9009 check observe layer7 inter 5sbackend be_cortex_tenant    option httpchk GET /alive    http-check expect status 200    server ctx1 10.0.0.1:8008 check observe layer7 inter 5s    server ctx2 10.0.0.2:8008 check observe layer7 inter 5s backup    server ctx3 10.0.0.3:8008 check observe layer7 inter 5s backup    server ctx4 10.0.0.4:8008 check observe layer7 inter 5s backupbackend be_minio    balance leastconn    option httpchk GET /minio/health/live    http-check expect status 200    server ctx1 10.0.0.1:9000 check observe layer7 inter 5s    server ctx2 10.0.0.2:9000 check observe layer7 inter 5s backup    server ctx3 10.0.0.3:9000 check observe layer7 inter 5s backup    server ctx4 10.0.0.4:9000 check observe layer7 inter 5s backup

cortex-tenant

Это просто прокси между Prometheus и Cortex. Главное - выбрать уникальное имя метки для хранения там tenant ID. В нашем случае это ctx_tenant

/etc/cortex-tenant.yml
listen: 0.0.0.0:8008target: http://127.0.0.1:8090/api/v1/pushlog_level: warntimeout: 10stimeout_shutdown: 10stenant:  label: ctx_tenant  label_remove: true  header: X-Scope-OrgID

Prometheus

В случае 4 хостов Prometheus-ы можно разбить их на две HA-пары, каждую со своим ID группы и раскидать job-ы по ним.

host1 /etc/prometheus/prometheus.yml
global:  scrape_interval: 60s  scrape_timeout: 5s  external_labels:    __ha_group__: group_1    __ha_replica__: replica_1remote_write:  - name: cortex_tenant    url: http://127.0.0.1:8080/pushscrape_configs:  - job_name: job1    scrape_interval: 60s    static_configs:      - targets:          - ctx1:9090        labels:          ctx_tenant: foobar  - job_name: job2    scrape_interval: 60s    static_configs:      - targets:          - ctx2:9090        labels:          ctx_tenant: deadbeef
host2 /etc/prometheus/prometheus.yml
global:  scrape_interval: 60s  scrape_timeout: 5s  external_labels:    __ha_group__: group_1    __ha_replica__: replica_2remote_write:  - name: cortex_tenant    url: http://127.0.0.1:8080/pushscrape_configs:  - job_name: job1    scrape_interval: 60s    static_configs:      - targets:          - ctx1:9090        labels:          ctx_tenant: foobar  - job_name: job2    scrape_interval: 60s    static_configs:      - targets:          - ctx2:9090        labels:          ctx_tenant: deadbeef

По сути конфигурации в пределах одной HA-группы должны отличаться только лейблом реплики.

Cortex

Ну и последнее. Так как мы будем запускать все модули вместе - конфиг получится довольно объемный. Поэтому разделим его на части, чтобы читабельнее было.

Многие модули, такие как Distributor, Ingester, Compactor, Ruler кластеризуются с помощью Hash-Ring в etcd. На весь кластер выделяется некоторое количество токенов, которые распределяются между всеми участниками кольца равномерно.

Упрощенно - при приходе, допустим, новой метрики её метки хешируются, результат делится по модулю на количество токенов и в итоге направляется на хост, который владеет данным диапазоном токенов. Если он не отвечает - то отправляют следующему по кольцу хосту.

Если хост выходит из кластера (помер, перезагружается и т.п.), то его диапазон перераспределяется по остальным.

Все настройки у нас будут лежать в /etc/cortex/cortex.yml

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

Глобальные настройки

# Список модулей для загрузкиtarget: all,compactor,ruler,alertmanager# Требовать ли заголовок X-Scope-OrgIdauth_enabled: true# Портыserver:  http_listen_port: 9009  grpc_listen_port: 9095limits:  # Разрешаем HA-трекинг  accept_ha_samples: true  # Названия меток, которые мы используем в Prometheus для  # маркировки групп и реплик  ha_cluster_label: __ha_group__  ha_replica_label: __ha_replica__  # Максимальный период в прошлое на который мы можем делать  # PromQL запросы (1 год).  # Всё что больше будет обрезано до этого периода.  # Это нужно для реализации retention period.  # Для фактического удаления старых блоков нужно еще настроить lifecycle  # правило в бакете S3 на пару дней глубже  max_query_lookback: 8760h

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

Другой трудностью изначально было то, что Cortex не поддерживал гибкий список модулей, которые нужно активировать. Была возможность либо указать all, который на самом деле ни разу не all:

# cortex -modulesalertmanagerallcompactorconfigsdistributor *flusheringester *purger *querier *query-frontend *query-schedulerruler *store-gateway *table-manager *Modules marked with * are included in target All.

Либо указать строго один модуль.

Поэтому пришлось сделать пулл-реквест чтобы добавить возможность загружать список любых модулей. В данном случае мы используем all + compactor, ruler и alertmanager

Хранилище

storage:  # Выбираем хранилище Blocks Storage  engine: blocks# Конфигурируем егоblocks_storage:  # Тип бэкенда  backend: s3    # Параметры доступа к S3  s3:    endpoint: 127.0.0.1:9001    bucket_name: cortex    access_key_id: foo    secret_access_key: bar    # TLS у нас нет    insecure: true    tsdb:    # Где хранить локальные блоки до загрузки в S3    dir: /data/cortex/tsdb    # Через какое время их удалять    retention_period: 12h    # Сжимать Write-Ahead Log    wal_compression_enabled: true  bucket_store:    # Где хранить индексы блоков, найденных в S3    # По сути это должно быть в модуле Store-Gateway,    # но по какой-то причине тут    sync_dir: /data/cortex/tsdb-sync    # Как часто сканировать S3 в поиске новых блоков    sync_interval: 1m    # Настраиваем различные кеши на наши memcached    # Каждый кеш имеет свой префикс ключей, так что пересекаться они не будут    index_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211    chunks_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211    metadata_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211

Distributor

distributor:  ha_tracker:    # Включить HA-трекер для Prometheus    enable_ha_tracker: true    # Таймаут после которого срабатывает failover на другую реплику Prometheus.    # Нужно настроить так чтобы метрики приходили не реже этого интервала,    # иначе будут ложные срабатывания.    ha_tracker_failover_timeout: 30s    # Настраиваем etcd для HA-трекера    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379  # Настраиваем etcd для Hash-Ring дистрибьютеров  ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Ingester

ingester:  lifecycler:    address: 10.0.0.1    # Название зоны доступности    availability_zone: dc1    # Немного ждём чтобы всё устаканилось перед перераспределением    # токенов на себя    join_after: 10s    # Храним токены чтобы не генерировать их каждый раз при запуске    tokens_file_path: /data/cortex/ingester_tokens    ring:      # На сколько Ingester-ов реплицировать метрики.      # Если указана зона доступности, то реплики будут выбираться из разных зон      replication_factor: 2      # etcd для Hash-Ring Ingester-ов      kvstore:        store: etcd        etcd:          endpoints:           - http://ctx1:2379           - http://ctx2:2379           - http://ctx3:2379           - http://ctx4:2379

Querier

По поводу подбора правильных величин лучше почитать документацию.

Основная идея в том, чтобы никогда не запрашивать те блоки, которые еще не обработал Compactor:

querier:  # Временные файлы  active_query_tracker_dir: /data/cortex/query-tracker  # Запросы с глубиной больше этой будут направляться в S3  query_store_after: 6h  # Запросы с глубиной меньше этой отправляются в Ingester-ы  query_ingesters_within: 6h5mfrontend_worker:  frontend_address: 127.0.0.1:9095query_range:  # Запросы будут разбиваться на куски такой длины и выполняться параллельно  split_queries_by_interval: 24h  # Выравнивать интервал запроса по его шагу  align_queries_with_step: true  # Включить кеширование результатов  cache_results: true    # Кешируем в memcached  results_cache:    # Сжимаем    compression: snappy    cache:      # TTL кеша      default_validity: 60s      memcached:        expiration: 60s      memcached_client:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211

Store-Gateway

Этот модуль подгружает из S3 бакета заголовки блоков (комбинации меток, временные интервалы и т.п.).

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

store_gateway:  # Включаем шардинг  sharding_enabled: true  sharding_ring:    # Включаем zone awareness    zone_awareness_enabled: true    # Идентификатор зоны    instance_availability_zone: dc1    # Сколько реплик держать    replication_factor: 2    # Hash-ring для Store-Gateway    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Compactor

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

compactor:  # Временная директория для блоков.  # Должно быть достаточно много места чтобы можно было загрузить блоки,  # скомпактить их и сохранить результат.  data_dir: /data/cortex/compactor  # Как часто запускать компакцию  compaction_interval: 30m  # Hash-Ring для компакторов  sharding_enabled: true  sharding_ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Ruler + AlertManager

Эти модули опциональны и нужны только если хочется генерировать алерты на основе правил.

  • Правила в стандартном Prometheus формате мы будем складывать в /data/cortex/rules/<tenant>/rulesN.yml на каждом хосте. Можно использовать для этого S3 или другие хранилища - см. документацию

  • Cortex периодически сканирует хранилище и перезагружает правила

  • Конфиги AlertManager в стандартном формате складываем в /data/cortex/alert-rules/<tenant>.yml

    Аналогично можно складывать в S3 и т.п.

  • Cortex запускает инстанс AlertManager (внутри своего процесса) отдельно для каждого tenant, если находит конфигурацию в хранилище

ruler:  # Временные файлы  rule_path: /data/cortex/rules-tmp  # Включаем шардинг  enable_sharding: true  # Какому AlertManager-у сообщать об алертах  alertmanager_url: http://ctx1:9009/alertmanager  # Откуда загружать правила  storage:    type: local    local:      directory: /data/cortex/rules    # Hash-ring для Ruler-ов  ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379alertmanager:  # Где хранить состояние алертов  data_dir: /data/cortex/alert-data  # Внешний URL нашего инстанса (нужен для генерации ссылок и т.п.)  external_url: http://ctx1:9009/alertmanager  # Кластеринг - какой адрес слушать и какой анонсировать пирам  cluster_bind_address: 0.0.0.0:9094  cluster_advertise_address: 10.0.0.1:9094  # Список пиров  peers:    - ctx2:9094    - ctx3:9094    - ctx4:9094  # Откуда загружать настройки  storage:    type: local    local:      path: /data/cortex/alert-rules

Заключение

Вот и всё, можно запускать все сервисы - сначала зависимости, потом Cortex, затем - Prometheus.

Я не претендую на полноту, но этого должно быть достаточно чтобы начать работать.

Нужно учитывать, что Cortex активно развивается и на момент написания статьи часть параметров в master-ветке и документации (которая генерируется из неё) уже объявлено deprecated. Так что, вполне возможно, в новых версиях нужно будет конфиги немного исправлять.

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

Подробнее..

Выбор архитектурного стиля (часть 1)

28.08.2020 14:05:29 | Автор: admin
Привет, хабр. Прямо сейчас в OTUS открыт набор на новый поток курса Software Architect. В преддверии старта курса хочу поделиться с вами своей авторской статьёй.



Введение


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

Немного истории


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

Если говорить вкратце, то микросервисы в нашем текущем понимании возникли следующим образом: в 2011 Джеймс Льюис, анализируя работы различных компаний, обратил внимание на появление нового паттерна micro-app, который оптимизировал SOA с точки зрения ускорения развертывания сервисов. Несколько позже, в 2012 году, на архитектурном саммите паттерн был переименован в микросервис. Таким образом, первоначальной целью внедрения микросервисов было улучшение пресловутого time to market.

На волне хайпа микросервисы были в 2015 году. По некоторым исследованиям, ни одна конференция не обходилась без доклада на тему микросервисов. Более того, некоторые конференции были посвящены исключительно микросервисам. Сейчас же очень многие проекты начинаются с применением данного архитектурного стиля, а если проект содержит тонны legacy-кода, то наверняка активно осуществляется миграция на микросервисы.

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

Монолит


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

Размер


Монолит очень большой. И общается он как правило с очень большой базой данных. Приложение становится слишком большим, чтобы его мог понять один разработчик в принципе. С монолитом хорошо работать могут только те, кто провел за этим кодом достаточно много времени, тогда как новички потратят очень много времени на попытки разобраться с монолитом и не факт, что разберутся. Обычно при работе с монолитом всегда есть некоторый условный senior, который знает монолит более-менее хорошо и бьет по рукам в течении года-полутора других новых разработчиков. Естественно, что такой условный senior является единой точкой отказа, и его уход может привести к гибели монолита.

Связанность


Монолит представляет из себя большой комок грязи (big ball of mud), изменения в котором могут привести к непредсказуемым последствиям. Внося изменения в одном месте, можно повредить монолит в другом (то самое ухо почесал, *@ отвалилась). Связано это с тем, что компоненты в монолите имеют очень сложные и, главное, неочевидные взаимосвязи.

Развертывание


Развертывание монолита ввиду сложных взаимосвязей между его компонентами это долгий процесс со своим ритуалом. Такой ритуал обычно не окончательно стандартизирован и передается из уст в уста.

Масштабируемость


Модули монолита могут иметь конфликтующие потребности в ресурсах, из-за чего необходимо искать компромисс с точки зрения железа. Представьте себе, что у вас монолит состоит из сервисов A и B. Сервис A требователен к размеру жесткого диска, а сервис B к оперативной памяти. В таком случае, либо машина, на которую ставится монолит, должна поддерживать требования обоих сервисов, либо придется руками, искуственно один из сервисов отключать.

Еще один пример (более классический): сервис A намного более популярен, чем сервис B, поэтому вы хотите, чтобы сервисов A было 100, а сервисов B было 10. Опять-таки два варианта: либо разворачиваем 100 полноценных монолитов, либо на каких-то из них руками сервисы B придется отключать.

Надежность


Поскольку все сервисы находятся вместе, если монолит падает, то падают все сервисы сразу. На самом деле, возможно это не так уж и плохо, по крайней мере частичных отказов в распределенной системе не будет, но с другой стороны, из-за ошибки в функциональности, которую использует 0.001% пользователей, вы можете потерять всех пользователей вашей системы.

Косность


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

Заключение


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



Читать ещё:


Подробнее..

Настройка отказоустойчивого кластера Kubernetes на серверах с публичной и приватной сетью с помощью Kubeadm

01.02.2021 14:09:27 | Автор: admin

Эта статья написана потому, что я бы хотел иметь такую статью перед глазами, когда развертывал кластер по документации. Сразу хочу сказать, что не являюсь экспертом в K8S, однако имел опыт с развертыванием продуктовых установок DC/OS (экосистемы, основанной на Apache Mesos). Долгое время K8S меня отпугивал тем, что, при попытке его изучения, тебя закидывают кучей концепций и терминов, отчего мозг взрывается.

Тем не менее, у меня возникла задача настроить отказоустойчивый Bare Metal кластер для комплексного приложения, в связи с чем и возникла данная статья. В процессе руководства я затрону следующие аспекты:

  • корректная установка с помощью kubeadm на узлах с несколькими NIC;

  • реализация отказоустойчивого Control Plane с доступом по общему IP и DNS-имени;

  • реализация Ingress контроллера на базе Nginx на выделенных узлах с доступом из публичной сети;

  • проброс K8S API в публичную сеть;

  • проброс K8S Dashboard UI в публичную сеть.

Я выполнял установку в среде Ubuntu 18.04, в связи с чем часть из шагов может не работать в вашем дистрибутиве. В тексте могут встречаться фразы "как я понял..." и "я не до сих пор не вполне понял...".

Сначала рассмотрим узловую топологию кластера, в котором мы будем развертывать K8S. Это упрощенная принципиальная топология, чтобы не загромождать публикацию лишними деталями.

Отличительной особенностью в моем кластере является то, что у всех узлов имеется два сетевых интерфейся - в моем случае, на eth0 всегда находится публичный адрес, а на eth1 - адрес из сети 10.120.0.0/16.

Стоит отметить, что K8S исключительно проще настраивать в случае, если ваши машины имеют по одному NIC. Если ваша инфраструктура позволяет вам использовать машины с одним сетевым устройством и иметь внешний балансировщик нагрузки - однозначно так необходимо делать.

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

Еще, kubeadm при развертывании считает, что что правильный IP-адрес - это тот, который находится в сети, где шлюз "по-умолчанию", если не переопределить, ничего хорошего не выйдет.

Тем не менее, в случае использования Enterprise решений, машины с несколькими сетевыми картами и доступом к ним из разных сетей - достаточно рядовая история, поэтому данная модель развертывания имеет право на существование.

Хочу отметить, что я использую Ansible, но в качестве упрощения не буду в статье демонстрировать playbook-и, ориентируясь на настройку руками. Итак, приступим.

Замена DNS-рекурсора

Я хочу обеспечить доступность всех узлов кластера через DNS-имена по внутренним IP-адресам, при этом сохранив доступность разрешения обычных имен узлов в интернете. Для этого, на серверах gw-1, gw-2 я разверну pdns-recursor и укажу его в качестве рекурсора на всех узлах кластера.

Базовая настройка pdns-recursor на gw-1, gw-2 включает указание следующих директив:

allow-from=10.120.0.0/8, 127.0.0.0/8etc-hosts-file=/etc/hosts.resolvexport-etc-hosts=onexport-etc-hosts-search-suffix=cluster

Сам файл /etc/hosts.resolv генерируется с помощью ansible и выглядит следующим образом:

# Ansible managed10.120.29.231  gw-1 gw-110.120.28.23  gw-2 gw-210.120.29.32  video-accessors-1 video-accessors-110.120.29.226  video-accessors-2 video-accessors-210.120.29.153  mongo-1 mongo-110.120.29.210  mongo-2 mongo-210.120.29.220  mongo-3 mongo-310.120.28.172  compute-1 compute-110.120.28.26  compute-2 compute-210.120.29.70  compute-3 compute-310.120.28.127  zk-1 zk-110.120.29.110  zk-2 zk-210.120.29.245  zk-3 zk-310.120.28.21  minio-1 minio-110.120.28.25  minio-2 minio-210.120.28.158  minio-3 minio-310.120.28.122  minio-4 minio-410.120.29.187  k8s-1 k8s-110.120.28.37  k8s-2 k8s-210.120.29.204  k8s-3 k8s-310.120.29.135  kafka-1 kafka-110.120.29.144  kafka-2 kafka-210.120.28.130  kafka-3 kafka-310.120.29.194  clickhouse-1 clickhouse-110.120.28.66  clickhouse-2 clickhouse-210.120.28.61  clickhouse-3 clickhouse-310.120.29.244  app-1 app-110.120.29.228  app-2 app-210.120.29.33  prometeus prometeus10.120.29.222  manager manager10.120.29.187 k8s-cp
Шаблон Ansible для генерации конфига
# {{ ansible_managed }}{% for item in groups['all'] %}{% set short_name = item.split('.') %}{{ hostvars[item]['host'] }}  {{ item }} {{ short_name[0] }}{% endfor %}10.120.0.1 k8s-cp

Теперь, необходимо сделать так, чтобы все узлы вместо DNS-рекурсоров, получаемых из настроек DHCP, использовали данные DNS-ы. В Ubuntu 18.04 используется systemd-resolved, поэтому необходимо ему указать требуемые серверы gw-1, gw-2. Для этого определим манифест, переопределяющий поведение systemd-resolved с помощью файла /etc/systemd/network/0-eth0.network на каждом хосте кластера:

[Match]Name=eth0[Network]DHCP=ipv4DNS=10.120.28.23 10.120.29.231Domains=cluster[DHCP]UseDNS=falseUseDomains=false

Делает он следующее, для DHCP-записи, полученной через eth0 будут игнорироваться DNS-серверы и поисковые домены. Вместо этого будут использоваться серверы 10.120.28.23, 10.120.29.231 и использоваться поисковый домен *.cluster. После создания данного файла требуется перезагрузить узел или сеть узла, поскольку простой перезапуск systemd-resolved не инициирует повторное получение данных по DHCP. Я перезагружаю для того, чтобы убедиться в корректном поведении при старте узла.

При успешной инициализации systemd-resolve --status выдаст следующий листинг:

Global          DNSSEC NTA: 10.in-addr.arpa                      16.172.in-addr.arpa                      168.192.in-addr.arpa                      17.172.in-addr.arpa                      18.172.in-addr.arpa                      19.172.in-addr.arpa                      20.172.in-addr.arpa                      21.172.in-addr.arpa                      22.172.in-addr.arpa                      23.172.in-addr.arpa                      24.172.in-addr.arpa                      25.172.in-addr.arpa                      26.172.in-addr.arpa                      27.172.in-addr.arpa                      28.172.in-addr.arpa                      29.172.in-addr.arpa                      30.172.in-addr.arpa                      31.172.in-addr.arpa                      corp                      d.f.ip6.arpa                      home                      internal                      intranet                      lan                      local                      private                      testLink 3 (eth1)      Current Scopes: none       LLMNR setting: yesMulticastDNS setting: no      DNSSEC setting: no    DNSSEC supported: noLink 2 (eth0)      Current Scopes: DNS       LLMNR setting: yesMulticastDNS setting: no      DNSSEC setting: no    DNSSEC supported: no         DNS Servers: 10.120.28.23                      10.120.29.231          DNS Domain: cluster

Это действие необходимо выполнить на всех узлах кластера. При корректном выполнении каждый узел сможет выполнить ping gw-1.cluster, ping gw-2.cluster и получить ответ от данных узлов по внутренним ip-адресам.

Отключение раздела подкачки

Выполняется на всех узлах. Kubernetes не хочет работать при наличии разделов подкачки на узлах. Для их отключения вы можете воспользоваться следующим скриптом:

sudo -- sh -c "swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab"

Для пущей уверенности удалите swap-раздел с помощью fdisk.

Внесение изменений в сетевые настройки ядра

Выполняется на всех узлах. Я буду использовать Flannel - простейший оверлейный сетевой провайдер для K8S. Обратите внимание, что довольно много провайдеров используют VXLAN. Это накладывает определенные особенности для сетевой инфраструктуры.

В простейшем случае, вам требуется обеспечить работоспособность multicast, поскольку VXLAN использует multicast-группы, для своей работы. Если multicast - не ваш вариант, но вы хотите использовать провайдер, основанный на VXLAN, можно настроить работу VXLAN через BGP или другими способами. Однако, сможет ли жить с этим выбранный вами провайдер сетевой инфраструктуры Kubernetes - это большой вопрос. В общем, Flannel поддерживает VXLAN через multicast. В моем случае это VXLAN+multicast over VXLAN+multicast over Ethernet, поскольку в моей сети виртуальные машины имеют VXLAN-бэкбон, работающий поверх Ethernet с использованием multicast - так тоже работает.

В /etc/modules добавьте br_netfilter, overlay.

Выполните modprobe br_netfilter && modprobe overlay, чтобы загрузить модули.

В /etc/sysctl.conf добавьте:

net.bridge.bridge-nf-call-ip6tables = 1net.bridge.bridge-nf-call-iptables = 1net.ipv4.ip_forward = 1

Выполните sysctl -p для применения изменений.

Установка containerd

Выполняется на всех узлах. Kubernetes рекомендует использовать containerd (впрочем, новые версии docker тоже используют containerd), поэтому установим его:

sudo apt-get updatesudo apt install containerdsudo sh -- -c "containerd config default | tee /etc/containerd/config.toml"sudo service containerd restart

Установим kubeadm, kubelet, kubectl

Выполняется на всех узлах. Здесь прям из руководства по установке K8S:

sudo apt-get update && sudo apt-get install -y apt-transport-https curlcurl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.listdeb https://apt.kubernetes.io/ kubernetes-xenial mainEOFsudo apt-get updatesudo apt-get install -y kubelet kubeadm kubectlsudo apt-mark hold kubelet kubeadm kubectl

Инициализация первого узла K8S

Выполняется на узлах, которые будут обслуживать Control Plane K8S - в моем случае k8s-{1,2,3}. Здесь уже есть нюансы, специфичные для моего развертывания:

kubeadm init --pod-network-cidr=10.244.0.0/16 \      --control-plane-endpoint=k8s-cp \      --apiserver-advertise-address=10.120.29.187

Для Flannel категорически важно использовать --pod-network-cidr=10.244.0.0/16. С другим адресным пространством для POD-ов K8S он не запустится.

--control-plane-endpoint string Specify a stable IP address or DNS name for the control plane.

Здесь Вы должны указать тот DNS или IP, который будет использоваться для связи всех шурушков K8S-а с Control Plane. Я решил использовать доменное имя k8s-cp, привязанное к отказоустойчивому ip-адресу 10.120.0.1 (см. далее, на текущий момент, k8s-cp указывает на один из серверов Control Plane: 10.120.29.187 k8s-cp).

Важный аргумент --api-server-advertise-address. Важно, что он влияет не только на api-server, но и на Etcd, что нигде не сказано, но очень важно для отказоустойчивой топологии. Если ничего не указать, то kubeadm возьмет адрес с той сети, в которой шлюз по-умолчанию, что не всегда верно. В моем случае это приводит к тому, что Etcd стартует на публичном интерфейсе, а кластер Etcd хочет работать по публичной сети, что меня не устраивает. Если этот адрес не указать правильно, то Flannel тоже не сможет корректно инициализироваться, будет падать с ошибками, что не может связаться с Control Plane (будет использовать тот же IP-адрес из сети со шлюзом по умолчанию для связи).

В общем, этот параметр привносит много геморроя и ведет к некорректной работе всего, что только может некорректно работать.

Теперь, если все хорошо, то Kubeadm развернет K8S на данном узле. Я рекомендую выполнить перезагрузку узла для того, чтобы убедиться что Control Plane стартует как надо. Убедитесь в этом, запросив список выполняемых задач:

ps xa  | grep -E '(kube-apiserver|etcd|kube-proxy|kube-controller-manager|kube-scheduler)'

Теперь можно скопmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/configировать настройки конфигурации для доступа администратора к кластеру в домашний каталог:

mkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/config

Для проверки запросите задачи, выполняемые на данном настроенном контроллере посредством команды kubectl get pods --all-namespaces. Вы должны получить вывод, примерно соответствующий следующему:

NAMESPACE              NAME                                         READY   STATUS      RESTARTS   AGEkube-system            etcd-k8s-1                                   1/1     Running     0          2d23hkube-system            kube-apiserver-k8s-1                         1/1     Running     0          2d23hkube-system            kube-controller-manager-k8s-1                1/1     Running     1          2d23hkube-system            kube-scheduler-k8s-1                         1/1     Running     1          2d23h

Я рекомендую посмотреть вывод этой команды еще пару раз, с перерывом 1 минуту, чтобы убедиться, что RESTARTS не растут, а статус Running.

Сам Kubernetes никакой сети не предоставляет, делегируя это плагинам CNI. Мы будем использовать простой CNI - Flannel. Его установка производится элементарно следующей командой:

kubectl apply -f https://github.com/coreos/flannel/raw/master/Documentation/kube-flannel.yml

Опять же, выполните kubectl get pods --all-namespaces несколько раз, чтобы убедиться, что Flannel выполняется без ошибок и RESTARTS не растут. Если что-то пошло не так, посмотрите журнал событий flannel следующим способом (только используйте настоящее имя POD-а Flannel:

kubectl logs -n kube-system kube-flannel-ds-xn2j9

Теперь вы можете подключить два других узла Control Plane с помощью команды, выполненной на каждом из них:

# ssh k8s-2sudo kubeadm join k8s-cp:6443 --apiserver-advertise-address=10.120.28.37 --token tfqsms.kiek2vk129tpf0b7 --discovery-token-ca-cert-hash sha256:0c446bfabcd99aae7e650d110f8b9d6058cac432078c4fXXXXX6055b4bd --control-planemkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/config# ssh k8s-3sudo kubeadm join k8s-cp:6443 --apiserver-advertise-address=10.120.29.204 --token tfqsms.kiek2vk129tpf0b7 --discovery-token-ca-cert-hash sha256:0c446bfabcd99aae7e650d110f8b9d6058cac432078c4fXXXXXec6055b4bd --control-planemkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/config

Если вдруг --token "протух", используйте команду kubeadm token create, чтобы сгенерировать новый.

После подключения узла смотрите внимательно за состоянием Etcd:

kubectl get pods --all-namespaces | grep etcdkube-system            etcd-k8s-1                                   1/1     Running     0          2d23hkube-system            etcd-k8s-2                                   1/1     Running     1          2d22hkube-system            etcd-k8s-3                                   1/1     Running     1          2d22h

Состояние должно быть Running, увеличение счетчика перезагрузок не должно происходить. Команда kubectl get pods --all-namespaces должна отображать трехкратный набор процессов на всех узлах Control Plane:

NAME                            READY   STATUS    RESTARTS   AGEcoredns-74ff55c5b-h2zjq         1/1     Running   0          2d23hcoredns-74ff55c5b-n6b49         1/1     Running   0          2d23hetcd-k8s-1                      1/1     Running   0          2d23hetcd-k8s-2                      1/1     Running   1          2d22hetcd-k8s-3                      1/1     Running   1          2d22hkube-apiserver-k8s-1            1/1     Running   0          2d23hkube-apiserver-k8s-2            1/1     Running   1          2d22hkube-apiserver-k8s-3            1/1     Running   1          2d22hkube-controller-manager-k8s-1   1/1     Running   1          2d23hkube-controller-manager-k8s-2   1/1     Running   1          2d22hkube-controller-manager-k8s-3   1/1     Running   1          2d22hkube-flannel-ds-2f6d5           1/1     Running   0          2d3hkube-flannel-ds-2p5vx           1/1     Running   0          2d3hkube-flannel-ds-4ng99           1/1     Running   3          2d22hkube-proxy-22jpt                1/1     Running   0          2d3hkube-proxy-25rxn                1/1     Running   0          2d23hkube-proxy-2qp8r                1/1     Running   0          2d3hkube-scheduler-k8s-1            1/1     Running   1          2d23hkube-scheduler-k8s-2            1/1     Running   1          2d22hkube-scheduler-k8s-3            1/1     Running   1          2d22h

Следующим шагом настроим отказоустойчивый IP-адрес, который будет использоваться для доступа к Control Plane. В моем случае было три варианта:

  1. Keepalived;

  2. Pacemaker;

  3. kube-vip.

Здесь я потратил достаточно много времени, чтобы все завести. Начал я с Keepalived, который прекрасно работает в других моих серверных системах. В общем, здесь он у меня не завелся, не знаю в чем проблема - в Ubuntu 18.04 или в VXLAN-сети, которую я использую в качестве Underlay. Tcpdump показывал веселые VRRP-пакетики, летящие между k8s-{1,2,3}, но IP-адрес вешался всеми 3мя узлами, все считали себя MASTER-ами. Поскольку debug ничего полезного не дал, я отказался от Keepalived и выполнил настройку на Pacemaker. Тут меня ждала очередная неприятность - corosync и pacemaker не взлетали самостоятельно при старте, несмотря на:

sudo systemctl enable corosyncsudo systemctl enable pacemaker

Я придерживаюсь такого мнения, что корневые компоненты или работают или не надо их использовать, в /etc/rc.local прописывать эти службы не хотелось. В итоге, я познакомился со специализированным решением по предоставлению отказоустойчивого IP-адреса kube-vip, разработанным специально для Kubernetes.

Нам понадобится создать три манифеста, каждый для запуска kube-vip на одном из узлов Control Plane:

apiVersion: v1kind: Podmetadata:  creationTimestamp: null  name: kube-vip-cp-k8s-1         # поменять имя, соответственно узлу  namespace: kube-systemspec:  nodeName: k8s-1                 # будет запускаться именно на этом узле  containers:  - args:    - start    env:    - name: vip_arp      value: "true"    - name: vip_interface      value: eth1    - name: vip_leaderelection      value: "true"    - name: vip_leaseduration      value: "5"    - name: vip_renewdeadline      value: "3"    - name: vip_retryperiod      value: "1"    - name: vip_address      value: 10.120.0.1          # указать реальный IP, который будет использоваться    image: plndr/kube-vip:0.3.1  # проверить актуальную версию    imagePullPolicy: Always    name: kube-vip-cp    resources: {}    securityContext:      capabilities:        add:        - NET_ADMIN        - SYS_TIME    volumeMounts:    - mountPath: /etc/kubernetes/admin.conf      name: kubeconfig    - mountPath: /etc/ssl/certs      name: ca-certs      readOnly: true  hostNetwork: true  volumes:  - hostPath:      path: /etc/kubernetes/admin.conf    name: kubeconfig  - hostPath:      path: /etc/ssl/certs    name: ca-certsstatus: {}

Данный манифест необходимо сформировать для каждого из серверов Control Plane и выполнить их:

kubectl apply -f cluster_config/vip-1.ymlkubectl apply -f cluster_config/vip-2.ymlkubectl apply -f cluster_config/vip-3.yml

В результате вы должны получить три нормально выполняющихся POD-а, каждый на своем узле, при этом адрес 10.120.0.1 должен нормально пинговаться. Проверьте, что только один из kube-vip владеет IP:

sudo arping 10.120.0.1ARPING 10.120.0.142 bytes from 1e:01:17:00:01:22 (10.120.0.1): index=0 time=319.476 usec42 bytes from 1e:01:17:00:01:22 (10.120.0.1): index=1 time=306.360 msec42 bytes from 1e:01:17:00:01:22 (10.120.0.1): index=2 time=349.666 usec

Чем хорош kube-vip? Он не только предоставляет отказоустойчивый IP, но и определяет когда сервер на хосте, где он выполняется становится недоступен, переставая балансировать на него трафик.

Теперь, когда kube-vip предоставляет отказоустойчивый доступ к Kubernetes необходимо на хостах-рекурсорах gw-1, gw-2 в /etc/hosts.resolv обновить записи для k8s-cp:

10.120.0.1 k8s-cp

Выполните перезагрузку pdns-recursor командой sudo service pdns-recursor restart и проверьте, что k8s-cp отвечает со всех узлов IP адресом 10.120.0.1. Проверьте, что kubectl все еще корректно работает с узла k8s-1, он будет соединяться по k8s-cp, но использовать уже другой IP-адрес.

На данном этапе у нас есть реализация отказоустойчивого Control Plane K8S. Я рекомендую несколько раз поочередно перезагружать k8s-{1,2,3}, чтобы проверить, что кластер остается в работоспособном состоянии.

Добавление узла Worker-а

В нашей топологии предполагается использование двух узлов gw-1, gw-2, на которых будет размещен Nginx Ingress и один узел общего назначения (compute-1).

Все эти узлы в кластер можно добавить следующим образом:

kubeadm token create --print-join-command kubeadm join k8s-cp:6443 --token rn0s5p.y6waq1t6y2y6z9vw     --discovery-token-ca-cert-hash sha256:0c446bfabcd99aae7e650d110f8b9d6058cac432078c4fXXXe22ec6055b4bd# ssh gw-1sudo kubeadm join k8s-cp:6443 --token rn0s5p.y6waq1t6y2y6z9vw     --discovery-token-ca-cert-hash sha256:0c446bfabcd99aae7e650d110f8b9d6058cac432078c4fXXXe22ec6055b4bd# ssh gw-2...# ssh compute-1

После добавления kubectl get pds --all-namespaces должен показать расширенный набор выполняющихся POD-ов, а kubectl get nodes должен вывести все узлы кластера:

kubectl get nodesNAME                STATUS   ROLES                  AGE     VERSIONcompute-1           Ready    compute                2d23h   v1.20.2gw-1                Ready    gateway                2d4h    v1.20.2gw-2                Ready    gateway                2d4h    v1.20.2k8s-1               Ready    control-plane,master   2d23h   v1.20.2k8s-2               Ready    control-plane,master   2d23h   v1.20.2k8s-3               Ready    control-plane,master   2d23h   v1.20.2

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

Назначение ролей узлам

Роль можно присвоить просто:

kubectl label node gw-1 node-role.kubernetes.io/gateway=truekubectl label node gw-2 node-role.kubernetes.io/gateway=truekubectl label node compute-1 node-role.kubernetes.io/compute=true# если надо удалить рольkubectl label node compute-1 node-role.kubernetes.io/compute-

Настройка Ingress

Сейчас все готово для того, чтобы можно было выполнить развертывание Nginx Ingress на узлах gw-1, gw-2. Воспользуемся манифестом Nginx Ingress, но внесем в него ряд изменений:

  • запускать будем с сетью hostNetwork;

  • запускать будем в виде Deployment с фактором масштабирования "2";

  • запускать будем на узлах с ролью gateway.

Разберем подробнее про hostNetwork. Дело в том, что при использовании K8S в рамках какого-то облачного провайдера, последний через API назначает каждому узлу External IP, который может быть использован приложениями для связи с внешним миром. Так вот, у нас в bare metal кластере никаких External IP нет:

kubectl get nodes --output wideNAME                STATUS   ROLES                  AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIMEcompute-1           Ready    compute                3d      v1.20.2   10.120.28.172   <none>        Ubuntu 18.04.5 LTS   4.15.0-135-generic   containerd://1.3.3gw-1                Ready    gateway                2d4h    v1.20.2   10.120.29.231   <none>        Ubuntu 18.04.5 LTS   4.15.0-135-generic   containerd://1.3.3gw-2                Ready    gateway                2d4h    v1.20.2   10.120.28.23    <none>        Ubuntu 18.04.5 LTS   4.15.0-135-generic   containerd://1.3.3k8s-1               Ready    control-plane,master   3d      v1.20.2   10.120.29.187   <none>        Ubuntu 18.04.5 LTS   4.15.0-135-generic   containerd://1.3.3k8s-2               Ready    control-plane,master   2d23h   v1.20.2   10.120.28.37    <none>        Ubuntu 18.04.5 LTS   4.15.0-135-generic   containerd://1.3.3k8s-3               Ready    control-plane,master   2d23h   v1.20.2   10.120.29.204   <none>        Ubuntu 18.04.5 LTS   4.15.0-135-generic   containerd://1.3.3

Собственно, назначить этот External IP можно только через API, при этом он сбрасывается самим Kubernetes время от времени и требует постоянной установки. В общем, это неудобно и использоваться нормально не может. Я читал длинную переписку на GitHub, которая закончилась ничем вразумительным, еще советуют использовать metallb, который тоже непонятно в каком состоянии. В общем, я решил просто завести Nginx Ingress, используя hostNetworking, поскольку это обеспечивает привязку данного Ingress с адресам 0.0.0.0:443, 0.0.0.0:80 и решает мою задачу.

Собственно, манифест для запуска Nginx Ingress выглядит так:

Очень большой фрагмент YAML
apiVersion: v1kind: Namespacemetadata:  name: ingress-nginx  labels:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx---# Source: ingress-nginx/templates/controller-serviceaccount.yamlapiVersion: v1kind: ServiceAccountmetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: controller  name: ingress-nginx  namespace: ingress-nginx---# Source: ingress-nginx/templates/controller-configmap.yamlapiVersion: v1kind: ConfigMapmetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: controller  name: ingress-nginx-controller  namespace: ingress-nginxdata:---# Source: ingress-nginx/templates/clusterrole.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm  name: ingress-nginxrules:  - apiGroups:      - ''    resources:      - configmaps      - endpoints      - nodes      - pods      - secrets    verbs:      - list      - watch  - apiGroups:      - ''    resources:      - nodes    verbs:      - get  - apiGroups:      - ''    resources:      - services    verbs:      - get      - list      - watch  - apiGroups:      - extensions      - networking.k8s.io   # k8s 1.14+    resources:      - ingresses    verbs:      - get      - list      - watch  - apiGroups:      - ''    resources:      - events    verbs:      - create      - patch  - apiGroups:      - extensions      - networking.k8s.io   # k8s 1.14+    resources:      - ingresses/status    verbs:      - update  - apiGroups:      - networking.k8s.io   # k8s 1.14+    resources:      - ingressclasses    verbs:      - get      - list      - watch---# Source: ingress-nginx/templates/clusterrolebinding.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm  name: ingress-nginxroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: ingress-nginxsubjects:  - kind: ServiceAccount    name: ingress-nginx    namespace: ingress-nginx---# Source: ingress-nginx/templates/controller-role.yamlapiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: controller  name: ingress-nginx  namespace: ingress-nginxrules:  - apiGroups:      - ''    resources:      - namespaces    verbs:      - get  - apiGroups:      - ''    resources:      - configmaps      - pods      - secrets      - endpoints    verbs:      - get      - list      - watch  - apiGroups:      - ''    resources:      - services    verbs:      - get      - list      - watch  - apiGroups:      - extensions      - networking.k8s.io   # k8s 1.14+    resources:      - ingresses    verbs:      - get      - list      - watch  - apiGroups:      - extensions      - networking.k8s.io   # k8s 1.14+    resources:      - ingresses/status    verbs:      - update  - apiGroups:      - networking.k8s.io   # k8s 1.14+    resources:      - ingressclasses    verbs:      - get      - list      - watch  - apiGroups:      - ''    resources:      - configmaps    resourceNames:      - ingress-controller-leader-nginx    verbs:      - get      - update  - apiGroups:      - ''    resources:      - configmaps    verbs:      - create  - apiGroups:      - ''    resources:      - events    verbs:      - create      - patch---# Source: ingress-nginx/templates/controller-rolebinding.yamlapiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: controller  name: ingress-nginx  namespace: ingress-nginxroleRef:  apiGroup: rbac.authorization.k8s.io  kind: Role  name: ingress-nginxsubjects:  - kind: ServiceAccount    name: ingress-nginx    namespace: ingress-nginx---# Source: ingress-nginx/templates/controller-service-webhook.yamlapiVersion: v1kind: Servicemetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: controller  name: ingress-nginx-controller-admission  namespace: ingress-nginxspec:  type: ClusterIP  ports:    - name: https-webhook      port: 443      targetPort: webhook  selector:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/component: controller---# Source: ingress-nginx/templates/controller-deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: controller  name: ingress-nginx-controller  namespace: ingress-nginxspec:  replicas: 2  selector:    matchLabels:      app.kubernetes.io/name: ingress-nginx      app.kubernetes.io/instance: ingress-nginx      app.kubernetes.io/component: controller  revisionHistoryLimit: 10  minReadySeconds: 0  template:    metadata:      labels:        app.kubernetes.io/name: ingress-nginx        app.kubernetes.io/instance: ingress-nginx        app.kubernetes.io/component: controller    spec:      hostNetwork: true      dnsPolicy: ClusterFirst      containers:        - name: controller          image: k8s.gcr.io/ingress-nginx/controller:v0.43.0@sha256:9bba603b99bf25f6d117cf1235b6598c16033ad027b143c90fa5b3cc583c5713          imagePullPolicy: IfNotPresent          lifecycle:            preStop:              exec:                command:                  - /wait-shutdown          args:            - /nginx-ingress-controller            - --election-id=ingress-controller-leader            - --ingress-class=nginx            - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller            - --validating-webhook=:8443            - --validating-webhook-certificate=/usr/local/certificates/cert            - --validating-webhook-key=/usr/local/certificates/key          securityContext:            capabilities:              drop:                - ALL              add:                - NET_BIND_SERVICE            runAsUser: 101            allowPrivilegeEscalation: true          env:            - name: POD_NAME              valueFrom:                fieldRef:                  fieldPath: metadata.name            - name: POD_NAMESPACE              valueFrom:                fieldRef:                  fieldPath: metadata.namespace            - name: LD_PRELOAD              value: /usr/local/lib/libmimalloc.so          livenessProbe:            httpGet:              path: /healthz              port: 10254              scheme: HTTP            initialDelaySeconds: 10            periodSeconds: 10            timeoutSeconds: 1            successThreshold: 1            failureThreshold: 5          readinessProbe:            httpGet:              path: /healthz              port: 10254              scheme: HTTP            initialDelaySeconds: 10            periodSeconds: 10            timeoutSeconds: 1            successThreshold: 1            failureThreshold: 3          ports:            - name: http              containerPort: 80              protocol: TCP            - name: https              containerPort: 443              protocol: TCP            - name: webhook              containerPort: 8443              protocol: TCP          volumeMounts:            - name: webhook-cert              mountPath: /usr/local/certificates/              readOnly: true          resources:            requests:              cpu: 100m              memory: 90Mi      nodeSelector:        node-role.kubernetes.io/gateway: "true"      serviceAccountName: ingress-nginx      terminationGracePeriodSeconds: 300      volumes:        - name: webhook-cert          secret:            secretName: ingress-nginx-admission---# Source: ingress-nginx/templates/admission-webhooks/validating-webhook.yaml# before changing this value, check the required kubernetes version# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisitesapiVersion: admissionregistration.k8s.io/v1kind: ValidatingWebhookConfigurationmetadata:  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhook  name: ingress-nginx-admissionwebhooks:  - name: validate.nginx.ingress.kubernetes.io    matchPolicy: Equivalent    rules:      - apiGroups:          - networking.k8s.io        apiVersions:          - v1beta1        operations:          - CREATE          - UPDATE        resources:          - ingresses    failurePolicy: Fail    sideEffects: None    admissionReviewVersions:      - v1      - v1beta1    clientConfig:      service:        namespace: ingress-nginx        name: ingress-nginx-controller-admission        path: /networking/v1beta1/ingresses---# Source: ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yamlapiVersion: v1kind: ServiceAccountmetadata:  name: ingress-nginx-admission  annotations:    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhook  namespace: ingress-nginx---# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:  name: ingress-nginx-admission  annotations:    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhookrules:  - apiGroups:      - admissionregistration.k8s.io    resources:      - validatingwebhookconfigurations    verbs:      - get      - update---# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  name: ingress-nginx-admission  annotations:    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhookroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: ingress-nginx-admissionsubjects:  - kind: ServiceAccount    name: ingress-nginx-admission    namespace: ingress-nginx---# Source: ingress-nginx/templates/admission-webhooks/job-patch/role.yamlapiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:  name: ingress-nginx-admission  annotations:    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhook  namespace: ingress-nginxrules:  - apiGroups:      - ''    resources:      - secrets    verbs:      - get      - create---# Source: ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yamlapiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:  name: ingress-nginx-admission  annotations:    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhook  namespace: ingress-nginxroleRef:  apiGroup: rbac.authorization.k8s.io  kind: Role  name: ingress-nginx-admissionsubjects:  - kind: ServiceAccount    name: ingress-nginx-admission    namespace: ingress-nginx---# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yamlapiVersion: batch/v1kind: Jobmetadata:  name: ingress-nginx-admission-create  annotations:    helm.sh/hook: pre-install,pre-upgrade    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhook  namespace: ingress-nginxspec:  template:    metadata:      name: ingress-nginx-admission-create      labels:        helm.sh/chart: ingress-nginx-3.21.0        app.kubernetes.io/name: ingress-nginx        app.kubernetes.io/instance: ingress-nginx        app.kubernetes.io/version: 0.43.0        app.kubernetes.io/managed-by: Helm        app.kubernetes.io/component: admission-webhook    spec:      containers:        - name: create          image: docker.io/jettech/kube-webhook-certgen:v1.5.1          imagePullPolicy: IfNotPresent          args:            - create            - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc            - --namespace=$(POD_NAMESPACE)            - --secret-name=ingress-nginx-admission          env:            - name: POD_NAMESPACE              valueFrom:                fieldRef:                  fieldPath: metadata.namespace      restartPolicy: OnFailure      serviceAccountName: ingress-nginx-admission      securityContext:        runAsNonRoot: true        runAsUser: 2000---# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yamlapiVersion: batch/v1kind: Jobmetadata:  name: ingress-nginx-admission-patch  annotations:    helm.sh/hook: post-install,post-upgrade    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded  labels:    helm.sh/chart: ingress-nginx-3.21.0    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/instance: ingress-nginx    app.kubernetes.io/version: 0.43.0    app.kubernetes.io/managed-by: Helm    app.kubernetes.io/component: admission-webhook  namespace: ingress-nginxspec:  template:    metadata:      name: ingress-nginx-admission-patch      labels:        helm.sh/chart: ingress-nginx-3.21.0        app.kubernetes.io/name: ingress-nginx        app.kubernetes.io/instance: ingress-nginx        app.kubernetes.io/version: 0.43.0        app.kubernetes.io/managed-by: Helm        app.kubernetes.io/component: admission-webhook    spec:      containers:        - name: patch          image: docker.io/jettech/kube-webhook-certgen:v1.5.1          imagePullPolicy: IfNotPresent          args:            - patch            - --webhook-name=ingress-nginx-admission            - --namespace=$(POD_NAMESPACE)            - --patch-mutating=false            - --secret-name=ingress-nginx-admission            - --patch-failure-policy=Fail          env:            - name: POD_NAMESPACE              valueFrom:                fieldRef:                  fieldPath: metadata.namespace      restartPolicy: OnFailure      serviceAccountName: ingress-nginx-admission      securityContext:        runAsNonRoot: true        runAsUser: 2000

Запустив его с помощью kubectl apply -f nginx-ingress.yaml, мы получим два POD-а Nginx, выполняющихся на узлах gw-1, gw-2 и слушающих 443-й и 80-й порты:

kubectl get pods --all-namespaces | grep nginxingress-nginx          ingress-nginx-admission-create-4mm9m         0/1     Completed   0          46hingress-nginx          ingress-nginx-admission-patch-7jkwg          0/1     Completed   2          46hingress-nginx          ingress-nginx-controller-b966cf6cd-7kpzm     1/1     Running     1          46hingress-nginx          ingress-nginx-controller-b966cf6cd-ckl97     1/1     Running     0          46h

На узлах gw-1, gw-2:

sudo netstat -tnlp | grep -E ':(443|80)'tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      2661/nginx: master  tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      2661/nginx: master  tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      2661/nginx: master  tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      2661/nginx: master  tcp6       0      0 :::443                  :::*                    LISTEN      2661/nginx: master  tcp6       0      0 :::443                  :::*                    LISTEN      2661/nginx: master  tcp6       0      0 :::80                   :::*                    LISTEN      2661/nginx: master  tcp6       0      0 :::80                   :::*                    LISTEN      2661/nginx: master  

Можно постучаться на публичные адреса gw-1, gw-2 по 80-му порту и получить приветственную страницу Nginx. Далее, Вы можете использовать публичные адреса gw-1, gw-2 для создания записей DNS, использования в CDN и т.п.

Протестировать работу inress можно, создав сервис, на который Ingress будет проксировать трафик (взято отсюда) - echo1.yaml:

apiVersion: v1kind: Servicemetadata:  name: echo1spec:  ports:  - port: 80    targetPort: 5678  selector:    app: echo1---apiVersion: apps/v1kind: Deploymentmetadata:  name: echo1spec:  selector:    matchLabels:      app: echo1  replicas: 2  template:    metadata:      labels:        app: echo1    spec:      containers:      - name: echo1        image: hashicorp/http-echo        args:        - "-text=echo1"        ports:        - containerPort: 5678

Выполните данный манифест с помощью kubectl apply -f echo1.yaml. Теперь создадим правило Ingress (ingress-echo1.yaml):

apiVersion: networking.k8s.io/v1beta1kind: Ingressmetadata:  name: echo-ingressspec:  rules:  - host: echo1.example.com    http:      paths:      - backend:          serviceName: echo1          servicePort: 80

Выполним данный манифест kubectl apply -f ingress-echo1.yaml. Теперь, если на локальном компьютере в /etchosts внести запись для echo1.example.com:

127.0.0.1localhost127.0.1.1manager# The following lines are desirable for IPv6 capable hosts::1     localhost ip6-localhost ip6-loopbackff02::1 ip6-allnodesff02::2 ip6-allroutersX.Y.Z.C echo1.example.com

То можно получить проксирование трафика через Nginx Ingress. Проверим с помощью curl:

curl echo1.example.comecho1

Установка K8S Dashboard

Для установки Dashboard необходимо выполнить следующую команду:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml

Dashboard имеет ряд ограничений, например, он не работает через http, если обращение осуществляется не с localhost. В целом, не рекомендуется как-либо предоставлять доступ к Dashboard извне кластера через Ingress. Кроме того, Dashboard использует встроенную систему RBAC Kubernetes, поэтому требуется создать пользователя и дать ему права на Dashboard. Инструкция взята с этой страницы, здесь приводится для простоты восприятия.

Создадим аккаунт:

cat <<EOF | kubectl apply -f -apiVersion: v1kind: ServiceAccountmetadata:  name: admin-user  namespace: kubernetes-dashboardEOF

Определим роль пользователя:

cat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  name: admin-userroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: cluster-adminsubjects:- kind: ServiceAccount  name: admin-user  namespace: kubernetes-dashboardEOF

Получим токен, с помощью которого пользователь сможет войти в Dashboard:

kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"

Если на вашей машине уже настроен kubect и есть $HOME/.kube/config, а сама машина "видит" k8s-cp, то вы можете запустить kubectl proxy и получить доступ к Dashboard по ссылке: http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/.

Если же ваша машина с браузером находится вне кластера, то переходим к следующему шагу, где мы настроим проброс API Kubernetes наружу через HAProxy.

Доступ к API K8S из внешней сети

На узлах gw-1, gw-2 необходимо установить haproxy. После установки измените конфигурационный файл /etc/haproxy/haproxy.cfg так, чтобы далее секции defaults он выглядел следующим образом:

defaults    # mode is inherited by sections that follow    mode tcpfrontend k8s    # receives traffic from clients    bind :6443    default_backend kubernetesbackend kubernetes    # relays the client messages to servers    server k8s k8s-cp:6443

Теперь вы можете обратиться к API K8S извне, указав в /etc/hosts своей локальной машины адрес gw-1 или gw-2 в качестве k8s-cp. Вам должны быть доступны с локальной машины все команды kubectl, включая kubectl proxy:

kubectl proxyStarting to serve on 127.0.0.1:8001

Можно открыть в браузере http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ и насладиться видом приглашения авторизации Dashboard K8S:

Вводим токен, полученный с помощью:

kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"eyJhbGciOiJSUzI1NiIsImtpZCI6IlFkcGxwMTN2YlpyNS1TOTYtUnVYdsfadfsdjfksdlfjm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWd6anprIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjM2RiOWFkMS0yYjdmLTQ3YTYtOTM3My1hZWI2ZjJkZjk0NTAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.ht-RyqLXY4UzBnxIzJcnQn8Kk2iHWZzKVeAjUhKkJ-vOAtyqBu50rExgiRWsEHWfJp1p9xN8sXFKg62FFFbHWftPf6PmwfcBK2lXPy6OL9OaooP17WJVn75KKXIZHPWLO9VmRXPB1S-v2xFuG_J8jHB8pZHJlFjp4zIXfB--QwhrxeoTt5zv3CfXCl1_VYgCoqaxRsa7e892-vtMijBo7EZfJiyuAUQr_TsAIDY3zOWFJeHbwPFWzL_1fF9Y2o3r0pw7hYZHUoI8Z-3hbfOi10QBRyQlNZTMFSh7Z38RRbV1tw2ZmMvgSQyHa9TZFy2kYik6VnugNB2cilamo_b7hg

и переходим на главный экран Dashboard:

Вместо заключения

Как я писал в начале статьи, у меня уже есть опыт развертывания оркестраций на базе Docker, Apache Mesos (DC/OS), тем не менее, документация Kubernetes мне показалась сложной, запутанной и фрагментарной. Чтобы получить текущий результат я прошерстил довольном много сторонних руководств, Issue GitHub и, конечно, документацию Kubernetes. Надеюсь, что данное руководство поможет вам сэкономить свое время.

Подробнее..

Почтовая система Mailion как нам удалось создать эффективное объектное хранилище для электронной почты

11.12.2020 16:14:18 | Автор: admin

Недавно на Хабре вышли две статьи про новую корпоративную почтовую систему Mailion от МойОфис (1, 2) уникальную российскую разработку, которая отличается беспрецедентными возможностями масштабирования и способна работать в системах с более чем 1 миллионом пользователей.

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


Хабр, привет! Меня зовут Виталий Исаев, я занимаюсь разработкой объектного хранилища почтовой системы Mailion. Этой статьёй мы открываем цикл публикаций, посвящённых архитектуре нашего хранилища, его масштабированию, отказоустойчивости и устройству его внутренних подсистем.

Немного истории

Структура электронного письма формировалась в течение последних пятидесяти лет. Этапы её развития зафиксированы несколькими стандартами. Первое электронное письмо было отправлено между компьютерами системы ARPANET ещё в 1971 г. Но разработчикам стандартов потребовалось ещё 11 лет на разработку RFC 822, который стал прообразом современного представления электронной почты. В нём был описан общий коммуникационный фреймворк текстовых сообщений. В это время электронное письмо рассматривалось просто как набор текстовых строк, а передача структурированных данных (изображений, видео, звука) не подразумевалась и никак не регламентировалась.

Примерно в середине 1990-х родилась концепция специальных расширений MIME (RFC 2045, RFC 2046, RFC 2047), которые позволили включать в состав электронного письма бинарные форматы данных в закодированном виде.

В 2001 году новый стандарт RFC 2822 отменил некоторые устаревшие положения RFC 822, и ввёл понятие частей сообщения message parts, или, проще говоря, "партов". А благодаря MIME появилась возможность создавать multipart-письма, которые могли бы состоять из множества частей различных типов. Наиболее актуальным стандартом электронной почты является RFC 5322, он был разработан в 2008 году.

Структура электронного письма

На сегодняшний день типичное почтовое сообщение состоит из заголовка и "партов". В заголовке в структурированном виде хранятся метаданные, такие как идентификатор письма, электронные адреса отправителя и получателя, дата отправки и другие служебные поля. К "партам", которые составляют само тело письма, относятся основной и альтернативный текст, изображения, прикреплённые файлы. Объём заголовочной части письма редко превышает 1 килобайт текста, тогда как размер тела письма ограничен лишь лимитами почтовой системы и может достигать десятков и даже сотен мегабайт.

Когда письма поступают в Mailion по любому из транспортных каналов (SMTP, IMAP, API), они не сохраняются как единое целое, а разбиваются на составные части. В Mailion мы извлекаем информацию о структуре письма и некоторые другие служебные данные, и затем сохраняем их в хранилище метаинформации. "Парты" писем, которые составляют основной объем данных, направляются в объектное хранилище.

Рис. 1. Разбиение электронного письма на фрагменты и сохранение по частям в хранилище метаданных и объектном хранилище.

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

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

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

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

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

Дедупликация

После детального анализа особенностей потока данных мы сформулировали основной принцип проектирования хранилища: оно должно эффективно проводить дедупликацию почтовых сообщений. Правильная работа с дубликатами позволяет экономить не только дисковое пространство, но и операции ввода-вывода за счёт кэширования наиболее часто встречающихся фрагментов данных. Недорогие, но ёмкие HDD-накопители не отличаются высокой пропускной способностью, их показатели IOPS часто являются лимитирующим фактором для производительности всей системы. Благодаря дедупликации данных увеличивается не только экономическая эффективность хранилища, но и его производительность.

Пример расчета ресурсов системы хранения

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

В классических почтовых системах mbox-формата, в которых копии писем разных пользователей хранятся в разных файлах, данная операция будет порождать поток произвольных чтений (random read) с диска. Стандартные HDD в этом режиме работы выдают производительность около 100 IOPS. При размере блока в 4 Кб чтение картинки в худшем случае, когда блоки файла при записи не были размещены последовательно, потребует 64 операции чтения. Следовательно, для чтения всех писем с картинками будет необходимо выполнить 64000 операции (64 операции * 1000 пользователей). Для того, чтобы обработать данную операцию за 1 секунду, потребуется 640 HDD одновременно. Если же в системе присутствует только один накопитель, то он будет работать со 100% утилизацией более 10 минут.

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

Накладные расходы дедупликации

Безусловно, дедупликация не бесплатна и сильно усложняет процесс записи и чтения, налагает дополнительные расходы на CPU и RAM. Однако иммутабельность данных частично компенсирует этот рост сложности. Благодаря неизменности писем, можно не поддерживать операцию записи по смещению (при необходимости его можно реализовать по принципу copy-on-write) и сохранять интерфейс хранилища максимально простым. На данный момент он состоит из записи, чтения, удаления данных и ещё пары вспомогательных методов.

Известные решения с дедупликацией

На момент старта проекта Mailion ни в одном из известных нам объектных хранилищ с открытым кодом подобные оптимизации не были реализованы. Например, в Minio отказались от реализации дедупликации ещё в 2017 г. Попытки доработки других готовых решений, таких, как Elliptics, Riak или Swift, при очевидных и значительных трудозатратах также не гарантировали успеха. ZFS в течение долгих лет не поддерживалась в Linux и по причине лицензионных разногласий официально не может поставляться в дистрибутивах, отличных от Oracle, да и к производительности дедупликации в этой файловой системе есть вопросы.

В Ceph дедупликация появилась в 2019 г., но Ceph имеет репутацию сложного для эксплуатации хранилища с большим количеством историй неуспеха. Многим известны истории отказа Ceph в Росреестре и DigitalOcean и даже потеря бизнеса компанией DataMouse. В багтрекере Ceph можно найти большое количество тикетов с зависаниями, сегфолтами и другими ошибками. Причем некоторые баги, даже в статусе Immediate, могут не устраняться по полгода и больше. Это наводит на мысли о том, что Ceph дорог не только с точки зрения эксплуатации, но и с точки зрения разработки: ресурсы разработчиков будут уходить на стабилизацию самого Ceph, а не на разработку собственного продукта.

Что такое DOS и как это работает

Мы убедились в отсутствии подходящих для наших задач продуктов с открытым исходным кодом, и решили разработать собственное хранилище с поддержкой дедупликации в самом ядре системы. Мы вдохновились программной статьей аналитиков из компании Storage Switzerland, в которой кратко описываются принципы проектирования современных программно-ориентированных хранилищ, и решили назвать наш продукт Dispersed Object Store (DOS). Код DOS написан на языках Go (95%), C и C++ (5%).

Модель данных и метаданных

Иерархия сущностей DOS включает в себя четыре уровня:

  • документы;

  • версии документов;

  • чанки (chunks);

  • сегменты.

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

Каждый документ со стороны публичного API адресуется идентификатором, который состоит из пространства имён (namespace) и собственно ключа (key). В рамках одного пространства имён все ключи являются уникальными. Непосредственно в хранилище из внешнего идентификатора и с помощью криптографической хеш-функции генерируется внутренний ключ (storekey), который обладает свойством глобальной уникальности. Во внутренних API все документы адресуются уже через storekey.

В DOS применяется стриминговая запись. В рамках обработки одного запроса на запись поток данных, который поступает на сервер, частично буферизуется в оперативной памяти и только по накоплению определенного объёма сохраняется в персистентном хранилище. Полной буферизации крупных файлов не происходит никогда, поскольку это привело бы к чрезмерным расходам RAM. Частичная буферизация данных (но не только она) предопределяет в модели данных DOS появление чанков крупных байтовых последовательностей. Каждая версия документа может состоять из одного или нескольких чанков.

Чанки, в свою очередь, с помощью специальных алгоритмов разделяются на сегменты более короткие байтовые последовательности. Они выступают в роли элементарных единиц хранения в DOS. У сегментов тоже есть свои идентификаторы, ими являются хеши от их содержимого. В каком-то смысле, внутренним адресом данных в DOS являются сами данные (эта идея не нова и используется, например, в P2P-файловом хранилище IPFS).

Все документы, версии, чанки и сегменты в совокупности формируют внутренние метаданные DOS. Для персистентного хранения метаданных (документов и сегментов) вводятся индексы. В качестве хранилища индексов используется широко известная встраиваемая key-value база данных RocksDB. Индексы с метаданными должны размещаться на быстрых SSD дисках.

В свою очередь раздробленные на сегменты данные размещаются в бэкендах хранения на медленных HDD дисках. На ранних этапах разработки для этого использовался простой файловый бэкенд, в котором каждое тело сегмента хранилось в отдельном файле. Впоследствии был разработан более совершенный блобовый бэкенд, в котором сегменты объединяются в крупные файлы (блобы), тем самым экономятся файловые дескрипторы и снижается количество переключений контекста между пространством пользователя и пространством ядра.

Рис. 2. Иерархия внутренних сущностей DOS.

Дедупликация в DOS

В нашей системе дедупликация реализована на двух разных уровнях: на уровне сегментов (блочная дедупликация); на уровне версий документов.

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

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

Однако есть и минусы: существенные накладные расходы на поддержание счётчиков ссылок. Например, одна ссылка потребует от 8 до 40 байт, поэтому для очень популярных сегментов с сотнями тысяч ссылок такие счётчики могут достигать размера в несколько десятков мегабайт. Индекс сегментов раздувается, тяжёлые сегменты становятся слишком большими для пересылки по сети, в результате начинает возрастать время отклика. Для решения этой проблемы приходится вводить сложную систему шардирования счётчика ссылок.

В результате хеширования содержимого сегмента создается идентификатор, обладающий свойством глобальной уникальности. Коллизии идентификаторов сегментов неизбежно приведут к повреждению данных, поэтому для хеширования используется криптографическая функция BLAKE3. Она вносит весомый вклад в потребление CPU при обработке запроса на запись. В случае, если клиент готов ради экономии CPU пожертвовать глобальной уникальностью (например, для короткоживущих данных в небольших инсталляциях хранилища), он может выбрать более быстрые хеш-функции, не обладающие свойством криптографической стойкости.

Второй способ дедупликации несёт значительно меньше накладных расходов, но требует более глубокого вовлечения клиента. Внутрь версии документа встроена очень компактная структура счётчика копий (copy counter). Клиенту DOS предоставляются методы для проверки наличия версии документа и модификации её счётчика копий. Если бизнес-логика клиента позволяет выявить ранее записанную версию, то её повторная запись сводится лишь к дешёвой операции инкремента счётчика копий. В почтовой системе именно так реализовано сохранение партов письма: клиент использует хеш от содержимого парта для построения идентификатора документа, проверяет наличие документа с таким идентификатором и при его наличии просто увеличивает счётчик копий нужной версии на единицу. Иммутабельность данных версии документа даёт гарантию того, что все накопленные инкременты счётчика копий версии относились к одним и тем же данным.

Дедупликация на уровне версий документов фактически обеспечивает ещё один (очень экономичный) вариант записи, при котором оптимизируется не только дисковое пространство и операции ввода-вывода, но ещё и CPU и RAM, а также снижается нагрузка на сеть за счёт облегчения запросов.

Рис. 3. Два уровня дедупликации данных в DOS.

Отказоустойчивость

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

Для борьбы с этой проблемой в хранилищах самых разных классов (компакт-диски, RAID-массивы, промышленные СХД) традиционно используются коды коррекции ошибок. Наибольшую популярность получили коды Рида-Соломона (Подробнее в статьях на Хабре: 1, 2, 3).

В DOS во время записи чанк разбивается на заданное количество сегментов равного размера (data segments, d). С помощью кодирования Рида-Соломона из сегментов данных генерируются избыточные сегменты (parity segments, p). При потере любые p из d + p сегментов могут быть восстановлены из имеющихся d с помощью обратной операции. Таким образом, отказоустойчивость приобретается путём дополнительных расходов дискового пространства. Конкретные значения параметров определяются на этапах внедрения и эксплуатации системы. Исходя из потребностей в отказоустойчивости инсталляции, пользователи могут выбирать между стоимостью хранения и возможностью восстановления (часто встречаются комбинации d + p = 2 + 1 и d + p = 5 + 2).

Для гарантии восстановления каждого чанка DOS размещает все входящие в его состав сегменты на бэкенды, которые находятся на независимых HDD. Таким образом, при выходе из строя одного диска не произойдет повреждение сразу нескольких сегментов одного чанка.

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

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

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

Конвейерная обработка данных

Во время обработки запроса на запись DOS буферизует первые несколько килобайт из входящего потока и отправляет их в библиотеку сигнатурного анализа файлов. Оттуда хранилище получает вычисленный MIME-тип данных. В контексте DOS все возможные MIME-типы подразделяются на группы форматов. Для каждой из групп в DOS организован отдельный конвейер преобразований (pipeline) и разные оптимизации.

Текстовые данные хорошо сжимаются. Для некоторых текстов с помощью алгоритмов ZSTD, Snappy, Brotli удаётся добиться более чем двукратного сжатия. Это существенная экономия дискового пространства и операций ввода-вывода, и она стоит дополнительных затрат CPU на компрессию и декомпрессию.

Для текстовых данных также можно повысить вероятность обнаружения дубликатов с помощью алгоритма content-defined chunking (CDC). Сущность алгоритма заключается в вычислении хеша Рабина в небольшом окне, скользящем по входящему потоку данных. В позициях, на которых вычисленный хеш принимает определённое значение, ставится граница между чанками. В результате поток разбивается на небольшие чанки разных размеров, однако при этом повышается вероятность обнаружения аналогичных чанков в других версиях с частично совпадающим содержимым.

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

Рис. 4. Различия в конвейерной обработке бинарных и текстовых данных в DOS.

Таким образом, конвейер для бинарных данных настроен на повышение пропускной способности сервера и на снижение времени отклика. А конвейер текстовых данных оптимизирует уровень потребления дискового пространства и IOPS ценой повышенного потребления CPU. Насколько же эффективны эти оптимизации?

Производительность конвейерной обработки данных

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

Параметры эксперимента

В систему сохраняется 10000 писем. Письма состоят только из текста и не включают в себя какие-либо бинарные объекты. Медианный размер письма около 500 Кб. При этом 70% писем имеют 50% совпадающих данных, остальные полностью уникальны. Полностью эквивалентные письма отсутствуют, поскольку они дедуплицировались бы на уровне счётчиков копий и сильно завысили бы итоговый результат. Настройки кодирования Рида-Соломона d + p = 2 + 1, избыточность данных составляет 50%.

Результат эксперимента

При прохождении через текстовый конвейер объем данных изменяется следующим образом: Входящий поток разделяется на чанки и компрессируется со средним коэффициентом сжатия 2.3. Затем кодирование Рида-Соломона увеличивает количество данных в 1.5 раза. Дедупликация cегментов (блочная дедупликация) снижает полученный с учётом избыточности объем данных в 1.17 раза.

Итоговое отношение записанных данных к хранимым данным составляет 2.3 * 1.17 / 1.5 = 1.79. При этом в нашей модели количество метаданных составляет 0.2% от количества данных, однако этот показатель очень вариативен и сильно зависит как от настроек конвейера, так и от характера и количества самих данных.

Удаление данных

В условиях совместного владения сегментами удаление версии документа становится нетривиальной операцией. Её нужно проводить очень аккуратно, чтобы, с одной стороны, не удалить данные, которые ещё кому-то нужны, а с другой не допустить утечек дискового пространства. Связи между всеми сущностями в DOS можно представить в виде большого ациклического ориентированного графа, в котором начальными вершинами являются документы, а конечными вершинами - сегменты. Также есть и промежуточный уровень версий документов. Для каждой из сущностей действуют свои правила удаления: Документ удаляется, если не содержит версий. Версия документа удаляется, если её счетчик копий равен нулю либо истекло время её жизни (TTL). Сегмент удаляется, если не имеет ссылок со стороны версий документов.

Клиент напрямую работает лишь с версиями документов; доступный клиенту метод удаления позволяет декрементировать счётчик копий у конкретной версии. Далее в действие приходят два асинхронных сборщика мусора (GC), один из которых проходит по индексу документов, а другой по индексу сегментов. GC документов удаляет версии с обнулённым счётчиком копий или истекшим TTL, собирает пустые документы и очищает счётчики ссылок сегментов от идентификаторов удаляемых версий. GC сегментов удаляет сегменты с пустыми счётчиками ссылок. Оба GC запускаются в фоновом режиме в периоды, свободные от пользовательской нагрузки.

Рис. 5. Иллюстрация работы сборщиков мусора (1 - граф связей в исходном состоянии; 2 - клиент декрементирует до нуля счётчик копий у одной из версий; 3 - GC документов удаляет версию и владеющий ей документ, GC сегментов удаляет сегмент без ссылок; 4 - граф связей в итоговом состоянии).

Заключение

Хранение больших объёмов электронной почты - крайне непростая и ресурсоёмкая задача. Однако производительность и экономическую эффективность почтовой системы можно значительно увеличить, если реализовать дедупликацию и компрессию электронных писем. Если вам близка эта тема, и вы тоже хотите создавать уникальные российские программные продукты, то приходите на работу в МойОфис. Прямо сейчас нашей команде требуется разработчик бэкенда откликайтесь, возможно, это именно вы.

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

Подробнее..

Перевод Инженерная надежность и отказоустойчивость распределенной системы

08.06.2021 16:18:29 | Автор: admin

Это гостевая публикация отПэдди Байерса (Paddy Byers), сооснователя и технического директораAbly платформы для стриминга данных в реальном времени. Оригинал статьи опубликован вблоге Ably.

Люди хотят быть уверены в надежности используемого сервиса. Однако в реальности отдельные компоненты неизбежно отказывают, и у нас должна быть возможность продолжать работу, несмотря на это.

В этой статье мы подробно рассмотрим концепции надежности и отказоустойчивости, которые стали определяющими при разработке платформы Ably.

Для начала дадим несколько определений:

Надежность cтепень того, насколько пользователи могут положиться на продукт или сервис для решения своих задач.Доступностьиустойчивостьявляются видами надежности.
Доступность степеньготовностипродукта или сервиса к эксплуатации по требованию. Это понятие часто сcодят к обеспечению необходимого излишка ресурсов с учетомстатистически независимых отказов.
Устойчивость cпособность продукта или сервисасоответствовать заявленным характеристикамв процессе использования. Это значит, что система не просто готова к эксплуатации: благодаря дополнительным мощностям, предусмотренным в ходе проектирования,она может продолжать работать под нагрузкой, как и ожидают пользователи.
Отказоустойчивость способность системы сохранятьнадежность(доступность и устойчивость) при отказе отдельных компонентов или сбоях в подсистемах.

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

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

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

Доступность, устойчивость и состояние компонентов системы

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

В физическом мире традиционно различают:

  • ситуации, угрожающие доступности, когда можно остановить и затем возобновить работу сервиса например, остановить автомобиль, чтобы сменить покрышку;

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

Разные требования к обеспечению бесперебойной работы определяют способ удовлетворения потребности в дополнительных мощностях.

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

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

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

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

Отказы неизбежны и естественны

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

В отличие от физического мира, отказы в цифровых системах обычно не определяются двумя состояниями. Обычные показатели устойчивости компонентов (например, среднее время безотказной работы) к таким системам не применимы. Сервисы выходят из строя постепенно, после нескольких сбоев. Классический пример здесь задача византийских генералов.

Предположим, что один из компонентов системы начал работать с перебоями и выдавать неверный результат. Или внешние партнеры не сообщали вам об отказе, пока ситуация не стала серьезной, и это сильно осложнило вашу работу.

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

Сервисы без сохранения состояния

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

Таким объектам достаточно иметь несколько независимо доступных компонентов, чтобы сервис продолжал работать. Без привязки к состоянию устойчивость отдельных составляющих не имеет значения.

Однако простого наличия дополнительных ресурсов недостаточно их нужно эффективно использовать. Необходимо иметь возможность определять их доступность и распределять между ними нагрузку.

Таким образом, надо задать себе следующие вопросы:

  • Как сохранить работоспособность системы после разных типов отказов?

  • Какой уровень избыточности возможно обеспечить?

  • Какие ресурсы и показатели производительности необходимы, чтобы его поддерживать?

  • Каких эксплуатационных расходов требует управление этим уровнем избыточности?

Выбранное решение должно удовлетворять следующим критериям:

  • Требования клиентов к высокой доступности сервиса

  • Уровень эксплуатационных расходов для бизнеса

  • Инженерная целесообразность

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

Чтобы увеличить степень статистической независимости отказов в Ably,мы распределили мощности по множественным зонам доступности в разных регионах. Оказывать услуги из нескольких зон доступности в одном регионе проще AWS делает это практически без усилий. Зоны доступности в целом хорошо позволяют поддерживать независимость отказов, но их создание само по себе предполагает обеспечение необходимой избыточности для поддержания высокого уровня доступности.

Однако это еще не все. Мы не можем полностью положиться на один регион иногда несколько зон доступности отказывают одновременно или все они становятся недоступными из-за локальных проблем со связью. А иногда региональные ограничения мощности делают поддержку сервисов невозможной. Поэтому мы обеспечиваем доступность нашего сервиса, предоставляя его из множества регионов. Это лучший способ гарантировать статистическую независимость отказов.

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

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

Сервисы с сохранением состояния

В Ablyустойчивость определяется бесперебойной работойсервисов с сохранением состояния, и добиться этого намного сложнее, чем просто обеспечить доступность.

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

Самая простая аналогия здесь полет на самолете. Если он упадет, это будет катастрофой, потому что вы в своем текущем состоянии находитесь в этом конкретномсамолете. Так что именно от неговы ожидаете бесперебойной работы. В случае отказа состояние будет потеряно, и вы не сможете пересесть в другой самолет.

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

В Ably мы предоставляем достаточно резервных мощностей для ресурсов без сохранения состояния, чтобы удовлетворить требования клиентов к доступности. Однако объектам с сохранением состояния необходимы не только дополнительные ресурсы, но и четкие инструкции по их использованию, чтобы обеспечить гарантированную функциональную бесперебойность сервиса.

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

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

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

Когда клиент отправляет сообщение в Ably, сервис принимает его и уведомляет, была попытка публикации успешной или нет. В этом случае главный вопрос в контексте обеспечения доступности будет таким:

В течение какой доли времени сервис может принимать (и обрабатывать) сообщения, а не отклонять их?

Минимальный целевой показатель доступности для нас 99,99; 99,999 или даже 99,9999%.

Если вы попытались опубликовать сообщение и мы уведомили вас, что попытка не удалась, это говорит онехватке доступности. Это плохо, но, по крайней мере, вы в курсе ситуации.

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

Архитектурный подход к обеспечению устойчивости

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

Размещение роли с сохранением состояния

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

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

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

Чтобы как можно более равномерно распределить каналы по доступным ресурсам, мы используем последовательное хеширование. При этом соответствующая служба обзора кластеров обеспечивает согласование между такими параметрами, как работоспособность узлов и их членство в хеш-кольце.

Это становится возможным благодаря последовательному алгоритму размещения в хеш-кольце

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

Выявить, хешировать, продолжить

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

Даже если роль не была выполнена и произошла потеря состояния, нужно обеспечить его поддержку (с необходимой избыточностью), чтобы восстановить непрерывное выполнение роли. Именно эта непрерывность позволяет сервису сохранять устойчивость к отказам такого рода. Состояние всех сообщений в очереди сохраняется во время перемещения роли. Если вместо этого у нас получилось просто переназначить роль, сохранив состояние, значит, мы обеспечили доступность сервиса но не егоустойчивость.

Слой обеспечения постоянства канала

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

Во-первых, данные о приеме сообщения записываютсякак минимум в двух разных зонах доступности. Во-вторых, к этим зонам доступности предъявляются те же требования избыточности при последующем процессе обработки данных. Это главный принцип работы слоя обеспечения постоянства в Ably: мы записываем сообщение в нескольких местах и следим за тем, чтобы процесс записи был транзакционным. Мы прекращаем его, только убедившись, что попытка записи быланеудачнойилиоднозначно успешной. Зная это, мы можем быть уверены, что дальнейшая обработка будет произведена даже при отказе ролей, ответственных за этот процесс.

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

Благодаря нашей математической модели при отказе узла мы точно знаем, сколько времени уйдет на выявление причины сбоя, достижение консенсуса и последующего перемещения роли. Эти данные вкупе с частотой отказов для каждой зоны доступности позволяют создать вероятностную модель возникновения комплексного сбоя, который может привести к потере текущего состояния сервиса. Описанные базовые принципы позволяют нам гарантироватьустойчивость работы сервиса с вероятностью 99,999999%.

Вопросы внедрения отказоустойчивости

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

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

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

Это классическая проблема достижения консенсуса: члены кластера, каждый из которых сам может пережить отказ, должны договориться о статусе одного из них. Протоколы достижения консенсуса, такие какRaft или Paxos, широко известны и имеют прочную теоретическую базу. Однако на практике в них существует ряд ограничений, касающихся масштабирования и ширины канала связи. В частности, они не эффективны в сетях, которые охватывают множество регионов, потому что их производительность падает, как только задержка при передаче данных между одноранговыми узлами становится слишком большой.

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

Работоспособность не определяется двумя состояниями

Классическая теория, которая легла в основу разработки протоколов Paxos и Raft, базируется на том, что при отказе или проблемах с работоспособностью сущности не просто сбоят или перестают отвечать на запросы. Вместо этого они могут выдавать отдельные симптомы неполадок, такие как отклонение запросов, высокий процент ошибок или в целом нестабильное и некорректное поведение (см.Задача византийских генералов). Эта проблема стоит очень остро и касается даже взаимодействия клиентов с сервисом.

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

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

Проблема доступности ресурсов

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

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

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

Но что если отказ произошел как раз из-за нехватки ресурсов процессора или памяти? Вы пытаетесь решить проблему, но для этого нужныпроцессор и память! Таким образом, есть множество областей, в которых необходимо иметь достаточный запас дополнительных ресурсов, чтобы вовремя принимать меры противодействия отказам.

Проблема масштабирования ресурсов

Добавим к сказанному выше, что проблема может возникнуть не только с доступностью ресурсов, но и вследствие частоты запросов на их масштабирование. В стабильной, работоспособной системе у вас может бытьNканалов,Nподключений,Nсообщений иNмощностей для их обработки. Теперь представим, что произошел отказ, который привел к сбою в работе кластера изNинстансов. Если объем работ, необходимых для компенсации сбоя, составляет N, то затраты на поддержку резервных мощностей примут и вовсе неподъемный объем, так что единственным возможным решением станет переключение на другой регион или кластер.

Тривиальные механизмы обеспечения отказоустойчивости могут демонстрировать сложность O(N) или даже хуже, и это необходимо учитывать при поиске решения проблемы. Это еще одно напоминание о том, что отказ может произойти не только в результате поломки его причиной может стать слишком большой масштаб, сложность инфраструктуры и другие факторы нерационального использования ресурсов.

Заключение

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

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

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

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

Сервис Ably включает множество слоев, на каждом из которых применяется целый ряд механизмов обеспечения отказоустойчивости. Перечень инженерных проблем, с которыми мы столкнулись, включал в числе прочего размещение ролей с сохранением состояния, выявление отказов, хеширование и возможность постепенного восстановления сервиса. Мы также гарантируем непрерывность работы сервиса на слое обеспечения постоянства: например, то, что дальнейшая обработка сообщения будет продолжена, после того как мы подтвердили его получение.

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

Разрабатывая платформу Ably с учетом описанных здесь принципов, мы стремились создать лучшее в своем классе корпоративное решение. Поэтому сейчас мы можем обеспечить клиентам гарантированно высокий уровень обслуживания благодаря доступности, устойчивости, надежности и отказоустойчивости нашего сервиса.


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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru