Environment_Infrastructure/setup/04-prod-terraform-iaac.md
Murat ÖZDEMİR 720c79d460 Add Hetzner Cloud production infrastructure with multi-node support
- This commit introduces the Terraform configuration to provision a production environment on Hetzner Cloud, building on the existing test setup.
- Key improvements and new features include:
* **Multi-node clusters:** Scaling to 3-node Swarm application and database clusters for improved resilience.
* **High availability:** Utilizing a Hetzner Floating IP for the application entry point and `spread` placement groups for fault tolerance across physical hosts.
* **Enhanced network security:** Internal management services (RabbitMQ, APISIX, Prometheus, Grafana) are restricted to the application subnet, expected to be accessed via an internal reverse proxy (SWAG).
* **Internal database replication:** New firewall rules enable PostgreSQL replication and MongoDB replica set traffic within the database subnet.
* **Refined test environment:** Updates to align `test` configuration with the new `prod` structure, including a dedicated floating IP and adjusted firewall rules.
* **Configuration standardization:** Environment-specific details moved to `locals.tf` for clarity, with upgraded server types and migration to Rocky Linux as the base image.
- Updates were also made to the latest version of Terraform to ensure consistency in the documentation
2026-05-10 15:43:22 +03:00

203 lines
7.2 KiB
Markdown

