В этой статье мы поделимся опытом развертывания в кластере Kubernetes устойчивой и масштабируемой инсталляции популярного решения для обеспечения «единого входа» (SSO) — Keycloak в связке с Infinispan (для кэширования пользовательских метаданных). Keycloak – проект с открытым исходным кодом компании Red Hat, предназначенный для управления аутентификацией и авторизацией в приложениях, функционирующих на серверах приложений WildFly, JBoss EAP, JBoss AS и прочих web-серверах. Keycloak упрощает реализацию защиты приложений, предоставляя им бэкенд авторизации практически без дополнительного кода. За подробной информацией о том, как это осуществляется, можно обратиться к этому руководству. Как правило, Keycloak устанавливается на отдельный виртуальный или выделенный сервер приложений WildFly. Пользователи однократно аутентифицируются с помощью Keycloak для всех приложений, интегрированных с данным решением. Таким образом, после входа в Keycloak пользователям не нужно снова входить в систему для доступа к другому приложению. Аналогично происходит и с выходом. Для хранения своих данных Keycloak поддерживает работу с рядом наиболее популярных реляционных систем управления базами данных (РСУБД): Oracle, MS SQL, MySQL, PostgreSQL. В нашем случае использовалась CockroachDB — современная распределенная СУБД (изначально Open Source, а впоследствии — под BSL), которая обеспечивает согласованность данных, масштабируемость и устойчивость к авариям. Одной из её приятных особенностей является совместимость с PostgreSQL на уровне протокола. Кроме того, в своей работе Keycloak активно использует кэширование: кэшируются пользовательские сессии, авторизационные и аутентификационные токены, успешные и неуспешные попытки авторизации. По умолчанию для хранения всего этого используется Infinispan. На ней мы остановимся подробнее. Infinispan — это масштабируемая, высокодоступная платформа для хранения данных типа ключ-значение, написанная на Java и распространяемая под свободной лицензией (Apache License 2.0). Основная область применения Infinispan — распределенный кэш, но также её применяют как KV-хранилище в базах данных типа NoSQL. Платформа поддерживает два способа запуска: развертывание в качестве отдельно-стоящего сервера / кластера серверов и использование в виде встроенной библиотеки для расширения функций основного приложения. KC в конфигурации по умолчанию использует встроенный кэш Infinispan. Он позволяет настраивать распределенные кэши, чтобы репликация и перекаты данных осуществлялись без простоя. Таким образом, даже если мы полностью отключим сам KC, а потом поднимем его обратно, авторизованных пользователей это не затронет. Сам IS хранит всё в памяти, а на случай переполнения (или полного отключения IS) можно настроить сбрасывание его данных в БД. В нашем случае эту функцию выполняет CockroachDB. Клиент уже использовал KC как бэкенд авторизации своего приложения, но переживал за устойчивость решения и сохранность кэшей при авариях / развертываниях. Поэтому перед нами стояли две задачи: Обеспечить надежность/устойчивость к авариям, высокую доступность. Сохранить пользовательские данные (сессии, токены) при потенциальном переполнении памяти. Изначально KC был запущен в 1 реплике и настройками кэширования по умолчанию, т.е. использовался встроенный Infinispan, который все держал в памяти. Источником данных был кластер CockroachDB. Для обеспечения надежности потребовалось развернуть несколько реплик KC. Keycloak позволяет это сделать, используя несколько механизмов автообнаружения. В первой итерации мы сделали 3 реплики KC, использующих IS в качестве модуля/плагина: К сожалению, IS, используемый как модуль, предоставлял недостаточно возможностей для настройки поведения кэшей (кол-во записей, объем занимаемой памяти, алгоритмы вытеснения в постоянное хранилище) и предлагал только файловую систему как постоянное хранилище для данных. Поэтому на следующей итерации мы развернули отдельный кластер Infinispan и отключили встроенный модуль IS в настройках Keycloak: Решение было развернуто в кластере Kubernetes. Keycloak и Infinispan запущены в одном namespace по 3 реплики. За основу для такой инсталляции был взят этот Helm-чарт. CockroachDB разворачивалась в отдельном пространстве имен и использовалась совместно с другими компонентами клиентского приложения. Полные примеры Helm-шаблонов доступны в нашем репозитории flant/examples. КС поддерживает несколько режимов запуска: standalone, standalone-ha, domain cluster, DC replication. Режим standalone-ha является идеальным вариантом для запуска в Kubernetes, потому что легко добавлять/удалять реплики, общий конфиг-файл хранится в ConfigMap, правильно выбранная стратегия развертывания обеспечивает доступность узлов при обновлении ПО. Хотя для KC не требуется постоянного файлового хранилища (PV/PVC) и можно было выбрать тип Deployment, мы используем StatefulSet. Это делается для того, чтобы задавать имя узлов в Java-переменной jboss.node.name при настройке обнаружения узлов на основе DNS_PING. Длина этой переменной должна быть меньше 23 символов. Для настройки KC используются: переменные окружения, которые задают режимы работы KC (standalone, standalone-ha и т.д.); конфигурационный файл /opt/jboss/keycloak/standalone/configuration/standalone-ha.xml, который позволяет выполнить максимально полную и точную настройку Keycloak; переменные JAVA_OPTS, определяющие поведение Java-приложения.  По умолчанию KC запускается со standalone.xml — этот конфиг сильно отличается от HA-версии. Для получения нужной нам конфигурации добавим в values.yaml: После первого запуска можно достать из pod’а c KC нужный конфиг и на его основе подготовить .helm/templates/keycloak-cm.yaml: После получения файла переменные JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES можно переименовать или удалить, чтобы KC не пытался создавать этот файл при каждом повторном деплое. Устанавливаем JAVA_OPTS в .helm/values.yaml:  Для корректной работы DNS_PING указываем: Все остальные манипуляции проводим с .helm/templates/keycloak-cm.yaml. Подключение базы: Настройки кэшей: Настройки JGROUPS и DNS_PING: Наконец, подключаем внешний Infinispan: Подготовленный XML-файл монтируем в контейнер из ConfigMap’а .helm/templates/keycloak-cm.yaml:  Для настройки Infinispan будем использовать конфиг по умолчанию /opt/infinispan/server/conf/infinispan.xml из Docker-образа infinispan/server:12.0 и на его основе готовим .helm/templates/infinispan-cm.yaml. Первым делом настраиваем auto-discovery. Для этого устанавливаем уже знакомые нам переменные окружения в .helm/templates/infinispan-sts.yaml: … и добавляем секцию jgroups в XML-конфиг: Для корректной работы Infinispan c CockroachDB нам пришлось пересобрать образ Infinispan, добавив в него новую версию SQL-драйвера PostgreSQL. Для сборки использовалась утилита werf с таким простым werf.yaml: Добавим в XML-конфиг секцию <data-source>: В Infinispan мы должны описать те кэши, которые в KC были созданы с типом distributed-cache. Например, offlineSessions: Таким же образом настраиваем и остальные кэши. Подключение XML-конфига происходит аналогично тому, что мы рассматривали Keycloak. На этом настройка Keycloak и Infinispan закончена. Повторюсь, что полные листинги доступны на GitHub: flant/examples. Использование Kubernetes в качестве фундамента позволило легко масштабировать решение, добавляя по мере необходимости или узлы Keycloak для обработки входящих запросов, или узлы Infinispan для увеличения емкости кэшей. С момента сдачи данной работы клиенту прошло 2 месяца. Каких-либо жалоб и недостатков за этот период не выявлено. Поэтому можно считать, что поставленные цели достигнуты: мы получили устойчивое, масштабируемое решение для обеспечения SSO. Читайте также в нашем блоге: «Обзор операторов PostgreSQL для Kubernetes. Часть 1: наш выбор и опыт»; «Наш опыт с графовой базой данных Dgraph в Kubernetes»; «Миграция Cassandra в Kubernetes: особенности и решения».