Environment_Infrastructure/facts/prod-kurulum-gecmisi.md
Murat ÖZDEMİR 67dc2986dd docs(infra): restructure and update infrastructure setup documentation
- Anglicized setup and facts markdown file names for better consistency.

- Updated 01-swarm-init-multinode.md to highlight Ansible automation of Swarm initialization and labeling.

- Overhauled 03-infra-stack-changes.md to describe the single monolithic file strategy and reflect current Redis, RabbitMQ, and etcd cluster configurations.

- Fixed minor overrides and typos in Patroni templates and Ansible bootstrap documents.

- Restructured README and roadmap mapping to align with the renamed setup documents.
2026-06-15 16:42:18 +03:00

15 KiB
Raw Blame History

Prod Ortamı Kurulum Geçmişi

Prod kurulum adımları ve mevcut yapı.

Bu dosya kurulum geçmişini korur. Güncel prod deploy akışı için ana kaynak repo kökündeki prod_env-ci_dc-pipeline.md dosyasıdır. Aşağıdaki manuel deploy adımları, ilk kurulum ve sorun giderme geçmişi olarak tutulur; normal prod deploy artık root .gitea/workflows/deploy-prod.yml üzerinden yürür.

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.tfvars.example dosyasından kopyalanarak doldurulur:

hcloud_token              = "<iklim_prod proje token'ı>"
location                  = "fsn1"
image                     = "rocky-10"
server_type_app           = "cpx42"
server_type_db            = "cpx32"
admin_ssh_public_key_path = "~/.ssh/id_rsa.pub"
admin_allowed_cidrs       = ["78.187.87.109/32", "95.70.151.248/32"]

admin_allowed_cidrs Ansible group_vars/all/vars.yml ile birebir uyumlu olmalıdır. Prod sunucuları lifecycle { prevent_destroy = true } ile korunur.

Apply

cd Environment_Infrastructure/terraform/hetzner/prod
terraform init
terraform plan
terraform apply

Inventory Üretimi

mkdir -p ../../../ansible/prod/inventory/generated
terraform output -raw ansible_inventory_yaml > ../../../ansible/prod/inventory/generated/prod.yml

Ansible

ansible.cfg

roles_path = roles:../roles ile prod'a özgü roller (roles/) yanı sıra ortak roller (../roles/) de kullanılır.

[defaults]
inventory = inventory/generated/prod.yml
remote_user = root
host_key_checking = False
retry_files_enabled = False
interpreter_python = auto_silent
roles_path = roles:../roles

[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False

group_vars/all/vars.yml

storagebox_account: "u469968"
storagebox_user: "{{ storagebox_account }}-sub5"
storagebox_url: "https://{{ storagebox_user }}.your-storagebox.de/"
storagebox_mount_point: "/mnt/storagebox"
storagebox_password: "{{ vault_storagebox_password }}"
storagebox_managed_directories:
  - path: "{{ storagebox_mount_point }}/ssl"
    mode: "0755"
  - path: "{{ storagebox_mount_point }}/swag/config"
    mode: "0755"
  - path: "{{ storagebox_mount_point }}/swag/site-confs"
    mode: "0755"
  - path: "{{ storagebox_mount_point }}/grafana/data"
    mode: "0755"
  - path: "{{ storagebox_mount_point }}/precipitation/images"
    mode: "0755"

iklim_password: "{{ vault_iklim_password }}"
act_runner_labels: "prod-runner,ubuntu-24.04,{{ inventory_hostname }}"
swarm_manager_ip: "10.20.10.11"
mongodb_replset_name: "rs0"
admin_allowed_cidrs: "78.187.87.109/32 95.70.151.248/32"
admin_ssh_public_key_path: "~/.ssh/id_rsa.pub"
timezone: "Europe/Istanbul"

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

group_vars/all/vault.yml

Ansible Vault ile şifrelidir. Tanımlaması gereken değişken:

vault_storagebox_password: "<storagebox-sub-user-parolası>"

group_vars/db/vars.yml

DB node'larında StorageBox uid/gid=999 ile mount edilir (MongoDB ve PostgreSQL container user'ı ile uyumlu):

storagebox_uid: "999"
storagebox_gid: "999"

host_vars — Per-Host Vault

vault_iklim_password her sunucu için ayrı host_vars/<hostname>/vault.yml dosyasında tanımlıdır. Hardening rolü bu değeri iklim OS kullanıcısının sistem parolası olarak uygular (password_hash('sha512')). Her sunucuya farklı parola verilebilir.

