# cast cluster — unattended populate & verify This directory is the **single source of truth** for lab/staging cluster deployment. Store scripts, configs, SQL, and credentials here only — **never** MariaDB datadirs or dumps. ## Fresh cluster workflow (cluster0 → clusterN) ### 0. FE: create NFS export + seed this tree Copy or rsync this entire `cluster/` tree to the FE export, e.g.: `10.7.0.10:/mnt/raid0/xendomains/domU_cast_cluster_0/shared/cluster/` Edit `cluster.env` for the new cluster (IPs, export path, `CLUSTER_NAME`, `PUBLIC_ORIGIN`). ### 1. Create 3 empty Alpine VMs Each VM needs hostname `cast01` / `cast02` / `cast03` and static IP from `cluster.env`. Copy bootstrap env once (before NFS is mounted): ```sh sudo install -m 644 bootstrap/cluster0-bootstrap.env /etc/cast-cluster.env ``` ### 2. Populate (unattended) **Option A — coordinator** (from a host with passwordless SSH to all cast nodes — typically your workstation, not cast01): ```sh ssh ai@cast01 # or run from laptop with SSH config for cast01–03 sudo sh /shared/cluster/populate_lab_setup.sh --coordinator ``` If inter-node SSH is not configured, run **Option B** on each VM instead; cluster verify uses HTTP checks to peer IPs. **Option B — per node** (parallel OK; replicas wait for primary): ```sh sudo sh /shared/cluster/populate_lab_setup.sh ``` Phases: | Phase | What | |-------|------| | `baseline` | NFS, hosts, `apk upgrade`, nginx/php83, packages | | `db` | cast01: GTID primary + schemas; cast02/03: replicas | | `app` | git/bundle deploy, nginx vhost, lab `config.php` | | `all` | baseline → db → app → `verify_lab_setup.sh --local` | Always invoke with `sh` — NFS may be mounted `noexec`. ### 3. DNS + FE redirect Update external DNS and FE nginx upstream to point at the new cluster (`FE_PROXY_TARGET` in `cluster.env`). ### 4. Global verify ```sh sudo sh /shared/cluster/verify_global_setup.sh ``` Checks `PUBLIC_ORIGIN` paths (crashes UI, assets, diag, short links). ## Staging / cloud Same scripts; change only `cluster.env`: - `FE_NFS_*` → object storage or config bucket mount - `APP_SOURCE=bundle` + `release/current/` tarball from CI - `PUBLIC_ORIGIN` → staging URL - IPs / hostnames → cloud private network ## Files | File | Purpose | |------|---------| | `cluster.env` | Cluster-specific variables (IPs, NFS, URLs, git) | | `credentials.txt` | Shared lab passwords | | `populate_lab_setup.sh` | Main unattended installer | | `verify_lab_setup.sh` | Internal cluster health | | `verify_global_setup.sh` | Post-DNS public checks | | `scripts/` | Baseline, MariaDB, deploy, nginx | | `sql/` | Schema + migrations | | `nginx/apps-port80.conf` | php83-fpm TCP vhost | | `bootstrap/` | Minimal `/etc/cast-cluster.env` for first boot | See `ARCHITECTURE.md` for topology. ## Journal (read from FE if VMs fail) Every populate/verify step appends to **`/shared/journal/{CLUSTER_NAME}.journal.tsv`** with status **OK**, **NOK**, **WARN**, or **START**. Human summary (regenerated automatically): ```sh less /shared/journal/cluster0.summary.md grep NOK /shared/journal/cluster0.journal.tsv ``` On FE host: ```sh less /mnt/raid0/xendomains/domU_cast_cluster_0/shared/journal/cluster0.summary.md ``` See `/shared/journal/README.md`. VM disk snapshots are out-of-band (ask sysop) — not stored on NFS.