mirror of
https://gitlab.sarex.io/infra/terraform-contour-mirror.git
synced 2026-08-05 18:31:00 +03:00
refactor: Use unified infrastructure.yaml config file
- Add infrastructure.yaml as single source of truth (like values.yaml in Helm) - Make all modules universal (no hardcoded entity names) - Use existing PostgreSQL cluster instead of creating new one - Add yc-database module for working with existing PostgreSQL - Add k8s-secret module with lifecycle.ignore_changes support - Update terragrunt.hcl files to read from infrastructure.yaml - Remove scripts, use native Terragrunt functions (yamldecode)
This commit is contained in:
parent
1bba8d0d53
commit
be34bc617b
174
README.md
174
README.md
@ -2,129 +2,143 @@
|
||||
|
||||
Этот репозиторий содержит инфраструктуру как код (IaC) для проекта Pulse, развернутого в Yandex Cloud.
|
||||
|
||||
## Архитектура
|
||||
|
||||
Проект использует **единый конфигурационный файл** `infrastructure.yaml` (аналог `values.yaml` в Helm), который описывает всю инфраструктуру декларативно. Модули Terraform универсальны и не знают про конкретные сущности - они используются для создания ресурсов, описанных в конфиге.
|
||||
|
||||
## Структура репозитория
|
||||
|
||||
```
|
||||
.
|
||||
├── modules/ # Переиспользуемые Terraform модули
|
||||
│ ├── yc-s3/ # Модуль для создания S3 бакета
|
||||
│ ├── yc-postgresql/ # Модуль для создания PostgreSQL кластера
|
||||
│ ├── k8s-namespace/ # Модуль для создания Kubernetes namespace
|
||||
│ └── k8s-secrets/ # Модуль для создания Kubernetes секретов
|
||||
├── live/ # Живая инфраструктура
|
||||
│ ├── terragrunt.hcl # Глобальная конфигурация Terragrunt
|
||||
│ ├── stage/ # Окружение stage
|
||||
│ │ ├── env.hcl # Конфигурация окружения
|
||||
│ │ ├── namespace/ # Namespace pulse
|
||||
│ │ ├── s3/ # S3 бакет pulse
|
||||
│ │ ├── postgresql/ # PostgreSQL база данных
|
||||
│ │ └── secrets/ # Kubernetes секреты
|
||||
│ ├── prod/ # Окружение production
|
||||
│ └── preprod/ # Окружение preprod
|
||||
├── scripts/ # Вспомогательные скрипты
|
||||
│ └── generate-pipeline.sh # Генератор GitLab CI пайплайна
|
||||
└── .gitlab-ci.yml # Основной GitLab CI конфигурация
|
||||
├── infrastructure.yaml # Единый конфигурационный файл (аналог values.yaml)
|
||||
├── modules/ # Универсальные Terraform модули
|
||||
│ ├── yc-s3/ # Модуль для создания S3 бакета
|
||||
│ ├── yc-database/ # Модуль для работы с существующим PostgreSQL
|
||||
│ ├── k8s-namespace/ # Модуль для создания Kubernetes namespace
|
||||
│ └── k8s-secret/ # Модуль для создания Kubernetes секретов
|
||||
└── live/ # Живая инфраструктура
|
||||
├── terragrunt.hcl # Глобальная конфигурация Terragrunt
|
||||
└── stage/ # Окружение stage
|
||||
├── env.hcl # Конфигурация окружения
|
||||
├── namespace/ # Namespace из конфига
|
||||
├── s3/ # S3 бакет из конфига
|
||||
├── database/ # База данных из конфига
|
||||
└── secrets/ # Секреты из конфига
|
||||
```
|
||||
|
||||
## Требования
|
||||
## Конфигурационный файл
|
||||
|
||||
- Terraform >= 1.0
|
||||
- Terragrunt >= 0.50.0
|
||||
- Доступ к Yandex Cloud
|
||||
- Доступ к Kubernetes кластеру
|
||||
Все ресурсы описываются в `infrastructure.yaml`:
|
||||
|
||||
## Переменные окружения
|
||||
```yaml
|
||||
environments:
|
||||
stage:
|
||||
namespaces:
|
||||
- name: pulse
|
||||
labels: {}
|
||||
annotations: {}
|
||||
|
||||
secrets:
|
||||
- name: dockerhub
|
||||
namespace: pulse
|
||||
type: dockerconfigjson
|
||||
# ...
|
||||
|
||||
buckets:
|
||||
- name: pulse
|
||||
acl: public-read
|
||||
# ...
|
||||
|
||||
databases:
|
||||
- cluster_id: pg-stage # ID существующего кластера
|
||||
database:
|
||||
name: pulse_db
|
||||
user:
|
||||
name: pulse
|
||||
password_length: 32
|
||||
```
|
||||
|
||||
Для работы с инфраструктурой необходимо установить следующие переменные окружения:
|
||||
Каждый `terragrunt.hcl` читает этот конфиг через `yamldecode(file(...))` и извлекает нужные параметры.
|
||||
|
||||
### Yandex Cloud
|
||||
- `YC_TOKEN` - токен для доступа к Yandex Cloud API
|
||||
- `YC_CLOUD_ID` - ID облака
|
||||
- `YC_FOLDER_ID` - ID каталога (или `YC_STAGE_FOLDER_ID`, `YC_PROD_FOLDER_ID`, `YC_PREPROD_FOLDER_ID`)
|
||||
## Модули
|
||||
|
||||
### Terraform State
|
||||
- `TF_STATE_BUCKET` - имя S3 бакета для хранения состояния Terraform
|
||||
- `TF_STATE_DYNAMODB_ENDPOINT` - endpoint YDB для блокировок состояния
|
||||
- `TF_STATE_DYNAMODB_TABLE` - имя таблицы YDB для блокировок
|
||||
Все модули **универсальны** и не содержат хардкода про конкретные сущности:
|
||||
|
||||
### Kubernetes
|
||||
- `KUBECONFIG` - путь к конфигурации Kubernetes (по умолчанию `~/.kube/config`)
|
||||
- `KUBE_CONTEXT` - контекст Kubernetes (опционально)
|
||||
- **yc-s3** - создает S3 бакет с любыми параметрами из конфига
|
||||
- **yc-database** - работает с существующим PostgreSQL кластером, создает БД и пользователя
|
||||
- **k8s-namespace** - создает namespace с любыми labels/annotations
|
||||
- **k8s-secret** - создает секреты с поддержкой `lifecycle.ignore_changes`
|
||||
|
||||
### Docker Registry
|
||||
- `DOCKER_REGISTRY_URL` - URL Docker registry (по умолчанию `cr.yandex`)
|
||||
- `DOCKER_REGISTRY_USERNAME` - имя пользователя Docker registry
|
||||
- `DOCKER_REGISTRY_PASSWORD` - пароль или токен Docker registry
|
||||
## Особенности
|
||||
|
||||
### Сеть
|
||||
- `YC_NETWORK_ID` - ID сети для PostgreSQL
|
||||
- `YC_SUBNET_ID` - ID подсети для PostgreSQL
|
||||
### Секреты с защитой от перезаписи
|
||||
|
||||
Секреты могут быть настроены с `lifecycle.ignore_changes: true`, чтобы не перезаписываться после создания:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
- name: pulse-s3-secret
|
||||
lifecycle:
|
||||
ignore_changes: true
|
||||
```
|
||||
|
||||
### Использование существующего PostgreSQL
|
||||
|
||||
Модуль `yc-database` работает с **существующим** кластером PostgreSQL в Yandex Cloud. Он только создает базу данных и пользователя, не создавая новый кластер.
|
||||
|
||||
### Зависимости между ресурсами
|
||||
|
||||
Terragrunt автоматически управляет зависимостями через блоки `dependency`. Например, секреты зависят от namespace, s3 и database, и получают их outputs.
|
||||
|
||||
## Использование
|
||||
|
||||
### Локальная разработка
|
||||
|
||||
1. Перейдите в директорию нужного компонента:
|
||||
1. Отредактируйте `infrastructure.yaml` для добавления/изменения ресурсов
|
||||
2. Перейдите в директорию компонента:
|
||||
```bash
|
||||
cd live/stage/namespace
|
||||
```
|
||||
|
||||
2. Инициализируйте Terragrunt:
|
||||
3. Инициализируйте Terragrunt:
|
||||
```bash
|
||||
terragrunt init
|
||||
```
|
||||
|
||||
3. Просмотрите план изменений:
|
||||
4. Просмотрите план:
|
||||
```bash
|
||||
terragrunt plan
|
||||
```
|
||||
|
||||
4. Примените изменения:
|
||||
5. Примените изменения:
|
||||
```bash
|
||||
terragrunt apply
|
||||
```
|
||||
|
||||
### Развертывание всех компонентов
|
||||
|
||||
Для развертывания всех компонентов окружения используйте:
|
||||
|
||||
```bash
|
||||
cd live/stage
|
||||
terragrunt run-all apply
|
||||
```
|
||||
|
||||
## Переменные окружения
|
||||
|
||||
- `YC_TOKEN` - токен Yandex Cloud
|
||||
- `YC_CLOUD_ID` - ID облака
|
||||
- `YC_FOLDER_ID` / `YC_STAGE_FOLDER_ID` - ID каталога
|
||||
- `DOCKER_REGISTRY_USERNAME` / `DOCKER_REGISTRY_PASSWORD` - для секрета dockerhub
|
||||
- `TF_STATE_BUCKET` - бакет для хранения state
|
||||
- `TF_STATE_DYNAMODB_ENDPOINT` / `TF_STATE_DYNAMODB_TABLE` - для блокировок state
|
||||
|
||||
## GitLab CI
|
||||
|
||||
Проект использует динамическую генерацию GitLab CI пайплайнов через downstream pipelines. При каждом коммите:
|
||||
Проект использует динамическую генерацию GitLab CI пайплайнов через downstream pipelines. Для каждого компонента создаются джобы: validate, plan, apply.
|
||||
|
||||
1. Запускается джоба `generate-pipeline`, которая сканирует структуру `live/` и создает джобы для каждого компонента
|
||||
2. Запускается downstream пайплайн с сгенерированными джобами
|
||||
## Принципы
|
||||
|
||||
Для каждого компонента создаются три джобы:
|
||||
- `validate-{env}-{component}` - валидация конфигурации
|
||||
- `plan-{env}-{component}` - планирование изменений
|
||||
- `apply-{env}-{component}` - применение изменений (manual для prod)
|
||||
|
||||
## Созданные ресурсы
|
||||
|
||||
### Stage окружение
|
||||
|
||||
- **Namespace**: `pulse` в Kubernetes
|
||||
- **S3 бакет**: `pulse` с публичным доступом
|
||||
- **PostgreSQL**: кластер `pulse-pg-stage` с базой `pulse_db` и пользователем `pulse`
|
||||
- **Секреты Kubernetes**:
|
||||
- `dockerhub` - доступ к Yandex Container Registry
|
||||
- `pulse-s3-secret` - ключи доступа к S3
|
||||
- `pulse-postgresql-secret` - параметры подключения к PostgreSQL
|
||||
|
||||
## Безопасность
|
||||
|
||||
⚠️ **Важно**: Все секреты (пароли, ключи доступа) хранятся в Terraform state в открытом виде. Убедитесь, что:
|
||||
|
||||
- Бэкенд для state зашифрован (AES-256)
|
||||
- Доступ к state строго ограничен
|
||||
- Используется блокировка состояния через YDB
|
||||
|
||||
## Лицензия
|
||||
|
||||
Внутренний проект компании.
|
||||
1. **DRY** - вся конфигурация в одном файле `infrastructure.yaml`
|
||||
2. **Универсальность** - модули не знают про конкретные сущности
|
||||
3. **Декларативность** - описание желаемого состояния, а не шагов
|
||||
4. **Идемпотентность** - повторный запуск безопасен
|
||||
5. **Изоляция** - каждый компонент имеет свой state
|
||||
|
||||
64
infrastructure.yaml
Normal file
64
infrastructure.yaml
Normal file
@ -0,0 +1,64 @@
|
||||
# Единый конфигурационный файл инфраструктуры
|
||||
# Аналог values.yaml в Helm
|
||||
|
||||
environments:
|
||||
stage:
|
||||
namespaces:
|
||||
- name: pulse
|
||||
labels:
|
||||
environment: stage
|
||||
project: pulse
|
||||
annotations:
|
||||
managed-by: terraform
|
||||
|
||||
secrets:
|
||||
- name: dockerhub
|
||||
namespace: pulse
|
||||
type: dockerconfigjson
|
||||
registry_url: cr.yandex
|
||||
# username и password из переменных окружения
|
||||
|
||||
- name: pulse-s3-secret
|
||||
namespace: pulse
|
||||
type: opaque
|
||||
keys:
|
||||
access_key: "" # Заполнится из outputs модуля s3
|
||||
secret_key: "" # Заполнится из outputs модуля s3
|
||||
bucket: "" # Заполнится из outputs модуля s3
|
||||
endpoint: https://storage.yandexcloud.net
|
||||
lifecycle:
|
||||
ignore_changes: true
|
||||
|
||||
- name: pulse-postgresql-secret
|
||||
namespace: pulse
|
||||
type: opaque
|
||||
keys:
|
||||
host: "" # Заполнится из outputs модуля database
|
||||
port: "6432"
|
||||
database: "" # Заполнится из outputs модуля database
|
||||
user: "" # Заполнится из outputs модуля database
|
||||
password: "" # Заполнится из outputs модуля database
|
||||
lifecycle:
|
||||
ignore_changes: true
|
||||
|
||||
buckets:
|
||||
- name: pulse
|
||||
acl: public-read
|
||||
versioning:
|
||||
enabled: false
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_headers: ["*"]
|
||||
allowed_methods: ["GET", "PUT", "POST", "DELETE", "HEAD"]
|
||||
allowed_origins: ["*"]
|
||||
expose_headers: ["ETag"]
|
||||
max_age_seconds: 3600
|
||||
|
||||
databases:
|
||||
- cluster_id: pg-stage # ID существующего кластера PostgreSQL
|
||||
database:
|
||||
name: pulse_db
|
||||
user:
|
||||
name: pulse
|
||||
password_length: 32
|
||||
password_special: false
|
||||
37
live/stage/database/terragrunt.hcl
Normal file
37
live/stage/database/terragrunt.hcl
Normal file
@ -0,0 +1,37 @@
|
||||
# Включение корневой конфигурации
|
||||
include "root" {
|
||||
path = find_in_parent_folders()
|
||||
}
|
||||
|
||||
# Включение конфигурации окружения
|
||||
include "env" {
|
||||
path = find_in_parent_folders("env.hcl")
|
||||
expose = true
|
||||
merge_strategy = "deep"
|
||||
}
|
||||
|
||||
# Чтение конфигурации инфраструктуры
|
||||
locals {
|
||||
infra_config = yamldecode(file("${get_parent_terragrunt_dir()}/../../infrastructure.yaml"))
|
||||
env_config = local.infra_config.environments[local.environment]
|
||||
db_config = local.env_config.databases[0] # Первая БД из списка
|
||||
}
|
||||
|
||||
# Путь к модулю
|
||||
terraform {
|
||||
source = "${get_parent_terragrunt_dir()}/../../modules//yc-database"
|
||||
}
|
||||
|
||||
# Входные переменные из конфига
|
||||
inputs = {
|
||||
cluster_id = local.db_config.cluster_id
|
||||
database_name = local.db_config.database.name
|
||||
user_name = local.db_config.user.name
|
||||
password_length = try(local.db_config.user.password_length, 32)
|
||||
password_special = try(local.db_config.user.password_special, false)
|
||||
permissions = [
|
||||
{
|
||||
database_name = local.db_config.database.name
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -10,19 +10,21 @@ include "env" {
|
||||
merge_strategy = "deep"
|
||||
}
|
||||
|
||||
# Чтение конфигурации инфраструктуры
|
||||
locals {
|
||||
infra_config = yamldecode(file("${get_parent_terragrunt_dir()}/../../infrastructure.yaml"))
|
||||
env_config = local.infra_config.environments[local.environment]
|
||||
namespace = local.env_config.namespaces[0] # Первый namespace из списка
|
||||
}
|
||||
|
||||
# Путь к модулю
|
||||
terraform {
|
||||
source = "${get_parent_terragrunt_dir()}/../../modules//k8s-namespace"
|
||||
}
|
||||
|
||||
# Входные переменные
|
||||
# Входные переменные из конфига
|
||||
inputs = {
|
||||
namespace_name = "pulse"
|
||||
labels = {
|
||||
environment = local.environment
|
||||
project = "pulse"
|
||||
}
|
||||
annotations = {
|
||||
"managed-by" = "terraform"
|
||||
}
|
||||
namespace_name = local.namespace.name
|
||||
labels = local.namespace.labels
|
||||
annotations = local.namespace.annotations
|
||||
}
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
# Включение корневой конфигурации
|
||||
include "root" {
|
||||
path = find_in_parent_folders()
|
||||
}
|
||||
|
||||
# Включение конфигурации окружения
|
||||
include "env" {
|
||||
path = find_in_parent_folders("env.hcl")
|
||||
expose = true
|
||||
merge_strategy = "deep"
|
||||
}
|
||||
|
||||
# Путь к модулю
|
||||
terraform {
|
||||
source = "${get_parent_terragrunt_dir()}/../../modules//yc-postgresql"
|
||||
}
|
||||
|
||||
# Входные переменные
|
||||
inputs = {
|
||||
cluster_name = "pulse-pg-${local.environment}"
|
||||
folder_id = local.folder_id
|
||||
network_id = get_env("YC_NETWORK_ID", "")
|
||||
subnet_id = get_env("YC_SUBNET_ID", "")
|
||||
zone = "ru-central1-a"
|
||||
environment = "PRODUCTION"
|
||||
postgresql_version = "15"
|
||||
resource_preset_id = "s2.micro"
|
||||
disk_type_id = "network-ssd"
|
||||
disk_size = 10
|
||||
database_name = "pulse_db"
|
||||
database_user = "pulse"
|
||||
deletion_protection = false
|
||||
}
|
||||
@ -10,14 +10,23 @@ include "env" {
|
||||
merge_strategy = "deep"
|
||||
}
|
||||
|
||||
# Чтение конфигурации инфраструктуры
|
||||
locals {
|
||||
infra_config = yamldecode(file("${get_parent_terragrunt_dir()}/../../infrastructure.yaml"))
|
||||
env_config = local.infra_config.environments[local.environment]
|
||||
bucket = local.env_config.buckets[0] # Первый bucket из списка
|
||||
}
|
||||
|
||||
# Путь к модулю
|
||||
terraform {
|
||||
source = "${get_parent_terragrunt_dir()}/../../modules//yc-s3"
|
||||
}
|
||||
|
||||
# Входные переменные
|
||||
# Входные переменные из конфига
|
||||
inputs = {
|
||||
bucket_name = "pulse"
|
||||
bucket_name = local.bucket.name
|
||||
folder_id = local.folder_id
|
||||
versioning_enabled = false
|
||||
versioning_enabled = try(local.bucket.versioning.enabled, false)
|
||||
cors_enabled = try(local.bucket.cors.enabled, false)
|
||||
cors_config = try(local.bucket.cors, {})
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ include "env" {
|
||||
# Зависимости от других компонентов
|
||||
dependency "namespace" {
|
||||
config_path = "../namespace"
|
||||
|
||||
mock_outputs = {
|
||||
name = "pulse"
|
||||
}
|
||||
@ -22,7 +21,6 @@ dependency "namespace" {
|
||||
|
||||
dependency "s3" {
|
||||
config_path = "../s3"
|
||||
|
||||
mock_outputs = {
|
||||
bucket_name = "pulse"
|
||||
access_key = "mock-access-key"
|
||||
@ -31,42 +29,76 @@ dependency "s3" {
|
||||
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
|
||||
}
|
||||
|
||||
dependency "postgresql" {
|
||||
config_path = "../postgresql"
|
||||
|
||||
dependency "database" {
|
||||
config_path = "../database"
|
||||
mock_outputs = {
|
||||
host = "mock-host.example.com"
|
||||
database_name = "pulse_db"
|
||||
database_user = "pulse"
|
||||
user_name = "pulse"
|
||||
password = "mock-password"
|
||||
}
|
||||
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
|
||||
}
|
||||
|
||||
# Чтение конфигурации инфраструктуры
|
||||
locals {
|
||||
infra_config = yamldecode(file("${get_parent_terragrunt_dir()}/../../infrastructure.yaml"))
|
||||
env_config = local.infra_config.environments[local.environment]
|
||||
secrets = local.env_config.secrets
|
||||
}
|
||||
|
||||
# Путь к модулю
|
||||
terraform {
|
||||
source = "${get_parent_terragrunt_dir()}/../../modules//k8s-secrets"
|
||||
source = "${get_parent_terragrunt_dir()}/../../modules//k8s-secret"
|
||||
}
|
||||
|
||||
# Входные переменные
|
||||
# Входные переменные из конфига с подстановкой значений из dependencies
|
||||
inputs = {
|
||||
namespace = dependency.namespace.outputs.name
|
||||
|
||||
# Docker Hub (Yandex Registry) credentials
|
||||
docker_registry_url = get_env("DOCKER_REGISTRY_URL", "cr.yandex")
|
||||
docker_registry_username = get_env("DOCKER_REGISTRY_USERNAME", "")
|
||||
docker_registry_password = get_env("DOCKER_REGISTRY_PASSWORD", "")
|
||||
|
||||
# S3 credentials from dependency
|
||||
s3_access_key = dependency.s3.outputs.access_key
|
||||
s3_secret_key = dependency.s3.outputs.secret_key
|
||||
s3_bucket_name = dependency.s3.outputs.bucket_name
|
||||
s3_endpoint = "https://storage.yandexcloud.net"
|
||||
|
||||
# PostgreSQL credentials from dependency
|
||||
db_host = dependency.postgresql.outputs.host
|
||||
db_port = dependency.postgresql.outputs.port
|
||||
db_name = dependency.postgresql.outputs.database_name
|
||||
db_user = dependency.postgresql.outputs.database_user
|
||||
db_password = dependency.postgresql.outputs.password
|
||||
secrets = [
|
||||
# dockerhub секрет
|
||||
{
|
||||
name = local.secrets[0].name
|
||||
namespace = dependency.namespace.outputs.name
|
||||
secret_type = local.secrets[0].type
|
||||
data = {
|
||||
".dockerconfigjson" = jsonencode({
|
||||
auths = {
|
||||
"${local.secrets[0].registry_url}" = {
|
||||
username = get_env("DOCKER_REGISTRY_USERNAME", "")
|
||||
password = get_env("DOCKER_REGISTRY_PASSWORD", "")
|
||||
auth = base64encode("${get_env("DOCKER_REGISTRY_USERNAME", "")}:${get_env("DOCKER_REGISTRY_PASSWORD", "")}")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
ignore_changes = false
|
||||
},
|
||||
# pulse-s3-secret
|
||||
{
|
||||
name = local.secrets[1].name
|
||||
namespace = dependency.namespace.outputs.name
|
||||
secret_type = local.secrets[1].type
|
||||
data = {
|
||||
access_key = base64encode(dependency.s3.outputs.access_key)
|
||||
secret_key = base64encode(dependency.s3.outputs.secret_key)
|
||||
bucket = base64encode(dependency.s3.outputs.bucket_name)
|
||||
endpoint = base64encode(local.secrets[1].keys.endpoint)
|
||||
}
|
||||
ignore_changes = try(local.secrets[1].lifecycle.ignore_changes, false)
|
||||
},
|
||||
# pulse-postgresql-secret
|
||||
{
|
||||
name = local.secrets[2].name
|
||||
namespace = dependency.namespace.outputs.name
|
||||
secret_type = local.secrets[2].type
|
||||
data = {
|
||||
host = base64encode(dependency.database.outputs.host)
|
||||
port = base64encode(local.secrets[2].keys.port)
|
||||
database = base64encode(dependency.database.outputs.database_name)
|
||||
user = base64encode(dependency.database.outputs.user_name)
|
||||
password = base64encode(dependency.database.outputs.password)
|
||||
}
|
||||
ignore_changes = try(local.secrets[2].lifecycle.ignore_changes, false)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
20
modules/k8s-secret/main.tf
Normal file
20
modules/k8s-secret/main.tf
Normal file
@ -0,0 +1,20 @@
|
||||
# Универсальный модуль для создания Kubernetes секрета
|
||||
# Не знает про конкретные сущности, работает с любыми данными
|
||||
|
||||
resource "kubernetes_secret" "this" {
|
||||
metadata {
|
||||
name = var.secret_name
|
||||
namespace = var.namespace
|
||||
labels = var.labels
|
||||
annotations = var.annotations
|
||||
}
|
||||
|
||||
type = var.secret_type
|
||||
|
||||
data = var.data
|
||||
|
||||
# Защита от перезаписи если указано в lifecycle
|
||||
lifecycle {
|
||||
ignore_changes = var.ignore_changes ? [data] : []
|
||||
}
|
||||
}
|
||||
9
modules/k8s-secret/outputs.tf
Normal file
9
modules/k8s-secret/outputs.tf
Normal file
@ -0,0 +1,9 @@
|
||||
output "secret_name" {
|
||||
description = "Имя созданного секрета"
|
||||
value = kubernetes_secret.this.metadata[0].name
|
||||
}
|
||||
|
||||
output "secret_id" {
|
||||
description = "ID созданного секрета"
|
||||
value = kubernetes_secret.this.id
|
||||
}
|
||||
38
modules/k8s-secret/variables.tf
Normal file
38
modules/k8s-secret/variables.tf
Normal file
@ -0,0 +1,38 @@
|
||||
variable "secret_name" {
|
||||
description = "Имя секрета"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "namespace" {
|
||||
description = "Kubernetes namespace"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "secret_type" {
|
||||
description = "Тип секрета (Opaque, kubernetes.io/dockerconfigjson, etc.)"
|
||||
type = string
|
||||
default = "Opaque"
|
||||
}
|
||||
|
||||
variable "data" {
|
||||
description = "Данные секрета (map строк)"
|
||||
type = map(string)
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Labels для секрета"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "annotations" {
|
||||
description = "Annotations для секрета"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "ignore_changes" {
|
||||
description = "Игнорировать изменения после создания (не перезаписывать)"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
# Секрет для доступа к Docker Hub (Yandex Registry)
|
||||
resource "kubernetes_secret" "dockerhub" {
|
||||
metadata {
|
||||
name = "dockerhub"
|
||||
namespace = var.namespace
|
||||
}
|
||||
|
||||
type = "kubernetes.io/dockerconfigjson"
|
||||
|
||||
data = {
|
||||
".dockerconfigjson" = jsonencode({
|
||||
auths = {
|
||||
"${var.docker_registry_url}" = {
|
||||
username = var.docker_registry_username
|
||||
password = var.docker_registry_password
|
||||
auth = base64encode("${var.docker_registry_username}:${var.docker_registry_password}")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
# Секрет для доступа к S3
|
||||
resource "kubernetes_secret" "pulse_s3" {
|
||||
metadata {
|
||||
name = "pulse-s3-secret"
|
||||
namespace = var.namespace
|
||||
}
|
||||
|
||||
type = "Opaque"
|
||||
|
||||
data = {
|
||||
access_key = base64encode(var.s3_access_key)
|
||||
secret_key = base64encode(var.s3_secret_key)
|
||||
bucket = base64encode(var.s3_bucket_name)
|
||||
endpoint = base64encode(var.s3_endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
# Секрет для доступа к PostgreSQL
|
||||
resource "kubernetes_secret" "pulse_postgresql" {
|
||||
metadata {
|
||||
name = "pulse-postgresql-secret"
|
||||
namespace = var.namespace
|
||||
}
|
||||
|
||||
type = "Opaque"
|
||||
|
||||
data = {
|
||||
host = base64encode(var.db_host)
|
||||
port = base64encode(tostring(var.db_port))
|
||||
database = base64encode(var.db_name)
|
||||
user = base64encode(var.db_user)
|
||||
password = base64encode(var.db_password)
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
output "dockerhub_secret_name" {
|
||||
description = "Name of the dockerhub secret"
|
||||
value = kubernetes_secret.dockerhub.metadata[0].name
|
||||
}
|
||||
|
||||
output "s3_secret_name" {
|
||||
description = "Name of the S3 secret"
|
||||
value = kubernetes_secret.pulse_s3.metadata[0].name
|
||||
}
|
||||
|
||||
output "postgresql_secret_name" {
|
||||
description = "Name of the PostgreSQL secret"
|
||||
value = kubernetes_secret.pulse_postgresql.metadata[0].name
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
variable "namespace" {
|
||||
description = "Kubernetes namespace where secrets will be created"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "docker_registry_url" {
|
||||
description = "Docker registry URL (e.g., cr.yandex)"
|
||||
type = string
|
||||
default = "cr.yandex"
|
||||
}
|
||||
|
||||
variable "docker_registry_username" {
|
||||
description = "Docker registry username"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "docker_registry_password" {
|
||||
description = "Docker registry password or token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "s3_access_key" {
|
||||
description = "S3 access key"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "s3_secret_key" {
|
||||
description = "S3 secret key"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "s3_bucket_name" {
|
||||
description = "S3 bucket name"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "s3_endpoint" {
|
||||
description = "S3 endpoint URL"
|
||||
type = string
|
||||
default = "https://storage.yandexcloud.net"
|
||||
}
|
||||
|
||||
variable "db_host" {
|
||||
description = "PostgreSQL host"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "db_port" {
|
||||
description = "PostgreSQL port"
|
||||
type = number
|
||||
default = 6432
|
||||
}
|
||||
|
||||
variable "db_name" {
|
||||
description = "PostgreSQL database name"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "db_user" {
|
||||
description = "PostgreSQL user"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "db_password" {
|
||||
description = "PostgreSQL password"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
46
modules/yc-database/main.tf
Normal file
46
modules/yc-database/main.tf
Normal file
@ -0,0 +1,46 @@
|
||||
# Модуль для работы с существующим PostgreSQL кластером
|
||||
# Создает базу данных и пользователя в существующем кластере
|
||||
|
||||
# Генерация пароля для пользователя
|
||||
resource "random_password" "user_password" {
|
||||
length = var.password_length
|
||||
special = var.password_special
|
||||
upper = true
|
||||
lower = true
|
||||
numeric = true
|
||||
}
|
||||
|
||||
# Получение информации о существующем кластере
|
||||
data "yandex_mdb_postgresql_cluster" "existing" {
|
||||
cluster_id = var.cluster_id
|
||||
}
|
||||
|
||||
# Создание базы данных
|
||||
resource "yandex_mdb_postgresql_database" "this" {
|
||||
cluster_id = var.cluster_id
|
||||
name = var.database_name
|
||||
owner = yandex_mdb_postgresql_user.this.name
|
||||
lc_collate = var.lc_collate
|
||||
lc_type = var.lc_type
|
||||
|
||||
dynamic "extension" {
|
||||
for_each = var.extensions
|
||||
content {
|
||||
name = extension.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Создание пользователя
|
||||
resource "yandex_mdb_postgresql_user" "this" {
|
||||
cluster_id = var.cluster_id
|
||||
name = var.user_name
|
||||
password = random_password.user_password.result
|
||||
|
||||
dynamic "permission" {
|
||||
for_each = var.permissions
|
||||
content {
|
||||
database_name = permission.value.database_name
|
||||
}
|
||||
}
|
||||
}
|
||||
35
modules/yc-database/outputs.tf
Normal file
35
modules/yc-database/outputs.tf
Normal file
@ -0,0 +1,35 @@
|
||||
output "cluster_id" {
|
||||
description = "ID кластера PostgreSQL"
|
||||
value = var.cluster_id
|
||||
}
|
||||
|
||||
output "cluster_fqdn" {
|
||||
description = "FQDN кластера PostgreSQL"
|
||||
value = data.yandex_mdb_postgresql_cluster.existing.host[0].fqdn
|
||||
}
|
||||
|
||||
output "host" {
|
||||
description = "Host адрес кластера"
|
||||
value = data.yandex_mdb_postgresql_cluster.existing.host[0].fqdn
|
||||
}
|
||||
|
||||
output "port" {
|
||||
description = "Порт кластера"
|
||||
value = 6432
|
||||
}
|
||||
|
||||
output "database_name" {
|
||||
description = "Имя базы данных"
|
||||
value = yandex_mdb_postgresql_database.this.name
|
||||
}
|
||||
|
||||
output "user_name" {
|
||||
description = "Имя пользователя"
|
||||
value = yandex_mdb_postgresql_user.this.name
|
||||
}
|
||||
|
||||
output "password" {
|
||||
description = "Пароль пользователя"
|
||||
value = random_password.user_password.result
|
||||
sensitive = true
|
||||
}
|
||||
52
modules/yc-database/variables.tf
Normal file
52
modules/yc-database/variables.tf
Normal file
@ -0,0 +1,52 @@
|
||||
variable "cluster_id" {
|
||||
description = "ID существующего PostgreSQL кластера в Yandex Cloud"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "database_name" {
|
||||
description = "Имя базы данных"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "user_name" {
|
||||
description = "Имя пользователя"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "password_length" {
|
||||
description = "Длина пароля"
|
||||
type = number
|
||||
default = 32
|
||||
}
|
||||
|
||||
variable "password_special" {
|
||||
description = "Использовать специальные символы в пароле"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "lc_collate" {
|
||||
description = "LC_COLLATE для базы данных"
|
||||
type = string
|
||||
default = "en_US.UTF-8"
|
||||
}
|
||||
|
||||
variable "lc_type" {
|
||||
description = "LC_TYPE для базы данных"
|
||||
type = string
|
||||
default = "en_US.UTF-8"
|
||||
}
|
||||
|
||||
variable "extensions" {
|
||||
description = "Список расширений PostgreSQL"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "permissions" {
|
||||
description = "Права доступа пользователя"
|
||||
type = list(object({
|
||||
database_name = string
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
# Генерация пароля для PostgreSQL (32 символа без специальных)
|
||||
resource "random_password" "pg_password" {
|
||||
length = 32
|
||||
special = false
|
||||
upper = true
|
||||
lower = true
|
||||
numeric = true
|
||||
}
|
||||
|
||||
# Создание кластера PostgreSQL
|
||||
resource "yandex_mdb_postgresql_cluster" "pulse" {
|
||||
name = var.cluster_name
|
||||
description = "PostgreSQL cluster for pulse project"
|
||||
environment = var.environment
|
||||
network_id = var.network_id
|
||||
folder_id = var.folder_id
|
||||
|
||||
config {
|
||||
version = var.postgresql_version
|
||||
|
||||
resources {
|
||||
resource_preset_id = var.resource_preset_id
|
||||
disk_type_id = var.disk_type_id
|
||||
disk_size = var.disk_size
|
||||
}
|
||||
|
||||
postgresql_config = var.postgresql_config
|
||||
}
|
||||
|
||||
database {
|
||||
name = var.database_name
|
||||
owner = var.database_user
|
||||
}
|
||||
|
||||
user {
|
||||
name = var.database_user
|
||||
password = random_password.pg_password.result
|
||||
permission {
|
||||
database_name = var.database_name
|
||||
}
|
||||
}
|
||||
|
||||
host {
|
||||
zone = var.zone
|
||||
subnet_id = var.subnet_id
|
||||
}
|
||||
|
||||
maintenance_window {
|
||||
type = var.maintenance_window_type
|
||||
day = var.maintenance_window_day
|
||||
hour = var.maintenance_window_hour
|
||||
}
|
||||
|
||||
deletion_protection = var.deletion_protection
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
output "cluster_id" {
|
||||
description = "ID of the PostgreSQL cluster"
|
||||
value = yandex_mdb_postgresql_cluster.pulse.id
|
||||
}
|
||||
|
||||
output "cluster_fqdn" {
|
||||
description = "FQDN of the PostgreSQL cluster"
|
||||
value = yandex_mdb_postgresql_cluster.pulse.host[0].fqdn
|
||||
}
|
||||
|
||||
output "host" {
|
||||
description = "Host address of the PostgreSQL cluster"
|
||||
value = yandex_mdb_postgresql_cluster.pulse.host[0].fqdn
|
||||
}
|
||||
|
||||
output "database_name" {
|
||||
description = "Name of the database"
|
||||
value = var.database_name
|
||||
}
|
||||
|
||||
output "database_user" {
|
||||
description = "Name of the database user"
|
||||
value = var.database_user
|
||||
}
|
||||
|
||||
output "password" {
|
||||
description = "Password for the database user"
|
||||
value = random_password.pg_password.result
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "port" {
|
||||
description = "Port of the PostgreSQL cluster"
|
||||
value = 6432
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
variable "cluster_name" {
|
||||
description = "Name of the PostgreSQL cluster"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "folder_id" {
|
||||
description = "Yandex Cloud folder ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "network_id" {
|
||||
description = "Network ID for the PostgreSQL cluster"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "subnet_id" {
|
||||
description = "Subnet ID for the PostgreSQL cluster"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "zone" {
|
||||
description = "Availability zone"
|
||||
type = string
|
||||
default = "ru-central1-a"
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment (PRODUCTION, PRESTABLE)"
|
||||
type = string
|
||||
default = "PRODUCTION"
|
||||
}
|
||||
|
||||
variable "postgresql_version" {
|
||||
description = "PostgreSQL version"
|
||||
type = string
|
||||
default = "15"
|
||||
}
|
||||
|
||||
variable "resource_preset_id" {
|
||||
description = "Resource preset ID"
|
||||
type = string
|
||||
default = "s2.micro"
|
||||
}
|
||||
|
||||
variable "disk_type_id" {
|
||||
description = "Disk type ID"
|
||||
type = string
|
||||
default = "network-ssd"
|
||||
}
|
||||
|
||||
variable "disk_size" {
|
||||
description = "Disk size in GB"
|
||||
type = number
|
||||
default = 10
|
||||
}
|
||||
|
||||
variable "database_name" {
|
||||
description = "Name of the database"
|
||||
type = string
|
||||
default = "pulse_db"
|
||||
}
|
||||
|
||||
variable "database_user" {
|
||||
description = "Name of the database user"
|
||||
type = string
|
||||
default = "pulse"
|
||||
}
|
||||
|
||||
variable "postgresql_config" {
|
||||
description = "PostgreSQL configuration"
|
||||
type = map(any)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "maintenance_window_type" {
|
||||
description = "Maintenance window type"
|
||||
type = string
|
||||
default = "WEEKLY"
|
||||
}
|
||||
|
||||
variable "maintenance_window_day" {
|
||||
description = "Maintenance window day"
|
||||
type = string
|
||||
default = "SAT"
|
||||
}
|
||||
|
||||
variable "maintenance_window_hour" {
|
||||
description = "Maintenance window hour"
|
||||
type = number
|
||||
default = 3
|
||||
}
|
||||
|
||||
variable "deletion_protection" {
|
||||
description = "Enable deletion protection"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
# Универсальный модуль для создания S3 бакета
|
||||
# Не знает про конкретные сущности
|
||||
|
||||
# Создание сервисного аккаунта для S3
|
||||
resource "yandex_iam_service_account" "sa" {
|
||||
name = "${var.bucket_name}-sa"
|
||||
@ -17,27 +20,31 @@ resource "yandex_iam_service_account_static_access_key" "sa_key" {
|
||||
description = "Static access key for ${var.bucket_name} bucket"
|
||||
}
|
||||
|
||||
# Создание S3 бакета с публичным доступом
|
||||
resource "yandex_storage_bucket" "pulse" {
|
||||
# Создание S3 бакета
|
||||
resource "yandex_storage_bucket" "this" {
|
||||
bucket = var.bucket_name
|
||||
access_key = yandex_iam_service_account_static_access_key.sa_key.access_key
|
||||
secret_key = yandex_iam_service_account_static_access_key.sa_key.secret_key
|
||||
|
||||
# Публичный доступ для чтения
|
||||
acl = "public-read"
|
||||
|
||||
# Настройка CORS для внешнего доступа
|
||||
cors_rule {
|
||||
allowed_headers = ["*"]
|
||||
allowed_methods = ["GET", "PUT", "POST", "DELETE", "HEAD"]
|
||||
allowed_origins = ["*"]
|
||||
expose_headers = ["ETag"]
|
||||
max_age_seconds = 3600
|
||||
}
|
||||
acl = var.acl
|
||||
|
||||
# Версионирование объектов
|
||||
versioning {
|
||||
enabled = var.versioning_enabled
|
||||
dynamic "versioning" {
|
||||
for_each = var.versioning_enabled ? [1] : []
|
||||
content {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
# Настройка CORS
|
||||
dynamic "cors_rule" {
|
||||
for_each = var.cors_enabled ? [1] : []
|
||||
content {
|
||||
allowed_headers = var.cors_config.allowed_headers
|
||||
allowed_methods = var.cors_config.allowed_methods
|
||||
allowed_origins = var.cors_config.allowed_origins
|
||||
expose_headers = try(var.cors_config.expose_headers, [])
|
||||
max_age_seconds = try(var.cors_config.max_age_seconds, 3600)
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [yandex_resourcemanager_folder_iam_member.storage_editor]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
output "bucket_name" {
|
||||
description = "Name of the created bucket"
|
||||
value = yandex_storage_bucket.pulse.bucket
|
||||
value = yandex_storage_bucket.this.bucket
|
||||
}
|
||||
|
||||
output "access_key" {
|
||||
|
||||
@ -8,8 +8,36 @@ variable "folder_id" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "acl" {
|
||||
description = "ACL для бакета"
|
||||
type = string
|
||||
default = "private"
|
||||
}
|
||||
|
||||
variable "versioning_enabled" {
|
||||
description = "Enable versioning for the bucket"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "cors_enabled" {
|
||||
description = "Enable CORS for the bucket"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "cors_config" {
|
||||
description = "CORS configuration"
|
||||
type = object({
|
||||
allowed_headers = list(string)
|
||||
allowed_methods = list(string)
|
||||
allowed_origins = list(string)
|
||||
expose_headers = optional(list(string))
|
||||
max_age_seconds = optional(number)
|
||||
})
|
||||
default = {
|
||||
allowed_headers = []
|
||||
allowed_methods = []
|
||||
allowed_origins = []
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user