prod/
  host_vars/
    iklim-app-01/vault.yml   ← vault_iklim_password: "<iklim OS kullanıcısı parolası>"
    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ı şifrelenir: ansible-vault encrypt host_vars/<hostname>/vault.yml

Hardening rolü PermitRootLogin prohibit-password uygular — key tabanlı root girişi açık, parola ile kapalıdır.

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:

storagebox_uid: "999"
storagebox_gid: "999"

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

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

Güncel Production Deploy Kaynakları

Alan Güncel kaynak
Root prod workflow .gitea/workflows/deploy-prod.yml
Detaylı CI/CD dokümanı prod_env-ci_dc-pipeline.md
Ana infra stack docker-stack-infra_db-prod.yml
Vault HA stack docker-stack-vault.yml
Vault bootstrap script init/vault/vault-bootstrap.sh
Prod env ve secret dosyaları prod/secrets/iklim.co/.env, .env.secrets.*

Güncel yapıda .deleted suffix'li eski stack dosyaları yoktur ve prod akışında dikkate alınmaz. Ana infra stack docker-stack-infra_db-prod.yml dosyasıdır. Vault stack'i bu dosyanın içinde değildir; vault-bootstrap.sh tarafından docker-stack-vault.yml ile deploy edilir.

Tarihsel Manuel DB Stack Deploy (2026-05-21)

Bu bölüm ilk prod DB/infra kurulum geçmişini korumak için bırakılmıştır. Güncel normal akışta bu adımlar elle çalıştırılmaz; root prod workflow ana stack deploy, Vault bootstrap, MongoDB replica set init ve DB init scriptlerini yönetir.

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

Tarihsel not: Bu komut bloğundaki docker-stack-db-prod.yml artık güncel stack dosyası değildir. Güncel ana stack docker-stack-infra_db-prod.yml dosyasıdır.

# 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
scp -P 23 u469968@u469968.your-storagebox.de:prod/secrets/iklim.co/.env /tmp/.env
chmod 600 /tmp/.env.secrets.shared
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 iklimco

# deploy başarılı bir şekilde tamamlanınca
rm /tmp/.env
rm /tmp/.env.secrets.shared
history -c && history -w

MongoDB Replica Set Init

Tarihsel not: İlk kurulumda rs.initiate elle verilmişti. Güncel root prod workflow içinde Initialize MongoDB Replica Set adımı replica set yoksa rs.initiate(), eksik üye varsa primary üzerinden rs.add() çalıştırır.

ssh root@<db-01-ip>

# 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
scp -P 23 u469968@u469968.your-storagebox.de:prod/secrets/iklim.co/.env /tmp/.env
chmod 600 /tmp/.env.secrets.shared
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=iklimco_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" }
  ]
})'

# bir süre sonra
docker exec -it $MONGO_CID mongosh \
  -u "$DATABASE_MONGODB_ROOT_USER" \
  -p "$DATABASE_MONGODB_ROOT_PASSWD" \
  --authenticationDatabase admin --eval 'rs.status()'

rm /tmp/.env
rm /tmp/.env.secrets.shared
history -c && history -w

Patroni Cluster Doğrulama

# app-01'den
curl -s http://10.20.20.11:8008/cluster | python3 -m json.tool

Tarihsel 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_db-prod.yml)
MongoDB rs.initiate (ilk deploy sonrası elle)
Deploy pipeline ilk çalışma bekliyor

Güncel Durum (2026-06-15)

