Keycloak HA cluster
Last updated
Last updated
https://habr.com/ru/companies/tuturu/articles/766284/
Разворачивая у нас в tutu Keycloak, мы столкнулись с необходимостью создания отказоустойчивого кластера. И если с БД всё более-менее понятно, то вот реализовать корректный обмен кешами между Keycloak оказалось довольно непростой для настройки задачей.
Мы упёрлись в то, что в документации Keycloak описано, как создать кластер, используя UDP-мультикаст. И это работает, если у вас все ноды будут находиться в пределах одного сегмента сети (например, ЦОДа). Если с этим сегментом что-то случится, то мы лишимся Keycloak. Нас это не устраивало.
Необходимо сделать так, чтобы ноды приложения были географически распределены между ЦОДами, находясь в разных сегментах сети.
В этом случае в документации Keycloak довольно неочевидно предлагается создать свой собственный кастомный JGroups транспортный стек, чтобы указать все необходимые вам параметры.
Бонусом приложу shell-скрипт, написанный для Consul, который предназначен для снятия анонсов путём выключения bird и попытки восстановления приложения.
Нами была выбрана инсталляция без контейнеризации, приложение завёрнуто в systemd-сервис.
Keycloak может принять конфигурацию из четырёх разных источников:
CLI: kc.sh --key=value.
Переменная окружения: KC_KEY=value.
Файл конфигурации: key=value.
Файл Java KeyStore: kc.key=value.
Когда в туториале будет заходить речь про добавление переменной в конфигурацию, то подразумевается, что вы сами выбираете удобный для вас вариант.
В туториале я буду описывать передачу параметров через файл конфигурации.
Нода keycloak1.
Keycloak версии 20, завёрнутая в systemd-сервис.
Интерфейс eth0 с локальным IP-адресом виртуалки. Каждой ноде этот адрес должен быть доступен.
Интерфейс eth1, в котором через bgp анонсируется anycast IP-адрес.
Отказоустойчивая база данных за пределами Keycloak, к которой мы подключаем приложение.
Сделать Keycloak отказоустойчивым и геораспределённым.
Нам нужно создать кластер, в котором можно жёстко прибить адреса нод в конфигурации.
Для этого надо создать custom transport stack.
TCPPING
Остановим Keycloak.
Скопируем файл conf/cache-ispn.xml в новый файл conf/custom-cache-ispn.xml.
Добавим в секцию infinispan следующее:
stack name ― имя стека, который мы потом используем в секции transport. Можно указать что угодно. Имя стека будет писаться в логах.
initial_hosts ― перечисляем IP-адреса с портами всех наших Keycloak-нод.
port_range ― TCPPING будет пытаться связать с каждой из нод кластера, начиная с указанного порта + port_range. В нашем случае будет использоваться только порт 7800.
stack.combine ― способ изменения параметров протокола. REPLACE заменяет протокол.
stack.position ― протокол, который мы меняем.
Теперь надо в конфигурации задать с помощью переменной cache-config-file наш .xml-файл, а также переменной http-host указать anycast-адрес (cache=ispn ― это дефолтное значение):
Из-за того, что мы используем anycast-адрес, надо указать IP-адрес хоста, по которому infinispan будет слушать порт 7800. Для этого при запуске сервера нам надо явно задать основной IP-адрес ноды:
После этого мы должны увидеть в логах, что JGroups запускается со стеком add_tcpping:
При запуске остальных нод с такой конфигурацией мы увидим, что кластер обнаружил новый хост и добавил его:
Готово!
Понять, что мы сейчас настроили в .xml-файле, нам помог дефолтный конфиг стека TCP, находящегося по пути:
Там мы можем увидеть, что в качестве протокола обнаружения используется MPING. В conf/custom-cache-ispn.xml c помощью stack.position мы выбираем MPING, а с помощью stack.combine заменяем его на TCPPING.
Вы настроили anycast (у нас анонсируется адрес с помощью bird), кластеризацию, но вам надо как-то снимать анонсы, если с приложением что-то случится. Вариантов много, я рассмотрю используемый нами.
В этом туториале я не буду разбирать, как настраивать консул, рассмотрим лишь shell-скрипт, который запускается с его помощью раз в 15 секунд.
Keycloak имеет встроенный healthcheck, на его основе и построим проверку.
Чтобы включить его, надо в конфигурации задать переменную:
После этого у приложения становятся доступны следующие эндпоинты:
Будем отслеживать последний эндпоинт, так как там есть проверка подключения к базе данных. Её тоже будем отслеживать:
Также попытаемся один раз восстановить работу Keycloak ребилдом приложения:
Запуск ребилда в фоне позволяет нам запускать скрипт сколько угодно часто, чтобы как можно быстрее реагировать на упавшее приложение и выключать bird.service.
Ну и для управления всем этим безобразием создаём tmp-файл для отслеживания времени запуска восстановления:
Подробная настройка консула выходит за рамки данного туторила.
Собираем это всё вместе в скрипт:
Целиком скрипт
И настраиваем конфиг консула:
Данная конфигурация позволила очень легко масштабировать приложение и сделать его геораспределённым. Конечно это далеко не всё, что можно сделать для отказоустойчивости, но, пожалуй, необходимый минимум.
Мы живём в такой конфигурации уже более полугода, и за это время она ни разу не давала сбой. За исключением не зависящих от кластера ситуаций.
https://dantheengineer.com/keycloak-on-distributed-sql-cockroach-part-2-2/
https://infinispan.org/docs/stable/titles/server/server.html