docs: describe one-instance-per-server topology and why MODE is exclusive
This commit is contained in:
@@ -47,9 +47,14 @@ curl -sH "Authorization: Bearer $UPDATER_API_KEY" \
|
|||||||
Tests rely on this — don't drop the nil-check.
|
Tests rely on this — don't drop the nil-check.
|
||||||
- **Stateless**: no DB, no config file, no on-disk audit log. Docker daemon is
|
- **Stateless**: no DB, no config file, no on-disk audit log. Docker daemon is
|
||||||
the source of truth.
|
the source of truth.
|
||||||
- **Compose mode and Swarm mode are selected once per deployment via `MODE`**, never
|
- **`MODE` is exclusive because each instance owns exactly one Docker daemon.**
|
||||||
mixed at request time. Swarm mode's security gate is opt-in label only — there is
|
A composite "handle both at once" mode has been considered and rejected: it would
|
||||||
no STACKS_ROOT-equivalent path check, since Swarm services have no local compose
|
still only reach one daemon, so it buys nothing. Fleets run one instance per
|
||||||
|
server and CI fans out. Reopen this only if a Swarm manager node starts running
|
||||||
|
standalone Compose stacks locally. Rationale and rejected alternatives:
|
||||||
|
`docs/superpowers/specs/2026-08-04-multi-server-fanout-design.md`.
|
||||||
|
- **Swarm mode's security gate is opt-in label only** — there is no
|
||||||
|
STACKS_ROOT-equivalent path check, since Swarm services have no local compose
|
||||||
file. Don't add one; don't weaken Compose mode's three-factor gate to match.
|
file. Don't add one; don't weaken Compose mode's three-factor gate to match.
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|||||||
@@ -46,6 +46,28 @@ Two things to get right when running in Swarm mode:
|
|||||||
mounted. This is a deployment concern the service cannot detect or work
|
mounted. This is a deployment concern the service cannot detect or work
|
||||||
around.
|
around.
|
||||||
|
|
||||||
|
## Deploying across multiple servers
|
||||||
|
|
||||||
|
An instance can only reach the Docker daemon it is configured against. It cannot
|
||||||
|
update stacks on other machines. The deployment model follows from that:
|
||||||
|
|
||||||
|
**One instance per server.** Each server runs its own package-updater against its
|
||||||
|
own local Docker socket, with `MODE` set to whatever that machine runs:
|
||||||
|
|
||||||
|
| Server | `MODE` | Updates |
|
||||||
|
|---|---|---|
|
||||||
|
| Swarm manager node | `swarm` | All Swarm services in the cluster |
|
||||||
|
| Standalone Compose host | `compose` | Compose stacks on that host |
|
||||||
|
|
||||||
|
**CI fans out.** The [Gitea composite action](gitea-action/README.md) takes a list of
|
||||||
|
endpoints and posts `/update` to every instance, so one workflow run reaches the
|
||||||
|
whole fleet. Each instance answers for its own machine; there is no aggregated
|
||||||
|
cross-server response and no instance coordinates any other.
|
||||||
|
|
||||||
|
This is why `MODE` is exclusive rather than a mode that handles both at once: an
|
||||||
|
instance that could do both would still only reach one daemon, so the extra
|
||||||
|
generality buys nothing.
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
1. Build and push the image (e.g. via your own CI).
|
1. Build and push the image (e.g. via your own CI).
|
||||||
@@ -68,7 +90,7 @@ All via environment variables.
|
|||||||
| `LOG_LEVEL` | no | `info` | `debug` / `info` / `warn` / `error`. |
|
| `LOG_LEVEL` | no | `info` | `debug` / `info` / `warn` / `error`. |
|
||||||
| `UPDATE_TIMEOUT` | no | `5m` | Per-job timeout (Go duration). |
|
| `UPDATE_TIMEOUT` | no | `5m` | Per-job timeout (Go duration). |
|
||||||
| `OPT_IN_LABEL` | no | `se.shcizo.auto-update` | Label name to check; value must equal `"true"`. |
|
| `OPT_IN_LABEL` | no | `se.shcizo.auto-update` | Label name to check; value must equal `"true"`. |
|
||||||
| `MODE` | no | `compose` | `compose` or `swarm`. Selects the update mechanism for the whole deployment; not mixed per-request. |
|
| `MODE` | no | `compose` | `compose` or `swarm`. Which paradigm *this instance's* Docker daemon runs. See [Deploying across multiple servers](#deploying-across-multiple-servers). |
|
||||||
|
|
||||||
## Endpoints
|
## Endpoints
|
||||||
|
|
||||||
@@ -100,7 +122,8 @@ These are tracked in the spec's section 2 and section 15 as deliberate out-of-sc
|
|||||||
|
|
||||||
- **Self-update wiring**: `internal/selfupdate.Wrapped` exists and is unit-tested but is not wired into the live queue. The HTTP response flush ordering for self-replacement is a future enhancement; for now, expect to manually rerun `docker compose up -d` on the host if pushing a new image of `package-updater` itself causes a mid-response interruption.
|
- **Self-update wiring**: `internal/selfupdate.Wrapped` exists and is unit-tested but is not wired into the live queue. The HTTP response flush ordering for self-replacement is a future enhancement; for now, expect to manually rerun `docker compose up -d` on the host if pushing a new image of `package-updater` itself causes a mid-response interruption.
|
||||||
- **No rollback**: Compose's "keep old container if new fails to start" is the only safety net.
|
- **No rollback**: Compose's "keep old container if new fails to start" is the only safety net.
|
||||||
- **Single host only in Compose mode**. Swarm mode (`MODE=swarm`) is the
|
- **One instance reaches one daemon.** An instance never updates another server;
|
||||||
multi-node path, but only from a manager node's point of view — the updater
|
fleets run one instance per server with CI fanning out to all of them (see
|
||||||
itself still needs manager API access (see "Swarm mode" above).
|
"Deploying across multiple servers"). Swarm mode is the exception in that a
|
||||||
|
single manager-node instance covers the whole cluster.
|
||||||
- **No per-repo API keys**: a single shared bearer token is used.
|
- **No per-repo API keys**: a single shared bearer token is used.
|
||||||
|
|||||||
Reference in New Issue
Block a user