Коммит f7b6a07d создал по автору Савин Арсений Юрьевич's avatar Савин Арсений Юрьевич
Просмотр файлов

feat(infra): remote terraform state in Yandex Object Storage

Backend now lives in s3://devfest-omsk-tfstate so multiple organizers
share a single source of truth. Makefile maps YC_S3_* from .env to
AWS_ACCESS_KEY_ID/SECRET for plan/deploy. tfstate bucket is managed in
tfstate.tf; versioning is enabled out-of-band with `yc storage bucket
update --versioning enabled` since cdn_sa is intentionally kept at
storage.editor.
владелец 2b374274
# Copy to .env and fill in. .env is gitignored — never commit it.
# Values come from `tofu -chdir=infrastructure/terraform output` and `yc`.
# S3-compatible static credentials for the bucket (output: s3_access_key, s3_secret_key)
# S3-compatible static credentials for the cdn_sa service account.
# Used by:
# - `make release:*` to upload the built site to the bucket
# - `make plan` / `make deploy` as backend creds for the remote tfstate
# in s3://devfest-omsk-tfstate (mapped to AWS_* by the Makefile).
# (output: s3_access_key, s3_secret_key)
YC_S3_ACCESS_KEY=
YC_S3_SECRET_KEY=
......
......@@ -34,11 +34,19 @@ release\:prod: ## Fetch program, build, upload to prod bucket (devfest.ru)
release\:stage: ## Fetch program, build, upload to stage bucket (devfest-static-assets)
@cd $(SITE_DIR) && YC_BUCKET=$(BUCKET_STAGE) NEXT_PUBLIC_SITE_URL=$(SITE_URL_STAGE) npm run release
# Remote state lives in s3://devfest-omsk-tfstate (Yandex Object Storage).
# Both targets export AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY from the
# YC_S3_* keys in repo-root .env so OpenTofu's S3 backend can read/write
# the state file. Same .env that's already used by `make release:*`.
TF_BACKEND_ENV = set -a; . ./.env; set +a; \
export AWS_ACCESS_KEY_ID="$$YC_S3_ACCESS_KEY"; \
export AWS_SECRET_ACCESS_KEY="$$YC_S3_SECRET_KEY";
plan: ## Terraform plan
@tofu -chdir=$(TF_DIR) plan
@$(TF_BACKEND_ENV) tofu -chdir=$(TF_DIR) plan
deploy: ## Terraform apply (infrastructure only)
@tofu -chdir=$(TF_DIR) apply
@$(TF_BACKEND_ENV) tofu -chdir=$(TF_DIR) apply
price-bumper-dry: ## Local dry run of the price-bumper (bundles, then runs with DRY_RUN=1; reads env from shell)
@pnpm --filter @devfest/price-bumper invoke:dry
......@@ -6,4 +6,5 @@ crash.log
crash.*.log
terraform/terraform.tfvars
terraform/*.auto.tfvars
terraform/.local-backup/
.DS_Store
......@@ -8,10 +8,54 @@ OpenTofu для Yandex Cloud: один S3-бакет под статически
- [`tofu`](https://opentofu.org/docs/intro/install/) (или `terraform`)
- [`yc`](https://yandex.cloud/docs/cli/quickstart)`yc init` один раз
- Корневой `.env` в репозитории — `YC_S3_ACCESS_KEY` / `YC_S3_SECRET_KEY` (нужны для backend, см. ниже). Если ты деплоишь сайт через `make release:*` — у тебя они уже есть.
- `infrastructure/terraform/terraform.tfvars` — скопируй из `terraform.tfvars.example` и заполни `yc_token`, `yc_cloud_id`, `yc_folder_id`
Не уверен, где брать ключи — спроси `/setup` (скилл проведёт через нужное).
## Remote state (общий backend)
Стейт лежит в Yandex Object Storage — `s3://devfest-omsk-tfstate`. Это значит:
- **Любой организатор с тем же `.env`** делает `make plan` / `make deploy` против общего стейта — никакой передачи `terraform.tfstate` руками.
- Бакет создан тем же `cdn_sa` (роль folder-уровня — `storage.editor`). Креды backend читаются из `YC_S3_ACCESS_KEY` / `YC_S3_SECRET_KEY` в корневом `.env` — Makefile прокидывает их как `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` в `tofu`.
- Сам бакет описан в `tfstate.tf`, конфиг backend — в `versions.tf`.
### Подключиться к remote state на новой машине
```bash
make kickstart # tofu init подцепит backend сам
# или вручную, если init уже был с локальным стейтом:
set -a; . ./.env; set +a
AWS_ACCESS_KEY_ID="$YC_S3_ACCESS_KEY" \
AWS_SECRET_ACCESS_KEY="$YC_S3_SECRET_KEY" \
tofu -chdir=infrastructure/terraform init -reconfigure
```
После этого `make plan` / `make deploy` работают как обычно, но пишут в общий стейт.
### Locking — внимание
Yandex Object Storage не поддерживает DynamoDB (это AWS), поэтому **state locking выключен**. Договоритесь в чате перед `make deploy`, чтобы не было одновременных apply — иначе можно затереть чужие изменения.
### Включить versioning стейта (одноразово)
Versioning требует роли `storage.admin`, которую `cdn_sa` намеренно не имеет (его ключ ходит по ноутбукам). Включается одной командой под OAuth-токеном организатора:
```bash
yc storage bucket update --name devfest-omsk-tfstate --versioning enabled
```
После этого все перезаписи `terraform.tfstate` хранятся как отдельные версии — можно откатить через `aws --endpoint-url=https://storage.yandexcloud.net s3api list-object-versions --bucket devfest-omsk-tfstate`.
### Бутстрап с нуля (если стейт-бакет потерян)
1. Закомментировать блок `backend "s3"` в `versions.tf`.
2. `tofu init -migrate-state` — стейт переедет обратно в локальный файл.
3. `make deploy` — пересоздаст `yandex_storage_bucket.tfstate`.
4. Раскомментировать `backend "s3"`.
5. `tofu init -migrate-state` — назад в облако.
## Bring-up (один раз, два apply)
```bash
......@@ -51,7 +95,7 @@ chmod 600 ../../.env
cd -
```
Этот `.env` используют `make release:prod` и `make release:stage`.
Этот `.env` используют `make release:prod`, `make release:stage`, а также `make plan` / `make deploy` (для backend-доступа к стейт-бакету).
## Откат сайта
......
# Remote state bucket. Holds terraform.tfstate for the whole infra.
#
# Versioning + lifecycle are NOT managed here on purpose: PutBucketVersioning
# on YC requires storage.admin, but cdn_sa is intentionally kept at
# storage.editor (it's the same key shipped to laptops via .env). We enable
# versioning once via `yc` CLI under the organizer's OAuth token — see
# infrastructure/README.md → "Remote state".
resource "yandex_storage_bucket" "tfstate" {
bucket = "devfest-omsk-tfstate"
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
anonymous_access_flags {
read = false
list = false
config_read = false
}
depends_on = [yandex_resourcemanager_folder_iam_member.sa_roles]
}
terraform {
required_version = ">= 1.5.0"
# Remote state lives in Yandex Object Storage so multiple organizers
# share a single source of truth. Credentials are read from env vars
# (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) — the Makefile maps them
# from YC_S3_ACCESS_KEY / YC_S3_SECRET_KEY in the repo root .env.
# Bucket itself is managed in tfstate.tf.
backend "s3" {
endpoints = {
s3 = "https://storage.yandexcloud.net"
}
bucket = "devfest-omsk-tfstate"
key = "terraform.tfstate"
region = "ru-central1"
# Yandex Object Storage is S3-compatible but does not implement
# AWS-only flows — these flags switch them off.
skip_region_validation = true
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_s3_checksum = true
}
required_providers {
yandex = {
source = "yandex-cloud/yandex"
......
......@@ -124,7 +124,12 @@ if [ "$CHECK_ONLY" -eq 0 ] && [ "$NEXT" = "ready" ]; then
if [ "$ROLE" = "infra" ] && [ ! -d infrastructure/terraform/.terraform ]; then
bold "→ Initializing Terraform"
tofu -chdir=infrastructure/terraform init
# Backend lives in Yandex Object Storage (s3://devfest-omsk-tfstate).
# Map YC_S3_* from .env to AWS_* so the S3 backend can authenticate.
set -a; . ./.env; set +a
AWS_ACCESS_KEY_ID="$YC_S3_ACCESS_KEY" \
AWS_SECRET_ACCESS_KEY="$YC_S3_SECRET_KEY" \
tofu -chdir=infrastructure/terraform init
ok "tofu initialized"
fi
fi
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать