加固部署隔离与备份来源契约(07-26-internal-ops-recovery-baseline)
这个提交包含在:
@@ -592,6 +592,80 @@ SERVER_HOST_PORT="${SERVER_HOST_PORT##*:}"
|
||||
curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
||||
```
|
||||
|
||||
## Scenario: Compose Project And Data-Path Isolation
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: changing Compose identity, host data mounts, database publication,
|
||||
deployment entrypoints or operational timers.
|
||||
- Purpose: ensure one approved Compose project owns one verified DB/storage pair
|
||||
without fixed global container names or accidental shared PGDATA.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Every deploy/timer Compose command uses `-p "$COMPOSE_PROJECT_NAME"`.
|
||||
- PostgreSQL mount: `${POSTGRES_DATA_DIR:-./data/postgres}`.
|
||||
- Application storage mount: `${STORAGE_HOST_DIR:-./data/storage}`.
|
||||
- DB has no host `ports`; server remains loopback-only.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Compose files must not set `container_name`. Operational commands resolve
|
||||
services through the approved project, never a historical global name.
|
||||
- A project-name change and its DB/storage path selection are one atomic,
|
||||
separately approved downtime operation. Never start two PostgreSQL instances
|
||||
against one PGDATA.
|
||||
- Relative defaults preserve local trialrun compatibility, but production must
|
||||
verify the resolved host paths against the active container mounts before
|
||||
any start, backup or migration.
|
||||
- Remote readiness may use host curl, but build-SHA JSON validation runs with
|
||||
Node inside the server container; the Docker host does not need Node.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- DB `ports` appears -> environment contract fails.
|
||||
- Any fixed `container_name` appears -> environment contract fails.
|
||||
- Required data mount loses its parameter -> environment contract fails.
|
||||
- Deploy/timer omits `-p` -> environment contract fails.
|
||||
- New project resolves the same PGDATA while the old DB runs -> stop before
|
||||
Compose start; do not use service health as permission to continue.
|
||||
|
||||
### 5. Tests Required
|
||||
|
||||
- `node deploy/trialrun/validate-env-contract.js`.
|
||||
- The isolated backup/restore drill mutates Compose with a DB port and fixed
|
||||
name and proves the contract rejects it.
|
||||
- `bash -n` covers deploy, timer, backup and restore scripts.
|
||||
|
||||
### 6. Good/Base/Bad Cases
|
||||
|
||||
- Good: one approved project resolves one stopped-and-migrated PGDATA and the
|
||||
matching active storage path; deploy, timer and backup all use that identity.
|
||||
- Base: local trialrun uses the documented relative defaults under one project.
|
||||
- Bad: start a renamed project before the old DB stops, or let it create a new
|
||||
empty directory and accept a green health check as proof of data continuity.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```bash
|
||||
docker-compose up -d db
|
||||
DB_CONTAINER=hyb-oa-trialrun-db ./scripts/backup-all.sh
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```bash
|
||||
docker-compose -p "$COMPOSE_PROJECT_NAME" -f deploy/trialrun/docker-compose.yml \
|
||||
--env-file deploy/trialrun/.env up -d db
|
||||
mapfile -t DB_CONTAINERS < <(docker-compose -p "$COMPOSE_PROJECT_NAME" \
|
||||
-f deploy/trialrun/docker-compose.yml --env-file deploy/trialrun/.env ps -q db)
|
||||
test "${#DB_CONTAINERS[@]}" -eq 1
|
||||
DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
||||
STORAGE_DIR_HOST=<verified-active-storage> ./scripts/backup-all.sh
|
||||
```
|
||||
|
||||
## Scenario: Paired Backup And Shadow-Only Restore
|
||||
|
||||
### 1. Scope / Trigger
|
||||
@@ -603,7 +677,7 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Paired backup: `DB_TOOL_MODE=docker DB_CONTAINER=<name> ./scripts/backup-all.sh`.
|
||||
- Paired backup: `DB_TOOL_MODE=docker DB_CONTAINER=<compose-resolved-id> STORAGE_DIR_HOST=<verified-active-directory> ./scripts/backup-all.sh`.
|
||||
- Optional safe batch identifier: `BACKUP_RUN_ID=<safe-id>`.
|
||||
- Manifest writer: host `node` is preferred; host `python3` is an equivalent
|
||||
fallback for Docker-only runtime machines.
|
||||
@@ -627,6 +701,10 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
||||
the running database. Confirm that the selected PostgreSQL container's database
|
||||
and user match at least one active application `DATABASE_URL`, then pass the
|
||||
verified runtime values without logging their contents.
|
||||
- Docker backup/restore has no default container name. Resolve the unique DB
|
||||
container with `compose -p <approved-project> ... ps -q db` and fail when the
|
||||
result is empty or ambiguous. Storage backup likewise requires an explicit
|
||||
`STORAGE_DIR_HOST`; it must never infer `STORAGE_DIR` or a repository default.
|
||||
- The DB dump must pass `pg_restore --list` and the storage archive must pass
|
||||
`tar -tzf` before the manifest may say `complete`.
|
||||
- DB restore queries `current_database()` and compares it exactly with
|
||||
@@ -704,6 +782,7 @@ source deploy/trialrun/.env
|
||||
./scripts/qa/run-backup-restore-drill.sh
|
||||
# Before an approved live backup, compare the selected DB container identity
|
||||
# with an active app connection using redacted boolean diagnostics.
|
||||
DB_TOOL_MODE=docker DB_CONTAINER=<verified-container> ./scripts/backup-all.sh
|
||||
DB_TOOL_MODE=docker DB_CONTAINER=<verified-container> \
|
||||
STORAGE_DIR_HOST=<verified-active-storage> ./scripts/backup-all.sh
|
||||
# Real restore remains an approved incident operation after shadow evidence.
|
||||
```
|
||||
|
||||
在新工单中引用
屏蔽一个用户