chore(deploy): co-locate api + worker on anjie

nikola and frootmig are flagging power events and drive warnings on
the iLO interface and need drive replacement. Move both moments
components onto anjie.kosherinata.internal until those hosts are
back in service. Update the nginx upstream and the readme topology
table to match; the postgres pg_ident.conf on magrathea now needs
to map anjie's cert CN to both moments_ro and moments_rw (two lines
for the same cert_cn).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 08:24:21 +03:00
parent c81512fa3e
commit 7843c2c13f
3 changed files with 12 additions and 13 deletions

View File

@@ -3,18 +3,15 @@ environments:
prod: prod:
components: components:
api: api:
hosts: [nikola.kosherinata.internal] hosts: [anjie.kosherinata.internal]
config: config:
# Reachable across the WG mesh from oolon (the per-site nginx
# ingress for rob.tn). Firewalld restricts ingress; see
# asset/firewalld/moments-api.xml.
bind: 0.0.0.0:42424 bind: 0.0.0.0:42424
db_role: moments_ro db_role: moments_ro
db_host: magrathea.kosherinata.internal db_host: magrathea.kosherinata.internal
db_port: 5432 db_port: 5432
db_name: moments db_name: moments
worker: worker:
hosts: [frootmig.kosherinata.internal] hosts: [anjie.kosherinata.internal]
config: config:
db_role: moments_rw db_role: moments_rw
db_host: magrathea.kosherinata.internal db_host: magrathea.kosherinata.internal
@@ -36,4 +33,4 @@ environments:
config: config:
server_name: rob.tn server_name: rob.tn
root: /var/www/rob.tn root: /var/www/rob.tn
api_upstream: http://nikola.kosherinata.internal:42424 api_upstream: http://anjie.kosherinata.internal:42424

View File

@@ -1,5 +1,5 @@
upstream moments_api { upstream moments_api {
server nikola.kosherinata.internal:42424 max_fails=3 fail_timeout=30s; server anjie.kosherinata.internal:42424 max_fails=3 fail_timeout=30s;
keepalive 8; keepalive 8;
} }

View File

@@ -49,14 +49,16 @@ Topology:
| Component | Host | Notes | | Component | Host | Notes |
| --------- | --------------------------------- | ------------------------------------------------------------------ | | --------- | --------------------------------- | ------------------------------------------------------------------ |
| api | `nikola.kosherinata.internal` | binds `0.0.0.0:42424`; firewalld service `moments-api` | | api | `anjie.kosherinata.internal` | binds `0.0.0.0:42424`; firewalld service `moments-api` |
| worker | `frootmig.kosherinata.internal` | no listening port; pollers only | | worker | `anjie.kosherinata.internal` | no listening port; pollers only |
| web | `oolon.kosherinata.internal` | per-site nginx ingress for rob.tn; `/api/*`nikola across the WG | | web | `oolon.kosherinata.internal` | per-site nginx ingress for rob.tn; `/api/*`anjie across the WG |
| db | `magrathea.kosherinata.internal` | postgres mTLS, passwordless | | db | `magrathea.kosherinata.internal` | postgres mTLS, passwordless |
Postgres roles `moments_rw` and `moments_ro` must exist on the primary, with `pg_ident.conf` mappings in place for `nikola.kosherinata.internal``moments_ro` and `frootmig.kosherinata.internal``moments_rw`. See `asset/sql/bootstrap-moments.sql` and `asset/postgres/ident.conf.tmpl`. api and worker are co-located on `anjie` while `nikola` and `frootmig` are out for drive replacement.
Inter-host traffic over the WG mesh: oolon's nginx connects to `http://nikola.kosherinata.internal:42424` in plaintext. The mesh provides the encryption layer; per-hop TLS for an internal HTTP read-only API on already-public data is deferred. If that changes, swap the api binary to rustls + the host cert pair, and update the nginx upstream to `https://`. Postgres roles `moments_rw` and `moments_ro` must exist on the primary, with `pg_ident.conf` mapping `anjie.kosherinata.internal` to **both** roles (one cert_cn line per mapping). See `asset/sql/bootstrap-moments.sql` and `asset/postgres/ident.conf.tmpl`.
Inter-host traffic over the WG mesh: oolon's nginx connects to `http://anjie.kosherinata.internal:42424` in plaintext. The mesh provides the encryption layer; per-hop TLS for an internal HTTP read-only API on already-public data is deferred. If that changes, swap the api binary to rustls + the host cert pair, and update the nginx upstream to `https://`.
Secrets resolved by `deploy.sh` via `pass`: Secrets resolved by `deploy.sh` via `pass`:
@@ -68,6 +70,6 @@ Optional, set if needed in `worker.env`: `GITEA_TOKEN`, `BUGZILLA_API_KEY`.
`rob.tn` currently resolves to GitHub Pages. After the first successful prod deploy: `rob.tn` currently resolves to GitHub Pages. After the first successful prod deploy:
1. Update Cloudflare DNS for `rob.tn` to the WAN IP that fronts `nikola` (unproxied — see architecture doc §11). 1. Update Cloudflare DNS for `rob.tn` to the WAN IP that fronts `oolon` (unproxied — see architecture doc §11).
2. Confirm `curl -fsS https://rob.tn/api/v1/healthz` returns `ok`. 2. Confirm `curl -fsS https://rob.tn/api/v1/healthz` returns `ok`.
3. Add an archival notice to the top of [grenade-events-react/readme.md](https://github.com/grenade/grenade-events-react) pointing at this repo, and archive the GitHub repo. 3. Add an archival notice to the top of [grenade-events-react/readme.md](https://github.com/grenade/grenade-events-react) pointing at this repo, and archive the GitHub repo.