mirror of
https://gitlab.sarex.io/infra/terraform-contour-mirror.git
synced 2026-08-06 02:31:35 +03:00
355 lines
14 KiB
Markdown
355 lines
14 KiB
Markdown
# Terraform Infrastructure
|
||
|
||
Infrastructure as Code для управления ресурсами Yandex Cloud и Kubernetes.
|
||
|
||
## Структура
|
||
|
||
```
|
||
terraform/
|
||
├── infrastructure.yaml # Конфигурация всех ресурсов (единый источник правды)
|
||
├── infrastructure-secrets.yaml # sops-зашифрованные секретные значения
|
||
├── .sops.yaml # правило шифрования
|
||
├── SOPS.md # как работать с sops и infrastructure-secrets.yaml
|
||
├── live/ # Terragrunt конфигурации по окружениям
|
||
│ ├── terragrunt.hcl # Общие настройки (backend, providers)
|
||
│ └── stage/ # Stage окружение
|
||
│ ├── env.hcl
|
||
│ ├── namespace/
|
||
│ ├── database/
|
||
│ ├── s3/
|
||
│ └── secrets/
|
||
└── modules/ # Terraform модули
|
||
├── k8s-namespace/ # Создание Kubernetes namespaces
|
||
├── k8s-secret/ # Создание Kubernetes secrets (с зависимостями)
|
||
├── k8s-secrets/ # Простое создание secrets
|
||
├── yc-database/ # PostgreSQL users и databases в Yandex Cloud
|
||
├── yc-valkey-user/ # Valkey/Redis users в Yandex Cloud
|
||
└── yc-s3/ # S3 бакеты с изолированным доступом
|
||
```
|
||
|
||
## Принцип работы
|
||
|
||
### infrastructure.yaml
|
||
|
||
Единый файл конфигурации для всех ресурсов. Структура:
|
||
|
||
```yaml
|
||
environments:
|
||
stage: # Имя окружения
|
||
namespaces: # Kubernetes namespaces
|
||
- name: pulse
|
||
labels: {}
|
||
annotations: {}
|
||
|
||
buckets: # S3 бакеты
|
||
- name: pulse-stage
|
||
acl: private
|
||
role: storage.uploader # опционально
|
||
versioning:
|
||
enabled: false
|
||
cors:
|
||
enabled: true
|
||
allowed_methods: [GET, PUT]
|
||
|
||
databases: # PostgreSQL databases
|
||
- cluster_id: "xxx"
|
||
database:
|
||
name: mydb
|
||
extensions: [pg_stat_statements]
|
||
user:
|
||
name: myuser
|
||
password_length: 32
|
||
conn_limit: 10
|
||
|
||
valkey_users: # Valkey/Redis users
|
||
- cluster_id: "xxx"
|
||
host: rc1a-example.mdb.yandexcloud.net
|
||
user:
|
||
name: myuser
|
||
permissions:
|
||
patterns: allkeys
|
||
pubSubChannels: allchannels
|
||
|
||
# k8s-секретов здесь НЕТ — они описываются целиком в infrastructure-secrets.yaml
|
||
# (форма + значения, шифруется только data). См. раздел «k8s-secret» ниже.
|
||
```
|
||
|
||
### Terragrunt
|
||
|
||
Terragrunt читает `infrastructure.yaml` и передаёт данные в модули:
|
||
|
||
```hcl
|
||
locals {
|
||
infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml"))
|
||
env_config = local.infra_config.environments[local.env_name]
|
||
}
|
||
|
||
inputs = {
|
||
buckets = local.env_config.buckets
|
||
}
|
||
```
|
||
|
||
### Порядок применения
|
||
|
||
Модули имеют зависимости и применяются в порядке:
|
||
|
||
```
|
||
namespace → database / s3 / valkey-users → secrets
|
||
```
|
||
|
||
## Модули
|
||
|
||
### yc-s3
|
||
|
||
Создание S3 бакетов с изолированным доступом.
|
||
|
||
**Особенности:**
|
||
- Каждый бакет получает отдельный Service Account
|
||
- SA не имеет глобальных IAM ролей на storage на уровне folder
|
||
- Доступ к бакету через IAM binding (`yandex_storage_bucket_iam_binding`)
|
||
- SA может работать только со своим бакетом
|
||
|
||
**Роли (Yandex Cloud IAM):**
|
||
- `storage.uploader` (default) — загрузка объектов
|
||
- `storage.viewer` — чтение объектов
|
||
- `storage.editor` — полный доступ
|
||
|
||
**Пример:**
|
||
```yaml
|
||
buckets:
|
||
- name: my-bucket
|
||
acl: private
|
||
role: storage.uploader # опционально
|
||
```
|
||
|
||
### yc-database
|
||
|
||
Создание PostgreSQL users и databases в существующем кластере.
|
||
|
||
**Особенности:**
|
||
- Пароль генерируется автоматически
|
||
- Поддержка extensions
|
||
- Поддержка permissions на другие БД
|
||
- `ignore_changes` на password
|
||
|
||
**Пример:**
|
||
```yaml
|
||
databases:
|
||
- cluster_id: "c9qa2coo5ukgcg93fldm"
|
||
database:
|
||
name: mydb
|
||
extensions: [pg_stat_statements]
|
||
user:
|
||
name: myuser
|
||
password_length: 32
|
||
conn_limit: 10
|
||
permissions: [other_db]
|
||
```
|
||
|
||
### yc-valkey-user
|
||
|
||
Создание Valkey/Redis users в существующем кластере Yandex Cloud.
|
||
|
||
**Особенности:**
|
||
- Пароль генерируется автоматически
|
||
- Поддержка ACL permissions
|
||
- `ignore_changes` на password
|
||
|
||
**Пример:**
|
||
```yaml
|
||
valkey_users:
|
||
- cluster_id: "c9qa2coo5ukgcg93fldm"
|
||
host: rc1a-example.mdb.yandexcloud.net
|
||
port: "6380"
|
||
user:
|
||
name: myuser
|
||
password_length: 32
|
||
permissions:
|
||
patterns: allkeys
|
||
pubSubChannels: allchannels
|
||
categories: "+@read +@write"
|
||
commands: "+GET -FLUSHALL"
|
||
sanitizePayload: sanitize-payload
|
||
```
|
||
|
||
### k8s-secret
|
||
|
||
Создаёт Kubernetes secrets. **Секреты описываются целиком в одном файле — зашифрованном
|
||
`infrastructure-secrets.yaml`** (форма + значения). В `infrastructure.yaml` секретов нет.
|
||
Ресурс в state адресуется парой `namespace/name` (поле `resource_key` удалено).
|
||
|
||
Каждый секрет описывается **в одном из двух режимов** (не смешивать):
|
||
|
||
**Режим A — adopt / статический: явная `data`.**
|
||
Значения заданы прямо (base64, как в k8s `.data`), шифруется только `data`. Модуль кладёт их
|
||
в секрет 1:1 и ничего не вычисляет. Используется для существующих секретов, взятых под terraform,
|
||
и для секретов со статическими значениями.
|
||
```yaml
|
||
- name: my-secret
|
||
namespace: myns
|
||
type: opaque # kafka|rabbitmq|database|database_url|valkey|s3|yc_sa|dockerconfigjson|opaque
|
||
lifecycle: { ignore_changes: false }
|
||
data:
|
||
username: <base64>
|
||
password: <base64>
|
||
```
|
||
|
||
**Режим B — динамический: `depends_on` + маппинг ключей.**
|
||
Значения собираются из outputs других юнитов (kafka/rabbitmq/database/valkey/s3/yc_sa),
|
||
которые создал terragrunt. **`data` НЕ указывается.**
|
||
```yaml
|
||
- name: my-kafka-secret
|
||
namespace: myns
|
||
type: kafka
|
||
depends_on: { kafka_ref: prod, kafka_user: myservice } # какой облачный ресурс читать
|
||
credential_keys: # какие поля источника и под каким ключом положить в секрет
|
||
username: username
|
||
password: password
|
||
host: host
|
||
custom_keys: { ssl: "true" } # статические несекретные литералы
|
||
constant_keys: { ca.crt: kafka_ca } # значения из общих constants (CA и т.п.)
|
||
random_keys: { token: { length: 32, special: false } } # сгенерировать random_password
|
||
```
|
||
|
||
Что означают поля режима B:
|
||
- **`depends_on`** — указатель на облачный ресурс-источник кредов. Для kafka —
|
||
`kafka_ref:kafka_user`, для rabbitmq — `rabbitmq_ref:rabbitmq_vhost:rabbitmq_user`, для
|
||
database — `cluster:db:user`, для valkey — `valkey_cluster:valkey_user`, для s3 — `bucket`,
|
||
для yc_sa — `service_account`. Модуль берёт host/port/user/password/… из outputs этого юнита.
|
||
- **`credential_keys`** — `{ ключ_в_секрете: имя_поля_источника }`; выбирает/переименовывает поля.
|
||
Если не задан — берутся все дефолтные поля типа.
|
||
- **`custom_keys`** — статические несекретные литералы прямо в секрет.
|
||
- **`constant_keys`** — значения из общих `constants` (`kafka_ca`, `postgres_ca`, …).
|
||
- **`random_keys`** — генерируемые `random_password` (токены/сессии).
|
||
|
||
**Типы (`type`)** и дефолтные поля: `database` (host/port/database/username/password/ca.crt),
|
||
`database_url` (postgresql+asyncpg URL), `valkey` (cert/host/login/password/port/url),
|
||
`kafka`/`rabbitmq` (host/port/user/password/…), `s3` (access_key/secret_key/bucket/endpoint),
|
||
`yc_sa` (access_key/secret_key/service_account_id/endpoint), `dockerconfigjson`, `opaque`.
|
||
|
||
**Как расшифровать/отредактировать секрет:** `sops infrastructure-secrets.yaml`. После правки
|
||
руками — `sops -e -i infrastructure-secrets.yaml`. Расшифрованный файл не коммитить.
|
||
|
||
**Как завести секрет после создания kafka-юзера/топиков (или rmq vhost/user):** добавить запись
|
||
в режиме B — `type`, `depends_on` на созданный ресурс, `credential_keys` для нужных полей; `data`
|
||
не указывать. terragrunt подставит значения из outputs соответствующего юнита.
|
||
|
||
Подробнее про sops: [SOPS.md](SOPS.md).
|
||
|
||
### k8s-namespace
|
||
|
||
Создание Kubernetes namespaces.
|
||
|
||
**Пример:**
|
||
```yaml
|
||
namespaces:
|
||
- name: pulse
|
||
manage: true
|
||
labels:
|
||
environment: stage
|
||
annotations:
|
||
managed-by: terraform
|
||
```
|
||
|
||
`manage` по умолчанию равен `true`. Для уже существующего неймспейс укажите
|
||
`manage: false`: модуль не будет пытаться создать или изменить его, но запись можно использовать
|
||
как цель для других стеков.
|
||
|
||
#### Раскатка общего regcred в неймспейс
|
||
|
||
Единый источник учётных данных —
|
||
`environments.<env>.vault.common.regcred.dockerconfigjson` в зашифрованном
|
||
`infrastructure-secrets.yaml`. Из него независимо работают два направления:
|
||
|
||
- vault-платформа записывает значение в Vault, если включён `vault.features.create_regcred`;
|
||
- стек `live/secrets` создаёт секрет `regcred` типа `kubernetes.io/dockerconfigjson`
|
||
в каждом неймспейс с `image_pull_secret: true`.
|
||
|
||
Для уже существующих неймспейс:
|
||
|
||
```yaml
|
||
environments:
|
||
yc-k8s-test:
|
||
namespaces:
|
||
- name: django
|
||
manage: false
|
||
image_pull_secret: true
|
||
- name: issues
|
||
manage: false
|
||
image_pull_secret: true
|
||
```
|
||
|
||
`manage: false` обязателен, если неймспейс уже существует и не находится в state
|
||
`live/namespace`: иначе terraform попытается создать его заново.
|
||
|
||
Если `regcred` в целевом неймспейс уже существует, перед первым apply его нужно один раз
|
||
принять в state стека `live/secrets`:
|
||
|
||
```bash
|
||
cd live/secrets
|
||
|
||
terragrunt import \
|
||
'kubernetes_secret.without_ignore["django/regcred"]' \
|
||
django/regcred
|
||
|
||
terragrunt import \
|
||
'kubernetes_secret.without_ignore["issues/regcred"]' \
|
||
issues/regcred
|
||
|
||
terragrunt plan
|
||
terragrunt apply
|
||
```
|
||
|
||
Если секрета ещё нет, импорт не требуется: apply создаст его. После одноразового импорта
|
||
дальнейшие изменения общего `dockerconfigjson` раскатываются обычным apply. Для k8s fan-out
|
||
включать `vault.features.create_regcred` не требуется — этот флаг управляет только записью
|
||
в Vault.
|
||
|
||
## Использование
|
||
|
||
### Локальный запуск
|
||
|
||
```bash
|
||
cd terraform/live/stage/s3
|
||
terragrunt init
|
||
terragrunt plan
|
||
terragrunt apply
|
||
```
|
||
|
||
### Применение всех модулей
|
||
|
||
```bash
|
||
cd terraform/live/stage
|
||
terragrunt run-all plan
|
||
terragrunt run-all apply
|
||
```
|
||
|
||
### Переменные окружения
|
||
|
||
```bash
|
||
export YC_TOKEN="..." # IAM токен (или YC_SERVICE_ACCOUNT_KEY_FILE)
|
||
export YC_CLOUD_ID="..."
|
||
export YC_FOLDER_ID="..." # Default folder
|
||
export YC_STAGE_FOLDER_ID="..." # Stage folder (опционально)
|
||
export KUBECONFIG="~/.kube/config"
|
||
export KUBE_CONTEXT="stage"
|
||
export S3_ACCESS_KEY="..." # Для terraform state
|
||
export S3_SECRET_KEY="..."
|
||
export DOCKER_REGISTRY_USERNAME="..." # Для dockerconfigjson secrets
|
||
export DOCKER_REGISTRY_PASSWORD="..."
|
||
```
|
||
|
||
## CI/CD
|
||
|
||
Pipeline автоматически:
|
||
1. Читает изменённые файлы
|
||
2. Определяет затронутые модули
|
||
3. Запускает `terragrunt plan` / `terragrunt apply`
|
||
|
||
## Требования
|
||
|
||
- Terraform >= 1.0
|
||
- Terragrunt >= 0.45
|
||
- Yandex Cloud провайдер ~> 0.100
|
||
- IAM токен с правами `storage.admin` для создания бакетов и bucket policy
|