Модуль 8. IaC + Compute: Terraform, K8s vs Serverless
Цель модуля: Научить студента проектировать и разворачивать инфраструктуру отказоустойчивой системы как код. Освоить Terraform для Yandex Cloud, применить принцип наименьших привилегий (IAM), настроить отказоустойчивость (Multi-AZ) и бэкапы (RPO/RTO), обосновать выбор платформы развертывания (K8s vs Serverless) с учетом FinOps и Cold Start. Связать IaC с безопасностью: SA, Lockbox, Security Groups.
Стадия роста проекта: Рост нагрузки ×10. Проект пережил интеграции, Outbox, Saga, Resilience, Observability и Security. Теперь система работает 24/7, метрики прозрачны, секреты в Lockbox. Но пришло время развернуть всё это в продакшене — и первое, что ломается — инфраструктура. ClickOps, дрейф, SA с правами editor, отсутствие Multi-AZ.
Перед началом: Модули 1–7 (особенно Observability — метрики для алертов, IAM — Lockbox) Инструменты: Terraform, Docker, Trivy, Yandex Cloud CLI Трудоёмкость: ~6 ч (теория 1.5 ч · практика 4 ч · ADR 0.5 ч)
🔥 Инцидент: когда «всё работает» — это уже проблема
Система защищена (Модуль 7). Но в понедельник — письмо от CFO: «Счёт за облако вырос в 2,3 раза. Объясните». В Billing: 12 ВМ по 8 ядер, загрузка 15–20% — греют воздух. Пики вечером досят до 60%, остальное время ты платишь за пустоту. Это FinOps-риск: хорошая архитектура не должна стоить как плохая эксплуатация.
И последняя ловушка: Terraform-код пишется «на коленке». Половина ресурсов — через Web-консоль (ClickOps). tfstate лежит локально у одного инженера. SA приложения имеет роль editor на всю папку — может удалять кластеры и менять IAM. terraform plan в CI нет — дрейф неизбежен: код один, реальность другая.
Четыре зоны риска:
- «Платим за воздух» — K8s + HPA для синхронных API (предсказуемый latency), Serverless для воркеров (оплата за вызов). Cold Start Go приемлем для воркеров.
- «Код ≠ реальность» — Terraform:
tfstateв Object Storage,terraform planв CI, SA с минимальными правами. - «Нет Multi-AZ» — отказ зоны = простой. Managed PostgreSQL в 3 зонах, ALB балансирует.
- «SA с правами editor» — критическая уязвимость. Разделение SA для Terraform и приложения.
📖 Теория: Раскрытые термины
Глоссарий модуля:
- IaC (Infrastructure as Code) — инфраструктура описана кодом (Terraform) и проходит ревью в Git, а не создаётся руками в консоли (ClickOps).
- Drift (дрейф) — расхождение между кодом и реальным состоянием облака; детектируется
terraform planв CI. - SA (Service Account) — «робот»-аккаунт для сервисов; права выдаются по принципу least privilege.
- Multi-AZ — размещение компонентов в нескольких зонах доступности (A/B/C) для отказоустойчивости.
- Cold Start — задержка при первом запросе к «холодному» контейнеру (актуально для Serverless).
- FinOps — финансовая осознанность в облаке: оценка стоимости решений и оптимизация счетов.
- ClickOps — создание/изменение ресурсов через web-консоль вручную; антипаттерн при IaC (нет аудита, нет воспроизводимости).
- RPO / RTO — Recovery Point/Time Objective: допустимая потеря данных / допустимое время простоя; настраиваются через бэкапы и failover.
- Multi-stage Build — сборка в несколько слоев (компиляция → минимальный runtime), уменьшает размер образа и CVE-поверхность.
8.1. Infrastructure as Code (Terraform) и Drift Detection
Создание ресурсов руками через Web-консоль (ClickOps) — запрещено. Любое изменение должно быть описано в Terraform и пройти ревью в Git.
State-файл: Хранит текущее состояние. В команде он хранится в удаленном бэкенде (Object Storage) с блокировкой.
Drift (Рассинхрон): Если кто-то изменил настройку в консоли, код и стейт расходятся. В CI/CD обязательно должен быть шаг terraform plan для детекции дрифта перед применением изменений.
hcl# terraform/backend.tf — remote state с блокировкой terraform { backend "s3" { endpoint = "storage.yandexcloud.net" bucket = "terraform-state" region = "ru-central1" key = "production/terraform.tfstate" # Блокировка для предотвращения конкурентных изменений dynamodb_table = "terraform-locks" } }
8.2. IAM: Разделение Service Accounts
SA для Terraform (CI/CD): Имеет права editor на папку, но токен короткоживущий (используется только в пайплайне).
SA для приложения: Долгоживущий, но с минимальными правами (lockbox.payloadViewer на конкретные секреты, managed-postgresql.user). Права editor у приложения — критическая уязвимость.
hcl# SA для Terraform (CI/CD) — editor, но короткоживущий токен resource "yandex_iam_service_account" "terraform" { name = "terraform-sa" } resource "yandex_resourcemanager_folder_iam_member" "terraform-editor" { folder_id = var.folder_id role = "editor" member = "serviceAccount:${yandex_iam_service_account.terraform.id}" } # SA для приложения — least privilege resource "yandex_iam_service_account" "app" { name = "app-sa" } resource "yandex_resourcemanager_folder_iam_member" "app-lockbox" { folder_id = var.folder_id role = "lockbox.payloadViewer" member = "serviceAccount:${yandex_iam_service_account.app.id}" # condition: доступ только к нужным secret IDs condition { title = "specific-secrets" expression = "resource.id in ['secret-db-password', 'secret-hmac-key']" } }
8.3. Управляемая БД и Гарантии восстановления (RPO/RTO)
Использование Managed PostgreSQL в 3 Зонах Доступности (AZ) обеспечивает High Availability.
RPO (Recovery Point Objective): Допустимый объем потери данных (настраивается через частоту бэкапов, например, каждые 1 час).
RTO (Recovery Time Objective): Допустимое время простоя (зависит от автоматического failover).
hclresource "yandex_mdb_postgresql_cluster" "main" { name = "main-pg-cluster" environment = "PRODUCTION" network_id = yandex_vpc_network.main.id # Multi-AZ: хосты в 3 зонах host { zone = "ru-central1-a" subnet_id = yandex_vpc_subnet.a.id } host { zone = "ru-central1-b" subnet_id = yandex_vpc_subnet.b.id } host { zone = "ru-central1-c" subnet_id = yandex_vpc_subnet.c.id } # Бэкапы: RPO = 1 час backup_window_start { hours = 3 minutes = 0 } backup_retain_period_days = 7 }
8.4. Безопасность Docker-образов
Multi-stage build минимизирует размер.
Запрет Root: Контейнер должен запускаться от пользователя nobody или app.
Сканирование CVE: В CI/CD должен быть шаг с trivy или grype для проверки финального образа на уязвимости.
dockerfile# Multi-stage build: компиляция в golang:alpine, запуск в scratch FROM golang:1.22-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server ./cmd/server FROM scratch COPY /app/server /server USER nobody ENTRYPOINT ["/server"]
Фундамент готов: инфраструктура описана кодом, права разделены, образы безопасны. Но сколько это стоит?
8.5. Выбор среды: K8s vs Serverless (Cold Start)
Serverless Containers: Масштабирование от 0. Но есть Cold Start (задержка при первом запросе). Для Go-воркеров Cold Start приемлем (1–2 с). Для синхронных API с p99 < 100 мс — недопустим.
Managed Kubernetes: Постоянно работающие поды. Нет Cold Start, выше предсказуемость latency, но есть фиксированная стоимость (node hours).
Таблица критериев:
| Критерий | Managed Kubernetes (Путь A) | Serverless Containers (Путь B) |
|---|---|---|
| Cold Start | Нет (поды всегда тёплые) | Есть (десятки мс … секунды) |
| Масштабирование | HPA по метрикам, шаг — поды | Под запрос (до 0), мгновенно |
| Statefulness | Подходит (StatefulSet, long-lived) | Только stateless |
| Ops-нагрузка | Высокая (обслуживание кластера) | Минимальная (управляемый сервис) |
| Стоимость при простое | Постоянная (плата за узлы) | Почти нулевая (pay-per-invocation) |
| Стоимость при пике | Предсказуемая | Может быть выше (за каждый запрос) |
| Контроль над рантаймом | Полный (версии, node affinity) | Ограниченный (квоты, лимиты YC) |
Связь с Serverless MVP: если ты прошёл курс «Git Hosting: Serverless MVP», ты уже выбрал Serverless (Cloud Functions) для Git-хостинга MVP. Здесь мы обосновываем выбор для production: K8s для синхронных API, Serverless для воркеров. Паттерн тот же — инфраструктура разная.
8.6. Этот паттерн в архетипах
IaC + Compute проявляется по-разному в четырёх архетипах проектов:
| Архетип | Compute | IaC-специфика | Почему критично |
|---|---|---|---|
| Финтех | K8s (синхронные API, нет Cold Start) | Terraform + Multi-AZ + PgBouncer | Двойное списание недопустимо; RPO = 0 |
| Git-хостинг | Serverless для воркеров, K8s для API | Terraform + NFS mount + CDN | Cold Start приемлем для Outbox Relay |
| Мессенджер | K8s (WebSocket, long-lived) | Terraform + HPA + StatefulSet | Cold Start недопустим для real-time |
| Лента | Serverless для индексации, K8s для API | Terraform + CDN + ClickHouse | Cold Start приемлем для фоновых задач |
Общий принцип: K8s для синхронных API (нет Cold Start), Serverless для воркеров (оплата за вызов). Различается только соотношение синхронных/асинхронных нагрузок.
8.7. Компромиссы (Trade-offs)
| Подход | Плюс | Минус | Когда выбирать |
|---|---|---|---|
| Managed Kubernetes | предсказуемый latency (нет Cold Start); полный контроль; StatefulSet | фиксированная стоимость (node-hours); сложнее в эксплуатации | синхронные API, p99 < 100ms, постоянная нагрузка |
| Serverless Containers | масштаб от 0; оплата за вызов; минимум эксплуатации | Cold Start (приемлем для Go-воркеров, недопустим для синхронных API) | асинхронные воркеры, спайк-нагрузка, free-tier |
| Terraform (IaC) | воспроизводимость, аудит, Drift Detection в CI | learning curve; tfstate → remote backend | production (ClickOps — запрещён) |
| Docker non-root + Trivy | least-privilege внутри контейнера; CVE-scan в CI | чуть сложнее Dockerfile (USER nobody, multi-stage) | всегда для production-образов |
Вывод для курса: для Go-воркеров — Serverless Containers; для синхронных API — K8s (Cold Start недопустим). Terraform обязателен. См. ADR-8.
🛠 Практические задания
Примерный план работы:
- Docker multi-stage + Trivy → образ < 20 МБ (Go),
USER nobody, CVE-scan в CI. - Terraform: VPC + 3 subnet (A/B/C) + Managed PG Multi-AZ + backups (RPO/RTO) + ObjectStorage tfstate.
- ADR-8: K8s vs Serverless → таблица критериев, обоснование выбора, план миграции.
- IAM + Network: 2 SA (Terraform + app), Security Groups (ALB public, app private, DB no public).
Задача 1: Безопасный Docker-образ (Multi-stage + Trivy)
Go: Мультистейдж сборка. Компиляция в golang:alpine, запуск в scratch (образ < 20 МБ).
Настроить запуск от USER nobody.
Добавить в GitHub Actions шаг: trivy image --exit-code 1 --severity CRITICAL my-image:latest.
✅ Готово, когда:
- Multi-stage сборка (Go:
scratch< 20 МБ) - Контейнер запускается от
USER nobody(не root) - GitHub Actions:
trivy image --exit-code 1 --severity CRITICAL— билд падает при CRITICAL CVE
Задача 2: Terraform для сети и БД (Multi-AZ + RPO/RTO)
Описать IaC:
- VPC, 3 подсети в зонах A, B, C.
- Managed PostgreSQL кластер (хосты в 3 зонах). Настроить автоматические бэкапы (окно, retention 7 дней). Описать RPO/RTO в ADR.
- Object Storage для
tfstateс блокировкой.
✅ Готово, когда:
- VPC + 3 подсети в зонах A, B, C
- Managed PostgreSQL кластер: хосты в 3 зонах, автоматические бэкапы (retention 7 дней)
- Object Storage для
tfstateс блокировкой - RPO/RTO описаны в ADR
Задача 3: Вычислительная инфраструктура (Обоснование в ADR)
Студент должен выбрать путь, используя таблицу критериев, и описать выбор в ADR:
Путь A (K8s): Описать создание MCK. Написать Helm-чарт (Deployment, HPA).
Путь B (Serverless): Описать реестр и Trigger. Оценить Cold Start для Go-воркеров.
✅ Готово, когда:
- Выбран путь (A: K8s или B: Serverless) и обоснован таблицей критериев из задания
- ADR-8 написан: контекст, альтернативы, решение, метрики ($/мес, cold start, RTO), план миграции
- Путь A: описан MCK + Helm-чарт (Deployment, HPA)
- Путь B: описаны реестр + Trigger + оценка Cold Start
Задача 4: IAM и Сегментация сети
Создать SA для Terraform и SA для приложения с разделением прав.
Настроить Security Groups: ALB в public subnet, ВМ/Поды в private subnet. Запретить публичный доступ к БД.
✅ Готово, когда:
- 2 отдельных SA: один для Terraform, один для приложения (разделение прав)
- Security Groups: ALB — public subnet, ВМ/Поды — private subnet
- Нет публичного доступа к БД (только из private subnet)
⚠️ Антипаттерн для разбора
Студенту дается фрагмент Terraform кода для анализа.
hcl# Terraform Антипаттерн resource "yandex_compute_instance" "app" { name = "app-server" # ❌ Нет zone_id и subnet_id (нет Multi-AZ) resources { cores = 8 # ❌ Core-fraction не указан, берет 100%, дорого (FinOps risk) } } resource "yandex_resourcemanager_folder_iam_binding" "admin" { folder_id = var.folder_id role = "editor" # ❌ Слишком широкие права для SA приложения members = ["serviceAccount:app-sa"] }
Вопросы студенту:
- Что произойдет с инфраструктурой, если использовать права
editorдля SA приложения, и этот аккаунт утечет? - Почему отсутствие зоны (zone_id) нарушает отказоустойчивость?
- Почему хранение
terraform.tfstateв локальном Git-репозитории приведет к конфликту и потере Drift Detection?
📐 Задание по визуализации (Mermaid)
Студенту необходимо нарисовать топологию сети в Yandex Cloud с сегментацией (public/private).
Шаблон для ориентира:
🧩 Проверь себя
🤖 Инструкция для LLM (Coding Plan Assistant) — Режим Модуля 8
Привет! Я начинаю Модуль 8 курса «Архитектура распределённых систем».
Дай короткий ответ (2–3 предложения): почему создание ресурсов через Web-консоль (ClickOps) разрушает воспроизводимость и аудит, и зачем нужен remote
tfstateс блокировкой?Объясни разницу между Managed Kubernetes и Serverless Containers с точки зрения SLA, latency (Cold Start для Go) и возможности mTLS. Дай таблицу критериев и попроси меня выбрать путь (A или B) и обосновать в ADR.
Покажи фрагмент Антипаттерна Terraform (без zone_id, с editor-правами) и попроси объяснить риски отказоустойчивости и безопасности.
Дай описание задания: написать Dockerfile (multi-stage, non-root, Trivy-scan), описать VPC+PG+ALB в Terraform (Multi-AZ, RPO/RTO), настроить IAM (разделение SA Terraform и приложения).
НЕ ПИШИ за меня Dockerfile и Terraform-манифесты. Дождись моего ответа с архитектурным планом. После этого дай фидбек по чек-листу ментора и укажи на слепые зоны (например, «нет проверки drift», «SA слишком широкий», «нет RPO/RTO»).
📝 Чек-лист ментора
-
Docker: Multi-stage build. Запуск от
non-rootпользователя. В CI есть шагtrivyдля проверки CVE. -
IaC: Вся инфраструктура описана в Terraform. В CI/CD есть шаг
terraform planдля Drift Detection. -
State:
terraform.tfstateхранится в Object Storage (S3) с блокировкой. -
Multi-AZ & Network: ВМ/Поды и хосты БД распределены по 3 Зонам. Security Groups запрещают публичный доступ к БД (сегментация public/private).
-
IAM (Least Privilege): SA для Terraform (CI/CD) и SA для приложения разделены. У приложения нет прав
editor. -
PostgreSQL (RPO/RTO): Настроены бэкапы. В ADR описаны целевые RPO и RTO.
-
Serverless vs K8s: Выбор обоснован в ADR по таблице критериев (учтен Cold Start для Go).
-
FinOps: Обоснован выбор ресурсов (прерываемые ВМ для воркеров, core-fraction).
-
Mermaid-диаграмма: Отображает балансировку ALB, распределение по Зонам, Security Groups и поток секретов из Lockbox.
-
ADR-8: Student сформулировал ADR «K8s vs Serverless для Go» (контекст, альтернативы, решение, метрики, план миграции).
🔗 Мостик к Модулю 9 (Эволюция БД)
В конце ревью скажи студенту:
- «Отлично, мы описали идеальную инфраструктуру в коде, обеспечили Multi-AZ и безопасность. Но БД — узкое место №1. В
pg_stat_activity— 950 коннектов, таблицаpayments— 800 ГБ,VACUUMидёт часами. Команда предлагает «просто добавить реплику» — но реплика разгрузит чтение, а узкое место сейчас запись и CPU мастера. В Модуле 9 мы освоим эволюцию масштабирования БД: от PgBouncer до шардирования и CQRS. Не начинайте с шардирования — каждая ступень добавляет сложность. Исчерпайте предыдущую первой.»
Если ты прошёл курс «Git Hosting: Serverless MVP», ты уже видел этот паттерн на упрощённом стеке: Terraform для Cloud Functions + YDB Serverless + Message Queue. Паттерн тот же — инфраструктура разная. Здесь мы разбираем его на полном стеке, с глубиной и trade-offs.