Новый клиент в SaaS-платформе — это не просто запись в БД. Это namespace в Kubernetes, schema в PostgreSQL, DNS-запись, IAM-роль, S3-бакет, строчка в биллинге и Welcome-письмо. Полуручные скрипты приводят к ошибкам: тенант получает чужой S3-бакет, DNS не прописан, schema создана с неправильными правами. Каждое новое поле в форме регистрации требует правок в четырёх местах. Наш опыт показывает: автоматизация — единственный способ масштабироваться без потери качества. При 50+ клиентах ручной онбординг становится узким местом — время создания одного тенанта растёт до 2–3 часов, а уровень ошибок превышает 15%. Стоимость одного такого инцидента — до $5000.
Что происходит без автоматизации
Типичная картина: onboarding-скрипт в виде bash-файла, который инженер запускает вручную после получения тикета. В скрипте — psql -c "CREATE SCHEMA tenant_$1", aws s3 mb s3://company-tenant-$1, несколько kubectl apply. Скрипт не идемпотентен, не логируется, не откатывается при ошибке на середине. Когда kubectl apply упал из-за нехватки IP в подсети — тенант наполовину создан. Поддержка тратит час на ручной откат. Это не гипотетический сценарий — это то, с чем мы приходим разбираться в большинстве проектов на стадии «у нас 50+ клиентов и мы больше не успеваем». В одном из кейсов отсутствие отката привело к тому, что два тенанта получили общий S3-бакет с пересекающимися ключами — инцидент стоил нескольких часов восстановления и репутационных потерь.
Типичная ошибка: частичный провижининг без отката
При создании тенанта скрипт создал namespace и S3-бакет, но упал на этапе создания БД. База не создана, но namespace и бакет уже существуют. Если не предусмотрен механизм отката, поддержка вручную удаляет namespace и бакет, рискуя оставить мусор в облаке. Это типичная проблема, которую решает оркестрированный workflow.Как построить идемпотентный provisioning pipeline?
Правильная автоматизация строится на трёх принципах: идемпотентность (повторный запуск безопасен), атомарность с retry/rollback на каждом шаге и единый источник состояния. Мы используем один из двух подходов в зависимости от стека.
Подход 1: Kubernetes Operator — для платформ на K8s
Tenant описывается как CRD:
---
apiVersion: platform.company.io/v1alpha1
kind: Tenant
metadata:
name: acme-corp
spec:
plan: professional
region: eu-west-1
adminEmail: [email protected]
quotas:
cpu: "8"
memory: 16Gi
storage: 100Gi
---Оператор (на базе controller-runtime или Capsule) перехватывает создание объекта и запускает reconciliation loop: создаёт Namespace, ResourceQuota, NetworkPolicy, ServiceAccount, RoleBinding. При ошибке статус обновляется, retry автоматический. Состояние — в etcd. Время создания тенанта — 30–50 мс, что в 50 раз быстрее, чем у Terraform-подхода (до 2–3 минут).
Подход 2: Terraform + Temporal — для гибридных сред
Terraform обеспечивает декларативность и state, Temporal — оркестрацию с retry-политиками и компенсационными транзакциями.
# modules/tenant/main.tf
resource "postgresql_schema" "tenant" {
name = "tenant_${var.tenant_id}"
database = var.database_name
owner = postgresql_role.tenant.name
}
resource "aws_s3_bucket" "tenant_storage" {
bucket = "${var.company_prefix}-${var.tenant_id}-storage"
tags = {
TenantID = var.tenant_id
Plan = var.plan
}
}
resource "aws_route53_record" "tenant_subdomain" {
zone_id = data.aws_route53_zone.main.zone_id
name = "${var.tenant_id}.${var.base_domain}"
type = "CNAME"
ttl = 300
records = [var.alb_dns_name]
}
Сравнение подходов:
| Критерий | Kubernetes Operator | Terraform + Temporal |
|---|---|---|
| Идемпотентность | Встроена в контроллер | Через Terraform state |
| Rollback | Компенсирующие действия в коде | Temporal compensate-функции |
| Изоляция state | Одно etcd | Отдельный workspace/директория |
| Скорость создания | Секунды | Минуты (из-за terraform apply) |
| Подходит для | Чистый Kubernetes | Гибридные cloud-среды |
Operator даёт мгновенное создание и меньший blast radius при сбое. Terraform проще интегрируется с AWS и имеет готовые модули. Выбор — за вами, мы помогаем оценить.
Почему идемпотентность критична для provisioning?
Идемпотентность гарантирует, что повторный запуск пайплайна не создаст дублирующихся ресурсов и не вызовет ошибок. Без этого свойства случайный перезапуск после таймаута может привести к созданию двух Namespace с одинаковым именем (первый упадёт с ошибкой, второй — дубль). В Kubernetes операторе идемпотентность заложена в архитектуру: каждый reconcile сверяет желаемое состояние с текущим. В связке Terraform + Temporal её обеспечивает state-файл: при повторном apply Terraform видит, что ресурсы уже существуют, и пропускает создание. Temporal дополнительно сохраняет историю шагов, что исключает повторное выполнение уже завершённых активностей.
Экономия от автоматизации достигает 40% затрат на онбординг.
Как Temporal обеспечивает атомарность?
Провижининг упал на шаге 7 из 10 — нужен механизм отката. Temporal решает эту задачу с помощью компенсационных транзакций: каждая активность имеет пару compensate, которая выполняется в случае сбоя. Например, если создание Kubernetes Namespace прошло, а следующая активность (создание базы) завершилась ошибкой, Temporal автоматически вызывает функцию для удаления Namespace. История workflow хранится в кластере Temporal и доступна через UI — можно увидеть точный шаг отказа, перезапустить workflow с этого же места или вручную скорректировать состояние.
// provisioning workflow
func ProvisionTenantWorkflow(ctx workflow.Context, req TenantRequest) error {
if err := workflow.ExecuteActivity(ctx, CreateDatabase, req).Get(ctx, nil); err != nil {
return err // Temporal handles retry based on policy
}
if err := workflow.ExecuteActivity(ctx, CreateKubernetesNamespace, req).Get(ctx, nil); err != nil {
workflow.ExecuteActivity(ctx, DeleteDatabase, req) // compensate
return err
}
// ...
}Благодаря такому подходу MTTR при сбое provisioning снижается с 2 часов до 15 минут. Это подтверждено на проектах с 100+ тенантами.
Какие уровни изоляции настраиваем?
Multi-tenant архитектура требует явной модели изоляции. Настраиваем:
- Data isolation: отдельные schemas PostgreSQL с row-level security или отдельные базы в зависимости от compliance.
- Network isolation: NetworkPolicy в Kubernetes, запрещающий cross-namespace трафик.
- IAM isolation: отдельная IAM-роль per tenant с минимальными правами через resource-based policies.
- Rate limiting: quota на уровне API Gateway (requests/min), ResourceQuota на уровне Kubernetes.
Secrets генерируются при провижининге и кладутся в Vault или AWS Secrets Manager — в зависимости от инфраструктуры.
Этапы работы
| Этап | Что делаем | Типичный срок |
|---|---|---|
| Аудит | Собираем текущие скрипты, выявляем узкие места, оцениваем стек | 1 день |
| Проектирование | Выбираем подход (Operator / Terraform+Temporal), проектируем pipeline с учётом роста до 500+ тенантов | 1–2 дня |
| Реализация | Пишем код, конфиги, тесты. Идемпотентный pipeline с rollback | 2–3 дня |
| Тестирование | Provisioning тестового тенанта в staging, интеграционные тесты | 1 день |
| Документация и runbook | Описываем процесс, ошибки, восстановление | 0.5 дня |
Типичные сроки: от 3 дней для простого Terraform-пайплайна до 5–7 дней для полноценного оркестрированного workflow. Стоимость рассчитывается индивидуально после анализа текущей инфраструктуры.
Что входит в работу
Работа включает поставку рабочего кода (Operator, Terraform модули или Temporal workflow), конфигурацию для staging и production, документацию и runbook по эксплуатации, обучение команды (1–2 сессии) и обратную связь с поддержкой в течение двух недель после сдачи.
Получите консультацию по автоматизации онбординга. Оценим ваш проект за 1 день. Свяжитесь с нами — поможем выбрать оптимальный подход и избежать типичных ошибок. Закажите аудит текущего процесса provisioning — выявим узкие места и предложим план внедрения.







