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

Казалось бы, проекты в Kubernetes можно разделять просто по неймспейсам. В таком случае проекты нужно разделять вручную, прописывая множество правил, RBAC, NetworkPolicy и т.д. Но при этом отсутствует нужная изоляция проектов. Современные крупные проекты используют несколько неймспейсов. Помимо этого на одном кластере может “жить” несколько проектов сразу, либо dev и preprod одного и того же проекта. Управлять этим всем вручную становится проблематично. Выход - разделение проектов по тенантам.

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

Тенанты в платформе контейнеризации

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

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

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

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

Как мультитенантность выглядит в dBrain

В текущей реализации платформы dBrain мы создали пользовательский ресурс (CRD), который называется tenants.dBrain.cloud. В качестве примера - два тенанта: Core и Extra. Для каждого тенанта отображается его статус (активный или неактивный), квоты неймспейсов, текущее количество неймспейсов в тенанте и список самих неймспейсов.

В YAML-файле это выглядит следующим образом: мы определяем тип ресурса (kind: Tenant), в рамках которого перечисляются неймспейсы, принадлежащие данному тенанту.

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

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

Что всем управляет?

Управлением тенантами занимается наш самописный мультитенант-контроллер, который размещен в неймспейсе kube-system. Этот контроллер отслеживает создание или редактирование CRD тенантов и применяет соответствующие изменения к кластеру. То есть контроллер создает неймспейсы, “вешает” на них NodeSelector и LimitRange, а также настраивает управление доступом на основе ролей (RBAC) для определенных групп пользователей. Речь про три группы - admin, view и edit, потому что они мапятся со стандартными группами Kubernetes.

Это только начало пути, мы продолжаем работу над развитием мультитенантности. Ведется разработка Network Policy, которые позволят изолировать тенанты друг от друга на сетевом уровне, чтобы они физически не могли “ходить” друг к другу. Такое решение предотвратит ошибки, связанные с несанкционированным доступом подов из одной среды (например, dev) к подам из другой среды (например, preprod) и наоборот.

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

Права доступа: как мы приручали Keycloak

В платформе dBrain права доступа пользователей организованы через Keycloak. Инициализация Keycloak представляет собой K8s job, которая запускается при старте кластера. Настройка автоматизированной инициализации Keycloak оказалась непростой.

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

По умолчанию создаются два клиента с правами администратора. Авторизация клиентов в kube-api, kubectl и Grafana осуществляется через Keycloak в платформе dBrain. После этого пользователь сможет авторизоваться в Gangway кластера и получить инструкцию по установке kubectl и настройке kubeconfig для доступа к этому кластеру. При этом пользователь сможет попасть только в доступные его статусу тенанты.

Для каждого тенанта создается клиент Grafana, один общий для всех клиентов скоуп Grafana и пользователи с ролями tenant-admin, tenant-viewer, tenant-editor, tenant-Grafana-admin (суперпользователь). Также создаются группы, к которым привязываются пользователи с соответствующими правами доступа. Для главного суперадмина по мониторингу (группа с тенантом Core) привязываются все пользователи с ролью tenant-Grafana-admin, что дает им полный доступ ко всем тенантам. Остальные пользователи имеют ограниченные права доступа в зависимости от их роли (admin, view или edit).

Наш контроллер пишет подробные логи своей работы. При запуске он проводит полную синхронизацию собственного состояния с состоянием кластера. Затем он последовательно проходит по всем неймспейсам каждого тенанта и создает для них стандартные LimitRange. Если обнаруживаются внесенные вручную изменения, контроллер корректирует LimitRange, RoleBindings и ClusterRoleBindings и применяет их снова. Таким образом, для всех неймспейсов создаются стандартные ClusterRoleBindings. В dBrain группы пользователей из тенантов мы маппим в группы Keycloak. Например, CoreAdmin с соответствующими ролями в кластере, такими как admin, view или edit, для конкретного тенанта и неймспейса. Это позволяет автоматически применять всю описанную логику к созданию новых неймспейсов и тенантов.

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

От проб и ошибок к оптимальной конфигурации

При настройке Keycloak мы столкнулись со сложностями: пришлось методом проб и ошибок определять параметры, необходимые для передачи прав доступа в Grafana. Изначально не было четкого понимания структуры, приходилось многократно менять подходы. Мы начали с создания одного клиента, одного скоупа и нескольких групп и пользователей для каждого тенанта. Затем мы перешли к созданию отдельного клиента Grafana для каждого тенанта, одного общего скоупа Grafana и набора пользователей с разными ролями для каждого тенанта.

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

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

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

В процессе рефакторинга мы создали дополнительную ConfigMap с тремя вложенными файлами-шаблонами. Эти шаблоны генерируются во время деплоя кластера с помощью Ansible и содержат всю необходимую информацию о клиентах, мапперах и скоупах. Сгенерированные YAML-файлы размещаются в папке проекта инициализации Keycloak. Это позволило упорядочить хранение данных и упростить их использование.

Для каждого клиента Grafana генерируются уникальные секреты, необходимые для аутентификации. Ранее эти секреты хранились централизованно, внутри конфигурационного файла мониторинга, и использовались для взаимодействия с Keycloak. Теперь же подход изменился. Секреты генерируются непосредственно при инициализации Keycloak, внутри самой системы. Затем эти секреты передаются в secrets Kubernetes, откуда конфигурация мониторинга их извлекает и использует для предоставления доступа. Таким образом, аутентификация стала более децентрализованной и безопасной.

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

Хотели бы узнать больше о технических аспектах реализации мультитенантности в dBrain? Или, может быть, у вас есть свой опыт в подобных проектах? Пишите в комментариях.

Читайте также:

Комментарии (0)