Alan Güncel durum
Prod deploy kaynak dokümanı prod_env-ci_dc-pipeline.md
Root prod workflow .gitea/workflows/deploy-prod.yml
Ana infra stack docker-stack-infra_db-prod.yml
Vault HA stack docker-stack-vault.yml
Vault deploy yöntemi init/vault/vault-bootstrap.sh tarafından bootstrap/deploy
Eski .deleted stack dosyaları Silindi, güncel akışta yok
Prod env dosyası StorageBox prod/secrets/iklim.co/.env -> workflow workspace ./.env
Shared secrets StorageBox prod/secrets/iklim.co/.env.secrets.shared
Service secrets StorageBox prod/secrets/iklim.co/.env.secrets.<svc>
SWAG secrets StorageBox prod/secrets/iklim.co/.env.secrets.swag
MongoDB replica set init Workflow içinde otomatik/idempotent adım olarak yönetiliyor
PostgreSQL init Patroni primary beklenerek ./init/postgresql/*.sql ile çalışıyor
MongoDB init Replica set hazırlandıktan sonra ./init/mongodb/*.js ile çalışıyor
DNS update Workflow GoDaddy API ile api, apigw, rabbitmq, grafana A kayıtlarını güncelliyor

Güncel prod workflow ana hatlarıyla şu sırayı izler:

  1. StorageBox'tan .env, .env.secrets.shared, service secret dosyaları ve .env.secrets.swag alınır.
  2. PostgreSQL ve MongoDB init template'leri ./init/postgresql ve ./init/mongodb altına üretilir.
  3. Harbor pull login yapılır.
  4. SWAG DNS/site config dosyaları hazırlanır.
  5. Vault için geçici TLS placeholder cert gerekirse oluşturulur.
  6. rabbitmq_erlang_cookie Docker secret'ı oluşturulur veya mevcutsa korunur.
  7. docker-stack-infra_db-prod.yml iklimco stack'ine deploy edilir.
  8. Runner job container iklimco-net overlay network'üne bağlanır.
  9. init-infra-prod.sh çalışır; bu script Vault bootstrap ve RabbitMQ prod hazırlığını yapar.
  10. Vault AppRole ID/Secret ID değerleri ve Docker secrets üretilir.
  11. Güncellenen .env ve .env.secrets.* dosyaları StorageBox'a yüklenir.
  12. etcd, APISIX, SWAG certificate, MongoDB replica set, DB init scriptleri ve DNS kayıtları doğrulanır/güncellenir.

Önemli Mimari Notlar

Ana Infra Stack ve Vault Ayrımı (2026-06-15)

Güncel durumda ana infra stack docker-stack-infra_db-prod.yml dosyasıdır. Bu stack Redis master/replica/sentinel, RabbitMQ cluster, APISIX, APISIX Dashboard, Prometheus, Grafana, SWAG, cert-reloader, cert-distributor, etcd, Patroni ve MongoDB replica set servislerini içerir.

Vault ana infra stack içinde değildir. Vault HA cluster docker-stack-vault.yml dosyasıyla, init/vault/vault-bootstrap.sh tarafından deploy edilir. Bootstrap akışı placeholder vault_unseal_key oluşturur, iklimco_vault servisini deploy eder, Vault init/unseal işlemini yapar ve Docker secret'ı gerçek unseal key ile rotate eder.

Tek Stack Yaklaşımı (2026-05-26)

docker-stack-infra-prod.yml ve docker-stack-db-prod.yml tek dosyada birleştirildi: docker-stack-infra_db-prod.yml. Her iki dosya da aynı iklimco stack adına deploy edildiğinden servis isimleri değişmedi.

Neden birleştirildi: External overlay network'lerde Docker embedded DNS hiçbir entry kaydetmez (servis VIP'leri, alias'lar dahil). Stack-owned network'te Docker DNS tam çalışır — vault retry_join, etcd alias'ları ve tüm servis discovery sorunları çözüldü.

Network: iklimco-net artık stack tarafından oluşturulur (MTU=1400, attachable). Ansible swarm rolündeki network oluşturma task'ı kaldırıldı.

MongoDB rs.initiate: Bu not ilk kurulum dönemine aittir. Güncel prod workflow Initialize MongoDB Replica Set adımında rs.initiate() ve gerektiğinde rs.add() işlemlerini yönetir.

Network silinirse: Stack'i yeniden deploy et — docker stack deploy -c docker-stack-infra_db-prod.yml iklimco

Vault retry_join (2026-05-25)

retry_join.leader_api_addr olarak iklimco_vault (Swarm servis adı) kullanılır. Stack-owned network sayesinde Docker DNS bu VIP'i kayıt eder. leader_tls_server_name: vault.iklim.co ile *.iklim.co sertifikası TLS doğrulamasını geçer.

Güncel Vault deploy akışında bu ayar docker-stack-vault.yml ve Vault template dosyaları üzerinden kullanılır. Vault stack deploy'u root workflow'da doğrudan değil, init-infra-prod.sh -> init/vault/init-prod.sh -> init/vault/vault-bootstrap.sh zinciriyle yapılır.

Runner / iklimco-net (2026-05-26)

Act runner config'de container.network: "bridge" kullanılır (önceki iklimco-net). Workflow'da "Connect Runner to Overlay Network" adımı "Deploy Swarm Stacks" sonrasına taşındı — böylece stack'in oluşturduğu iklimco-net'e runner job container bağlanabilir.