From e3787d80f6817ba0b79af9843529fd23887ea7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Murat=20=C3=96ZDEM=C4=B0R?= Date: Wed, 20 May 2026 19:55:49 +0300 Subject: [PATCH] docs(infra): align DB stack and APISIX production guidance Update Environment_Infrastructure to match the current root stack conventions for database images, shared secret names, and APISIX real IP handling. - update test Ansible DB image defaults to PostGIS 18/PostGIS 3.6 and MongoDB 8.3.2 - align Patroni configuration with DATABASE_POSTGRES_* secret variable names - document APISIX real IP template configuration and Harbor rebuild workflow - replace the separate DB stack env file guidance with the shared .env.secrets.shared flow - update production setup and roadmap snippets to use current PostGIS, MongoDB, and APISIX rebuild commands --- .../roles/db_stack/templates/patroni.yml.j2 | 6 +-- ansible/test/group_vars/all/vars.yml | 4 +- roadmap/prod-env/05-apisix-remove-ssl.md | 9 ++-- roadmap/prod-env/08-deploy-pipeline-update.md | 8 +-- roadmap/test-env/05-apisix-remove-ssl.md | 9 ++-- setup/07-prod-ansible-bootstrap.md | 10 +--- setup/08-prod-db-cluster-kurulum.md | 49 ++++++++----------- setup/09-prod-runner-ha-ve-swarm.md | 23 +++++---- 8 files changed, 54 insertions(+), 64 deletions(-) diff --git a/ansible/prod/roles/db_stack/templates/patroni.yml.j2 b/ansible/prod/roles/db_stack/templates/patroni.yml.j2 index b0072c3..19efdb5 100644 --- a/ansible/prod/roles/db_stack/templates/patroni.yml.j2 +++ b/ansible/prod/roles/db_stack/templates/patroni.yml.j2 @@ -40,7 +40,7 @@ bootstrap: users: postgres: - password: "${POSTGRES_PASSWORD}" + password: "${DATABASE_POSTGRES_ROOT_PASSWD}" options: - superuser @@ -52,10 +52,10 @@ postgresql: authentication: replication: username: replicator - password: "${REPLICATOR_PASSWORD}" + password: "${DATABASE_POSTGRES_REPLICATOR_PASSWORD}" superuser: username: postgres - password: "${POSTGRES_PASSWORD}" + password: "${DATABASE_POSTGRES_ROOT_PASSWD}" parameters: unix_socket_directories: "/var/run/postgresql" diff --git a/ansible/test/group_vars/all/vars.yml b/ansible/test/group_vars/all/vars.yml index 4770ccc..eb10138 100644 --- a/ansible/test/group_vars/all/vars.yml +++ b/ansible/test/group_vars/all/vars.yml @@ -21,8 +21,8 @@ wireguard_clients: allowed_ips: 10.8.0.2/32 # DB Stack -db_postgres_image: "postgis/postgis:17-3.5" -db_mongo_image: "mongo:8" +db_postgres_image: "postgis/postgis:18-3.6" +db_mongo_image: "mongo:8.3.2" db_postgres_root_user: "{{ vault_postgres_root_user }}" db_postgres_password: "{{ vault_postgres_password }}" db_mongo_root_user: "{{ vault_mongo_root_user }}" diff --git a/roadmap/prod-env/05-apisix-remove-ssl.md b/roadmap/prod-env/05-apisix-remove-ssl.md index 50262db..f2eedb6 100644 --- a/roadmap/prod-env/05-apisix-remove-ssl.md +++ b/roadmap/prod-env/05-apisix-remove-ssl.md @@ -10,12 +10,11 @@ Changes made for test already apply to prod. - [ ] `ssls/1` PUT block removed from `init/apisix-core/init.sh` - [ ] `dev` SSL block removed or confirmed non-impactful for prod -- [ ] Custom APISIX image (`custom-apisix:3.12.0`) config.yaml contains `real_ip_header` - and `set_real_ip_from` for overlay CIDR (`10.0.0.0/8`) -- [ ] New image built and pushed to Harbor if config.yaml was changed: +- [ ] Custom APISIX image (`custom-apisix:3.12.0`) `template/apisix-core/config.yaml.template` contains + `real_ip_header`, `real_ip_recursive`, and `set_real_ip_from` (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) +- [ ] New image built and pushed to Harbor if config.yaml.template was changed: ```bash - docker build -t registry.tarla.io/iklimco/custom-apisix:3.12.0 . - docker push registry.tarla.io/iklimco/custom-apisix:3.12.0 + bash ops/push-harbor-custom-images.sh ``` ## Prod-specific note diff --git a/roadmap/prod-env/08-deploy-pipeline-update.md b/roadmap/prod-env/08-deploy-pipeline-update.md index 80f36fd..b941a18 100644 --- a/roadmap/prod-env/08-deploy-pipeline-update.md +++ b/roadmap/prod-env/08-deploy-pipeline-update.md @@ -212,7 +212,7 @@ Insert **after** `Bootstrap SWAG Certificate` and **before** `Review Environment echo "⏳ Waiting for PostgreSQL..." until docker run --rm --network iklimco-net \ -e PGPASSWORD="${DATABASE_POSTGRES_ROOT_PASSWD}" \ - postgis/postgis:17-3.5 \ + postgis/postgis:18-3.6 \ pg_isready -h postgresql -U "${DATABASE_POSTGRES_ROOT_USER}" -q 2>/dev/null; do sleep 5 done @@ -220,19 +220,19 @@ Insert **after** `Bootstrap SWAG Certificate` and **before** `Review Environment echo "▶ $(basename "$sql_file")" docker run --rm -i --network iklimco-net \ -e PGPASSWORD="${DATABASE_POSTGRES_ROOT_PASSWD}" \ - postgis/postgis:17-3.5 \ + postgis/postgis:18-3.6 \ psql -h postgresql -U "${DATABASE_POSTGRES_ROOT_USER}" < "$sql_file" done echo "⏳ Waiting for MongoDB..." - until docker run --rm --network iklimco-net mongo:8 \ + until docker run --rm --network iklimco-net mongo:8.3.2 \ mongosh "mongodb://${DATABASE_MONGODB_ROOT_USER}:${DATABASE_MONGODB_ROOT_PASSWD}@mongodb/admin" \ --eval "db.runCommand({ping:1})" --quiet 2>/dev/null; do sleep 5 done for js_file in $(ls ./init/mongodb/*.js 2>/dev/null | sort); do echo "▶ $(basename "$js_file")" - docker run --rm -i --network iklimco-net mongo:8 \ + docker run --rm -i --network iklimco-net mongo:8.3.2 \ mongosh "mongodb://${DATABASE_MONGODB_ROOT_USER}:${DATABASE_MONGODB_ROOT_PASSWD}@mongodb/admin" \ --quiet < "$js_file" done diff --git a/roadmap/test-env/05-apisix-remove-ssl.md b/roadmap/test-env/05-apisix-remove-ssl.md index 0975a89..c97b484 100644 --- a/roadmap/test-env/05-apisix-remove-ssl.md +++ b/roadmap/test-env/05-apisix-remove-ssl.md @@ -47,7 +47,7 @@ APISIX's custom image (`registry.tarla.io/iklimco/custom-apisix:3.12.0`) include `config.yaml`. That config must set real IP headers so APISIX sees real client IPs, not SWAG's overlay IP. -Locate the APISIX `config.yaml` in the custom image build source and ensure it contains: +The source file is `template/apisix-core/config.yaml.template` — `ops/push-harbor-custom-images.sh` generates `build/apisix-core/config.yaml` from this template at build time and deletes it afterward. Ensure the template contains: ```yaml nginx_config: @@ -63,8 +63,11 @@ nginx_config: Docker Swarm overlay networks use `10.x.x.x` addressing. These CIDR ranges cover all typical overlay subnet allocations. -If the custom image config does not have these, add them and rebuild+push the image to Harbor -before deploying. +If the template does not have these, add them and rebuild+push the image to Harbor before deploying: + +```bash +bash ops/push-harbor-custom-images.sh +``` ## Step 3 — Remove APISIX TLS upstream configs (if any) diff --git a/setup/07-prod-ansible-bootstrap.md b/setup/07-prod-ansible-bootstrap.md index 85e7bcf..3895118 100644 --- a/setup/07-prod-ansible-bootstrap.md +++ b/setup/07-prod-ansible-bootstrap.md @@ -258,15 +258,9 @@ Applied to `iklim-app-*` nodes. Gitea Act Runner is installed on each app node a Applied to `iklim-db-*` nodes. On each DB node, it creates `/opt/iklimco/db` and `/opt/iklimco/backup` directories, as well as a local reference directory for MongoDB. The actual production configuration, including node-specific `mongod.conf`, replica set auth key, and Patroni configurations, is set up on StorageBox at `/mnt/storagebox/db/mongodb-0X/config/` and `/mnt/storagebox/db/postgresql-0X/config/` in the `08-prod-db-cluster-kurulum.md` step. etcd data is stored on local Docker named volumes (not StorageBox). -## /opt/iklimco/stacks/.env +## DB Stack Env Variables -Password variables required by the DB cluster stacks are stored in the `/opt/iklimco/stacks/.env` file. This file is stored on StorageBox as `prod/secrets/iklim.co/.env.stacks`. Before the first deploy, it is fetched on `iklim-app-01` with the following command: - -```bash -scp -P 23 STORAGEBOX_USER@STORAGEBOX_USER.your-storagebox.de:prod/secrets/iklim.co/.env.stacks \ - /opt/iklimco/stacks/.env -chmod 600 /opt/iklimco/stacks/.env -``` +Password variables required by the DB cluster stack (`docker-stack-db.prod.yml`) — `DATABASE_POSTGRES_ROOT_PASSWD`, `DATABASE_POSTGRES_REPLICATOR_PASSWORD`, `DATABASE_MONGODB_ROOT_PASSWD` — are stored in `prod/secrets/iklim.co/.env.secrets.shared` on StorageBox, alongside the other shared secrets. No separate file is needed. ## StorageBox Directory Structure diff --git a/setup/08-prod-db-cluster-kurulum.md b/setup/08-prod-db-cluster-kurulum.md index c09fd0c..45b4324 100644 --- a/setup/08-prod-db-cluster-kurulum.md +++ b/setup/08-prod-db-cluster-kurulum.md @@ -238,7 +238,7 @@ MongoDB services are defined in `docker-stack-db.prod.yml` (repo root). Each ser ```yaml mongodb-01: - image: mongo:8 + image: mongo:8.3.2 volumes: - mongodb-01-data:/data/db - mongodb-01-log:/data/log @@ -268,8 +268,8 @@ Run **once** after the stack is deployed: ```bash # On iklim-app-01 (overlay network erişimi için): -docker run --rm -it --network iklimco-net mongo:8 \ - mongosh "mongodb://mongo-root:${MONGO_ROOT_PASSWORD}@mongodb-01/admin" +docker run --rm -it --network iklimco-net mongo:8.3.2 \ + mongosh "mongodb://mongo-root:${DATABASE_MONGODB_ROOT_PASSWD}@mongodb-01/admin" # Inside mongosh: rs.initiate({ @@ -293,12 +293,12 @@ Patroni coordinates PostgreSQL primary/standby roles through etcd. If the primar ### 5.1 Custom Image (Patroni + PostGIS) -Patroni is installed on top of the `postgis/postgis:17-3.5` image. This image is pushed to Harbor and used in the stack. +Patroni is installed on top of the `postgis/postgis:18-3.6` image. This image is pushed to Harbor and used in the stack. `build/patroni-postgis/Dockerfile`: ```dockerfile -FROM postgis/postgis:17-3.5 +FROM postgis/postgis:18-3.6 USER root @@ -328,9 +328,9 @@ Or manually: ```bash cd build/patroni-postgis -docker build -t registry.tarla.io/iklimco/custom-patroni-postgis:17-3.5 . +docker build -t registry.tarla.io/iklimco/custom-patroni-postgis:18-3.6 . echo "$HARBOR_CI_TOKEN" | docker login registry.tarla.io -u robot-ci-push-iklimco --password-stdin -docker push registry.tarla.io/iklimco/custom-patroni-postgis:17-3.5 +docker push registry.tarla.io/iklimco/custom-patroni-postgis:18-3.6 ``` ### 5.2 etcd Cluster @@ -417,7 +417,7 @@ bootstrap: users: postgres: - password: "${POSTGRES_PASSWORD}" + password: "${DATABASE_POSTGRES_ROOT_PASSWD}" options: - superuser @@ -429,10 +429,10 @@ postgresql: authentication: replication: username: replicator - password: "${REPLICATOR_PASSWORD}" + password: "${DATABASE_POSTGRES_REPLICATOR_PASSWORD}" superuser: username: postgres - password: "${POSTGRES_PASSWORD}" + password: "${DATABASE_POSTGRES_ROOT_PASSWD}" parameters: unix_socket_directories: "/var/run/postgresql" @@ -451,10 +451,10 @@ Patroni services are defined in `docker-stack-db.prod.yml`. Each service uses th ```yaml patroni-01: - image: registry.tarla.io/iklimco/custom-patroni-postgis:17-3.5 + image: registry.tarla.io/iklimco/custom-patroni-postgis:18-3.6 environment: - POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" - REPLICATOR_PASSWORD: "${REPLICATOR_PASSWORD}" + DATABASE_POSTGRES_ROOT_PASSWD: "${DATABASE_POSTGRES_ROOT_PASSWD}" + DATABASE_POSTGRES_REPLICATOR_PASSWORD: "${DATABASE_POSTGRES_REPLICATOR_PASSWORD}" TZ: "Europe/Istanbul" volumes: - postgresql-01-data:/var/lib/postgresql/data @@ -512,28 +512,19 @@ All DB services (etcd, MongoDB, Patroni) are in the single `docker-stack-db.prod ### .env File -The `/opt/iklimco/stacks/.env` file is stored on StorageBox as `prod/secrets/iklim.co/.env.stacks`. Fetch it once before first deploy: +DB stack password variables (`DATABASE_POSTGRES_ROOT_PASSWD`, `DATABASE_POSTGRES_REPLICATOR_PASSWORD`, `DATABASE_MONGODB_ROOT_PASSWD`) are stored in `prod/secrets/iklim.co/.env.secrets.shared` on StorageBox. Fetch it to `iklim-app-01` before deploy: ```bash -scp -P 23 STORAGEBOX_USER@STORAGEBOX_USER.your-storagebox.de:prod/secrets/iklim.co/.env.stacks \ - /opt/iklimco/stacks/.env -chmod 600 /opt/iklimco/stacks/.env -``` - -File content (`/opt/iklimco/stacks/.env`, not committed to the repo): - -```env -DATABASE_POSTGRES_ROOT_USER=postgres -POSTGRES_PASSWORD= -REPLICATOR_PASSWORD= -MONGO_ROOT_PASSWORD= +scp -P 23 STORAGEBOX_USER@STORAGEBOX_USER.your-storagebox.de:prod/secrets/iklim.co/.env.secrets.shared \ + /tmp/.env.secrets.shared +chmod 600 /tmp/.env.secrets.shared ``` ### Deploy Steps ```bash # On iklim-app-01, in the repo working directory: -export $(cat /opt/iklimco/stacks/.env | xargs) +set -a; . /tmp/.env.secrets.shared; set +a # Automatic ETCD_INITIAL_CLUSTER_STATE detection: DEPLOY_FILE="docker-stack-db.prod.yml" @@ -585,8 +576,8 @@ Run once after the stack is deployed: ```bash # From iklim-app-01 via overlay network: -docker run --rm -it --network iklimco-net mongo:8 \ - mongosh "mongodb://mongo-root:${MONGO_ROOT_PASSWORD}@mongodb-01/admin" +docker run --rm -it --network iklimco-net mongo:8.3.2 \ + mongosh "mongodb://mongo-root:${DATABASE_MONGODB_ROOT_PASSWD}@mongodb-01/admin" # Inside mongosh: rs.initiate({ diff --git a/setup/09-prod-runner-ha-ve-swarm.md b/setup/09-prod-runner-ha-ve-swarm.md index 3654cb6..f70010a 100644 --- a/setup/09-prod-runner-ha-ve-swarm.md +++ b/setup/09-prod-runner-ha-ve-swarm.md @@ -154,16 +154,19 @@ The prod stack uses the `registry.tarla.io/iklimco/custom-apisix:3.12.0` image. nginx_config: http: real_ip_header: "X-Real-IP" - set_real_ip_from: "10.0.0.0/8" + real_ip_recursive: "on" + set_real_ip_from: + - "10.0.0.0/8" + - "172.16.0.0/12" + - "192.168.0.0/16" ``` -`set_real_ip_from: 10.0.0.0/8` covers all container addresses in the Swarm overlay network; this skips SWAG's internal overlay IP and writes the real client IP to APISIX access logs. +These three CIDR ranges cover all typical Docker Swarm overlay subnet allocations. APISIX reads the real client IP from SWAG's `X-Real-IP` header instead of the overlay container IP. -If the image requires a rebuild because `config.yaml` changed: +If the image requires a rebuild because `config.yaml` changed, run from the project root: ```bash -docker build -t registry.tarla.io/iklimco/custom-apisix:3.12.0 . -docker push registry.tarla.io/iklimco/custom-apisix:3.12.0 +bash ops/push-harbor-custom-images.sh ``` During deploy, `init/apisix-core/init.sh` is run once by the pipeline. It writes the APISIX configuration to Patroni etcd with the `/apisix` prefix; the 3 replicas in prod read this etcd state commonly, so no separate init per replica is required. Detail: `roadmap/prod-env/05-apisix-remove-ssl.md`. @@ -293,7 +296,7 @@ PostgreSQL and MongoDB init scripts run through Swarm overlay DNS service names echo "⏳ Waiting for PostgreSQL..." until docker run --rm --network iklimco-net \ -e PGPASSWORD="${DATABASE_POSTGRES_ROOT_PASSWD}" \ - postgis/postgis:17-3.5 \ + postgis/postgis:18-3.6 \ pg_isready -h postgresql -U "${DATABASE_POSTGRES_ROOT_USER}" -q 2>/dev/null; do sleep 5 done @@ -301,19 +304,19 @@ PostgreSQL and MongoDB init scripts run through Swarm overlay DNS service names echo "▶ $(basename "$sql_file")" docker run --rm -i --network iklimco-net \ -e PGPASSWORD="${DATABASE_POSTGRES_ROOT_PASSWD}" \ - postgis/postgis:17-3.5 \ + postgis/postgis:18-3.6 \ psql -h postgresql -U "${DATABASE_POSTGRES_ROOT_USER}" < "$sql_file" done echo "⏳ Waiting for MongoDB..." - until docker run --rm --network iklimco-net mongo:8 \ + until docker run --rm --network iklimco-net mongo:8.3.2 \ mongosh "mongodb://${DATABASE_MONGODB_ROOT_USER}:${DATABASE_MONGODB_ROOT_PASSWD}@mongodb/admin" \ --eval "db.runCommand({ping:1})" --quiet 2>/dev/null; do sleep 5 done for js_file in $(ls ./init/mongodb/*.js 2>/dev/null | sort); do echo "▶ $(basename "$js_file")" - docker run --rm -i --network iklimco-net mongo:8 \ + docker run --rm -i --network iklimco-net mongo:8.3.2 \ mongosh "mongodb://${DATABASE_MONGODB_ROOT_USER}:${DATABASE_MONGODB_ROOT_PASSWD}@mongodb/admin" \ --quiet < "$js_file" done @@ -650,7 +653,7 @@ Expected: valid JSON weather response. - After the first deploy, `docker exec $(docker ps -q -f name=iklimco_swag) nginx -t` succeeds and returns `syntax is ok`. - The output of `cat /mnt/storagebox/swag/site-confs/api.conf | grep server_name` contains `server_name api.iklim.co;`. - The `ssls/1` PUT block does not exist inside `init/apisix-core/init.sh`. -- The `registry.tarla.io/iklimco/custom-apisix:3.12.0` image exists in Harbor and its `config.yaml` contains `set_real_ip_from: 10.0.0.0/8` configuration. +- The `registry.tarla.io/iklimco/custom-apisix:3.12.0` image exists in Harbor and its `config.yaml` contains `real_ip_header`, `real_ip_recursive`, and `set_real_ip_from` (covering `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) configuration. - After the first deploy, real client IP appears in APISIX access logs, not the SWAG overlay IP: `docker exec $(docker ps -q -f name=iklimco_apisix | head -1) tail -5 /usr/local/apisix/logs/access.log` - `docker service ps iklimco_cert-reloader` shows that the service is running. - `docker service ls` does not contain `iklimco_etcd`, `iklimco_postgresql`, `iklimco_mongodb`, `iklimco_pg-proxy`, or `iklimco_mongo-proxy`; they are removed by the post-deploy step in `deploy-prod.yml` (base stack services superseded by the `iklim-db` stack or deprecated in prod).