landing: central container memory/load stats for all three sites

Collect RSS groups, top processes, and loadavg from inside each podman
container (not host /proc), merge into performance.memory, show compact
labels with cgroup limits and byte tooltips, and cover this in the test suite.
This commit is contained in:
Hernâni Marques 2026-07-17 08:55:00 +02:00
parent 9e0f85024b
commit 78bbd80d1f
No known key found for this signature in database
11 changed files with 474 additions and 69 deletions

View file

@ -3,6 +3,7 @@
#
# - Bank: full account+ledger scan via collect_bank_stats.py → bank container
# - Exchange / merchant: existing in-container scripts, then amount_alt enrich
# - All three: container RSS / loadavg / top procs via mem-snapshot (podman exec)
# - Never wipes a good stats.json on failure (writes stats-run.json only)
#
# Install: scripts/taler-landing/install-landing-stats-host.sh
@ -50,6 +51,50 @@ ec_bank=0
ec_ex=0
ec_mer=0
# Resolve helper scripts (checkout or ~/.local/lib)
COLLECT_RES="$LIB/collect_container_resources.sh"
MERGE_RES="$LIB/merge_resources.py"
if [ ! -f "$COLLECT_RES" ] && [ -f "$ROOT/collect_container_resources.sh" ]; then
COLLECT_RES="$ROOT/collect_container_resources.sh"
fi
if [ ! -f "$MERGE_RES" ] && [ -f "$ROOT/merge_resources.py" ]; then
MERGE_RES="$ROOT/merge_resources.py"
fi
MEM_SRC="${MEM_SNAPSHOT_SRC:-$ADMIN_LOG/scripts/taler-shared/mem-snapshot.sh}"
merge_container_resources() {
local label="$1" ctr="$2" stats_file="$3"
[ -f "$stats_file" ] || return 0
if ! ctr_running "$ctr"; then
log "WARN: $label resources: $ctr not running"
return 1
fi
if [ ! -x "$COLLECT_RES" ] && [ -f "$COLLECT_RES" ]; then
chmod +x "$COLLECT_RES" 2>/dev/null || true
fi
if [ ! -f "$COLLECT_RES" ] || [ ! -f "$MERGE_RES" ]; then
log "WARN: $label resources: helpers missing ($COLLECT_RES / $MERGE_RES)"
return 1
fi
local resf="$WORKDIR/${label}-resources.json"
set +e
ADMIN_LOG="$ADMIN_LOG" MEM_SNAPSHOT_SRC="$MEM_SRC" \
bash "$COLLECT_RES" "$ctr" "$resf" >>"$LOG_DIR/${label}-resources.log" 2>&1
local ec=$?
set -e
if [ "$ec" -ne 0 ] || [ ! -s "$resf" ]; then
log "WARN: $label resources: collect failed (ec=$ec)"
return 1
fi
if ! "$PY" -c 'import json,sys; d=json.load(open(sys.argv[1])); sys.exit(0 if d.get("ok") and d.get("memory") else 1)' "$resf" 2>/dev/null; then
log "WARN: $label resources: bad payload"
return 1
fi
"$PY" "$MERGE_RES" "$stats_file" "$resf" >>"$LOG_DIR/${label}-resources.log" 2>&1
log "$label: resources merged (RSS + loadavg from $ctr)"
return 0
}
read_pass() {
local name="$1" f
for f in \
@ -136,28 +181,8 @@ collect_bank() {
set -e
if [ "$ec_bank" -eq 0 ] && [ -f "$WORKDIR/bank-stats.json" ]; then
# merge in-container memory snapshot when helper exists
if ctr_running "$BANK_CTR" && podman exec "$BANK_CTR" test -f /usr/local/lib/landing-mem-snapshot.sh 2>/dev/null; then
set +e
mem_json=$(podman exec "$BANK_CTR" bash -c '
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
MEM_JSON=""
# shellcheck disable=SC1091
. /usr/local/lib/landing-mem-snapshot.sh
mem_snapshot_json 2>/dev/null || true
# print MEM_JSON lines only if function set them via echo — fallback empty
if [ -n "${MEM_JSON:-}" ]; then printf "%s" "$MEM_JSON"; fi
' 2>/dev/null)
set -e
if [ -n "${mem_json:-}" ]; then
"$PY" - "$WORKDIR/bank-stats.json" <<'PY' || true
import json, sys
path = sys.argv[1]
# optional: leave memory as-is if merge fails
print("mem merge skipped (structured merge via podman helper optional)", file=sys.stderr)
PY
fi
fi
# Always attach in-container RSS/loadavg (not host /proc)
merge_container_resources bank "$BANK_CTR" "$WORKDIR/bank-stats.json" || true
publish_json "$BANK_CTR" "$BANK_LANDING_IN" \
"$WORKDIR/bank-stats.json" "$WORKDIR/bank-stats-run.json"
log "bank: OK → ${BANK_CTR}:${BANK_LANDING_IN}/stats.json"
@ -220,14 +245,16 @@ run_incontainer_stats() {
log "ERROR: $label stats failed (ec=$ec)"
return "$ec"
fi
# enrich with alt units on host
# enrich with alt units + ensure resources (RSS/loadavg) on host
podman cp "${ctr}:${landing}/stats.json" "$WORKDIR/${label}-stats.json" 2>/dev/null || return 0
set +e
"$PY" "$LIB/enrich_stats_alt.py" "$WORKDIR/${label}-stats.json" \
--exchange-config "$EXCHANGE_CONFIG_URL" >>"$LOG_DIR/${label}.log" 2>&1
set -e
# Prefer fresh container snapshot (also fills gaps if in-container mem helper missing)
merge_container_resources "$label" "$ctr" "$WORKDIR/${label}-stats.json" || true
podman cp "$WORKDIR/${label}-stats.json" "${ctr}:${landing}/stats.json"
log "$label: OK + alt enrich → ${ctr}:${landing}/stats.json"
log "$label: OK + alt + resources${ctr}:${landing}/stats.json"
return 0
}