# Terraform — iklim.co Hetzner Cloud Altyapısı Bu dizin, iklim.co test ve prod ortamlarının Hetzner Cloud altyapısını Terraform ile yönetir. ## Dizin Yapısı ```text terraform/hetzner/ test/ — test ortamı: 1 app + 1 db node, single-node Swarm prod/ — prod ortamı: 3 app + 3 db node, 3-manager HA Swarm ``` Her ortam kendi bağımsız Terraform state dosyasına sahiptir; birbirini etkilemez. ## Ortam Karşılaştırması | Özellik | Test | Prod | | --- | --- | --- | | App node sayısı | 1 (`iklim-app-01`) | 3 (`iklim-app-01/02/03`) | | DB node sayısı | 1 (`iklim-db-01`) | 3 (`iklim-db-01/02/03`) | | App sunucu tipi | `cpx42` | `cpx42` | | DB sunucu tipi | `cpx42` | `cpx32` | | Swarm mimarisi | Single-node | 3-manager HA | | App subnet | `10.10.10.0/24` | `10.20.10.0/24` | | DB subnet | `10.10.20.0/24` | `10.20.20.0/24` | | Floating IP | Var | Var | | Placement group | Yok | Spread (farklı fiziksel host) | ## Ön Koşullar - Terraform >= 1.5 kurulu olmalı. - Her ortam için **ayrı** Hetzner Cloud projesi ve API token'ı hazır olmalı (aşağıya bak). - `terraform.tfvars` dosyası oluşturulmuş olmalı (bkz. `terraform.tfvars.example`). ## ⚠️ Her Ortam İçin Ayrı Hetzner Projesi Gereklidir Test ve prod **farklı Hetzner Cloud projeleri** altında çalışır; her projenin kendi API token'ı olmalıdır. **Neden ayrı proje?** - Hetzner Cloud, aynı SSH public key'i bir projede yalnızca bir kez kabul eder. Test ortamının token'ı prod için kullanılırsa `terraform apply` SSH key çakışmasıyla (`uniqueness_error`) hata verir. - Kaynak izolasyonu: test destroy işlemi prod kaynaklarını etkilemez. - Maliyet ve kota takibi ortam bazında yapılabilir. **Token nasıl alınır:** 1. [Hetzner Cloud Console](https://console.hetzner.cloud) → ilgili projeyi seç (ör. `iklim_prod`). 2. **Security → API Tokens → Generate API Token** — Read & Write izni ver. 3. Token yalnızca bir kez gösterilir; hemen kopyala. 4. `terraform.tfvars` içindeki `hcloud_token` değerine yapıştır. > Test `terraform.tfvars` içindeki token prod dizinine **kopyalanmamalıdır.** Her ortamın kendi dizininde kendi token'ı olmalıdır. ## terraform.tfvars Kurulumu ```bash # Test için: cd terraform/hetzner/test cp terraform.tfvars.example terraform.tfvars # hcloud_token = iklim_test projesi token'ı # Prod için: cd terraform/hetzner/prod cp terraform.tfvars.example terraform.tfvars # hcloud_token = iklim_prod projesi token'ı ``` `terraform.tfvars` içindeki değişkenler: | Değişken | Açıklama | | --- | --- | | `hcloud_token` | **Ortama özel** Hetzner Cloud API token'ı — test ve prod token'ları birbirinden farklıdır | | `location` | Sunucu lokasyonu (örn. `fsn1`) | | `image` | Sunucu işletim sistemi (örn. `rocky-10`) | | `server_type_app` | App sunucu tipi | | `server_type_db` | DB sunucu tipi | | `admin_ssh_public_key_path` | Admin SSH public key dosya yolu | | `admin_allowed_cidrs` | SSH erişimine izin verilen CIDR listesi | > `terraform.tfvars` hassas bilgi içerir; repository'e commit edilmez. ## Terraform Komutları Tüm komutlar ilgili ortam dizininden çalıştırılmalıdır: ```bash # Test: cd terraform/hetzner/test # Prod: cd terraform/hetzner/prod ``` ### Başlatma ```bash terraform init ``` ### Değişiklik önizleme ```bash terraform plan ``` ### Uygulama ```bash terraform apply ``` ### Kaynakları kaldırma ```bash terraform destroy ``` ## Ansible Inventory Üretimi `terraform apply` tamamlandıktan sonra Ansible inventory'si şu komutlarla üretilir: ```bash # Test inventory: cd terraform/hetzner/test terraform output -raw ansible_inventory_yaml \ > ../../../ansible/test/inventory/generated/test.yml # Prod inventory: cd terraform/hetzner/prod terraform output -raw ansible_inventory_yaml \ > ../../../ansible/prod/inventory/generated/prod.yml ``` Sunucu ekleme/silme veya IP değişimi sonrası inventory yeniden üretilmelidir. ## Outputs ### Test | Output | Açıklama | | --- | --- | | `ansible_inventory_yaml` | Ansible inventory YAML | | `test_private_ips` | Node private IP haritası | | `test_public_ips` | Node public IPv4 haritası | | `test_floating_ip` | Swarm giriş noktası floating IP | ### Prod | Output | Açıklama | | --- | --- | | `ansible_inventory_yaml` | Ansible inventory YAML | | `prod_private_ips` | Node private IP haritası (`app` ve `db` alt anahtarlarıyla) | | `prod_public_ips` | Node public IPv4 haritası | | `prod_floating_ip` | Swarm giriş noktası floating IP — DNS A kaydı bu IP'ye yönlendirilir | ```bash # Floating IP'yi görmek için: terraform output prod_floating_ip # veya test_floating_ip ``` ## Güvenlik Duvarı Özeti ### App Firewall (her iki ortam) | Port | Protokol | Kaynak | Açıklama | | --- | --- | --- | --- | | `22` | TCP | `admin_allowed_cidrs` | SSH | | `80` | TCP | 0.0.0.0/0 | HTTP (SWAG) | | `443` | TCP | 0.0.0.0/0 | HTTPS (SWAG) | | `2377` | TCP | DB subnet | Docker Swarm control plane | | `7946` | TCP/UDP | DB subnet | Docker Swarm node discovery | | `4789` | UDP | DB subnet | Docker Swarm VXLAN overlay | ### DB Firewall (her iki ortam) App subnet kaynaklı: | Port | Açıklama | | --- | --- | | `22/tcp` | SSH | | `5432/tcp` | PostgreSQL | | `27017/tcp` | MongoDB | | `2377/tcp` | Docker Swarm control plane | | `7946/tcp,udp` | Docker Swarm node discovery | | `4789/udp` | Docker Swarm VXLAN overlay | Prod'a özgü (app subnet kaynaklı): | Port | Açıklama | | --- | --- | | `2379/tcp` | etcd client (Patroni + APISIX) | Prod'a özgü (DB subnet içi, karşılıklı): | Port | Açıklama | | --- | --- | | `5432/tcp` | Patroni replikasyon | | `27017/tcp` | MongoDB replica set internal | | `2379/tcp` | etcd client | | `2380/tcp` | etcd peer | | `8008/tcp` | Patroni REST API | > IP kısıtlaması (admin paneli, dashboard vb.) Hetzner Firewall'da değil, SWAG nginx konfigürasyonunda yapılır. ## Sonraki Adım Terraform apply ve inventory üretiminin ardından Ansible bootstrap çalıştırılır. Detaylar için `ansible/README.md` dosyasına bakın.