added peti hetzner

This commit is contained in:
2026-06-08 12:29:05 +02:00
parent 4e86091f7d
commit 2be14ac72e
+76 -41
View File
@@ -154,63 +154,87 @@ data:
set -u set -u
SHARED=/shared SHARED=/shared
HDR=/scripts/metrics-header.prom HDR=/scripts/metrics-header.prom
HETZNER="${HETZNER_HOST:?set HETZNER_HOST}"
# Space-separated endpoint labels. For each LABEL there must be an env var
# <LABEL>_HOST (uppercased) and optionally <LABEL>_HMAC.
ENDPOINTS="${ENDPOINTS:-hetzner}"
# Subset of ENDPOINTS that also get iperf3 throughput tests (the data-heavy part).
TPUT_ENDPOINTS="${TPUT_ENDPOINTS:-$ENDPOINTS}"
IRTT_PORT="${IRTT_PORT:-2112}" IRTT_PORT="${IRTT_PORT:-2112}"
IPERF_PORT="${IPERF_PORT:-5201}" IPERF_PORT="${IPERF_PORT:-5201}"
IRTT_INTERVAL="${IRTT_INTERVAL:-20ms}" IRTT_INTERVAL="${IRTT_INTERVAL:-20ms}"
IRTT_DURATION="${IRTT_DURATION:-60}" # seconds (numeric, for timeout math) IRTT_DURATION="${IRTT_DURATION:-60}" # seconds (numeric, for timeout math)
TPUT_EVERY="${TPUT_EVERY:-900}" # seconds between throughput tests TPUT_EVERY="${TPUT_EVERY:-900}" # seconds between throughput tests
TPUT_TIME="${TPUT_TIME:-10}" # iperf3 seconds per direction TPUT_TIME="${TPUT_TIME:-10}" # iperf3 seconds per direction
IRTT_TARGET="${IRTT_TARGET:-hetzner}" IPERF_PARALLEL="${IPERF_PARALLEL:-4}" # parallel streams: one can't fill the pipe over the RTT
TPUT_TARGET="${TPUT_TARGET:-hetzner}"
HMAC_OPT="" upper() { echo "$1" | tr '[:lower:]' '[:upper:]'; }
[ -n "${IRTT_HMAC:-}" ] && HMAC_OPT="--hmac=${IRTT_HMAC}" host_for() { U="$(upper "$1")"; eval "printf '%s' \"\${${U}_HOST:-}\""; }
hmac_for() { U="$(upper "$1")"; eval "printf '%s' \"\${${U}_HMAC:-}\""; }
mkdir -p "$SHARED" mkdir -p "$SHARED"
: > "$SHARED/.irtt.prom"; : > "$SHARED/.irttload.prom"; : > "$SHARED/.tput.prom" FRAGMENTS=""
for ep in $ENDPOINTS; do
: > "$SHARED/.irtt.$ep.prom"
: > "$SHARED/.irttload.$ep.prom"
: > "$SHARED/.tput.$ep.prom"
FRAGMENTS="$FRAGMENTS $SHARED/.irtt.$ep.prom $SHARED/.irttload.$ep.prom $SHARED/.tput.$ep.prom"
done
cp "$HDR" "$SHARED/metrics" # serve header immediately so first scrapes don't 404 cp "$HDR" "$SHARED/metrics" # serve header immediately so first scrapes don't 404
# Concatenate fragments into the served file via temp + atomic rename. # Concatenate header + all fragments into the served file via temp + atomic rename.
assemble() { assemble() {
cat "$HDR" "$SHARED/.irtt.prom" "$SHARED/.irttload.prom" "$SHARED/.tput.prom" \ cat "$HDR" $FRAGMENTS > "$SHARED/.metrics.tmp" 2>/dev/null
> "$SHARED/.metrics.tmp" 2>/dev/null
mv "$SHARED/.metrics.tmp" "$SHARED/metrics" mv "$SHARED/.metrics.tmp" "$SHARED/metrics"
} }
# Each fragment is written to <file>.tmp then renamed, so assemble() never # $1 condition $2 label $3 host $4 hmac $5 outfile $6 duration(seconds)
# cats a partially written file (the cause of the impossible loss spikes). # Writes <outfile>.tmp then renames, so assemble() never cats a partial file.
run_irtt() { # $1 condition $2 outfile $3 duration(seconds) run_irtt() {
timeout "$(( $3 + 25 ))" irtt client -i "$IRTT_INTERVAL" -d "${3}s" -q $HMAC_OPT \ hmopt=""
-o - "${HETZNER}:${IRTT_PORT}" 2>/dev/null \ [ -n "$4" ] && hmopt="--hmac=$4"
| python3 /scripts/irtt_to_prom.py "$1" "$IRTT_TARGET" > "$2.tmp" timeout "$(( $6 + 25 ))" irtt client -i "$IRTT_INTERVAL" -d "${6}s" -q $hmopt \
mv "$2.tmp" "$2" -o - "$3:${IRTT_PORT}" 2>/dev/null \
| python3 /scripts/irtt_to_prom.py "$1" "$2" > "$5.tmp"
mv "$5.tmp" "$5"
} }
# $1 label $2 host
run_tput() { run_tput() {
P="${IPERF_PARALLEL:-4}" # parallel streams: a single stream can't fill the pipe over the RTT
TO="$(( TPUT_TIME + 20 ))" TO="$(( TPUT_TIME + 20 ))"
TMP="$SHARED/.tput.prom.partial" TMP="$SHARED/.tput.$1.partial"
: > "$TMP" : > "$TMP"
timeout "$TO" iperf3 -c "$HETZNER" -p "$IPERF_PORT" -t "$TPUT_TIME" -P "$P" --connect-timeout 5000 -R -J 2>/dev/null \ timeout "$TO" iperf3 -c "$2" -p "$IPERF_PORT" -t "$TPUT_TIME" -P "$IPERF_PARALLEL" --connect-timeout 5000 -R -J 2>/dev/null \
| python3 /scripts/tput_to_prom.py download "$TPUT_TARGET" > "$TMP" | python3 /scripts/tput_to_prom.py download "$1" > "$TMP"
timeout "$TO" iperf3 -c "$HETZNER" -p "$IPERF_PORT" -t "$TPUT_TIME" -P "$P" --connect-timeout 5000 -J 2>/dev/null \ timeout "$TO" iperf3 -c "$2" -p "$IPERF_PORT" -t "$TPUT_TIME" -P "$IPERF_PARALLEL" --connect-timeout 5000 -J 2>/dev/null \
| python3 /scripts/tput_to_prom.py upload "$TPUT_TARGET" >> "$TMP" | python3 /scripts/tput_to_prom.py upload "$1" >> "$TMP"
mv "$TMP" "$SHARED/.tput.prom" mv "$TMP" "$SHARED/.tput.$1.prom"
} }
last_tput=0 last_tput=0
while true; do while true; do
run_irtt idle "$SHARED/.irtt.prom" "$IRTT_DURATION" # blocks ~IRTT_DURATION = loop cadence # idle irtt to every endpoint in parallel (both paths sampled at the same instant)
for ep in $ENDPOINTS; do
h="$(host_for "$ep")"; [ -z "$h" ] && continue
run_irtt idle "$ep" "$h" "$(hmac_for "$ep")" "$SHARED/.irtt.$ep.prom" "$IRTT_DURATION" &
done
wait
assemble assemble
now=$(date +%s) now=$(date +%s)
if [ $(( now - last_tput )) -ge "$TPUT_EVERY" ]; then if [ $(( now - last_tput )) -ge "$TPUT_EVERY" ]; then
LOAD_DUR=$(( 2 * TPUT_TIME + 4 )) LOAD_DUR=$(( 2 * TPUT_TIME + 4 ))
run_irtt under_load "$SHARED/.irttload.prom" "$LOAD_DUR" & # concurrent = bufferbloat for ep in $TPUT_ENDPOINTS; do
LOADPID=$! h="$(host_for "$ep")"; [ -z "$h" ] && continue
run_tput # concurrent under-load irtt to the same endpoint = bufferbloat
wait "$LOADPID" 2>/dev/null run_irtt under_load "$ep" "$h" "$(hmac_for "$ep")" "$SHARED/.irttload.$ep.prom" "$LOAD_DUR" &
LOADPID=$!
run_tput "$ep" "$h"
wait "$LOADPID" 2>/dev/null
assemble
done
last_tput="$now" last_tput="$now"
assemble
fi fi
done done
--- ---
@@ -266,11 +290,28 @@ spec:
image: gitea.dooplex.hu/admin/wan-probe:0.1.0 image: gitea.dooplex.hu/admin/wan-probe:0.1.0
command: ["/bin/sh", "/scripts/probe-loop.sh"] command: ["/bin/sh", "/scripts/probe-loop.sh"]
env: env:
- name: ENDPOINTS
value: "hetzner abonet" # space-separated endpoint labels
# - name: TPUT_ENDPOINTS
# value: "hetzner" # uncomment to keep iperf3 OFF the abonet VM (saves its egress)
- name: HETZNER_HOST - name: HETZNER_HOST
# MUST be the Hetzner origin: a DNS-only (grey-cloud) record or raw IP. # MUST be the origin (DNS-only record or raw IP), NOT a Cloudflare-proxied
# NOT the Cloudflare-proxied jarrs.eu — CF only forwards HTTP/HTTPS, so # name -- CF only forwards HTTP/HTTPS, so UDP 2112 / TCP 5201 never arrive.
# UDP 2112 (irtt) / TCP 5201 (iperf3) never reach the origin behind it. value: "metrics.jarrs.eu"
value: "metrics.jarrs.eu" # DNS-only A record -> Hetzner IPv4 - name: ABONET_HOST
value: "hetzner.abonet.hu" # colleague's VM (verify it's a direct A record, not CF-proxied)
- name: HETZNER_HMAC
valueFrom:
secretKeyRef:
name: wan-monitor-irtt
key: hetzner
optional: true
- name: ABONET_HMAC
valueFrom:
secretKeyRef:
name: wan-monitor-irtt
key: abonet
optional: true
- name: IRTT_PORT - name: IRTT_PORT
value: "2112" value: "2112"
- name: IPERF_PORT - name: IPERF_PORT
@@ -278,19 +319,13 @@ spec:
- name: IRTT_INTERVAL - name: IRTT_INTERVAL
value: "20ms" value: "20ms"
- name: IRTT_DURATION - name: IRTT_DURATION
value: "60" # seconds (numeric) value: "60"
- name: TPUT_EVERY - name: TPUT_EVERY
value: "900" # 15 min value: "900"
- name: TPUT_TIME - name: TPUT_TIME
value: "10" value: "10"
- name: IPERF_PARALLEL - name: IPERF_PARALLEL
value: "4" value: "4"
- name: IRTT_HMAC # shared key; apply via secret (see below)
valueFrom:
secretKeyRef:
name: wan-monitor-irtt
key: hmac
optional: true
resources: resources:
requests: { cpu: 20m, memory: 48Mi } requests: { cpu: 20m, memory: 48Mi }
limits: { memory: 96Mi } limits: { memory: 96Mi }