20260914 强制 TOML 配置并完善 TA 刷新与 daemon
This commit is contained in:
312
docs/docker.md
312
docs/docker.md
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user