# 04 - Prod Terraform IaC
Bu asamanin amaci prod Hetzner Cloud Project icinde HA odakli IaaS kaynaklarini Terraform ile olusturmaktir. Bu dokuman prod Terraform ajanina tek basina verilebilir.
## Kapsam
Terraform prod ortaminda sunlari olusturur:
- Private network: `iklim-prod-net`
- Subnetler:
- App/Swarm subnet: `10.20.10.0/24`
- DB subnet: `10.20.20.0/24`
- Firewall:
- Public ingress: sadece `22/tcp`, `80/tcp`, `443/tcp`
- Private ingress: `07-private-network-port-matrisi.md` dosyasindaki prod kurallari
- SSH key
- Placement groups:
- `iklim-prod-app-spread`
- `iklim-prod-db-spread`
- Floating IP: app entry point icin sabit IPv4 (`iklim-app-01`'e atanir)
- Servers:
- `iklim-app-01`
- `iklim-app-02`
- `iklim-app-03`
- `iklim-db-01`
- `iklim-db-02`
- `iklim-db-03`
- Ansible inventory output
DB cluster yazilimi Terraform ile kurulmayacak. DB node'lari sadece makine, network ve firewall seviyesinde hazirlanacak.
## Onerilen Dosya Yapisi
```text
terraform/
hetzner/
prod/
versions.tf
providers.tf
variables.tf
locals.tf
network.tf
firewall.tf
placement.tf
servers.tf
floating_ip.tf
outputs.tf
terraform.tfvars.example
```
`terraform.tfvars`, state dosyalari ve token repo'ya commit edilmeyecek.
## Degiskenler
`environment` sabiti `locals.tf` icindedir; `tfvars` ile override edilmez.
Minimum degiskenler:
```hcl
hcloud_token = "secret"
location = "fsn1"
image = "rocky-10"
server_type_swarm = "cx42"
server_type_db = "cx52"
admin_ssh_public_key_path = "~/.ssh/id_ed25519.pub"
admin_allowed_cidrs = ["X.X.X.X/32"]
```
Server type degerleri kapasiteye gore degisebilir. Bu dokuman topoloji ve guvenlik kararini tanimlar; sizing daha sonra revize edilebilir.
## Server Rolleri ve Private IP Plani
| Server | Private IP | Rol |
| --- | --- | --- |
| `iklim-app-01` | `10.20.10.11` | Swarm manager + app worker + runner (primary, FIP alir) |
| `iklim-app-02` | `10.20.10.12` | Swarm manager + app worker + runner |
| `iklim-app-03` | `10.20.10.13` | Swarm manager + app worker + runner |
| `iklim-db-01` | `10.20.20.11` | Manuel DB cluster node |
| `iklim-db-02` | `10.20.20.12` | Manuel DB cluster node |
| `iklim-db-03` | `10.20.20.13` | Manuel DB cluster node |
Private IP'ler `locals.tf` icinde `swarm_private_ips` ve `db_private_ips` map'leri olarak sabit tanimlanir. Sunucu listesi `for_each` ile bu map'lerden turetilir.
## Placement Group Karari
Prod icin iki ayri spread placement group:
```text
iklim-prod-app-spread: iklim-app-01/02/03
iklim-prod-db-spread: iklim-db-01/02/03
```
Bu sayede Swarm quorum node'lari kendi aralarinda farkli fiziksel host'lara, DB node'lari da kendi aralarinda farkli fiziksel host'lara yerlestirilmeye calisilir.
Notlar:
- Hetzner kabinet secimi dogrudan sunmaz.
- Spread placement group farkli fiziksel host hedefler.
- Farkli lokasyon/region felaket kurtarma bu asamada konu disidir.
- Ileride scale buyudugunde multi-location DR ayri tasarlanmalidir.
## Floating IP
`iklim-prod-app-fip` adli IPv4 floating IP olusturulur ve `iklim-app-01`'e atanir. DNS A kaydi bu IP'ye yonlendirilir. Failover gerekirse floating IP baska bir app node'una tasinabilir.
## Public Firewall
Public ingress:
| Port | Kaynak | Hedef |
| --- | --- | --- |
| `22/tcp` | `admin_allowed_cidrs` | Tum prod node'lari |
| `80/tcp` | `0.0.0.0/0`, `::/0` | `iklim-app-*` (Floating IP uzerinden) |
| `443/tcp` | `0.0.0.0/0`, `::/0` | `iklim-app-*` (Floating IP uzerinden) |
Prod'da su portlar public acilmayacak:
- `8200/tcp` Vault
- `5432/tcp` PostgreSQL
- `27017/tcp` MongoDB
- `6379/tcp` Redis
- `5672/tcp`, `15672/tcp`, `61613/tcp`, `15674/tcp` RabbitMQ
- `2377/tcp`, `7946/tcp`, `7946/udp`, `4789/udp` Docker Swarm
- `9180/tcp` APISIX Admin API
- `9090/tcp` Prometheus
- `3000/tcp` Grafana
## Private Firewall
Private ingress (app subnet `10.20.10.0/24` kaynakli):
| Port | Servis | Erisim yontemi |
| --- | --- | --- |
| `15672/tcp` | RabbitMQ Management | SWAG arkasinda `443` — IP kisitli |
| `9090/tcp` | Prometheus | SWAG arkasinda `443` — IP kisitli |
| `3000/tcp` | Grafana | SWAG arkasinda `443` — IP kisitli |
| `9000/tcp` | APISIX Dashboard | SWAG arkasinda `443` — IP kisitli |
| `9180/tcp` | APISIX Admin API | Docker overlay icinden sadece Dashboard erisir |
| `8200/tcp` | Vault | Docker overlay / private network |
| `2377/tcp` | Docker Swarm control plane | App subnet icinden |
| `7946/tcp`, `7946/udp` | Docker Swarm node discovery | App subnet icinden |
| `4789/udp` | Docker Swarm VXLAN overlay | App subnet icinden |
| `6379/tcp` | Redis | App subnet icinden |
| `5672/tcp` | RabbitMQ AMQP | App subnet icinden |
| `61613/tcp` | RabbitMQ STOMP | App subnet icinden |
| `15674/tcp` | RabbitMQ Web STOMP | App subnet icinden |
DB firewall ek kurallar (db subnet `10.20.20.0/24` kaynakli):
| Port | Servis | Kural |
| --- | --- | --- |
| `5432/tcp` | PostgreSQL replication | DB subnet icinden |
| `27017/tcp` | MongoDB replica set | DB subnet icinden |
IP kisitlamasi Hetzner firewall'da degil, SWAG nginx konfigurasyonunda yapilir.
## Lifecycle ve Resize Politikasi
### server_type Degisikligi (Yeniden Boyutlandirma)
`server_type` degistirmek Terraform destroy+create **tetiklemez**. `hcloud` provider
bunu natively destekler: sunucuyu durdurur, Hetzner Resize API'sini cagirir,
yeniden baslatir. `terraform.tfvars` icinde degeri guncelle, `terraform apply` calistir.
Downtime olur (sunucu durur ve baslar) ancak disk, kurulu yazilim ve Docker volumes
korunur. `ignore_changes` veya manuel adim gerekmez.
### Hangi Degisiklikler Sunucuyu Zorla Yeniden Olusturur?
| Degisen alan | Davranis | Not |
| --- | --- | --- |
| `server_type` | In-place resize (provider native) | `terraform apply` yeterli |
| `hcloud_server_network` | Sadece attachment guncellenir | Ayri resource kullanildigi icin |
| `hcloud_firewall_attachment` | Sadece attachment guncellenir | Ayri resource kullanildigi icin |
| `placement_group_id` | Hetzner API degisime izin vermiyor → destroy+create | Degistirme |
| `image` | Disk imaji degisir → destroy+create | Degistirme |
| `location` | Baska datacenter'a tasinamaz → destroy+create | Degistirme |
### Network ve Firewall Attachment Ayrimi
`network` blogu ve `firewall_ids` `hcloud_server` icine gomulmez. Bunun yerine
ayri resource tanimlanir:
- `hcloud_server_network` — private IP atamasi (`for_each` ile her node icin)
- `hcloud_firewall_attachment` — firewall iliskisi (`for_each` ile turetilen server listesi)
### prevent_destroy Korumasi
Her sunucuya `lifecycle { prevent_destroy = true }` eklenir. Kasitli silmek icin
once lifecycle blogunu gecici olarak kaldir.
## Kabul Kriterleri
- `terraform plan` sadece prod Hetzner Project token'i ile calisir.
- 6 server olusur (`iklim-app-01/02/03`, `iklim-db-01/02/03`).
- Swarm node'lari `iklim-prod-app-spread` placement group icindedir.
- DB node'lari `iklim-prod-db-spread` placement group icindedir.
- Public firewall sadece `22`, `80`, `443` ingress'e izin verir.
- Private firewall `07-private-network-port-matrisi.md` ile uyumludur.
- DB replication portlari yalnizca DB subnet'ten erisilebilir.
- Floating IP olusur ve `iklim-app-01`'e atanir.
- Terraform state ve secret tfvars commit edilmez.