Материалы к экзамену · Урок 2

Тенанты и доступ

Как платформа делит себя между тенантами, откуда берутся квоты и почему тенант — это не просто namespace.

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

Тенант — это объект, а не папка

В привычном Kubernetes изоляция начинается и заканчивается пространством имён. Здесь иначе: тенант (tenant) — это объект платформы, и создание его влечёт за собой целый набор последствий.

Заводите вы его так же, как всё остальное:

apiVersion: apps.cozystack.io/v1alpha1
kind: Tenant
metadata:
  name: acme
  namespace: tenant-root

Платформа в ответ создаёт пространство имён (namespace), раздаёт права, вешает сетевые политики, заводит квоты и, если попросили, поднимает внутри собственный мониторинг и хранилище.

Заодно тенант получает свой домен, и собирается он по правилу <имя>.<домен родителя>. Платформа живёт на cloud.example.com — значит, у тенанта alpha будет alpha.cloud.example.com, а у его ребёнка betabeta.alpha.cloud.example.com. При необходимости домен переопределяют вручную.

Тенанты вкладываются друг в друга

Вот что отличает эту модель от плоского списка namespace: тенанты образуют дерево.

tenant-rootкорень платформыtenant-acmeклиентtenant-betaклиентtenant-acme-devотдел клиента
Тенант живёт внутри родителя. Имя пространства имён складывается из слова tenant- и имени.

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

Путь тенантаПространство имён
root/acmetenant-acme
root/alpha/betatenant-alpha-beta
root/a/b/ctenant-a-b-c

Корень в имя не попадает — tenant-root-alpha-beta будет неверным ответом.

Отсюда же ограничение на имена: дефис в имени тенанта запрещён, потому что он занят под разделитель предков. Тенант acme-dev не существует — существует dev внутри acme, и живёт он в tenant-acme-dev.

В самом объекте эти два имени лежат в разных полях, и их различие тоже спрашивают: metadata.namespace — пространство имён родителя, там и лежит сам CR, а собственное пространство имён тенанта платформа записывает в status.namespace. Отсюда и способ перечислить детей — kubectl get tenants -n tenant-alpha покажет CR-ы, созданные внутри alpha.

Зачем это нужно: провайдер отдаёт клиенту тенант, а тот делит его между своими отделами — и не приходит к провайдеру за каждым новым окружением.

Что наследуется, а что включается

У тенанта есть ровно четыре переключателя, и на экзамене спрашивают их буквальные имена: etcd — свой кластер etcd, monitoring — свой мониторинг, ingress — свой контроллер входящего трафика с TLS, seaweedfs — своё объектное хранилище S3. Каждый можно включить или оставить выключенным.

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

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

Квоты и переподписка

Тенанту назначают предел по процессорам и памяти:

spec:
  resourceQuotas:
    cpu: 16
    memory: 32Gi

Размер ресурсов задают пресетом (preset) в формате <серия>.<размер>. Серия закрепляет соотношение процессора к памяти: t1 — 1:0.5, c1 — 1:1, s1 — 1:2, u1 — 1:4, m1 — 1:8. Размеры идут от nano до 4xlarge, так что u1.medium — это универсальная серия среднего размера.

Если рядом с пресетом явно написан блок resources, побеждает он:

resources:
  cpu: 4
  memory: 8Gi

И ловушка, ради которой всё это и спрашивают: пресеты тенантов и приложений — не то же самое, что типы экземпляров KubeVirt (instance types) для виртуальных машин с сериями U, O, CX, M и RT. Строка u1.medium допустима в обеих системах и означает разное, так что смотрите, о каком объекте вопрос.

А теперь то, на чём спотыкаются практически все, — переподписка (overcommit, cpuAllocationRatio, по умолчанию 10).

Работает она так: когда вы запускаете нагрузку с пределом в четыре процессора, платформа просит у планировщика не четыре, а предел, делённый на коэффициент — то есть четыре десятых. Гарантируется малое, а взять при необходимости можно много.

Формула короткая, и её стоит запомнить дословно:

