Environment_Infrastructure/facts/prod-kurulum-gecmisi.md
Murat ÖZDEMİR 9e20f2fcf8 chore(prod): capture production bootstrap access configuration
Document and commit the production bootstrap state after the initial Hetzner and Ansible rollout.

- switch Ansible prod runbooks to use the shared vault password file

- record production admin CIDRs, SSH key path, encrypted group vault, and encrypted per-host vault files

- add generated production inventory and the prod setup history notes from the first bootstrap

- keep root password login disabled while preserving key-based root access for Ansible bootstrap continuity

- document separate Hetzner projects and tokens for test/prod and commit the prod provider lock file

- remove the private Redis firewall allowance from the prod Terraform firewall and matching setup docs
2026-05-19 17:49:59 +03:00

4.9 KiB
Raw Blame History

Prod Ortamı Kurulum Geçmişi

İlk prod kurulumunda yaşanan sorunlar, uygulanan düzeltmeler ve mevcut durum.

Terraform

Ayrı Hetzner Projesi Zorunluluğu

Test ve prod ayrı Hetzner Cloud projelerinde çalışır. Her proje için ayrı API token alınmalıdır.

Aynı token kullanılırsa terraform apply sırasında SSH key çakışması hatası alınır:

SSH key not unique (uniqueness_error, <fingerprint>)

Hetzner Cloud, aynı public key'i bir projede yalnızca bir kez kabul eder. Test projesinde oluşturulmuş key, prod projesine ait olmayan bir token ile kullanılamaz.

Düzeltme: terraform/hetzner/prod/terraform.tfvars içindeki hcloud_token değeri iklim_prod projesinin token'ı olmalıdır.

State Kaybı Sonrası SSH Key Import

Terraform state'i olmadan terraform apply çalıştırılırsa, Hetzner'da zaten var olan SSH key yeniden oluşturulmaya çalışılır ve yukarıdaki hata alınır. Bu durumda key Hetzner Console → Security → SSH Keys üzerinden ID'si bulunarak import edilir:

terraform import hcloud_ssh_key.admin <ID>

prevent_destroy

Prod sunucuları lifecycle { prevent_destroy = true } ile korunur. terraform destroy çalıştırmak gerekirse bu değer geçici olarak false yapılır, işlem bittikten sonra tekrar true'ya alınır.

Inventory Üretimi

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

admin_allowed_cidrs

group_vars/all/vars.yml içindeki admin_allowed_cidrs değeri başlangıçta 127.0.0.1/8 olarak tanımlanmıştı. Bu değer hardening rolü tarafından firewalld'a uygulanır ve SSH erişimini tamamen kapatır.

Doğru değer: tüm admin IP'lerini boşlukla ayrılmış string olarak içermelidir.

admin_allowed_cidrs: "78.187.87.109/32 95.70.151.248/32"

Terraform terraform.tfvars içindeki admin_allowed_cidrs ile birebir uyumlu olmalıdır.

admin_ssh_public_key_path

group_vars/all/vars.yml içinde başlangıçta ~/.ssh/id_ed25519.pub yazıyordu; bu makinede bu dosya yoktu. Doğru değer:

admin_ssh_public_key_path: "~/.ssh/id_rsa.pub"

PermitRootLogin

Hardening rolü başlangıçta PermitRootLogin no uyguluyordu. Bu ayar sshd handler'ı ilk play'in sonunda tetiklediğinden, aynı Ansible çalışmasının devamındaki play'ler (swarm, db_labels, db_stack, act_runner) root ile bağlanamıyordu.

Düzeltme: PermitRootLogin prohibit-password olarak değiştirildi. Key tabanlı root girişi açık kalır; parola ile root girişi engellenir. Ansible tüm bootstrap boyunca root ile bağlanmaya devam edebilir.

vault_iklim_password — Per-Host Şifre

vault_iklim_password başlangıçta group_vars/all/vault.yml içinde tek bir değer olarak tanımlıydı; tüm sunuculara aynı şifre uygulanıyordu.

Her sunucuya farklı şifre vermek için host_vars/<hostname>/vault.yml yapısına geçildi:

prod/
  group_vars/all/vault.yml        ← vault_iklim_password KALDIRILDI
  host_vars/
    iklim-app-01/vault.yml        ← vault_iklim_password: "<şifre>"
    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

Her dosya ayrı ayrı şifrelenir: ansible-vault encrypt host_vars/<hostname>/vault.yml

StorageBox Mount

StorageBox WebDAV mount (/mnt/storagebox) davfs2 ile yapılır. Aynı anda 6 sunucunun mount denemesi zaman zaman sorun çıkarabilir; Ansible idempotent çalıştığından tekrar çalıştırmak yeterlidir.

Bootstrap Çalıştırma Sırası

cd Environment_Infrastructure/ansible/prod

# 1. Tüm node'lar
ansible-playbook prod-bootstrap.yml \
  --tags base,hardening,docker,node_dirs,storagebox,storagebox_ssh_key \
  --ask-vault-pass

# 2. Swarm kurulumu
ansible-playbook prod-bootstrap.yml \
  --tags swarm \
  --ask-vault-pass

# 3. DB node label'ları
ansible-playbook prod-bootstrap.yml \
  --tags db_labels \
  --ask-vault-pass

# 4. DB node konfigürasyonu
ansible-playbook prod-bootstrap.yml \
  --tags db_stack \
  --ask-vault-pass

# 5. Act runner kurulumu
ansible-playbook prod-bootstrap.yml \
  --tags act_runner \
  --ask-vault-pass

Mevcut Durum (2026-05-19)

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 (Patroni koordinasyonu için)
Ansible db_stack (StorageBox DB dizinleri)
Ansible act_runner (3 prod runner Gitea'da Idle)
DB stack deploy (docker-stack-db.prod.yml) bekliyor
MongoDB replica set init bekliyor
Ana infra stack deploy (docker-stack-infra.prod.yml) bekliyor
Deploy pipeline ilk çalışma bekliyor