From 2198f932cdeba071d98473602f9190e6c995f028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Murat=20=C3=96ZDEM=C4=B0R?= Date: Tue, 12 May 2026 18:34:24 +0300 Subject: [PATCH] Implement: Gitea Actions runner, automated DB stack, and Turkish localization * Introduces an Ansible role for installing and registering `act_runner` for Gitea Actions. * Automates PostgreSQL and MongoDB deployment on Docker Swarm in the test environment, leveraging Docker named volumes for data persistence. * Translates core documentation, including `README.md` and `setup/04-test-db-docker-kurulum.md`, to Turkish. * Adds comprehensive documentation for firewall architecture (`facts/firewall.md`) and Docker Swarm node recovery (`facts/swarm-node-recovery.md`). * Enhances security hardening by ensuring `fail2ban` is enabled and streamlining admin SSH key management via Ansible. * Updates Ansible vault structure to support new secret variables and adds `.vault_pass` to `.gitignore`. --- .gitignore | 2 + README.md | 201 +++++++++--------- ansible/roles/act_runner/defaults/main.yml | 8 + ansible/roles/act_runner/handlers/main.yml | 6 + ansible/roles/act_runner/tasks/main.yml | 86 ++++++++ .../templates/gitea-act-runner.service.j2 | 16 ++ ansible/roles/db_stack/tasks/app_node.yml | 17 ++ ansible/roles/db_stack/tasks/db_node.yml | 12 ++ ansible/roles/db_stack/tasks/main.yml | 6 + .../roles/db_stack/templates/db.stack.yml.j2 | 42 ++++ .../roles/db_stack/templates/mongod.conf.j2 | 7 + ansible/roles/hardening/tasks/main.yml | 12 ++ ansible/test/group_vars/all/vars.yml | 9 + ansible/test/group_vars/all/vault.yml | 53 ++--- ansible/test/host_vars/iklim-app-01/vault.yml | 7 + ansible/test/host_vars/iklim-db-01/vault.yml | 7 + ansible/test/test-app-post-stack.yml | 19 ++ ansible/test/test-db-post-stack.yml | 14 ++ facts/firewall.md | 131 ++++++++++++ facts/swarm-node-recovery.md | 76 +++++++ setup/04-test-db-docker-kurulum.md | 133 ++++-------- 21 files changed, 647 insertions(+), 217 deletions(-) create mode 100644 ansible/roles/act_runner/defaults/main.yml create mode 100644 ansible/roles/act_runner/handlers/main.yml create mode 100644 ansible/roles/act_runner/tasks/main.yml create mode 100644 ansible/roles/act_runner/templates/gitea-act-runner.service.j2 create mode 100644 ansible/roles/db_stack/tasks/app_node.yml create mode 100644 ansible/roles/db_stack/tasks/db_node.yml create mode 100644 ansible/roles/db_stack/tasks/main.yml create mode 100644 ansible/roles/db_stack/templates/db.stack.yml.j2 create mode 100644 ansible/roles/db_stack/templates/mongod.conf.j2 create mode 100644 ansible/test/host_vars/iklim-app-01/vault.yml create mode 100644 ansible/test/host_vars/iklim-db-01/vault.yml create mode 100644 ansible/test/test-app-post-stack.yml create mode 100644 ansible/test/test-db-post-stack.yml create mode 100644 facts/firewall.md create mode 100644 facts/swarm-node-recovery.md diff --git a/.gitignore b/.gitignore index ee1f016..9e746ec 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ runner-config.secret.yaml *.swo *.pdf + +ansible/.vault_pass diff --git a/README.md b/README.md index 58aa4c3..c76103c 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,87 @@ -# 🌍 Environment Infrastructure +# 🌍 Sunucu Ortam ve Altyapıları -Infrastructure-as-code and operational runbook repository for `iklim.co` test and production environments on Hetzner Cloud. +`iklim.co` test ve prod ortamları için Hetzner Cloud üzerinde Infrastructure-as-code ve operasyonel runbook deposu. -This repository defines: -- 🧱 Terraform resources for Hetzner infrastructure (`test` and `prod`) -- 🤖 Ansible bootstrap playbooks, shared roles, and inventory targets -- 📚 End-to-end setup guides and roadmap documents (mostly Turkish) -- 📊 Sizing/cost analysis and supporting reference assets +Bu depo şunları kapsar: +- 🧱 Hetzner altyapısı için Terraform kaynakları (`test` ve `prod`) +- 🤖 Ansible bootstrap playbook'ları, paylaşımlı roller ve envanter hedefleri +- 📚 Uçtan uca kurulum rehberleri ve yol haritası dokümanları +- 📊 Boyutlandırma/maliyet analizi ve referans kaynakları -## 🎯 Scope +## 🎯 Kapsam -The main goal is to standardize and document infrastructure provisioning with clear responsibility boundaries: +Temel amaç, sorumluluk sınırları net biçimde tanımlanmış, standart ve belgelenmiş bir altyapı provisioning süreci oluşturmaktır: -- 🧱 **Terraform**: creates cloud infrastructure (servers, private networks, firewalls, placement groups, floating IPs, SSH key registration, inventory output) -- 🤖 **Ansible**: prepares OS, security hardening, Docker/Swarm, runner setup, and StorageBox mount workflows via in-repo playbooks and shared roles -- 🚀 **Application/stack deployment**: handled in related deployment workflows and stack manifests referenced by roadmap docs +- 🧱 **Terraform**: bulut altyapısını oluşturur (sunucular, private ağlar, firewall'lar, placement group'lar, floating IP'ler, SSH key kaydı, envanter çıktısı) +- 🤖 **Ansible**: OS hazırlığı, güvenlik sertleştirme, Docker/Swarm, runner kurulumu ve StorageBox mount süreçlerini depodaki playbook ve paylaşımlı roller aracılığıyla yönetir +- 🚀 **Uygulama/stack dağıtımı**: yol haritası dokümanlarında referans verilen ilgili deployment workflow'ları ve stack manifest'leri tarafından yönetilir -## 📌 Current Repository Status +## 📌 Mevcut Depo Durumu -As of now, this repository primarily contains: +Bu depo şu an ağırlıklı olarak şunları içermektedir: -- 🧱 Ready Terraform code for: +- 🧱 Hazır Terraform kodu: - `terraform/hetzner/test` - `terraform/hetzner/prod` -- 🤖 Ansible automation assets for both environments: +- 🤖 Her iki ortam için Ansible otomasyon varlıkları: - `ansible/test/test-bootstrap.yml` - `ansible/prod/prod-bootstrap.yml` - `ansible/roles/*` - - `ansible/test/group_vars/*` and `ansible/prod/group_vars/*` -- 📦 Inventory artifacts and output targets: - - `ansible/test/inventory/generated/test.yml` (tracked sample) - - `ansible/prod/inventory/generated/prod.yml` (expected output path) -- 📘 Detailed setup phases: - - `setup/00-genel-yol-haritasi.md` through `setup/09-prod-runner-ha-ve-swarm.md` -- 🛣️ Environment roadmap steps: + - `ansible/test/group_vars/*` ve `ansible/prod/group_vars/*` +- 📦 Envanter çıktıları ve hedef yollar: + - `ansible/test/inventory/generated/test.yml` (takip edilen örnek) + - `ansible/prod/inventory/generated/prod.yml` (beklenen çıktı yolu) +- 📘 Detaylı kurulum aşamaları: + - `setup/00-genel-yol-haritasi.md` — `setup/09-prod-runner-ha-ve-swarm.md` +- 🛣️ Ortam yol haritası adımları: - `roadmap/test-env/*` - `roadmap/prod-env/*` -- 📈 Capacity planning and reference charts: +- 📈 Kapasite planlama ve referans grafikler: - `hetzner-sizing-report.md` - `test-app-graphs.png` - `test-db-graphs.png` -## 🧭 Target Environment Topology +## 🧭 Hedef Ortam Topolojisi ### 🧪 Test -| Node | Role | Private IP | Suggested Type | +| Node | Rol | Private IP | Önerilen Tip | | --- | --- | --- | --- | | `iklim-app-01` | Swarm manager + app worker + test runner | `10.10.10.11` | `cpx42` | -| `iklim-db-01` | DB host (manual/stack-based DB setup path) | `10.10.20.11` | `cpx42` | +| `iklim-db-01` | DB host (manuel/stack tabanlı DB kurulum yolu) | `10.10.20.11` | `cpx42` | ### 🏭 Production -| Node | Role | Private IP | Suggested Type | +| Node | Rol | Private IP | Önerilen Tip | | --- | --- | --- | --- | -| `iklim-app-01` | Swarm manager + app worker + runner (primary FIP target) | `10.20.10.11` | `cpx42` | +| `iklim-app-01` | Swarm manager + app worker + runner (birincil FIP hedefi) | `10.20.10.11` | `cpx42` | | `iklim-app-02` | Swarm manager + app worker + runner | `10.20.10.12` | `cpx42` | | `iklim-app-03` | Swarm manager + app worker + runner | `10.20.10.13` | `cpx42` | | `iklim-db-01` | DB cluster node | `10.20.20.11` | `cpx32` | | `iklim-db-02` | DB cluster node | `10.20.20.12` | `cpx32` | | `iklim-db-03` | DB cluster node | `10.20.20.13` | `cpx32` | -## 🔐 Security and Network Baseline +## 🔐 Güvenlik ve Ağ Temeli -Core decisions reflected in Terraform and setup docs: +Terraform ve kurulum dokümanlarına yansıtılan temel kararlar: -- Test and prod are separated into different Hetzner Cloud projects/tokens. -- Public ingress is limited to: - - `22/tcp` (admin CIDRs only) +- Test ve prod, ayrı Hetzner Cloud proje ve token'larıyla birbirinden yalıtılmıştır. +- Kamuya açık gelen trafik şunlarla sınırlıdır: + - `22/tcp` (yalnızca admin CIDR'ları) - `80/tcp` - `443/tcp` -- Critical services remain private-only (for example Vault `8200`, PostgreSQL `5432`, MongoDB `27017`, internal observability and broker ports). -- Placement groups are used for host-spread strategy. -- `prevent_destroy = true` is enabled on server resources to reduce accidental deletion risk. -- Terraform state and secret files must not be committed. +- Kritik servisler yalnızca private ağda erişilebilir (örneğin Vault `8200`, PostgreSQL `5432`, MongoDB `27017`, iç gözlemlenebilirlik ve broker portları). +- Host dağılımı stratejisi için placement group'lar kullanılmaktadır. +- Sunucu kaynaklarında yanlışlıkla silinmeye karşı `prevent_destroy = true` etkinleştirilmiştir. +- Terraform state ve gizli dosyalar commit'lenmemelidir. -See: -- `setup/01-private-network-port-matrisi.md` +Ayrıca bkz.: +- [[facts/firewall.md]] — tüm firewall kurallarının araç bazında özet dökümantasyonu +- [[setup/01-private-network-port-matrisi.md]] - `terraform/hetzner/test/firewall.tf` - `terraform/hetzner/prod/firewall.tf` -## 🗂️ Repository Structure +## 🗂️ Depo Yapısı ```text Environment_Infrastructure/ @@ -123,35 +124,37 @@ Environment_Infrastructure/ │ └── hetzner/ │ ├── test/ │ └── prod/ +├── facts/ +│ └── firewall.md ├── hetzner-sizing-report.md ├── setup-vs-roadmap-map.md ├── test-app-graphs.png └── test-db-graphs.png ``` -## ✅ Prerequisites +## ✅ Ön Koşullar - Terraform `>= 1.6` -- Hetzner Cloud account(s) and API token per environment -- SSH key pair (public key path used in Terraform variables) -- Linux/macOS shell tools (`bash`, `cp`, `sed` or editor of your choice) -- Optional but expected in later phases: Ansible, Docker, access to Gitea/Harbor/StorageBox +- Hetzner Cloud hesabı ve ortam başına API token +- SSH anahtar çifti (public key yolu Terraform değişkenlerinde kullanılır) +- Linux/macOS kabuk araçları (`bash`, `cp`, `sed` veya tercih edilen metin editörü) +- İlerleyen aşamalarda gerekli: Ansible, Docker, Gitea/Harbor/StorageBox erişimi -## 🛠️ Terraform Usage +## 🛠️ Terraform Kullanımı -### 1) 🧪 Test Infrastructure +### 1) 🧪 Test Altyapısı ```bash cd terraform/hetzner/test cp terraform.tfvars.example terraform.tfvars ``` -Edit `terraform.tfvars` values: +`terraform.tfvars` değerlerini düzenle: - `hcloud_token` - `admin_allowed_cidrs` -- optional overrides (`location`, image, server types, key path) +- isteğe bağlı geçersiz kılmalar (`location`, image, sunucu tipleri, key yolu) -Then run: +Ardından çalıştır: ```bash terraform init @@ -161,19 +164,19 @@ mkdir -p ../../../ansible/test/inventory/generated terraform output -raw ansible_inventory_yaml > ../../../ansible/test/inventory/generated/test.yml ``` -### 2) 🏭 Production Infrastructure +### 2) 🏭 Production Altyapısı ```bash cd terraform/hetzner/prod cp terraform.tfvars.example terraform.tfvars ``` -Edit `terraform.tfvars` values: +`terraform.tfvars` değerlerini düzenle: - `hcloud_token` (prod token) - `admin_allowed_cidrs` -- optional overrides +- isteğe bağlı geçersiz kılmalar -Then run: +Ardından çalıştır: ```bash terraform init @@ -183,83 +186,83 @@ mkdir -p ../../../ansible/prod/inventory/generated terraform output -raw ansible_inventory_yaml > ../../../ansible/prod/inventory/generated/prod.yml ``` -## 🧱 Setup Flow (Canonical Order) +## 🧱 Kurulum Akışı (Kanonik Sıra) -Use setup documents in this sequence: +Kurulum dokümanlarını bu sırayla kullan: -1. `setup/00-genel-yol-haritasi.md` — global decisions and boundaries -2. `setup/01-private-network-port-matrisi.md` — private/public port policy -3. `setup/02-test-terraform-iaac.md` — test Terraform phase -4. `setup/03-test-ansible-bootstrap.md` — test OS/bootstrap/hardening -5. `setup/04-test-db-docker-kurulum.md` — test DB stack setup on Swarm -6. `setup/05-test-runner-ve-deploy-onkosullari.md` — test runner and deploy prerequisites -7. `setup/06-prod-terraform-iaac.md` — prod Terraform phase -8. `setup/07-prod-ansible-bootstrap.md` — prod OS/bootstrap/hardening +1. `setup/00-genel-yol-haritasi.md` — genel kararlar ve sınırlar +2. `setup/01-private-network-port-matrisi.md` — private/public port politikası +3. `setup/02-test-terraform-iaac.md` — test Terraform aşaması +4. `setup/03-test-ansible-bootstrap.md` — test OS/bootstrap/sertleştirme +5. `setup/04-test-db-docker-kurulum.md` — test DB stack kurulumu (Swarm üzerinde) +6. `setup/05-test-runner-ve-deploy-onkosullari.md` — test runner ve deploy ön koşulları +7. `setup/06-prod-terraform-iaac.md` — prod Terraform aşaması +8. `setup/07-prod-ansible-bootstrap.md` — prod OS/bootstrap/sertleştirme 9. `setup/08-prod-db-cluster-kurulum.md` — prod DB cluster stack (MongoDB + Patroni/etcd) -10. `setup/09-prod-runner-ha-ve-swarm.md` — prod runner HA and deploy lock model +10. `setup/09-prod-runner-ha-ve-swarm.md` — prod runner HA ve deploy kilit modeli -## 🛣️ Roadmap Documents +## 🛣️ Yol Haritası Dokümanları -The roadmap folders track integration work for Swarm stacks, SWAG, APISIX, pipeline updates, and verification checklists: +Yol haritası klasörleri; Swarm stack'leri, SWAG, APISIX, pipeline güncellemeleri ve doğrulama kontrol listeleri için entegrasyon çalışmalarını takip eder: - `roadmap/test-env/*` - `roadmap/prod-env/*` -These documents often reference files from related repositories (for example application root repo workflows and stack files). Treat them as implementation guidance aligned with this infrastructure baseline. +Bu dokümanlar zaman zaman ilgili repolardan (örneğin uygulama ana deposu workflow ve stack dosyaları) dosyalara referans verir. Bu altyapı temeliyle hizalanmış uygulama rehberleri olarak değerlendirilmelidir. -## 💰 Sizing and Cost Snapshot +## 💰 Boyutlandırma ve Maliyet Özeti -Reference: `hetzner-sizing-report.md` +Referans: `hetzner-sizing-report.md` -Suggested baseline: +Önerilen temel yapı: - **Test:** `2 x cpx42` (app + db) - **Prod:** `3 x cpx42` (app) + `3 x cpx32` (db) -Approximate monthly total in the report: +Rapordaki yaklaşık aylık toplam: - Test: `$59.98` - Prod: `$139.44` -- Combined: `$199.42` +- Toplam: `$199.42` -## 🔑 Secrets and State Management +## 🔑 Gizli Bilgi ve State Yönetimi -Never commit: +Kesinlikle commit'lenmemeli: - `terraform.tfvars`, `*.tfvars`, `*.tfstate`, `.terraform/` -- private keys, certificates, `.env` secrets -- runner tokens and vault password files +- private key'ler, sertifikalar, `.env` gizli bilgileri +- runner token'ları ve vault parola dosyaları -See `.gitignore` for enforced patterns. +Zorunlu kalıplar için `.gitignore` dosyasına bkz. -Recommended: -- keep runtime secrets in secure secret stores / encrypted vault files -- keep generated runtime artifacts outside version control unless explicitly sanitized +Önerilen: +- çalışma zamanı gizli bilgilerini güvenli gizli depolarda / şifreli vault dosyalarında tut +- üretilen çalışma zamanı artifakt'larını, açıkça temizlenmedikçe versiyon kontrolü dışında tut -## ⚠️ Known Gaps / Notes +## ⚠️ Bilinen Eksikler / Notlar -- `ansible/prod/inventory/generated/prod.yml` is an expected output path but may not exist until generated. -- Some roadmap steps target files under the broader `iklim.co` application repository, not this repository alone. +- `ansible/prod/inventory/generated/prod.yml` beklenen bir çıktı yoludur; üretilene kadar mevcut olmayabilir. +- Bazı yol haritası adımları, yalnızca bu depo değil, daha geniş kapsamlı `iklim.co` uygulama deposundaki dosyaları hedef alır. -## ✅ Quick Validation Checklist +## ✅ Hızlı Doğrulama Kontrol Listesi -After Terraform apply: +Terraform apply sonrası: -- servers are created with expected names and private IPs -- floating IP exists and is attached -- firewalls expose only intended public ports -- placement groups are assigned -- generated inventory YAML is exported to `ansible/{test,prod}/inventory/generated/*.yml` +- sunucular beklenen isimler ve private IP'lerle oluşturulmuş +- floating IP mevcut ve bağlı +- firewall'lar yalnızca amaçlanan public portları açıyor +- placement group'lar atanmış +- üretilen envanter YAML `ansible/{test,prod}/inventory/generated/*.yml` yoluna aktarılmış -After bootstrap/deploy phases: +Bootstrap/deploy aşamaları sonrası: -- Swarm state and labels match documentation -- DB accessibility is private-only -- Vault/API gateways follow public/private exposure rules -- runner and deploy lock behavior align with environment policy +- Swarm durumu ve etiketleri dökümantasyonla eşleşiyor +- DB erişimi yalnızca private ağdan mümkün +- Vault/API gateway'leri public/private erişim kurallarına uyuyor +- Runner ve deploy kilit davranışı ortam politikasıyla örtüşüyor -## 🔗 References +## 🔗 Referanslar - Hetzner Terraform Provider: https://registry.terraform.io/providers/hetznercloud/hcloud/latest - Hetzner Networks: https://docs.hetzner.com/cloud/networks/overview/ - Hetzner Firewalls: https://docs.hetzner.com/cloud/firewalls/overview - Hetzner Placement Groups: https://docs.hetzner.com/cloud/placement-groups/overview - Docker Swarm overlay networking: https://docs.docker.com/engine/network/drivers/overlay/ -- Gitea act_runner: https://docs.gitea.com/usage/actions/act-runner \ No newline at end of file +- Gitea act_runner: https://docs.gitea.com/usage/actions/act-runner diff --git a/ansible/roles/act_runner/defaults/main.yml b/ansible/roles/act_runner/defaults/main.yml new file mode 100644 index 0000000..2d3a378 --- /dev/null +++ b/ansible/roles/act_runner/defaults/main.yml @@ -0,0 +1,8 @@ +--- +act_runner_version: "0.2.12" +act_runner_arch: "linux-amd64" +act_runner_gitea_url: "https://git.tarla.io" +act_runner_name: "iklim-test-app" +act_runner_labels: "ubuntu-latest,ubuntu-22.04,ubuntu-20.04" +# Gitea'dan alınan tek seferlik registration token; kayıt olmadıysa boş bırakılır. +act_runner_registration_token: "{{ vault_gitea_runner_token | default('') }}" diff --git a/ansible/roles/act_runner/handlers/main.yml b/ansible/roles/act_runner/handlers/main.yml new file mode 100644 index 0000000..f53d3a5 --- /dev/null +++ b/ansible/roles/act_runner/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: restart act_runner + ansible.builtin.systemd: + name: gitea-act-runner + state: restarted + daemon_reload: true diff --git a/ansible/roles/act_runner/tasks/main.yml b/ansible/roles/act_runner/tasks/main.yml new file mode 100644 index 0000000..cdf8299 --- /dev/null +++ b/ansible/roles/act_runner/tasks/main.yml @@ -0,0 +1,86 @@ +--- +- name: Install deploy prerequisites + ansible.builtin.dnf: + name: + - gettext + - jq + - git + state: present + +- name: Create gitea-runner system user + ansible.builtin.user: + name: gitea-runner + system: true + shell: /bin/bash + create_home: true + home: /var/lib/gitea-runner + groups: docker + append: true + +- name: Download act_runner binary + ansible.builtin.get_url: + url: "https://dl.gitea.com/act_runner/{{ act_runner_version }}/act_runner-{{ act_runner_version }}-{{ act_runner_arch }}" + dest: /usr/local/bin/act_runner + mode: "0755" + owner: root + group: root + +- name: Create act_runner config directory + ansible.builtin.file: + path: /etc/gitea-act-runner + state: directory + owner: gitea-runner + group: gitea-runner + mode: "0750" + +# Kayıt öncesinde varsayılan config.yaml üretilir; dosya varsa tekrar yazılmaz. +- name: Generate default config.yaml + ansible.builtin.shell: + cmd: /usr/local/bin/act_runner generate-config > /etc/gitea-act-runner/config.yaml + creates: /etc/gitea-act-runner/config.yaml + become_user: gitea-runner + +- name: Fix config.yaml ownership + ansible.builtin.file: + path: /etc/gitea-act-runner/config.yaml + owner: gitea-runner + group: gitea-runner + mode: "0640" + +# .runner dosyası varsa runner zaten kayıtlıdır; creates ile idempotent hale gelir. +- name: Register runner with Gitea + ansible.builtin.command: + argv: + - /usr/local/bin/act_runner + - register + - --instance + - "{{ act_runner_gitea_url }}" + - --token + - "{{ act_runner_registration_token }}" + - --no-interactive + - --name + - "{{ act_runner_name }}" + - --config + - /etc/gitea-act-runner/config.yaml + - --labels + - "{{ act_runner_labels }}" + args: + chdir: /var/lib/gitea-runner + creates: /var/lib/gitea-runner/.runner + become_user: gitea-runner + when: act_runner_registration_token | length > 0 + no_log: true + +- name: Deploy gitea-act-runner systemd service + ansible.builtin.template: + src: gitea-act-runner.service.j2 + dest: /etc/systemd/system/gitea-act-runner.service + mode: "0644" + notify: restart act_runner + +- name: Enable and start gitea-act-runner + ansible.builtin.systemd: + name: gitea-act-runner + enabled: true + state: started + daemon_reload: true diff --git a/ansible/roles/act_runner/templates/gitea-act-runner.service.j2 b/ansible/roles/act_runner/templates/gitea-act-runner.service.j2 new file mode 100644 index 0000000..6447a0f --- /dev/null +++ b/ansible/roles/act_runner/templates/gitea-act-runner.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=Gitea Actions runner +After=network.target docker.service + +[Service] +ExecStart=/usr/local/bin/act_runner daemon --config /etc/gitea-act-runner/config.yaml +ExecReload=/bin/kill -s HUP $MAINPID +WorkingDirectory=/var/lib/gitea-runner +User=gitea-runner +Group=gitea-runner +TimeoutSec=0 +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/db_stack/tasks/app_node.yml b/ansible/roles/db_stack/tasks/app_node.yml new file mode 100644 index 0000000..eba6e02 --- /dev/null +++ b/ansible/roles/db_stack/tasks/app_node.yml @@ -0,0 +1,17 @@ +--- +- name: Stack durumunu kontrol et + ansible.builtin.shell: docker stack ls | grep iklim-db + register: stack_status + failed_when: false + changed_when: false + +- name: DB stack dosyasını oluştur + ansible.builtin.template: + src: db.stack.yml.j2 + dest: /opt/iklimco/stacks/db.yml + mode: '0600' + register: stack_file + +- name: DB stack'i deploy et + ansible.builtin.shell: docker stack deploy -c /opt/iklimco/stacks/db.yml iklim-db + when: stack_status.rc != 0 or stack_file.changed diff --git a/ansible/roles/db_stack/tasks/db_node.yml b/ansible/roles/db_stack/tasks/db_node.yml new file mode 100644 index 0000000..a680625 --- /dev/null +++ b/ansible/roles/db_stack/tasks/db_node.yml @@ -0,0 +1,12 @@ +--- +- name: MongoDB config dizinini oluştur + ansible.builtin.file: + path: /opt/iklimco/db/mongodb/config + state: directory + mode: '0750' + +- name: MongoDB konfigürasyon dosyasını oluştur + ansible.builtin.template: + src: mongod.conf.j2 + dest: /opt/iklimco/db/mongodb/config/mongod.conf + mode: '0644' diff --git a/ansible/roles/db_stack/tasks/main.yml b/ansible/roles/db_stack/tasks/main.yml new file mode 100644 index 0000000..3aff898 --- /dev/null +++ b/ansible/roles/db_stack/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- include_tasks: db_node.yml + when: inventory_hostname in groups['db'] + +- include_tasks: app_node.yml + when: inventory_hostname in groups['app'] diff --git a/ansible/roles/db_stack/templates/db.stack.yml.j2 b/ansible/roles/db_stack/templates/db.stack.yml.j2 new file mode 100644 index 0000000..2b3ebfa --- /dev/null +++ b/ansible/roles/db_stack/templates/db.stack.yml.j2 @@ -0,0 +1,42 @@ +version: "3.8" + +networks: + iklimco-net: + external: true + +volumes: + postgresql_data: + mongodb_data: + +services: + postgresql: + image: {{ db_postgres_image }} + environment: + POSTGRES_USER: "{{ db_postgres_root_user }}" + POSTGRES_PASSWORD: "{{ db_postgres_password }}" + POSTGRES_DB: postgres + PGDATA: /var/lib/postgresql/data/pgdata + volumes: + - postgresql_data:/var/lib/postgresql/data + networks: + - iklimco-net + deploy: + placement: + constraints: + - node.labels.role == db + + mongodb: + image: {{ db_mongo_image }} + environment: + MONGO_INITDB_ROOT_USERNAME: "{{ db_mongo_root_user }}" + MONGO_INITDB_ROOT_PASSWORD: "{{ db_mongo_root_password }}" + volumes: + - mongodb_data:/data/db + - /opt/iklimco/db/mongodb/config/mongod.conf:/etc/mongod.conf + command: ["--config", "/etc/mongod.conf"] + networks: + - iklimco-net + deploy: + placement: + constraints: + - node.labels.role == db diff --git a/ansible/roles/db_stack/templates/mongod.conf.j2 b/ansible/roles/db_stack/templates/mongod.conf.j2 new file mode 100644 index 0000000..009c9bf --- /dev/null +++ b/ansible/roles/db_stack/templates/mongod.conf.j2 @@ -0,0 +1,7 @@ +storage: + dbPath: /data/db +net: + port: 27017 + bindIp: 0.0.0.0 +security: + authorization: enabled diff --git a/ansible/roles/hardening/tasks/main.yml b/ansible/roles/hardening/tasks/main.yml index ff66d49..33406ba 100644 --- a/ansible/roles/hardening/tasks/main.yml +++ b/ansible/roles/hardening/tasks/main.yml @@ -38,6 +38,12 @@ dest: /etc/fail2ban/jail.local notify: Restart fail2ban +- name: Ensure fail2ban is running and enabled + ansible.builtin.service: + name: fail2ban + state: started + enabled: yes + - name: Ensure firewalld is running ansible.builtin.service: name: firewalld @@ -67,6 +73,12 @@ create_home: yes state: present +- name: Add SSH key to iklim user + ansible.posix.authorized_key: + user: iklim + state: present + key: "{{ lookup('file', admin_ssh_public_key_path) }}" + - name: Configure journald log limits ansible.builtin.lineinfile: path: /etc/systemd/journald.conf diff --git a/ansible/test/group_vars/all/vars.yml b/ansible/test/group_vars/all/vars.yml index 221b47e..7d7b8c7 100644 --- a/ansible/test/group_vars/all/vars.yml +++ b/ansible/test/group_vars/all/vars.yml @@ -6,4 +6,13 @@ storagebox_password: "{{ vault_storagebox_password }}" iklim_password: "{{ vault_iklim_password }}" swarm_manager_ip: "10.10.10.11" 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" + +# DB Stack +db_postgres_image: "postgis/postgis:17-3.5" +db_mongo_image: "mongo:8" +db_postgres_root_user: "{{ vault_postgres_root_user }}" +db_postgres_password: "{{ vault_postgres_password }}" +db_mongo_root_user: "{{ vault_mongo_root_user }}" +db_mongo_root_password: "{{ vault_mongo_root_password }}" diff --git a/ansible/test/group_vars/all/vault.yml b/ansible/test/group_vars/all/vault.yml index 031b24b..6cfaf84 100644 --- a/ansible/test/group_vars/all/vault.yml +++ b/ansible/test/group_vars/all/vault.yml @@ -1,25 +1,30 @@ $ANSIBLE_VAULT;1.1;AES256 -32653536356331386232373033363738363336323461363432653031666166343462393737643730 -3162386266326333386533373630663563386337613338310a376137623835333461363662323035 -65636332376331643335323265336439613331613238393363626330313831653233373864313033 -3430303335306366660a636139623264386437383763316665373230643939653039633330623834 -64636564313232626462373638653538393261323031616563653164323961393664656439393639 -37313335313739353564626364313663363038316132633739623338343436303337643162396635 -34323838346664303464396438393534636265636262323364643364323163653464303931626130 -37663138363966386530323133613661316230303362323937313132306236323339323839633139 -34633733333531373233386436313837343364326334386535626262356537376137646163326666 -38306238666639623639393137623266363465313264326566663839303664303233666335663731 -32633232376164383265313835326433366134613230613164373034663931396161623631666236 -37613631353233346464363236383539663461333739396432626638323134383230343163396335 -64363333396130326463316538306162363034353936383063333531396233356437333064613230 -63353337306632323364336233313836663365623436336532623239633434656563666637333636 -31343836353230306461613936383766636138663361343864623466376235666536306133306435 -65666338333465653434386166366633383539323566613935363434363735313231336166626364 -65353033363366386432306434333135653862616635373837376233326262646330326363626134 -32343735386137363334306464383935613531373533363330363635633236313930373865393738 -38363564663066363439396532656236656636646365363038393535303632356364613538353737 -32636434383766343765643634633464353262396466393265643963383634323730343162323837 -39343139613538653531616466303638336133396165646138663463383238616431613563343031 -39333139643033326630343630366630633766383861353663353534633436646363356334626438 -62306661376339343437643732333032373362633062326365666430616634613537316635653465 -64306362386339333562 +63323534653836623136643065383166323661663339653335373139663436316433396137343739 +3039383336313861616236336565323664613333383262610a363436376132316437656239633334 +63633863643938373537333562346433313766393732616362306132313430333964373031313962 +3230393837316434650a323933313934316334633931323165633730373933326564316661666332 +36323735323736656662366463383431393433643331616134623662663333313364343232653138 +61316165636431663230376234626532663431373632393731383434386435353638363365323861 +33373462333966663033666463303032343961653533343932363462623637616232666561646335 +36623039353633343035353765326339616233666562636438636137303835643736386633623139 +66656137646461363165313937306266393035383339363233303066396564346638643539313361 +38653930393766656634343334373961323031333938663636623138666661346133316631393231 +36393237313439313435363866333364343231613330373439626462356139303061313632343338 +38633364343663333139396431663938653263633739383036613935373332653666656633323331 +37316535386139633263633266353739386164666564666136643665353135356231323633373363 +36646464383766663532353132326162363734366337373537656132366139386362616438616531 +34386234306231376162643934643565343364336530616562303739363935353636343633376464 +34333835363666303236313363663665653566326365386439653362663462343136316664613036 +37616163653561633030376534393533653466373839633661653338313166383332613234666164 +37343737316139393930313739366562643539326233366334353566633339333130366263663030 +63393437383566393932353230396534383330646364646561323631653338396132303461613636 +37306533663732626532393130323933633365336233613334666330343436326137306666636234 +30623831303632613866333734306337623230373433373332646534306135333039343231643536 +30356238323263653236326662303365343464356264376565326432323335323862366365666561 +32316633396430376234636661623464316135333862623531393661343332303033323636393736 +37653830393665363330393135616465363161623861343531636130636132663263613836646264 +66373962313263643334373664303338636232643535313164343434653532303261346566623232 +30363662393764316534393030303137663534613564656531616466396232323561623939303733 +30383637376432346138333365376431396236356263333634613462316131343634663861356564 +39613965333334303036626237373534333337633261353065336534373732306166643666323435 +36666239653536653838623864396635663764313738303533323163633261613665 diff --git a/ansible/test/host_vars/iklim-app-01/vault.yml b/ansible/test/host_vars/iklim-app-01/vault.yml new file mode 100644 index 0000000..9a6f714 --- /dev/null +++ b/ansible/test/host_vars/iklim-app-01/vault.yml @@ -0,0 +1,7 @@ +$ANSIBLE_VAULT;1.1;AES256 +35336562303132663037626538366261663764666633306237386564303161633730666336643237 +6563346339646130666531323165316561653861346663610a353036636565333138373139383438 +33386634306666373932353134323830343666653266343262373236306631613735636430663838 +3338643164613532370a323935383938383331303036353238376332616130383935623362626235 +35653566383764303765373866376636323461383332346661363866623962643439376439363432 +3335613733386536613765396136316364333934643465653234 diff --git a/ansible/test/host_vars/iklim-db-01/vault.yml b/ansible/test/host_vars/iklim-db-01/vault.yml new file mode 100644 index 0000000..018f99a --- /dev/null +++ b/ansible/test/host_vars/iklim-db-01/vault.yml @@ -0,0 +1,7 @@ +$ANSIBLE_VAULT;1.1;AES256 +37303034646630363834303561383932623131306331326530383861633838353039633364326162 +6237613235633062356633346663336137353331653866640a333663646539646435363031343034 +30633662633838376130656462646566366633333734333761616563333861386166313832333332 +3733363866323036660a373966373766393965623231656266323630376133303963363639383631 +37393539363765336632333162623065363537613862356261666636366261313136373266623364 +3338646231343632336234303436636235633363646661656339 diff --git a/ansible/test/test-app-post-stack.yml b/ansible/test/test-app-post-stack.yml new file mode 100644 index 0000000..9e5e747 --- /dev/null +++ b/ansible/test/test-app-post-stack.yml @@ -0,0 +1,19 @@ +--- +# 05 · Test runner ve deploy ön koşulları +# +# Ön koşul: Gitea arayüzünden (Organization → Settings → Actions → Runners) +# bir Registration Token alın ve group_vars/all/vault.yml içindeki +# vault_gitea_runner_token değişkenine ekleyin. +# +# ansible-playbook test-app-post-stack.yml --vault-password-file=.vault_pass +# +# Token tanımlı değilse kurulum tamamlanır ancak kayıt adımı atlanır. +# Sonraki çalıştırmada .runner dosyası varsa kayıt tekrar yapılmaz (idempotent). + +- name: "App Node -Gitea runner ve deploy ön koşulları" + hosts: app + become: true + + roles: + - role: act_runner + tags: [act_runner] diff --git a/ansible/test/test-db-post-stack.yml b/ansible/test/test-db-post-stack.yml new file mode 100644 index 0000000..759e347 --- /dev/null +++ b/ansible/test/test-db-post-stack.yml @@ -0,0 +1,14 @@ +--- +- name: DB Node - StorageBox Dizinleri ve MongoDB Konfigürasyonu + hosts: db + become: yes + roles: + - role: db_stack + tags: [db_stack] + +- name: App Node - DB Stack Deploy + hosts: app + become: yes + roles: + - role: db_stack + tags: [db_stack] diff --git a/facts/firewall.md b/facts/firewall.md new file mode 100644 index 0000000..afd0658 --- /dev/null +++ b/facts/firewall.md @@ -0,0 +1,131 @@ +# Firewall Mimarisi + +## Genel Bakış + +Trafik filtreleme iki katmanda uygulanıyor. Bir paket sunucuya ulaşmadan önce Hetzner Cloud Firewall'dan geçmek zorunda; geçse bile sunucu içinde firewalld tarafından tekrar denetleniyor. + +``` +İnternet → Hetzner Cloud Firewall (Terraform) → firewalld (Ansible) → Uygulama +``` + +| Katman | Araç | Yönetim | Kapsam | +|--------|------|---------|--------| +| 1. Hetzner Cloud Firewall | Terraform | `terraform/hetzner/{test,prod}/firewall.tf` | Ağ seviyesi, sunucuya ulaşmadan drop | +| 2. firewalld | Ansible | `ansible/roles/hardening/tasks/main.yml` | İşletim sistemi seviyesi, default zone: drop | + +Admin IP'leri her iki katmanda da aynı değişkenden besleniyor: +- **Terraform:** `var.admin_allowed_cidrs` → `terraform.tfvars` +- **Ansible:** `admin_allowed_cidrs` → `group_vars/all/vars.yml` + +Mevcut admin CIDR'ları: `78.187.87.109/32`, `95.70.151.248/32` + +--- + +## Katman 1 — Hetzner Cloud Firewall (Terraform) + +Her ortam için iki ayrı firewall tanımı var: `app` ve `db` node'larına uygulanıyor. + +### Test Ortamı + +Subnet'ler: app `10.10.10.0/24`, db `10.10.20.0/24` + +#### App Firewall (`iklim-test-firewall-app`) + +| Port | Protokol | Kaynak | Açıklama | +|------|----------|--------|----------| +| 22 | TCP | admin CIDRs | SSH | +| 80 | TCP | 0.0.0.0/0, ::/0 | HTTP public | +| 443 | TCP | 0.0.0.0/0, ::/0 | HTTPS public | +| 2377 | TCP | 10.10.10.0/24, 10.10.20.0/24 | Docker Swarm control plane | +| 7946 | TCP/UDP | 10.10.10.0/24, 10.10.20.0/24 | Docker Swarm node discovery | +| 4789 | UDP | 10.10.10.0/24, 10.10.20.0/24 | Docker Swarm VXLAN overlay | +| 8200 | TCP | 10.10.10.0/24 | Vault API | +| 6379 | TCP | 10.10.10.0/24 | Redis | +| 5672 | TCP | 10.10.10.0/24 | RabbitMQ AMQP | +| 61613 | TCP | 10.10.10.0/24 | RabbitMQ STOMP | +| 15674 | TCP | 10.10.10.0/24 | RabbitMQ Web STOMP | +| 15672 | TCP | 10.10.10.0/24 | RabbitMQ Management (SWAG üzerinden 443) | +| 9000 | TCP | 10.10.10.0/24 | APISIX Dashboard (SWAG üzerinden 443, IP kısıtlı) | +| 9180 | TCP | 10.10.10.0/24 | APISIX Admin API (sadece Docker overlay) | +| 9090 | TCP | 10.10.10.0/24 | Prometheus (SWAG üzerinden 443) | +| 3000 | TCP | 10.10.10.0/24 | Grafana (SWAG üzerinden 443, IP kısıtlı) | + +#### DB Firewall (`iklim-test-firewall-db`) + +| Port | Protokol | Kaynak | Açıklama | +|------|----------|--------|----------| +| 22 | TCP | admin CIDRs | SSH | +| 5432 | TCP | 10.10.10.0/24 | PostgreSQL (app'ten) | +| 27017 | TCP | 10.10.10.0/24 | MongoDB (app'ten) | +| 2377 | TCP | 10.10.10.0/24 | Docker Swarm control plane | +| 7946 | TCP/UDP | 10.10.10.0/24 | Docker Swarm node discovery | +| 4789 | UDP | 10.10.10.0/24 | Docker Swarm VXLAN overlay | + +### Prod Ortamı + +Subnet'ler: app `10.20.10.0/24`, db `10.20.20.0/24` + +App firewall test ile aynı kurallara sahip (kaynak IP'ler prod subnet'leri). + +#### DB Firewall (`iklim-prod-firewall-db`) — test'ten farklı kurallar + +| Port | Protokol | Kaynak | Açıklama | +|------|----------|--------|----------| +| 5432 | TCP | 10.20.20.0/24 | PostgreSQL replikasyon (DB subnet içi) | +| 27017 | TCP | 10.20.20.0/24 | MongoDB replica set internal | +| 2379 | TCP | 10.20.20.0/24 | etcd client | +| 2380 | TCP | 10.20.20.0/24 | etcd peer | +| 8008 | TCP | 10.20.20.0/24 | Patroni REST API | + +--- + +## Katman 2 — firewalld (Ansible) + +Tüm ortamlarda `hardening` role tarafından uygulanıyor. Default zone `drop` — listelenmeyen her trafik reddedilir. + +| Port | Protokol | Kaynak | Açıklama | +|------|----------|--------|----------| +| 22 | TCP | admin CIDRs | SSH (rich rule, zone: drop) | + +SSH rich rule örneği: +``` +rule family="ipv4" source address="78.187.87.109/32" service name="ssh" accept +``` + +### SSH Sertleştirme (sshd_config) + +| Parametre | Değer | Açıklama | +|-----------|-------|----------| +| PasswordAuthentication | no | Sadece key ile giriş | +| PermitRootLogin | prohibit-password | Root key ile girebilir, şifre ile giremez | +| PermitEmptyPasswords | no | Boş şifre yasak | +| MaxAuthTries | 3 | 3 başarısız denemede bağlantı kesilir | + +> **Not:** `MaxAuthTries 3` ssh-agent'ta birden fazla key varken "Too many authentication failures" hatasına yol açar. `~/.ssh/config`'de `IdentitiesOnly yes` ile doğru key'i belirtmek gerekir. + +--- + +## Kural Güncelleme Rehberi + +### Admin IP değiştiğinde + +İki dosyayı güncelle: + +``` +terraform/hetzner/test/terraform.tfvars → admin_allowed_cidrs +ansible/test/group_vars/all/vars.yml → admin_allowed_cidrs +``` + +Sonra uygula: + +```bash +# Terraform +cd terraform/hetzner/test && terraform apply + +# Ansible +cd ansible/test && ansible-playbook test-bootstrap.yml --tags hardening +``` + +### Yeni port açılacaksa + +Hetzner seviyesinde `firewall.tf`'e kural ekle, Terraform ile uygula. Sunucu içinde firewalld'da açmak gerekiyorsa hardening role'una da ekle. diff --git a/facts/swarm-node-recovery.md b/facts/swarm-node-recovery.md new file mode 100644 index 0000000..ff7e849 --- /dev/null +++ b/facts/swarm-node-recovery.md @@ -0,0 +1,76 @@ +# Docker Swarm — Node Recovery + +Test ortamında tek manager (`iklim-app-01`) ve tek worker (`iklim-db-01`) bulunur. Hangi node'un yeniden kurulduğuna göre recovery süreci farklılaşır. + +## Senaryo 1: `iklim-app-01` (Manager) Yeniden Kurulur + +### Sorun + +Yeni `iklim-app-01` üzerinde `docker swarm init` farklı cluster ID ile yeni bir küme başlatır. `iklim-db-01` hâlâ eski kümeye bağlıdır. Ansible `swarm` role'u `iklim-db-01`'de Swarm'ı `active` görür, join denemez. İki node iki ayrı kümede kalır. + +### Çözüm + +```bash +# 1. iklim-db-01 üzerinde — eski kümeden çık +docker swarm leave --force + +# 2. Ansible ile her iki node'u yeniden kur +cd ansible/test +ansible-playbook -i inventory/generated/test.yml test-bootstrap.yml --ask-vault-pass + +# 3. DB stack'i yeniden deploy et +ansible-playbook -i inventory/generated/test.yml test-db-post-stack.yml --ask-vault-pass +``` + +DB verileri `iklim-db-01`'deki named volume'larda korunur, kayıp yaşanmaz. + +--- + +## Senaryo 2: `iklim-db-01` (Worker) Yeniden Kurulur + +### Durum + +Yeni `iklim-db-01` Swarm'dan habersiz başlar (`inactive`). Manager (`iklim-app-01`) eski dead node kaydını tutar. + +### Çözüm + +```bash +# 1. Ansible bootstrap — yeni node otomatik join olur +cd ansible/test +ansible-playbook -i inventory/generated/test.yml test-bootstrap.yml --ask-vault-pass + +# 2. iklim-app-01 üzerinde — eski dead node kaydını temizle +docker node ls # eski node ID'yi bul +docker node rm + +# 3. DB stack'i yeniden deploy et (backup'tan restore sonrası) +ansible-playbook -i inventory/generated/test.yml test-db-post-stack.yml --ask-vault-pass +``` + +Ansible `swarm` role'u `inactive` durumu gördüğü için token alıp join eder, `role=db` label'ını uygular. DB servisleri placement constraint sayesinde yeni node'a schedule edilir. + +--- + +## Senaryo 3: Her İki Node Yeniden Kurulur + +Her şey sıfırdan kurulur, Swarm uyumsuzluğu yaşanmaz. + +```bash +cd ansible/test +ansible-playbook -i inventory/generated/test.yml test-bootstrap.yml --ask-vault-pass +ansible-playbook -i inventory/generated/test.yml test-db-post-stack.yml --ask-vault-pass +``` + +--- + +## Özet + +| Senaryo | Manuel Adım | Ansible Yeterli mi? | +|---|---|---| +| Manager (`iklim-app-01`) ölür | `docker swarm leave --force` (worker'da) | Sonrasında evet | +| Worker (`iklim-db-01`) ölür | `docker node rm ` (manager'da) | Büyük ölçüde evet | +| Her ikisi ölür | Yok | Evet | + +## Neden Prod'da Bu Sorun Yok + +Prod ortamında birden fazla manager node (en az 3) çalıştırılır. Tek manager düşse diğerleri liderliği devralır, küme sağlıklı kalmaya devam eder. diff --git a/setup/04-test-db-docker-kurulum.md b/setup/04-test-db-docker-kurulum.md index 780b5e7..1cebaa3 100644 --- a/setup/04-test-db-docker-kurulum.md +++ b/setup/04-test-db-docker-kurulum.md @@ -4,18 +4,18 @@ Bu aşamanın amacı `iklim-db-01` node'unu Swarm'a worker olarak eklemek ve Pos ## Mimari Karar -Yol haritasında DB'lerin "manuel" kurulacağı belirtilmiştir. Test ortamında bu "manuel" süreç, DB'lerin işletim sistemine doğrudan kurulması yerine, **Swarm Worker** üzerinde Docker konteynerleri olarak ayağa kaldırılması şeklinde uygulanacaktır. +Yol haritasında DB'lerin "manuel" kurulacağı belirtilmiştir. Test ortamında bu "manuel" süreç, DB'lerin işletim sistemine doğrudan kurulması yerine, **Swarm Worker** üzerinde Docker konteynerleri olarak ayağa kaldırılması şeklinde uygulanacaktır. Kurulum **Ansible** ile otomatize edilmiştir (`test-db-post-stack.yml`). **Neden?** 1. **Yönetim Kolaylığı:** Docker ile versiyon geçişleri ve konfigürasyon yönetimi çok daha hızlıdır. 2. **Overlay Network:** Uygulama servisleri (`iklim-app-01`), DB'lere `iklimco-net` overlay network üzerinden şifreli ve izole bir şekilde erişebilir. -3. **Veri Kalıcılığı:** Veriler Hetzner StorageBox (`/mnt/storagebox`) üzerinde saklanarak host bağımsızlığı sağlanır. +3. **Veri Kalıcılığı:** Veriler `iklim-db-01` üzerindeki Docker named volume'larında saklanır. StorageBox yalnızca backup için kullanılır. ## Ön Koşullar - `03-test-ansible-bootstrap.md` her iki node'da tamamlanmış olmalı. -- StorageBox `/mnt/storagebox` olarak her iki node'da mount edilmiş olmalı. - Docker `iklim-db-01` üzerinde kurulu olmalı (Bootstrap role bunu yapar). +- Ansible vault'unda `vault_postgres_root_user`, `vault_postgres_password`, `vault_mongo_root_user`, `vault_mongo_root_password` tanımlı olmalı. ## 1. Firewall Güncellemesi @@ -34,109 +34,54 @@ cd terraform/hetzner/test terraform apply ``` -## 2. DB Node'u Swarm'a Ekleme +## 2. Vault Güncellemesi -**iklim-app-01 üzerinde (Manager)** join token alın: ```bash -docker swarm join-token worker +cd ansible/test +ansible-vault edit group_vars/all/vault.yml ``` -**iklim-db-01 üzerinde (Worker)** Swarm'a katılın: -```bash -docker swarm join --token 10.10.10.11:2377 -``` - -**iklim-app-01 üzerinde** node'u etiketleyin: -```bash -docker node update --label-add role=db iklim-db-01 -``` - -## 3. StorageBox Dizin Yapısı - -**iklim-db-01 üzerinde:** -```bash -mkdir -p /mnt/storagebox/test/db/postgresql/data -mkdir -p /mnt/storagebox/test/db/mongodb/data -mkdir -p /mnt/storagebox/test/db/mongodb/log -mkdir -p /mnt/storagebox/test/db/mongodb/config -``` - -## 4. Veritabanı Konfigürasyonları - -### MongoDB Config (`mongod.conf`) -`/mnt/storagebox/test/db/mongodb/config/mongod.conf` dosyasını oluşturun: +Şu değişkenleri ekle: ```yaml -storage: - dbPath: /data/db - journal: - enabled: true -systemLog: - destination: file - logAppend: true - path: /data/log/mongod.log -net: - port: 27017 - bindIp: 0.0.0.0 -security: - authorization: enabled +vault_postgres_root_user: "postgres" +vault_postgres_password: "GÜÇLÜ_ŞİFRE" +vault_mongo_root_user: "mongoadmin" +vault_mongo_root_password: "GÜÇLÜ_ŞİFRE" ``` -## 5. DB Stack Kurulumu +## 3. Ansible ile Kurulum -`/opt/iklimco/stacks/db.yml` (iklim-app-01 üzerinde): -```yaml -version: "3.8" - -networks: - iklimco-net: - external: true - -services: - postgresql: - image: postgis/postgis:17-3.5 - environment: - POSTGRES_USER: "${DATABASE_POSTGRES_ROOT_USER}" - POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" - POSTGRES_DB: postgres - PGDATA: /var/lib/postgresql/data/pgdata - volumes: - - /mnt/storagebox/test/db/postgresql/data:/var/lib/postgresql/data - networks: - - iklimco-net - deploy: - placement: - constraints: - - node.labels.role == db - - mongodb: - image: mongo:8 - environment: - MONGO_INITDB_ROOT_USERNAME: "${DATABASE_MONGO_ROOT_USER}" - MONGO_INITDB_ROOT_PASSWORD: "${MONGO_ROOT_PASSWORD}" - volumes: - - /mnt/storagebox/test/db/mongodb/data:/data/db - - /mnt/storagebox/test/db/mongodb/log:/data/log - - /mnt/storagebox/test/db/mongodb/config/mongod.conf:/etc/mongod.conf - command: ["--config", "/etc/mongod.conf"] - networks: - - iklimco-net - deploy: - placement: - constraints: - - node.labels.role == db -``` - -### Deploy ```bash -# .env dosyasını oluşturun (Hassas veriler için) -# DATABASE_POSTGRES_ROOT_USER, POSTGRES_PASSWORD, DATABASE_MONGO_ROOT_USER, MONGO_ROOT_PASSWORD - -docker stack deploy -c /opt/iklimco/stacks/db.yml iklim-db +cd ansible/test +ansible-playbook -i inventory/generated/test.yml test-db-post-stack.yml --ask-vault-pass ``` -## 6. Kabul Kriterleri +**Playbook ne yapar?** + +`iklim-db-01` üzerinde: +- `/opt/iklimco/db/mongodb/config/` dizinini oluşturur +- `mongod.conf` dosyasını yerleştirir + +`iklim-app-01` üzerinde: +- `/opt/iklimco/stacks/db.yml` stack dosyasını oluşturur (şifreler vault'tan enjekte edilir) +- `docker stack deploy` ile PostgreSQL ve MongoDB servislerini başlatır + +## 4. Volume ve Veri Yapısı + +DB verileri `iklim-db-01` üzerindeki Docker named volume'larında tutulur: + +| Volume | İçerik | +|---|---| +| `iklim-db_postgresql_data` | PostgreSQL veri dosyaları | +| `iklim-db_mongodb_data` | MongoDB veri dosyaları | + +MongoDB log'ları stdout'a yazılır (`docker logs` ile izlenir). Konfigürasyon: `/opt/iklimco/db/mongodb/config/mongod.conf` + +> StorageBox DB verisi için **kullanılmaz**. Yalnızca backup stratejisinde görev alır. + +## 5. Kabul Kriterleri - `docker node ls` komutunda `iklim-db-01` Ready ve Active görünür. - `docker stack services iklim-db` her iki servisi 1/1 replica ile gösterir. - Uygulama node'undan `iklim-db_postgresql` ve `iklim-db_mongodb` DNS isimleriyle erişim sağlanır. -- Reboot sonrası veriler `/mnt/storagebox` üzerinden korunur. +- Reboot sonrası veriler named volume'lardan korunur (`docker volume ls` ile kontrol).