запрос = предел ÷ cpuAllocationRatio

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

Важно, где живёт этот коэффициент: он настраивается на уровне всей платформы, а не в отдельном тенанте. Один на всех — свойством тенанта его в вариантах ответа не называйте.

Изоляция

По умолчанию тенанты друг друга не видят: сетевые политики запрещают ходить между пространствами имён. И это не переключатель — отключить изоляцию нельзя. Флаг isolated существовал до версии 1.0 и убран; если он попадётся в вариантах ответа, это ловушка.

Отрезаны поды не только от соседей. По умолчанию они не достучатся ни до kube-apiserver, ни до собственного etcd тенанта. Нужен доступ — его открывают точечно, метками на поде:

policy.cozystack.io/allow-to-apiserver: "true"
policy.cozystack.io/allow-to-etcd: "true"

Значение — строка "true", и это тоже спрашивают.

Как люди попадают внутрь

Три пути, и это тоже спрашивают.

Дашборд — веб-интерфейс платформы. Вход через Keycloak, то есть по корпоративной учётной записи, а не по отдельному паролю.

kubeconfig — файл доступа, который выдаётся тенанту. Внутри него не сертификат, а вызов внешней программы: kubectl при первом обращении открывает браузер, вы входите через Keycloak, и выданный токен действует до истечения срока. Отсюда, кстати, требование поставить kubelogin: без него kubectl не знает, как логиниться.

Terraform — тот же API, только описанием.

Откуда берётся сам файл кубконфига

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

У каждого тенанта в его пространстве имён лежит одноимённый секрет: у тенанта alpha это секрет alpha в пространстве tenant-alpha. Скрипт достаёт оттуда три вещи:

Что берётсяИз какого поляЗачем
токен доступаtokenим тенант предъявляет себя API-серверу
сертификат центра сертификацииca.crtпо нему kubectl убеждается, что сервер настоящий
пространство имён по умолчаниюnamespaceчтобы не писать -n в каждой команде

Адрес самого API-сервера скрипт берёт не из секрета, а из текущего кубконфига администратора, который скрипт запускает. Итого файл собирается из токена, CA-сертификата и адреса сервера — и в этом варианте никакого Keycloak и никакого kubelogin не требуется: токен уже внутри файла.

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

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

Тенант заведён, квоты выданы, люди внутрь пущены. Теперь им надо что-то заказать.

Что спросят на экзамене

  • Что тенант — объект платформы, а не просто пространство имён.
  • Что имя пространства имён = tenant- плюс цепочка предков через дефис, без корня.
  • Что дефис в имени тенанта запрещён.
  • Что тенанты вкладываются друг в друга, а вложенный создаётся внутри родителя.
  • Что выключенный сервис наследуется от ближайшего предка, у которого он включён, — не обязательно от родителя.
  • Имена четырёх переключателей дословно: etcd, monitoring, ingress, seaweedfs.
  • Что домен тенанта собирается как <имя>.<домен родителя>.
  • Что metadata.namespace — пространство имён родителя, а status.namespace — своё.
  • Что кубконфиг тенанта скрипт из документации собирает из токена, CA-сертификата и адреса сервера: первые два — из одноимённого секрета тенанта, адрес — из кубконфига администратора.
  • Формат пресетов <серия>.<размер>: t1 (1:0.5), c1 (1:1), s1 (1:2), u1 (1:4), m1 (1:8); размеры от nano до 4xlarge.
  • Что явно заданный блок resources перекрывает пресет.
  • Что пресеты тенантов — не instance types KubeVirt (U, O, CX, M, RT): u1.medium есть и там, и там.
  • Что запрос нагрузки = её предел, делённый на cpuAllocationRatio (по умолчанию 10).
  • Что cpuAllocationRatio настраивается на уровне платформы, а не отдельного тенанта.
  • Что изоляцию отключить нельзя, а флаг isolated убран в версии 1.0.
  • Имена меток дословно: policy.cozystack.io/allow-to-apiserver и allow-to-etcd, значение — строка "true".
  • Что вход идёт через Keycloak, а права — обычным RBAC.