# 01 - Test Terraform IaC Bu asamanin amaci test Hetzner Cloud Project icinde minimum IaaS kaynaklarini Terraform ile olusturmaktir. Bu dokuman tek basina uygulanabilir olacak sekilde yazilmistir. ## Kapsam Terraform test ortaminda sunlari olusturur: - Private network: `iklim-test-net` - Subnetler: - App/Swarm subnet: `10.10.10.0/24` - DB subnet: `10.10.20.0/24` - Firewall: - Public ingress: sadece `22/tcp`, `80/tcp`, `443/tcp` - Private ingress: `01-private-network-port-matrisi.md` dosyasindaki test kurallari - SSH key - Placement group: `iklim-test-spread` - Floating IP: swarm entry point icin sabit IPv4 - Server: - `iklim-app-01` - `iklim-db-01` - Ansible inventory output Terraform DB yazilimini kurmaz. DB node sadece makine, network ve firewall seviyesinde hazirlanir. ## Onerilen Dosya Yapisi ```text terraform/ hetzner/ test/ versions.tf providers.tf variables.tf locals.tf network.tf firewall.tf placement.tf servers.tf outputs.tf terraform.tfvars.example ``` `terraform.tfvars` commit edilmeyecek. `.gitignore` icinde ignore edilmelidir. ## Degiskenler Minimum degiskenler: ```hcl hcloud_token = "secret" location = "fsn1" image = "rocky-10" server_type_swarm = "cpx42" server_type_db = "cpx42" admin_ssh_public_key_path = "~/.ssh/id_ed25519.pub" admin_allowed_cidrs = ["X.X.X.X/32"] ``` `environment` sabiti `locals.tf` icindedir; `tfvars` ile override edilmez. `location` icin tek lokasyonla baslanir. Farkli region/lokasyon felaket kurtarma bu asamada konu disidir; ileride dokumana eklenmelidir. Server type karari `../hetzner-sizing-report.md` dokumanindaki mevcut test ortami metriklerine dayanir. Test app node uzerinde 10 mikroservis ve altyapi servisleri birlikte calistigi icin `cpx32` RAM acisindan riskli bulunmustur. Test DB node icin de tek node CPU spike riski nedeniyle `cpx42` onerilir. ## Server Rolleri | Server | Private IP | Rol | | --- | --- | --- | | `iklim-app-01` | `10.10.10.11` | Swarm manager + app worker + Gitea runner | | `iklim-db-01` | `10.10.20.11` | Manuel DB kurulumu icin hazir DB node | Private IP'ler Terraform icinde sabit tanimlanmalidir. Ansible inventory ve firewall kurallari deterministik kalir. ## Onerilen Kaynaklar ve Maliyet | Server | Rol | Server Type | CPU | RAM | SSD | Aylik | | --- | --- | --- | ---: | ---: | ---: | ---: | | `iklim-app-01` | Swarm manager + app worker + Gitea runner | `cpx42` | 8 AMD | 16 GB | 320 GB | $29.99 | | `iklim-db-01` | PostgreSQL/PostGIS + MongoDB node | `cpx42` | 8 AMD | 16 GB | 320 GB | $29.99 | | **Toplam** | 2 server | | **16 vCPU** | **32 GB** | **640 GB** | **$59.98** | ## Firewall Kurallari Public ingress: | Port | Kaynak | Hedef | | --- | --- | --- | | `22/tcp` | `admin_allowed_cidrs` | Tum test node'lari | | `80/tcp` | `0.0.0.0/0`, `::/0` | `iklim-app-01` | | `443/tcp` | `0.0.0.0/0`, `::/0` | `iklim-app-01` | Public ingress icin `8200/tcp`, `5432/tcp`, `27017/tcp`, `5672/tcp`, `15672/tcp`, `6379/tcp`, `2379/tcp`, `9000/tcp`, `9180/tcp`, `9090/tcp`, `3000/tcp` acilmayacak. ### App (swarm) Firewall — Private Ingress App subnet kaynakli (iklim-app-01): | Port | Servis | Erisim yontemi | | --- | --- | --- | | `2377/tcp` | Docker Swarm control plane | App subnet icinden | | `7946/tcp,udp` | Docker Swarm node discovery | App subnet icinden | | `4789/udp` | Docker Swarm VXLAN overlay | App subnet icinden | | `8200/tcp` | Vault | Docker overlay / private network | | `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 | | `15672/tcp` | RabbitMQ Management | 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 | | `9090/tcp` | Prometheus | SWAG arkasinda `443` — IP kisitli | | `3000/tcp` | Grafana | SWAG arkasinda `443` — IP kisitli | DB subnet kaynakli (`iklim-db-01` Swarm'a worker olarak katildigi icin): | Port | Servis | Kaynak | | --- | --- | --- | | `2377/tcp` | Docker Swarm control plane | `10.10.20.0/24` | | `7946/tcp,udp` | Docker Swarm node discovery | `10.10.20.0/24` | | `4789/udp` | Docker Swarm VXLAN overlay | `10.10.20.0/24` | ### DB Firewall — Private Ingress | Port | Servis | Kaynak | | --- | --- | --- | | `22/tcp` | SSH | `admin_allowed_cidrs` | | `5432/tcp` | PostgreSQL | `10.10.10.0/24` (app subnet) | | `27017/tcp` | MongoDB | `10.10.10.0/24` (app subnet) | | `2377/tcp` | Docker Swarm control plane | `10.10.10.0/24` (app subnet) | | `7946/tcp,udp` | Docker Swarm node discovery | `10.10.10.0/24` (app subnet) | | `4789/udp` | Docker Swarm VXLAN overlay | `10.10.10.0/24` (app subnet) | IP kisitlamasi Hetzner firewall'da degil, SWAG nginx konfigurasyonunda yapilir. Bu portlarin hicbiri `admin_allowed_cidrs` kaynagiyla public'ten acilmaz. Diger private ingress kurallari icin `01-private-network-port-matrisi.md` kaynak alinacak. ## Placement Group `iklim-test-spread` placement group `type = "spread"` olacak. Testte iki server oldugu icin bu grup `iklim-app-01` ve `iklim-db-01` makinelerinin farkli fiziksel host'lara dagitilmasini hedefler. Not: Spread placement group farkli kabinet veya lokasyon garantisi degildir; tek fiziksel host arizasinin etkisini azaltir. ## Terraform Cikti Beklentisi `outputs.tf` minimum su bilgileri uretmelidir: ```hcl output "ansible_inventory_yaml" { sensitive = false } output "test_private_ips" { sensitive = false } output "test_public_ips" { sensitive = false } ``` Inventory output'u daha sonra `ansible/inventory/generated/test.yml` dosyasina yazilabilir. Inventory dosyasinda secret bulunmayacaksa commit edilebilir; secret veya token icerirse commit edilmeyecek. ## 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 - `hcloud_firewall_attachment` — firewall iliskisi Gomulu tanimlamada bazi provider versiyonlari bu alanlardaki degisiklikleri sunucu recreation olarak yorumlar. Ayri resource kullanildiginda sadece attachment guncellenir, sunucu dokunulmaz. ### prevent_destroy Korumasi Her sunucuya `lifecycle { prevent_destroy = true }` eklenir. Bu blok varken Terraform hicbir kosulda sunucuyu silemez, plan asamasinda hata verir. Kasitli silmek icin once lifecycle blogunu gecici olarak kaldir. ## Kabul Kriterleri - `terraform plan` sadece test Hetzner Project token'i ile calisir. - `terraform apply` sonrasinda 2 server olusur. - Iki server private network uzerinden birbirine erisebilir. - Public internetten sadece `22`, `80`, `443` firewall seviyesinde aciktir. - Vault `8200` public'ten kapali kalir. - Terraform state repo'ya commit edilmez.