20260914 强制 TOML 配置并完善 TA 刷新与 daemon
Some checks failed
ci / rust (push) Has been cancelled
ci / docker (push) Has been cancelled
ci / audit (push) Has been cancelled

This commit is contained in:
2026-09-14 16:58:08 +08:00
parent 6383e2c12c
commit 4975128945
48 changed files with 2795 additions and 1459 deletions

View File

@@ -1,199 +1,189 @@
# Docker and Compose
Run commands from the project root. A Linux Docker engine is required for the
documented deployment and integration tests.
For every field, default, constraint and complete example, see the [TOML configuration reference](configuration.md)。
The image contains the validator, `rsync`, CA certificates and `/usr/bin/time`.
The root filesystem is read-only; input and configuration are read-only; output and
state mounts are writable. The application receives a subcommand and one configuration option:
`validate|daemon --config FILE`.
Build the image:
```bash
docker build -f docker/Dockerfile -t panda-rpki:v0.1.0 .
```
The image defaults to an unprivileged user and includes CA certificates,
`rsync` and `/usr/bin/time`. Compose uses a read-only root filesystem and a
temporary `/tmp`. Host input mounts are read-only; state and outputs are writable.
## Single validate run
## Single anchor
Create a TOML whose paths are valid **inside** the container:
```toml
config-version = 1
mode = "validate"
out = "/output"
rrdp-state-dir = "/state/repository"
ccr-out = "/output/result.ccr"
[[anchors]]
id = "anchor"
tal = "/input/anchor.tal"
# ta = "/input/anchor.cer" # uncomment for fixed TA mode
```
Run the provided Compose template:
```bash
mkdir -p state output
export CONFIG_FILE="$PWD/validate.toml"
export INPUT_DIR="$PWD/input" STATE_DIR="$PWD/state" OUTPUT_DIR="$PWD/output"
export TAL_FILE=anchor.tal TA_FILE=anchor.cer
export PUID="$(id -u)" PGID="$(id -g)"
docker compose -f docker/compose.yaml run --build --rm validator
```
Supply absolute host paths as above. Relative Compose paths are resolved from
the Compose file's directory, which is `docker/`. Build context is explicitly
the project root. `PANDA_RPKI_RRDP_SYNC_MODE` defaults to `auto`.
The template mounts the TOML read-only at `/config/validate.toml` and invokes
`panda-rpki validate --config /config/validate.toml`. Host variables only select
mounts, image and container UID/GID; they are not read as validator settings.
## All five regional anchors
## All five RIRs
Follow [Prepare all five RIR trust anchors](trust-anchors.md) to download and
check each TAL and matching certificate under `input/`, then run:
Put the five RIR TAL files (TA certificates are optional) under `input/` and list them as named anchors in a
TOML with `/input/...` paths. Then set `CONFIG_FILE` to that file and run:
```bash
mkdir -p state output
export INPUT_DIR="$PWD/input" STATE_DIR="$PWD/state" OUTPUT_DIR="$PWD/output"
export PUID="$(id -u)" PGID="$(id -g)"
export AFRINIC_TAL_FILE=afrinic.tal AFRINIC_TA_FILE=afrinic.cer
export APNIC_TAL_FILE=apnic.tal APNIC_TA_FILE=apnic.cer
export ARIN_TAL_FILE=arin.tal ARIN_TA_FILE=arin.cer
export LACNIC_TAL_FILE=lacnic.tal LACNIC_TA_FILE=lacnic.cer
export RIPE_TAL_FILE=ripe.tal RIPE_TA_FILE=ripe.cer
export CONFIG_FILE="$PWD/all5.toml"
export INPUT_DIR="$PWD/input" STATE_DIR="$PWD/state/all5" OUTPUT_DIR="$PWD/output/all5"
docker compose -f docker/compose.all5.yaml run --build --rm validator
```
| Template variable | Default | Applies to |
| --- | --- | --- |
| `PANDA_RPKI_WORKERS` | 8 | all5 and daemon object workers |
| `PANDA_RPKI_REPO_WORKERS` | 8 | all5 and daemon transport workers |
| `PANDA_RPKI_WORKER_QUEUE_CAPACITY` | 256 | all5 object queue |
| `PANDA_RPKI_REPO_QUEUE_CAPACITY` | 256 | all5 daemon repository queue |
| `PANDA_RPKI_HTTP_TIMEOUT_SECS` | 600 | all5 HTTP timeout (native CLI default is 300) |
| `PANDA_RPKI_RRDP_SYNC_MODE` | auto | single-anchor and all5 |
The application still receives only `validate --config`; workers, timeouts,
logging and sync policy belong in `all5.toml`.
The all5 CCR is `OUTPUT_DIR/all5.ccr`; the single-anchor CCR is
`OUTPUT_DIR/result.ccr`. Reuse `STATE_DIR` and select new `OUTPUT_DIR` values
to retain multiple cycles. These templates take no bundled RIR input files.
## Daemon
## All-five daemon service
Daemon TOML paths are also container paths. `state-root` owns run directories,
protocol state and the `latest` pointer:
The all-five daemon template combines the five regional TAL/TA pairs with the
continuous daemon controller. Prepare the same `input/` directory used by the
all-five one-shot example, then run:
```toml
config-version = 1
mode = "daemon"
state-root = "/data"
interval-secs = 600
retain-runs = 10
run-timeout-secs = 1800
shutdown-grace-secs = 30
[[anchors]]
id = "anchor"
tal = "/input/anchor.tal"
# ta = "/input/anchor.cer" # uncomment for fixed TA mode
```
Run the daemon template:
```bash
mkdir -p data/all5-daemon
export INPUT_DIR="$PWD/input" DAEMON_DIR="$PWD/data/all5-daemon"
export AFRINIC_TAL_FILE=afrinic.tal AFRINIC_TA_FILE=afrinic.cer
export APNIC_TAL_FILE=apnic.tal APNIC_TA_FILE=apnic.cer
export ARIN_TAL_FILE=arin.tal ARIN_TA_FILE=arin.cer
export LACNIC_TAL_FILE=lacnic.tal LACNIC_TA_FILE=lacnic.cer
export RIPE_TAL_FILE=ripe.tal RIPE_TA_FILE=ripe.cer
mkdir -p daemon-state
export CONFIG_FILE="$PWD/daemon.toml"
export INPUT_DIR="$PWD/input" DAEMON_DIR="$PWD/daemon-state"
export PUID="$(id -u)" PGID="$(id -g)"
docker compose -f docker/compose.all5-daemon.yaml up -d --build
docker compose -f docker/compose.all5-daemon.yaml logs -f validator
docker compose -f docker/compose.daemon.yaml up --build
```
The template uses a 60-second interval by default, keeps 10 completed runs,
and leaves the daemon running until it is stopped. It writes each run below
`DAEMON_DIR/runs/` and keeps shared protocol state below `DAEMON_DIR/state/`.
The default diagnostic format is `text`, with a source file and line appended
to each diagnostic record; select `json` explicitly for machine consumers.
Set `PANDA_RPKI_INTERVAL_SECS` only when a different interval is intended. Stop
the service without deleting its data with:
For an all-RIR daemon use `docker/compose.all5-daemon.yaml` with an all-RIR
TOML. Stop the whole Compose service for an upgrade so the state-root lock and
validator child are released; do not delete `daemon-state`.
## Configuration and persistence
Run `docker compose ... run --rm validator validate --config ... --check-config`
only when the image command is overridden deliberately; the normal templates
already pass the config. Check/print never download TA files or open RocksDB.
Successful validate publication, when `publish-dir` is configured, is an
immutable `publish/runs/<id>` plus `publish/latest`; daemon publication uses its
existing `state-root/runs/run_<seq>` directory. Cache files are written beside
the protocol state as `<state>.ta-cache/`.
If a host path is not writable by the selected UID/GID, pre-create it and fix
ownership. Preserve existing state and run history when changing images. Verify
`summary.json`, `result-complete.json`, `vrps.csv`, `vaps.csv`, timing files and
the `latest` link after a deployment.
## Complete configuration examples
Copy a template before editing it; set `CONFIG_FILE` to the copy's absolute path.
These templates use automatic **TA certificate** refresh, text/info logs and
`require-complete = true`. They never download or update TAL files.
| Compose file | Complete TOML |
| --- | --- |
| `compose.yaml` | [validate.toml](../docker/examples/validate.toml) |
| `compose.all5.yaml` | [validate-all5.toml](../docker/examples/validate-all5.toml) |
| `compose.daemon.yaml` | [daemon.toml](../docker/examples/daemon.toml) |
| `compose.all5-daemon.yaml` | [daemon-all5.toml](../docker/examples/daemon-all5.toml) |
For example, from the repository root:
```bash
docker compose -f docker/compose.all5-daemon.yaml stop
```
## Upgrade a daemon deployment
An upgrade replaces the image and recreates the container. It does not replace
the host data directory. Keep the same `DAEMON_DIR`, Compose project, TAL/TA
inputs and controller settings so the new image can resume the existing
protocol state. Do not run two writable daemons against the same state root.
Run the following from the deployment directory that owns the Compose file. Use
a unique image tag for every candidate so the image used by a run is auditable:
```bash
export COMPOSE_FILE="${COMPOSE_FILE:-docker/compose.all5-daemon.yaml}"
export COMPOSE_PROJECT_NAME=panda-rpki-all5
export PANDA_RPKI_IMAGE=panda-rpki:2026-09-11-logging
export SOURCE_DIR=/path/to/panda-rpki-oss
# Keep the existing INPUT_DIR, DAEMON_DIR, TAL/TA variables and settings.
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" ps
test "$(jq -r .state "$DAEMON_DIR/daemon-status.json")" = sleeping
# Build from the source tree before stopping the running service.
docker build -f "$SOURCE_DIR/docker/Dockerfile" -t "$PANDA_RPKI_IMAGE" "$SOURCE_DIR"
docker image inspect "$PANDA_RPKI_IMAGE" --format '{{.Id}}'
# Save configuration and create a consistent stopped-state backup.
backup="upgrade-backup-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$backup"
cp "$COMPOSE_FILE" "$backup/compose.yaml"
if test -f .env; then cp .env "$backup/env"; fi
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" stop
cp -a "$DAEMON_DIR/state" "$backup/state"
cp -a "$DAEMON_DIR/daemon-status.json" "$DAEMON_DIR/lifecycle.json" "$backup/"
# Recreate the same service with the new image; bind-mounted data is retained.
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" up -d --no-build --force-recreate
docker inspect "$(docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" ps -q validator)" \
--format 'image={{.Config.Image}} running={{.State.Running}}'
```
After the first upgraded run, verify that the run sequence advanced, the run
completed, and the new log/outputs exist. The exact run directory is printed by
the daemon status and can be selected with `find`:
```bash
latest_run="$(find "$DAEMON_DIR/runs" -mindepth 1 -maxdepth 1 -type d -name 'run_*' | sort | tail -1)"
test -s "$latest_run/run-summary.json"
test -s "$latest_run/validator.log"
test -s "$latest_run/result.ccr"
grep -Eq '\[src/[^]]+\.rs:[0-9]+\]$' "$latest_run/validator.log"
jq . "$latest_run/run-summary.json"
```
The upgrade process preserves completed run directories until normal retention
removes them. Archive important runs separately if they must outlive
`PANDA_RPKI_RETAIN_RUNS`.
### Roll back
Stop the upgraded service before changing the image. Restore the previous
Compose file and `.env`, or set `PANDA_RPKI_IMAGE` to the previously verified
tag, then recreate the service with the same project and data directory:
```bash
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" stop
cp "$backup/compose.yaml" "$COMPOSE_FILE"
if test -f "$backup/env"; then cp "$backup/env" .env; fi
unset PANDA_RPKI_IMAGE
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" up -d --no-build --force-recreate
```
If the new binary changed the state schema or wrote incompatible state, stop the
service and move the current state aside before restoring the matching
stopped-state backup:
```bash
mv "$DAEMON_DIR/state" "$DAEMON_DIR/state-incompatible-$(date -u +%Y%m%dT%H%M%SZ)"
cp -a "$backup/state" "$DAEMON_DIR/state"
```
Never delete `DAEMON_DIR` or use `docker compose down -v` as part of a routine
upgrade. Check [state compatibility](command-line-reference.md#state-and-network)
before deploying a binary with storage changes.
## Single-anchor daemon service
```bash
mkdir -p data/daemon
export INPUT_DIR="$PWD/input" DAEMON_DIR="$PWD/data/daemon"
export TAL_FILE=anchor.tal TA_FILE=anchor.cer
cp docker/examples/daemon-all5.toml daemon.toml
export CONFIG_FILE="$PWD/daemon.toml"
export INPUT_DIR="$PWD/input" DAEMON_DIR="$PWD/daemon-state"
mkdir -p "$DAEMON_DIR"
export PUID="$(id -u)" PGID="$(id -g)"
export PANDA_RPKI_INTERVAL_SECS=600 PANDA_RPKI_RETAIN_RUNS=10
docker compose -f docker/compose.daemon.yaml up -d --build
docker compose -f docker/compose.daemon.yaml logs -f validator
# Stop gracefully; persistent host data remains available.
docker compose -f docker/compose.daemon.yaml stop
docker compose -p panda-all5 -f docker/compose.all5-daemon.yaml up -d --build
```
Defaults: interval 60 seconds, retain 10, `PANDA_RPKI_RUN_TIMEOUT_SECS=0`
(disabled), log level `info`, log format `text`. The daemon template passes
`PANDA_RPKI_LOG_LEVEL` and `PANDA_RPKI_LOG_FORMAT` into the container.
It restarts unless stopped and allows 40 seconds for shutdown, exceeding the
controller's default 30-second grace plus termination wait.
For single validation, `/state` is the writable mount parent. Set
`rrdp-state-dir = "/state/repository"` so both `/state/repository` and its
sibling `/state/repository.ta-cache` are persistent and writable. Existing
fixed-mode deployments with `rrdp-state-dir = "/state"` must plan a stopped
state-layout migration or mount `/state.ta-cache` separately before enabling
automatic mode. Do not silently point an existing database at an empty path.
Daemon needs no such migration: `/data/state` and `/data/state.ta-cache` already
share the writable `/data` mount.
For multiple anchors, append TAL/TA pairs after `--` in the daemon service's
command. For other validator options, edit that same list. A finite run can
be invoked with `docker compose run --rm validator daemon ...` using the
[daemon CLI](command-line-reference.md#daemon); use `--max-runs` before `--`.
## Graceful stop, edit and reload
Compose variables are expanded into arguments; only the two documented log
environment variables are read directly by the binary. For single-anchor/all5
logs, pass them with `docker compose run -e PANDA_RPKI_LOG_LEVEL=debug ...`.
Use the same project name, Compose file and environment file on every command.
The following assumes the `panda-all5` project created above:
```bash
dc() { docker compose -p panda-all5 -f docker/compose.all5-daemon.yaml "$@"; }
dc stop --timeout 40 validator
dc ps -a
cp -a "$CONFIG_FILE" "$CONFIG_FILE.bak.$(date -u +%Y%m%dT%H%M%SZ)"
```
Edit the existing top-level field to `log-format = "text"`. To enable automatic
TA refresh, remove each desired anchor's `ta` field; keep its `id` and `tal`.
`ta-refresh-policy = "allow-valid-cache"` allows a revalidated cache fallback
and marks that run partial; `"require-fresh"` rejects the fallback. Missing or
invalid cache cannot satisfy the first automatic run if all downloads fail.
Check the changed file and recreate the service:
```bash
dc run --rm --no-deps validator daemon --config /config/daemon.toml --check-config
dc run --rm --no-deps validator daemon --config /config/daemon.toml --print-config
# Continue only after Configuration valid. and the expected effective settings.
dc up -d --no-build --force-recreate validator
dc logs --since 2m validator
```
Recreation is intentional: editors and `sed -i` can replace the host file's
inode, while an existing single-file bind mount may retain the old file.
`start` or `restart` alone is not the documented configuration reload procedure.
Recreation retains bind-mounted state and run history.
SIGTERM prevents further cycles and allows the active child up to
`shutdown-grace-secs` (30 here) to finish. If it does not finish, the controller
sends TERM, then KILL after another 2 seconds. Docker's 40-second timeout leaves
room for controller cleanup. It is not an unlimited wait for the current cycle.
To avoid interrupting a long cycle, stop during the daemon's sleeping interval.
`exited` confirms the container stopped; exit 3 means the controller recorded
partial runs under `require-complete`, and does not mean stop failed. Inspect
lifecycle logs to establish whether the last child finished or was interrupted.
The controller logs are in `docker compose logs`; child diagnostics are in
`data/runs/run_XXXXXX/validator.log` on the host. Existing logs keep their original
format. JSON summary/metadata files remain JSON even with text diagnostics.
`latest` exists only after a complete success; partial runs do not create or
replace it, including the first run after upgrading an old state root.