136 lines
6.1 KiB
Markdown
136 lines
6.1 KiB
Markdown
# Parallel deployment
|
|
|
|
## Environments
|
|
|
|
The Gitea workflow deploys two permanent, isolated environments on the target
|
|
server. The old HTTPS installation is outside this workflow and remains
|
|
unchanged until a separately approved cutover.
|
|
|
|
| Branch | Compose project | VPN address | Secret |
|
|
|---|---|---|---|
|
|
| `main` | `assessor_prod` | `http://10.0.0.154:4173` | `PROD_ENV_FILE` |
|
|
| `experimental` | `assessor_experimental` | `http://10.0.0.154:4174` | `STAGING_ENV_FILE` |
|
|
|
|
`assessor_prod` already exists on the server. Keep its current `PROD_ENV_FILE`
|
|
database name, user and password unchanged so the existing PostgreSQL volume
|
|
remains accessible. Do not rotate only the secret; password rotation requires a
|
|
coordinated PostgreSQL role change.
|
|
|
|
`STAGING_ENV_FILE` contains a complete environment file with credentials that
|
|
are distinct from stable:
|
|
|
|
```text
|
|
ASSESSOR_POSTGRES_DB=assessor_experimental
|
|
ASSESSOR_POSTGRES_USER=assessor_experimental
|
|
ASSESSOR_POSTGRES_PASSWORD=replace-with-a-long-random-experimental-password
|
|
```
|
|
|
|
Do not reuse the stable database name, user, or password for experimental.
|
|
Secret values must never be printed into an Actions log.
|
|
|
|
Optional AI Overview settings may be added only when the company-managed
|
|
LiteLLM gateway is approved for the privacy-safe ASSESSOR payload:
|
|
|
|
```text
|
|
ASSESSOR_AI_MODE=litellm
|
|
ASSESSOR_AI_BASE_URL=https://work-litellm.example/v1
|
|
ASSESSOR_AI_MODEL=approved-model-alias
|
|
ASSESSOR_AI_API_KEY=<deployment secret>
|
|
ASSESSOR_AI_TIMEOUT_MS=60000
|
|
ASSESSOR_AI_CHUNK_CHARS=80000
|
|
ASSESSOR_AI_MAX_OUTPUT_BYTES=262144
|
|
ASSESSOR_AI_MAX_CALLS=64
|
|
ASSESSOR_AI_JOB_LEASE_MS=300000
|
|
```
|
|
|
|
Do not place these values in the repository or expose them through client-side
|
|
configuration. The workstation uses the ignored `.env.postgres`; staging and
|
|
production use their existing complete environment-file secrets. See
|
|
[`AI_OVERVIEW_SECURITY.md`](AI_OVERVIEW_SECURITY.md) for the transmitted-data
|
|
and trust-boundary contract.
|
|
|
|
## Mandatory server inventory
|
|
|
|
Before the first push-triggered deployment, connect to the target server over
|
|
SSH and perform a read-only inventory. Record only technical identifiers; do
|
|
not inspect database rows, uploaded files, application pages, or report text.
|
|
|
|
```sh
|
|
docker version
|
|
docker compose version
|
|
docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Label "com.docker.compose.project"}}'
|
|
docker volume ls
|
|
docker network ls
|
|
docker ps --filter publish=4173 --format '{{.Names}}\t{{.Ports}}'
|
|
docker ps --filter publish=4174 --format '{{.Names}}\t{{.Ports}}'
|
|
```
|
|
|
|
Also identify the existing reverse proxy and firewall policy without changing
|
|
them. Confirm that `10.0.0.154` is a host address, port 4174 is unused, the
|
|
runner uses this Docker daemon, and `assessor_experimental` does not belong to
|
|
an unrelated stack. Port 4173 and project `assessor_prod` are intentionally
|
|
adopted as stable; only its expected `app` and `postgres` services may exist.
|
|
Any other conflict blocks deployment and is never resolved by stopping or
|
|
deleting the existing resource.
|
|
|
|
## Deployment behavior
|
|
|
|
Pushes to a branch are serialized per environment with an atomic lock container
|
|
on the shared Docker host. This is necessary because Gitea currently ignores the
|
|
GitHub-compatible workflow `concurrency` key. A normal job removes its lock; a
|
|
crashed job's `--rm` lock expires after two hours. The workflow installs
|
|
dependencies, runs the complete test suite and production build, validates the
|
|
resolved Compose configuration, checks port/project isolation, backs up an
|
|
existing target environment, then builds and starts only that environment.
|
|
|
|
Compose project scoping produces independent PostgreSQL, artifact and report-archive volumes.
|
|
The Report Engine (2.10.0) archives every report run write-once under
|
|
`ASSESSOR_REPORT_ARCHIVE_DIR` (`/var/lib/assessor/report-archive` in Compose). The archive
|
|
holds complete technical customer data, is deleted together with its client and must be
|
|
included in backup and retention decisions like the artifact volume. The Report Engine
|
|
reads PostgreSQL through its own pool with `default_transaction_read_only=on`; it runs
|
|
no migrations and writes nothing to the database.
|
|
PostgreSQL has no host-published port. `restart: unless-stopped` preserves both
|
|
environments across Docker daemon and server restarts.
|
|
|
|
Feature promotion is a merge from `experimental` to `main`. Stable is rebuilt
|
|
from the merged commit; databases and artifacts are never copied between the
|
|
environments.
|
|
|
|
## Backups and restore checks
|
|
|
|
Before every update of an existing environment, the workflow writes a custom
|
|
PostgreSQL dump, an artifact archive and SHA-256 checksums to the external
|
|
Docker volume `assessor_deployment_backups`:
|
|
|
|
```text
|
|
<compose-project>/<UTC timestamp>/postgres.dump
|
|
<compose-project>/<UTC timestamp>/artifacts.tar.gz
|
|
<compose-project>/<UTC timestamp>/SHA256SUMS
|
|
```
|
|
|
|
The workflow never deletes backups. Retention and off-host copying require a
|
|
separate approved operations policy. The backup volume must be included in the
|
|
server's protected backup system; a volume on the same Docker host is not a
|
|
complete disaster-recovery copy.
|
|
|
|
Restore tests must use an isolated temporary Compose project and synthetic data.
|
|
Verify `SHA256SUMS`, restore `postgres.dump` with `pg_restore --exit-on-error`
|
|
into a fresh database, extract the artifact archive into a fresh artifact
|
|
volume, and confirm only content-free counts and health state. Never restore an
|
|
experimental backup into stable or a stable backup into experimental. Never
|
|
run `docker compose down --volumes` for either permanent environment.
|
|
|
|
## Acceptance and later cutover
|
|
|
|
After deployment, verify both in-container health checks and VPN access. Import
|
|
different synthetic fixtures into stable and experimental, redeploy each branch,
|
|
and confirm persistence and isolation. Confirm the old HTTPS site remains
|
|
available without reading its domain content.
|
|
|
|
Changing `assessor.soc-ops.de` is not part of the deployment workflow. After
|
|
explicit production acceptance, validate the existing proxy configuration,
|
|
preserve its old upstream, point it to stable, reload it, and verify TLS and
|
|
health. Rollback restores the saved upstream; the archived old stack and its
|
|
data must remain recoverable until a later retention decision.
|