Terraform¶
Команды, управляющие облаком через Terraform, могут развёртывать Turbo EA тем же способом. Репозиторий содержит четыре корневых модуля в deploy/terraform/: по одному на каждый управляемый контейнерный сервис со страницы Управляемые контейнерные сервисы, плюс модуль, устанавливающий Helm-чарт со страницы Kubernetes и облако в уже существующий кластер. Эта страница — о модулях; две страницы по ссылкам остаются источником сведений о том, что каждая платформа умеет, а что нет.
Что создают модули¶
| Модуль | Платформа | Создаёт | Вы предоставляете |
|---|---|---|---|
ecs-fargate |
AWS ECS Fargate | Кластер, задачу и сервис, Application Load Balancer с HTTPS, EFS, секреты Secrets Manager, RDS for PostgreSQL (опционально) | VPC с публичными и приватными подсетями (NAT), сертификат ACM |
azure-container-apps |
Azure Container Apps | Окружение, container app, Log Analytics, учётную запись хранилища и файловый ресурс, Flexible Server (опционально) | Группу ресурсов; при желании делегированную подсеть и частную зону DNS |
cloud-run |
Google Cloud Run | Сервис, Filestore, секреты Secret Manager, удалённый репозиторий Artifact Registry, глобальный HTTPS-балансировщик с управляемым сертификатом, Cloud SQL (опционально) | Проект, VPC с подсетью, доступ к частным сервисам в этой VPC |
kubernetes |
Любой кластер Kubernetes | Namespace, Secret с учётными данными, релиз Helm | Кластер и kubeconfig, сервер PostgreSQL |
Три облачных модуля создают ту же группу контейнеров, что и шаблоны со страницы управляемых контейнерных сервисов: пограничный nginx на порту 8920 перед фронтендом, бэкендом и опциональным сервером MCP, все на общем localhost; ровно один бэкенд, который никогда не масштабируется и не уменьшается до нуля; /app/data на постоянном ресурсе, принадлежащем пользователю 1000; TLS завершается на границе платформы. Та же форма, другой инструмент — всё, что страница платформы говорит о пробах, наложении при развёртывании и стартовой блокировке, действует без изменений.
Три соглашения действуют для всех четырёх:
- Версия — явный входной параметр. У
image_tag(илиchart_versionдля модуля Kubernetes) нет значения по умолчанию, которое могло бы устареть;terraform.tfvars.exampleрядом с каждым модулем содержит текущий выпуск. - База данных создаётся по умолчанию, с переключателем на собственную.
create_database = falseвместе сdb_hostиdb_passwordнаправляет бэкенд на уже работающий сервер. Откуда бы ни взялась база, объекты хранилища секретов (SECRET_KEYи пароль базы) принадлежат модулю, поэтому определение контейнера имеет единственную форму. - Сеть никогда не создаётся. Идентификаторы VPC, VNet и подсетей — входные параметры; README каждого модуля перечисляет, что они уже должны обеспечивать.
Быстрый старт¶
cd deploy/terraform/<модуль>
cp terraform.tfvars.example terraform.tfvars
# отредактируйте terraform.tfvars, а секрет держите вне любых файлов:
export TF_VAR_secret_key="$(openssl rand -base64 48)"
terraform init
terraform plan
terraform apply
Направьте DNS на выходное значение модуля — alb_dns_name в AWS, fqdn в Azure, load_balancer_ip в Google Cloud, — откройте public_url и зарегистрируйтесь: первый пользователь становится администратором.
Состояние содержит секреты
secret_key, сгенерированные пароли базы и значения секретов Container Apps попадают в состояние Terraform. Используйте зашифрованный удалённый бэкенд с контролем доступа (S3 с SSE и блокировкой, контейнер Azure Storage, бакет GCS, Terraform Cloud) — и никогда terraform.tfstate на ноутбуке для боевого экземпляра. Храните SECRET_KEY вместе с резервными копиями базы: его потеря делает недействительными все сессии и все зашифрованные настройки, и одна база их не вернёт.
AWS ECS Fargate¶
Перед началом: VPC как минимум с двумя публичными подсетями (балансировщик) и двумя приватными подсетями в разных зонах доступности (задача, точки монтирования EFS, база данных); NAT-шлюз, чтобы приватные подсети могли получать образы с ghcr.io; сертификат ACM для имени хоста в том же регионе.
Задаваемые параметры: region, vpc_id, public_subnet_ids, private_subnet_ids, certificate_arn, public_url, image_tag. При желании route53_zone_id — тогда модуль сам создаст alias-запись. Создаваемый экземпляр RDS приватный, зашифрованный, с включённой защитой от удаления и семью днями резервных копий; свою базу подключайте через create_database = false, db_host и db_security_group_id (модуль откроет порт со стороны задачи).
Развёртывание идёт по схеме «остановить, затем запустить» (deployment_minimum_healthy_percent = 0): обновление версии стоит одну-две минуты простоя и никогда не запускает два бэкенда. Для оболочки: aws ecs execute-command … --container backend --interactive --command sh.
Azure Container Apps¶
Перед началом: существующая группа ресурсов. Для интеграции с VNet — подсеть /27, делегированная Microsoft.App/environments. Для базы, недоступной из интернета, — вторая подсеть, делегированная Microsoft.DBforPostgreSQL/flexibleServers, и частная зона DNS с окончанием .postgres.database.azure.com, привязанная к VNet: задавайте postgresql_delegated_subnet_id и postgresql_private_dns_zone_id вместе. Без них создаваемый сервер сохраняет публичную конечную точку, ограниченную сервисами Azure.
Задаваемые параметры: subscription_id, resource_group_name, location, public_url, image_tag, а также глобально уникальные storage_account_name и postgresql_server_name. Первое развёртывание отвечает на выходе fqdn; привяжите собственный домен через az containerapp hostname add / bind, как описано на странице управляемых контейнерных сервисов, затем задайте public_url и примените снова. У Flexible Server в Azure нет флага защиты от удаления, поэтому модуль ставит блокировки CanNotDelete на сервер и учётную запись хранилища (db_deletion_protection, storage_deletion_protection).
Google Cloud Run¶
Перед началом: проект, сеть VPC с подсетью в регионе и доступ к частным сервисам в этой VPC — он нужен частному IP Cloud SQL. Если в VPC его ещё нет, один раз задайте create_private_service_connection = true; второй пиринг в VPC, где он уже есть, завершится ошибкой.
Задаваемые параметры: project_id, region, network, subnetwork, public_url, image_tag. Модуль включает API, создаёт удалённый репозиторий Artifact Registry, проксирующий ghcr.io (Cloud Run не может тянуть образы с ghcr.io напрямую), экземпляр Filestore для /app/data (уровень по умолчанию BASIC_HDD начинается с 1 ТиБ и составляет основную часть стоимости), запускает разовое задание, передающее ресурс пользователю 1000, и ставит перед сервисом глобальный HTTPS-балансировщик с сертификатом, управляемым Google. Создайте A-запись DNS для хоста public_url, указывающую на load_balancer_ip; сертификат остаётся в состоянии PROVISIONING, пока запись не начнёт разрешаться. Загрузки больше 32 МиБ — крупный импорт рабочего пространства — не проходят по пути HTTP/1 в Cloud Run; это ограничение платформы, а не настройка модуля.
Kubernetes¶
Модуль kubernetes оборачивает опубликованный чарт в helm_release — для команд, чьи кластеры тоже управляются Terraform. Он создаёт namespace и Secret с SECRET_KEY и POSTGRES_PASSWORD (либо использует через existing_secret Secret от External Secrets или Sealed Secrets), формирует собственные ключи values чарта и передаёт Secret по имени — секреты никогда не проходят через values. Задаваемые параметры: chart_version, public_url, db_host и либо secret_key + db_password, либо existing_secret. Класс Ingress, аннотации и TLS передаются через объект ingress; всё, чего модуль не раскрывает (backend.resources, seed.demo…), — через extra_values, список документов values чарта, объединяемых после сгенерированного.
Два блока provider читают kubeconfig; замените их аутентификацией вашего кластера (токен EKS, учётные данные AKS, плагин аутентификации GKE), если Terraform создаёт и сам кластер.
Обновление и удаление¶
Обновление версии — это изменение image_tag (или chart_version) и terraform apply; бэкенд выполняет миграции при запуске, под стартовой блокировкой на платформах, где старый и новый экземпляры накладываются. Сначала прочитайте примечания к выпуску и сделайте резервную копию базы, как при любой другой установке — Эксплуатация и обновления применимы.
terraform destroy отклоняется, пока включена защита от удаления: отключите db_deletion_protection (AWS, Google Cloud и блокировки в Azure), deletion_protection у сервиса Cloud Run и в AWS решите вопрос с финальным снимком RDS (db_skip_final_snapshot), примените, затем удаляйте. Имя удалённого экземпляра Cloud SQL нельзя использовать повторно в течение недели.
Проверка без облака¶
Каждый модуль содержит тесты в tests/, работающие с имитациями провайдеров: настоящие схемы провайдеров, вымышленные значения, никаких учётных данных, ничего не создаётся. Они фиксируют связку, от которой зависят страницы платформ, — один бэкенд, граница на порту 8920, секреты по ссылке, том данных, переключатели на собственные ресурсы, — и CI запускает их вместе с terraform validate и tflint при каждом изменении. Чего они доказать не могут — что облако примет план; этой проверкой служит первый terraform plan в реальной учётной записи. OpenTofu не тестируется, но модули избегают всех возможностей, доступных только в Terraform.
Устранение неполадок¶
| Симптом | Причина и решение |
|---|---|
Error creating Service Networking Connection … already exists (Google Cloud) |
В VPC уже есть доступ к частным сервисам. Задайте create_private_service_connection = false. |
Сертификат, управляемый Google, остаётся в PROVISIONING |
A-запись DNS хоста public_url ещё не разрешается в load_balancer_ip. Исправьте DNS и подождите; применять ничего не нужно. |
Permission denied в /app/data на Cloud Run |
Ресурс не принадлежит пользователю 1000 — например, после восстановления. Измените chown_job_token и примените, чтобы запустить задание снова. |
postgresql_delegated_subnet_id and postgresql_private_dns_zone_id must be set together (Azure) |
Частный доступ требует обоих; зона должна быть привязана к VNet, а подсеть — отличаться от подсети окружения. |
db_host is required when create_database is false |
Для собственной базы нужны db_host и db_password (TF_VAR_db_password). |
terraform destroy отказывает |
Включена защита от удаления или блокировка CanNotDelete; см. Обновление и удаление. |
Приложение отвечает по URL платформы, но не по public_url |
DNS указывает в другое место, или в public_url всё ещё FQDN платформы — задайте окончательный origin и примените; cookie сессии привязан к нему. |