# cast01–cast03 lab architecture ## Goals Simulate production patterns on three isolated Alpine VMs with **shared config only** on NFS (`/shared/cluster/`). No database files on NFS — each node owns `/var/lib/mysql` locally. ## Topology ```text ┌─────────────────────────────────────┐ │ FE 10.7.0.10 (NFS export /shared) │ └──────────────────┬──────────────────┘ │ NFS (configs/scripts only) ┌─────────────────────────────┼─────────────────────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ cast01 │ async GTID │ cast02 │ async GTID │ cast03 │ │ .16.236 │◄──────────────│ .16.237 │◄──────────────│ .16.238 │ │ PRIMARY │ replica │ REPLICA │ replica │ REPLICA │ │ MariaDB │ │ MariaDB │ │ MariaDB │ │ nginx/php │ │ nginx/php │ │ nginx/php │ └───────────┘ └───────────┘ └───────────┘ ▲ │ │ └──────── writes + default reads ────────────────────────────┘ (replicas: read-only, failover drill) ``` | Node | IP | MariaDB | App role (planned) | |------|-----|---------|-------------------| | cast01 | 10.7.16.236 | **Primary** (read/write) | Crashes console primary target | | cast02 | 10.7.16.237 | Replica (read_only) | App worker / read replica | | cast03 | 10.7.16.238 | Replica (read_only) | App worker / read replica | ## MariaDB - **Engine:** MariaDB 11.8 (Alpine 3.24 packages — no edge required) - **Replication:** GTID async primary → two replicas (`server-id` 1/2/3) - **Bootstrap:** app DB dump over TCP (`--skip-ssl`); GTID position copied from primary; `replicate-do-db` filters ongoing binlog to app schemas only (avoids `mysql.help_topic` dump failures) - **Databases:** `androidcast_crashes`, `url_shortener` (schemas from project SQL in `/shared/cluster/sql/`) - **Credentials:** see `credentials.txt` (same lab password on all nodes) ## PHP - **Version:** 8.3 (`php83-fpm`) on all nodes — matches modern Alpine; prod BE remains 8.1 until upgraded separately - **Socket:** `127.0.0.1:9000` ## DNS Pending external records. Until then: `/etc/hosts` block maintained by `baseline.sh`. ## Service split (selected for lab) | Service | Write DB host | Read DB host (lab default) | Future app path (local disk) | |---------|---------------|----------------------------|------------------------------| | Crashes / tickets / RA | cast01 | cast01 (cast02/03 for drill) | `/var/www/androidcast/` per node | | URL shortener API | cast01 | cast01 | `/var/www/url-shortener/` on cast03 | | Graphs ingest | cast01 | cast01 | shared crashes backend | App deploys stay on **local VM disk**, not NFS. ## Operations ```sh sudo sh /shared/cluster/scripts/baseline.sh # per-node baseline sudo sh /shared/cluster/scripts/mariadb-primary.sh # cast01 only sudo sh /shared/cluster/scripts/load-schemas.sh # cast01 only sudo sh /shared/cluster/scripts/mariadb-replica.sh # cast02 + cast03 sudo sh /shared/cluster/scripts/verify-cluster.sh # any node ``` NFS mount may be `noexec` — always invoke scripts with `sh`. ## Not in scope yet - ProxySQL / MaxScale (add if read splitting becomes necessary) - Galera multi-primary (overkill for 3-node lab; async matches prod BE migration path) - TLS between nodes (intra VLAN)