# Prod Ortamı Kurulum Geçmişi Prod kurulum adımları ve mevcut yapı. ## Terraform ### Hetzner Cloud Yapılandırması Test ve prod ayrı Hetzner Cloud projelerinde çalışır; her proje için ayrı API token kullanılır. `terraform/hetzner/prod/terraform.tfvars` içindeki `hcloud_token` değeri `iklim_prod` projesinin token'ına aittir. Prod sunucuları `lifecycle { prevent_destroy = true }` ile korunur. ### Inventory Üretimi ```bash cd Environment_Infrastructure/terraform/hetzner/prod mkdir -p ../../../ansible/prod/inventory/generated terraform output -raw ansible_inventory_yaml > ../../../ansible/prod/inventory/generated/prod.yml ``` ## Ansible ### Yapılandırma Notları `group_vars/all/vars.yml` içindeki `admin_allowed_cidrs` tüm admin IP'lerini boşlukla ayrılmış string olarak içerir ve Terraform `terraform.tfvars` ile birebir uyumludur: ```yaml admin_allowed_cidrs: "78.187.87.109/32 95.70.151.248/32" admin_ssh_public_key_path: "~/.ssh/id_rsa.pub" ``` Hardening rolü `PermitRootLogin prohibit-password` uygular — key tabanlı root girişi açık, parola ile root girişi kapalıdır. `vault_iklim_password` per-host `host_vars//vault.yml` dosyalarında tanımlıdır, her sunucu farklı şifreye sahiptir: ```text prod/ host_vars/ iklim-app-01/vault.yml iklim-app-02/vault.yml iklim-app-03/vault.yml iklim-db-01/vault.yml iklim-db-02/vault.yml iklim-db-03/vault.yml ``` ### StorageBox Mount StorageBox WebDAV mount (`/mnt/storagebox`) davfs2 ile yapılır. DB node'larında `uid=999,gid=999` parametreleriyle mount edilir (PostgreSQL/MongoDB container uid'i ile uyumlu). `group_vars/db/vars.yml` içinde tanımlanır: ```yaml storagebox_uid: "999" storagebox_gid: "999" ``` ### Bootstrap Çalıştırma Sırası ```bash cd Environment_Infrastructure/ansible/prod # 1. Tüm node'lar — base, hardening, docker, dizinler, storagebox ansible-playbook prod-bootstrap.yml \ --tags base,hardening,docker,node_dirs,storagebox,storagebox_ssh_key \ --vault-password-file=../.vault_pass # 2. Swarm kurulumu ansible-playbook prod-bootstrap.yml \ --tags swarm \ --vault-password-file=../.vault_pass # 3. DB node label'ları ansible-playbook prod-bootstrap.yml \ --tags db_labels \ --vault-password-file=../.vault_pass # 4. DB node konfigürasyonu (StorageBox dizinleri, patroni.yml, mongod.conf, keyfile) ansible-playbook prod-bootstrap.yml \ --tags db_stack \ --limit db \ --vault-password-file=../.vault_pass # 5. Act runner kurulumu ansible-playbook prod-bootstrap.yml \ --tags act_runner \ --vault-password-file=../.vault_pass ``` ## DB Stack Deploy ### Custom Image Build `build/patroni-postgis/` altında PostGIS + Patroni imajı bulunur (`registry.tarla.io/iklimco/custom-patroni-postgis:18-3.6`). `ops/push-harbor-custom-images.sh` ile Harbor'a push edilir. ### Stack Deploy ```bash # Lokal → app-01 scp ./docker-stack-* root@178.104.210.41:/home/iklim/ # app-01'de cd /home/iklim # password; 'https://passwords.tarla.io' içinde "tarla.io›[Hetzner] Utils Server" klasörünün altında scp -P 23 u469968@u469968.your-storagebox.de:prod/secrets/iklim.co/.env.secrets.shared \ /tmp/.env.secrets.shared chmod 600 /tmp/.env.secrets.shared scp -P 23 u469968@u469968.your-storagebox.de:prod/secrets/iklim.co/.env.secrets \ /tmp/.env chmod 600 /tmp/.env export $(grep -v '^\s*#' /tmp/.env.secrets.shared | grep -v '^\s*$' | xargs) export $(grep -v '^\s*#' /tmp/.env | grep -v '^\s*$' | xargs) docker stack deploy --with-registry-auth -c docker-stack-db.prod.yml iklim-db # deploy başarılı bir şekilde tamamlanınca rm /tmp/.env rm /tmp/.env.secrets.shared history -c && history -w ``` ### MongoDB Replica Set Init ```bash ssh root@ # password; 'https://passwords.tarla.io' içinde "tarla.io›[Hetzner] Utils Server" klasörünün altında scp -P 23 u469968@u469968.your-storagebox.de:prod/secrets/iklim.co/.env.secrets.shared \ /tmp/.env.secrets.shared chmod 600 /tmp/.env.secrets.shared scp -P 23 u469968@u469968.your-storagebox.de:prod/secrets/iklim.co/.env.secrets \ /tmp/.env chmod 600 /tmp/.env export $(grep -v '^\s*#' /tmp/.env.secrets.shared | grep -v '^\s*$' | xargs) export $(grep -v '^\s*#' /tmp/.env | grep -v '^\s*$' | xargs) MONGO_CID=$(docker ps --filter name=iklim-db_mongodb-01 --format "{{.ID}}" | head -1) docker exec -it $MONGO_CID mongosh \ -u "$DATABASE_MONGODB_ROOT_USER" \ -p "$DATABASE_MONGODB_ROOT_PASSWD" \ --authenticationDatabase admin --eval ' rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "mongodb-01:27017" }, { _id: 1, host: "mongodb-02:27017" }, { _id: 2, host: "mongodb-03:27017" } ] })' rm /tmp/.env rm /tmp/.env.secrets.shared history -c && history -w ``` ### Patroni Cluster Doğrulama ```bash # app-01'den curl -s http://10.20.20.11:8008/cluster | python3 -m json.tool ``` ## Mevcut Durum (2026-05-21) | Adım | Durum | | --- | --- | | Terraform — 6 sunucu, ağ, firewall, floating IP | ✅ | | Ansible base + hardening + docker + node_dirs | ✅ | | Ansible storagebox + storagebox_ssh_key | ✅ | | Ansible swarm (3 manager app + 3 worker db) | ✅ | | Ansible db_labels | ✅ | | Ansible db_stack (StorageBox DB dizinleri + config) | ✅ | | Ansible act_runner (3 prod runner Gitea'da Idle) | ✅ | | DB stack deploy (etcd + MongoDB + Patroni) | ✅ | | MongoDB replica set init (rs0: 1 primary, 2 secondary) | ✅ | | Patroni HA cluster (1 leader, 2 replica, lag=0) | ✅ | | Ana infra stack deploy (docker-stack-infra.prod.yml) | ⏳ bekliyor | | Deploy pipeline ilk çalışma | ⏳ bekliyor |