I. Вступление
Здравствуйте, уважаемые читатели.
Сегодня поговорим о том, что делать, если хочется чтобы ваш веб-сервис был постоянно доступным для конечных пользователей, т.е. отказоустойчивым. При этом обладая минимальным количеством знаний в данной области, а также ни в коем случае не тратя огромные ресурсы (временные, финансовые и проч.) на реализацию.
II. Немного о решаемой проблеме
*Прим. автора: для тех, кто хорошо понимает, о чем речь — можно пропустить данный раздел.
Представим, что мониторинг состояния здоровья веб-сервиса (и сопутствующие уведомления при каких-либо проблемах) — несущественная задача. Тем не менее, задача обеспечения отказоустойчивости уже куда глубже и сложнее. В целях реализации оной так или иначе могут использоваться различные методы. Ниже в краткой форме приведены некоторые из них.
III.a. Изменение DNS записей веб-сервиса
Само по себе практически ничем не поможет в решении задачи обеспечения отказоустойчивости. В случае падения веб-сервиса, даже если вы оперативно поменяете IP адрес в A-записи на резервный сервер и понадеетесь что сейчас у всех всё заработает, а вы пока разберетесь с основном сервером, — это не сработает, и всякие надежды разобьются о рифы реальности.
DNS серверы, провайдеры, иные промежуточные звенья и просто конечные пользователи уже закэшировали прошлую A-запись из DNS записей вашего доменного имени. Когда они решат обновиться — крайне сложный для прогнозирования вопрос, но в худшем случае крайне не скоро (может пройти несколько суток и более). И низкий TTL кэша записей вам не поможет, ибо:
Далеко не все соблюдают TTL;
Устанавливать значение TTL близкое к нулю — лишить смысла механизм кэширования. Тем более, если он будет несколько минут, то это уже достаточно позорное время для простоя вашего сервиса.
Аналогично, в данной задаче вам не поможет и добавление нескольких A-записей. Так называемый Round-robin DNS работает совершенно непредсказуемо (как на уровне сети/операционной системы, так и на уровне браузеров).
III.b. Создание нескольких (под)доменов веб-сервиса
Практика и здравый смысл показывает, что это крайне плохое решение. Действительно, можно сделать несколько доменных адресов (или поддоменов) вашего веб-сервиса под разные серверы и раздать со словами: "при условии неработоспособности A, используйте B". Однако, пользователь не должен вручную выбирать адрес, ведь он наверняка будет постоянно ошибаться. Будет добавлять в закладки только резервный узел и не переходить обратно на основной (а что, если они разные по быстродействию/прочему приоритету?). Да и вообще, пользователь не должен знать о том, что что-то пошло не так, это не его проблемы.
III.c. Низкоуровневая работа в рамках "всемирной паутины"
Можно подумать о том, возможно ли основному и резервным серверам назначить один и тот же IP адрес. Или же оперативно на сетевом уровне переключать некий "плавающий" IP между серверами в зависимости от их доступности. Первое возможно, напр., через anycast. Второе возможно, напр., через VRRP с keepalived и имея свою подсеть IP адресов (фактически, для анонсирования минимальный размер /24 (IPv4) или /48 (IPv6) для подсети).
Но всё это, мягко говоря, дорого/сложно (особенно учитывая что IPv4 заканчиваются) хоть и при должном старании, крупных финансовых вливаниях вполне надежно. Тем не менее, такую схему многим реализовать практически нереально (малые и средние веб-проекты, например). Также можно заказать плавающий IP адрес у какого-то ДЦ-провайдера (на рынке бывают предложения даже среди нескольких ДЦ одного провайдера) — однако, зависеть от какого-то провайдера, в т.ч. от ценовой политики, географии, инфраструктуры его ДЦ, и уповать на его волю желание не велико.
III.d. Промежуточные выводы
Таким образом, выше вкратце и несколько скомкано описаны проблемы отказоустойчивости веб-сервисов. Возможный вариант комплексного решения описывается в последующих главах текущей статьи.
III. Что же делать?
*Прим. автора: в данной главе идёт речь о Cloudflare, но это не является основным объектом статьи.
Небезызвестная компания Cloudflare (по некоторым оценкам, проксирует через свою инфраструктуру почти 20% всего интернета) предлагает мигрировать управление DNS к ним, вместе с тем гарантируя совершенно бесплатно следующее:
Our authoritative DNS is the fastest in the world, offering DNS lookup speed of 11ms on average and worldwide DNS propagation in less than 5 seconds.
Работает с точки зрения использования это дюже просто: у регистратора своего доменного имени вы прописываете NS-серверы Cloudflare, далее в панели управления последнего прописываете необходимые вам DNS-записи, не забывая поставить галочку "Proxied" напротив каждой записи (впрочем, это делается автоматически). И всё, оно уже работает.
Но что будет, если, например, теперь осуществить ping по вашему доменному имени? Вы не увидите там своего IP адреса, там будет IP адрес от Cloudflare. Ибо это работает примерно по следующей схеме:
Веб-браузер пользователя запрашивает ваш сайт по доменному имени;
Доменное имя через DNS разрешается в IP адрес Cloudflare;
Веб-браузер осуществляет HTTP запрос по разрешенному ранее IP адресу, заботливо прикладывая к оному Host header;
Cloudflare принимает запрос, связывает его с вашей зоной, анализирует DNS записи которые вы ранее там прописали, и далее работает в качестве обратного прокси-сервера между сервером из A-записи и конечным клиентом.
Благодаря этому и возможно "мгновенное" переключение DNS через Cloudflare. Потому что фактически реальные DNS записи вашего доменного имени не меняются, а меняются настройки реверсного прокси-сервера Cloudflare, которые оформлены в виде DNS записей. К слову, у данного подхода есть ещё ряд преимуществ, и они также бесплатны:
Ввиду того, что вы скрываете реальные IP адреса своих серверов за Cloudflare, это повышает их безопасность и приватность;
Global content delivery network (CDN)
Также вы получаете превосходную защиту от DDoS-атак "из коробки". Опять же, потому что злоумышленники не знают ваших реальных IP адресов.
"Разве Cloudflare не является единой точкой отказа?" — Спросите вы. Фактически, нет. Там уже позаботились о решении проблемы отказоустойчивости (причем сразу несколькими рабочими промышленными методами, включая anycast, round-robin и т.д.). Работоспособность этой инфраструктуры нарушается крайне редко. То есть, используя искомое, вы вполне сможете приблизиться по uptime своего веб-сервиса к таким гигантам как Google, Amazon и т.д.
Резюмируя, отметим: мы можем использовать это в качестве практически мгновенного переключения DNS записей. Однако, делать это вручную — не вариант, но это уже дело техники. Ведь главное, что мы получили инструмент для крайне быстрой смены своих серверов для доменного имени (A-запись в DNS Cloudflare). Об автоматизации этого процесса (и не только) в главе ниже.
IV. Как же быть?
Естественным образом я подвожу вас к моему проекту с открытым исходным кодом под названием Dhaf. Он представляет собой легковесный распределенный высоко-доступный failover (т.е. представляет собой кластер) для веб-сервисов, написанный на современном кросс-платформенном C# .NET (поддерживаются Linux, Windows и macOS).
Исторически, данная разработка начиналась как простенький автоматический переключатель DNS-записей в Cloudflare в зависимости от доступности указанных в нём серверов для собственных нужд. Однако, с течением времени, это переросло в failover для веб-сервисов общего назначения, где вышеописанный функционал — лишь одна из возможностей.
Вкратце, в архитектуре dhaf заложены некие "переключатели" (switchers) — различные провайдеры для управления точками входа в веб-сервис, средства проверки работоспособности (health checkers) и провайдеры для уведомлений (notifiers). Он чрезвычайно гибкий, расширяемый и простой в использовании и настройке. У кластера существует т.н. лидер и ведомые узлы. Все из них занимаются мониторингом, но управляет состоянием и уведомлениями только лидер. Стоит отметить, что в качестве distributed configuration store (DCS) используется etcd. Какие основные возможности? А вот какие:
Failover (при необходимости dhaf автоматически и прозрачно переключит ваши серверы для конечных клиентов);
Switchover (ручное переключение ваших серверов) — полезно для отладки и выполнения обслуживания серверов незаметно для конечного клиента;
Поддержка нескольких различных сервисов в одном кластере dhaf (которые могут "располагаться", например, на поддоменах вашего доменного имени);
Поддержка использования нескольких провайдеров уведомлений в одном dhaf кластере;
Точки входа (entry points) для веб-сервиса располагаются по приоритету (т.е. если снова стал доступен некогда упавший сервер и он приоритетнее текущего — также произойдет автоматическое переключение, т.н. switching);
Провайдеры уведомлений об изменениях состояния ваших веб-сервисов и/или dhaf кластера. Из коробки уже доступны следующие:
Действительно гибкая настройка health checks для сервиса;
Вся пользовательская конфигурация в "дружелюбном" формате YAML;
Только вы решаете, откуда проверять доступность ваших веб-сервисов;
Удобный CLI для проверки и управления кластером dhaf;
REST API для проверки и управления кластером dhaf;
Возможность легко создавать собственные провайдеры для switchers, health checkers и notifiers на C#, подключая их динамически в качестве расширений;
Возможность подключения собственных исполняемых скриптов (напр., на языке Python) в качестве switchers и health checkers;
Является распределенным высоко-доступным кластером.
Какие преимущества от того, что dhaf — кластер? Возможно, это для кого-то не очевидно, так что я поясню:
Если какой-то из узлов dhaf кластера отвалится, то сам кластер по-прежнему продолжит свою работу (до тех пор, пока это возможно). Если вышел из строя лидер, то тотчас начнётся т.н. гонка за лидера, т.е. методом "кто первый того и тапки" будет определен новый лидер.
Health checks для веб-сервиса осуществляются всеми доступными узлами, и решение о (не)доступности оного является коллективным. И только если большинство считает, что сервис недоступен, то лидер кластера начинает процедуру по переключению точки входа в веб-сервис (в случае с switcher-провайдером Cloudflare он меняет A-запись в DNS Cloudflare на очередной сервер). Это элегантно решает проблему, когда один из узлов решил, что веб-сервис недоступен, хотя на самом деле это не так для реальных пользователей.
Теперь самое время рассказать о том как всё это добро запустить. Однако, это уже написано в `README.md` репозитория на Github (см. секцию "Quick Start"), и повторяться не очень хочется. Ниже показано, как может выглядит конфигурация кластера на одном из узлов (все credentials являются вымышленными):
config-n1.dhaf
dhaf:
cluster-name: habr-cr
node-name: node1
etcd:
hosts: http://111.111.1.1:2379,http://111.122.2.2:2379,http://111.133.3.3:2379
services:
- name: main
domain: habr.com
entry-points:
- name: serv1
ip: 111.101.1.1
- name: serv2
ip: 122.102.2.2
switcher:
type: cloudflare
api-token: f-PFRRRqVVVtPaaa_Z000YN3Bb222bHHb111KKKb
zone: habr.com
health-checker:
type: web
schema: https
notifiers:
- type: email
name: habr-mail
from: dhaf@habr.com
to: admin@habr.com
smtp:
server: smtp.habr.com
port: 465
security: ssl
username: dhaf@habr.com
password: 5bjAQjFGKoCIVNAc
- type: tg
token: 4105703518:BtiWEX6mKwpW0ASS2BSSG6zSkh3M04CsmiX
join-code: ox3a35kYloJrc9OY
name: habr-tg
Консольный вывод одного из узлов dhaf:
Работа с CLI может выглядеть так так:
V. Некоторые рекомендации по работе с dhaf
Ниже вы сможете обнаружить условия, при которых кластер dhaf будет работать максимально плодотворно:
Не размещайте узлы кластера dhaf на тех же серверах, где и ваш веб-сервис;
Можно (и это логично) размещать узлы etcd там же где dhaf;
Кол-во узлов etcd должно быть не менее трёх (тогда возможно пережить падение одного из узлов). Подробнее тут;
Кол-во узлов dhaf должно быть не менее двух, но, учитывая, что они обычно работают там же где и etcd, то логично их сделать также три. Тем не менее, даже при кластере из двух узлов dhaf способен пережить падение одного из узлов;
Для работы узла кластера dhaf (даже в связке с etcd) достаточно самого дешевого виртуального сервера (включая облачные), поскольку нагрузка на него минимальна в силу того что он не занимается обслуживанием кучи пользователей, — он занимается здоровьем вашего веб-сервиса;
Располагайте узлы dhaf кластера (и etcd) у разных провайдеров в разных дата-центрах. Это убережет вас от тех неприятных случаев, когда у одного из провайдеров накрылась вся его инфраструктура. Также узлы стоит географически располагать там, откуда предполагается использование вашего сервиса (т.е. с точки зрения сети "приближаем" узлы к реальным пользователям);
Точек входа для веб-сервиса в конфигурации dhaf должно быть указано не менее двух, однако, пожалуй, это очевидно;
Если у вас есть желание и/или потребность балансировать нагрузку, то dhaf не для этого. Его цель — обеспечивать рабочую точку входа в веб-сервис, и он ни в коем случае не является прокси-сервером. Таким образом, если есть необходимость вышеуказанного, то точками входа в конфигурации dhaf должны быть серверы с, напр., HAProxy (или его аналог), функционал коих, в частности, предназначен как раз для этого.
VI. Заключение
Описываемый в статье вариант для решения проблемы отказоустойчивости веб-сервисов не претендует на единственно возможный. Решение предложено, использовать его или нет — решать вам.
Учитывая, что dhaf — проект с полностью открытым исходным кодом, буду рад если вы присоединитесь к разработке. В случае обнаружения багов просьба создать bug report. Если же вам не хватает какого-то функционала, стоит создать feature request.
Вы можете присоединиться к Telegram-чату для обсуждения работы dhaf.
С уважением,
Петр Осетров.
UPD #1 от 13.10.2021: В версии dhaf v1.1.0 добавлен новый swticher provider на базе Google Cloud. Может подойти для тех, кто по каким-то причинам не хочет использовать Cloudflare. Месячный uptime гарантируется >= 99.99%. Про данный провайдер в основном readme.md есть отдельная секция "Quick start/With Google Cloud switcher provider". В целом, отмечу, что пререквизиты для такой отказоустойчивой точки входа сложнее чем для Cloudflare или Exec провайдеров (зато не требуется миграция DNS).