Performance optimization engagement for a 7-host Proxmox R&D cluster.
Captures the accumulated work across host tuning, network analysis,
fleet assessment, and kubernetes architecture planning.
Contents:
- Host-side tunings (scripts/): CPU governor, swappiness, BBR, NFS
nconnect, tuned profiles -- complete on 5 of 7 hosts
- Validation + benchmarking scripts: iperf matrix, bond/NFS fixes
- Collected host data (returned-logs/): check.sh output from all 7
hosts + iperf results, including newly-validated pfv-tsys9
- AGENTS.md: operating context for AI agents
- PROJECT.md: board-ready fleet assessment with VM placement and
storage redundancy analysis (40 VMs across 7 hosts)
- K8S.md: kubernetes architecture deep-dive covering cnode/wnode
distribution, StorageClass design, and ETL/HPC workload planning
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
294 lines
11 KiB
Bash
Executable File
294 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
###############################################################################
|
|
# iperf-full-matrix.sh
|
|
#
|
|
# Two test suites:
|
|
# A. Management network (vmbr0 / VLAN1): all-pairs single-stream TCP, 10s
|
|
# B. Storage network (VLAN1000): tsys6+tsys7 → tsys4+tsys5, stress test
|
|
#
|
|
# Output: returned-logs/iperf/
|
|
###############################################################################
|
|
set -uo pipefail
|
|
|
|
SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=8 -o ServerAliveInterval=10 -o StrictHostKeyChecking=accept-new)
|
|
LOG_DIR="/home/reachableceo/projects/perfopt/returned-logs/iperf"
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
HOSTS=(pfv-tsys1 pfv-tsys3 pfv-tsys4 pfv-tsys5 pfv-tsys6 pfv-tsys7)
|
|
|
|
# Storage IPs (known, static on VLAN1000)
|
|
declare -A SIP
|
|
SIP[pfv-tsys1]="10.100.100.1"
|
|
SIP[pfv-tsys3]="10.100.100.3"
|
|
SIP[pfv-tsys4]="10.100.100.4"
|
|
SIP[pfv-tsys5]="10.100.100.5"
|
|
SIP[pfv-tsys6]="10.100.100.6"
|
|
SIP[pfv-tsys7]="10.100.100.7"
|
|
|
|
# ===========================================================================
|
|
# STEP 0: Discover management IPs (vmbr0)
|
|
# ===========================================================================
|
|
echo "==================================================================="
|
|
echo " STEP 0: Discover management network IPs (vmbr0)"
|
|
echo "==================================================================="
|
|
declare -A MIP
|
|
for host in "${HOSTS[@]}"; do
|
|
if ! ssh "${SSH_OPTS[@]}" "root@$host" 'echo ok' >/dev/null 2>&1; then
|
|
echo " [$host] UNREACHABLE"
|
|
continue
|
|
fi
|
|
mip=$(ssh "${SSH_OPTS[@]}" "root@$host" 'ip -o -4 addr show dev vmbr0 2>/dev/null | awk "{print \$4}" | cut -d/ -f1 | head -1')
|
|
if [ -n "$mip" ]; then
|
|
MIP[$host]="$mip"
|
|
echo " [$host] vmbr0 = $mip"
|
|
else
|
|
echo " [$host] no vmbr0 IPv4 — skipping"
|
|
fi
|
|
done
|
|
|
|
# ===========================================================================
|
|
# STEP 1: Ensure iperf3 installed on all hosts
|
|
# ===========================================================================
|
|
echo ""
|
|
echo "==================================================================="
|
|
echo " STEP 1: Ensure iperf3 installed"
|
|
echo "==================================================================="
|
|
for host in "${HOSTS[@]}"; do
|
|
[ -z "${MIP[$host]:-}" ] && continue
|
|
if ! ssh "${SSH_OPTS[@]}" "root@$host" 'command -v iperf3 >/dev/null 2>&1' 2>/dev/null; then
|
|
echo -n " [$host] installing iperf3... "
|
|
ssh "${SSH_OPTS[@]}" "root@$host" \
|
|
'DEBIAN_FRONTEND=noninteractive apt-get update -qq >/dev/null 2>&1 && \
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq iperf3 >/dev/null 2>&1 && echo OK || echo FAILED'
|
|
else
|
|
echo " [$host] iperf3 already installed"
|
|
fi
|
|
done
|
|
|
|
# Helper: start iperf3 server in one-shot mode bound to a specific IP
|
|
start_server() {
|
|
local host="$1" ip="$2"
|
|
ssh "${SSH_OPTS[@]}" "root@$host" \
|
|
"pkill -x iperf3 2>/dev/null; nohup iperf3 -s -1 -B ${ip} >/dev/null 2>&1 &" 2>/dev/null
|
|
sleep 1
|
|
}
|
|
|
|
# Helper: run iperf3 client test, save output, extract result
|
|
run_test() {
|
|
local client="$1" server="$2" sip="$3" cip="$4" label="$5" logfile="$6"
|
|
shift 6
|
|
local extra="$*"
|
|
|
|
echo -n " [$label] ... "
|
|
{
|
|
echo "=== iperf3: $label ==="
|
|
echo "Client: $client ($cip) → Server: $server ($sip)"
|
|
echo "Date: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
echo "Args: $extra"
|
|
echo ""
|
|
ssh "${SSH_OPTS[@]}" "root@$client" "iperf3 -c ${sip} -B ${cip} ${extra}" 2>&1
|
|
echo ""
|
|
echo "=== END ==="
|
|
} > "$logfile" 2>&1
|
|
|
|
# Extract result
|
|
sum=$(grep '\[SUM\].*sender$' "$logfile" | tail -1)
|
|
if [ -n "$sum" ]; then
|
|
bitrate=$(echo "$sum" | awk '{print $6, $7}')
|
|
retrans=$(echo "$sum" | awk '{print $8}')
|
|
else
|
|
single=$(grep 'sender$' "$logfile" | tail -1)
|
|
bitrate=$(echo "$single" | awk '{print $7, $8}')
|
|
retrans=$(echo "$single" | awk '{print $9}')
|
|
fi
|
|
echo "${bitrate:-?} (retrans: ${retrans:-?})"
|
|
}
|
|
|
|
# ===========================================================================
|
|
# SUITE A: Management network (vmbr0) — all pairs, single-stream TCP, 10s
|
|
# ===========================================================================
|
|
echo ""
|
|
echo "==================================================================="
|
|
echo " SUITE A: MANAGEMENT NETWORK (vmbr0) — all-pairs, 10s TCP"
|
|
echo " Expectation: ~940 Mbps for every pair (1 GbE line rate)"
|
|
echo "==================================================================="
|
|
echo ""
|
|
|
|
# Test each unique pair (i < j to avoid duplicates)
|
|
for ((i=0; i<${#HOSTS[@]}; i++)); do
|
|
for ((j=i+1; j<${#HOSTS[@]}; j++)); do
|
|
client="${HOSTS[$i]}"
|
|
server="${HOSTS[$j]}"
|
|
[ -z "${MIP[$client]:-}" ] && continue
|
|
[ -z "${MIP[$server]:-}" ] && continue
|
|
|
|
label="mgmt: ${client}→${server}"
|
|
logfile="$LOG_DIR/mgmt-${client}-to-${server}.log"
|
|
|
|
start_server "$server" "${MIP[$server]}"
|
|
run_test "$client" "$server" "${MIP[$server]}" "${MIP[$client]}" \
|
|
"$label" "$logfile" "-t 10 -P 1"
|
|
done
|
|
done
|
|
|
|
# ===========================================================================
|
|
# SUITE B: Storage network (VLAN1000) — stress test the cross-rack LACP
|
|
# ===========================================================================
|
|
echo ""
|
|
echo "==================================================================="
|
|
echo " SUITE B: STORAGE NETWORK (VLAN1000) — stress test cross-rack link"
|
|
echo " tsys6 + tsys7 (Rack 3) → tsys4 + tsys5 (Rack 5)"
|
|
echo " Expectation: limited by tsys4 USB dongle + tsys5 broken bond"
|
|
echo "==================================================================="
|
|
echo ""
|
|
|
|
# --- B.1: Individual tests (one client → one server at a time) ---
|
|
echo "--- B.1: Individual tests (sequential) ---"
|
|
echo ""
|
|
|
|
for client in pfv-tsys6 pfv-tsys7; do
|
|
for server in pfv-tsys4 pfv-tsys5; do
|
|
label="stor: ${client}→${server} (8-stream)"
|
|
logfile="$LOG_DIR/stor-indiv-${client}-to-${server}-8stream.log"
|
|
start_server "$server" "${SIP[$server]}"
|
|
run_test "$client" "$server" "${SIP[$server]}" "${SIP[$client]}" \
|
|
"$label" "$logfile" "-P 8 -t 20 -l 128k -O 2"
|
|
done
|
|
done
|
|
|
|
# --- B.2: Reverse direction (tsys4/5 → tsys6/7) ---
|
|
echo ""
|
|
echo "--- B.2: Reverse direction (tsys4/5 → tsys6/7) ---"
|
|
echo ""
|
|
|
|
for client in pfv-tsys4 pfv-tsys5; do
|
|
for server in pfv-tsys6 pfv-tsys7; do
|
|
label="stor: ${client}→${server} (8-stream rev)"
|
|
logfile="$LOG_DIR/stor-indiv-${client}-to-${server}-8stream.log"
|
|
start_server "$server" "${SIP[$server]}"
|
|
run_test "$client" "$server" "${SIP[$server]}" "${SIP[$client]}" \
|
|
"$label" "$logfile" "-P 8 -t 20 -l 128k -O 2"
|
|
done
|
|
done
|
|
|
|
# --- B.3: Simultaneous stress test (4 flows at once) ---
|
|
echo ""
|
|
echo "--- B.3: Simultaneous 4-flow stress test ---"
|
|
echo " tsys6→tsys4 + tsys6→tsys5 + tsys7→tsys4 + tsys7→tsys5"
|
|
echo " All running in parallel for 30 seconds"
|
|
echo ""
|
|
|
|
# Start 4 iperf3 servers (one-shot mode won't work for parallel; use persistent)
|
|
for server in pfv-tsys4 pfv-tsys5; do
|
|
ssh "${SSH_OPTS[@]}" "root@$server" "pkill -x iperf3 2>/dev/null; nohup iperf3 -s -B ${SIP[$server]} >/dev/null 2>&1 &" 2>/dev/null
|
|
echo " [server started: $server]"
|
|
done
|
|
sleep 1
|
|
|
|
STRESS_LOG="$LOG_DIR/stor-stress-4flow"
|
|
mkdir -p "$STRESS_LOG"
|
|
|
|
# Launch 4 clients in parallel, each writing to its own log
|
|
ssh "${SSH_OPTS[@]}" "root@pfv-tsys6" "iperf3 -c ${SIP[pfv-tsys4]} -B ${SIP[pfv-tsys6]} -P 4 -t 30 -l 128k -O 2" > "$STRESS_LOG/tsys6-to-tsys4.log" 2>&1 &
|
|
PID1=$!
|
|
ssh "${SSH_OPTS[@]}" "root@pfv-tsys6" "iperf3 -c ${SIP[pfv-tsys5]} -B ${SIP[pfv-tsys6]} -P 4 -t 30 -l 128k -O 2" > "$STRESS_LOG/tsys6-to-tsys5.log" 2>&1 &
|
|
PID2=$!
|
|
ssh "${SSH_OPTS[@]}" "root@pfv-tsys7" "iperf3 -c ${SIP[pfv-tsys4]} -B ${SIP[pfv-tsys7]} -P 4 -t 30 -l 128k -O 2" > "$STRESS_LOG/tsys7-to-tsys4.log" 2>&1 &
|
|
PID3=$!
|
|
ssh "${SSH_OPTS[@]}" "root@pfv-tsys7" "iperf3 -c ${SIP[pfv-tsys5]} -B ${SIP[pfv-tsys7]} -P 4 -t 30 -l 128k -O 2" > "$STRESS_LOG/tsys7-to-tsys5.log" 2>&1 &
|
|
PID4=$!
|
|
|
|
echo " [4 clients launched, waiting 40s for completion...]"
|
|
wait $PID1 $PID2 $PID3 $PID4 2>/dev/null
|
|
echo " [all 4 flows complete]"
|
|
|
|
# Kill servers
|
|
for server in pfv-tsys4 pfv-tsys5; do
|
|
ssh "${SSH_OPTS[@]}" "root@$server" 'pkill -x iperf3 2>/dev/null; true' 2>/dev/null
|
|
done
|
|
|
|
# ===========================================================================
|
|
# SUITE C: All hosts cleanup
|
|
# ===========================================================================
|
|
echo ""
|
|
echo "==================================================================="
|
|
echo " Cleanup: killing iperf3 everywhere"
|
|
echo "==================================================================="
|
|
for host in "${HOSTS[@]}"; do
|
|
ssh "${SSH_OPTS[@]}" "root@$host" 'pkill -x iperf3 2>/dev/null; true' 2>/dev/null
|
|
done
|
|
|
|
# ===========================================================================
|
|
# RESULTS SUMMARY
|
|
# ===========================================================================
|
|
echo ""
|
|
echo "==================================================================="
|
|
echo " RESULTS SUMMARY"
|
|
echo "==================================================================="
|
|
|
|
echo ""
|
|
echo "===== SUITE A: Management network (vmbr0) ====="
|
|
echo ""
|
|
printf "%-40s %15s %10s\n" "TEST" "THROUGHPUT" "RETRANS"
|
|
printf "%-40s %15s %10s\n" "----" "----------" "-------"
|
|
for f in "$LOG_DIR"/mgmt-*.log; do
|
|
[ -r "$f" ] || continue
|
|
label=$(head -1 "$f" | sed 's/^=== iperf3: //; s/ ===$//')
|
|
single=$(grep 'sender$' "$f" | tail -1)
|
|
bitrate=$(echo "$single" | awk '{print $7, $8}')
|
|
retrans=$(echo "$single" | awk '{print $9}')
|
|
printf "%-40s %15s %10s\n" "$label" "${bitrate:-?}" "${retrans:--}"
|
|
done
|
|
|
|
echo ""
|
|
echo "===== SUITE B.1+B.2: Storage network individual ====="
|
|
echo ""
|
|
printf "%-45s %15s %10s\n" "TEST" "THROUGHPUT" "RETRANS"
|
|
printf "%-45s %15s %10s\n" "----" "----------" "-------"
|
|
for f in "$LOG_DIR"/stor-indiv-*.log; do
|
|
[ -r "$f" ] || continue
|
|
label=$(head -1 "$f" | sed 's/^=== iperf3: //; s/ ===$//')
|
|
sum=$(grep '\[SUM\].*sender$' "$f" | tail -1)
|
|
if [ -n "$sum" ]; then
|
|
bitrate=$(echo "$sum" | awk '{print $6, $7}')
|
|
retrans=$(echo "$sum" | awk '{print $8}')
|
|
else
|
|
single=$(grep 'sender$' "$f" | tail -1)
|
|
bitrate=$(echo "$single" | awk '{print $7, $8}')
|
|
retrans=$(echo "$single" | awk '{print $9}')
|
|
fi
|
|
printf "%-45s %15s %10s\n" "$label" "${bitrate:-?}" "${retrans:--}"
|
|
done
|
|
|
|
echo ""
|
|
echo "===== SUITE B.3: Simultaneous 4-flow stress test ====="
|
|
echo ""
|
|
printf "%-30s %15s %10s\n" "FLOW" "THROUGHPUT" "RETRANS"
|
|
printf "%-30s %15s %10s\n" "----" "----------" "-------"
|
|
total_mbps=0
|
|
for f in "$STRESS_LOG"/*.log; do
|
|
[ -r "$f" ] || continue
|
|
flow=$(basename "$f" .log)
|
|
sum=$(grep '\[SUM\].*sender$' "$f" | tail -1)
|
|
if [ -n "$sum" ]; then
|
|
bitrate=$(echo "$sum" | awk '{print $6, $7}')
|
|
retrans=$(echo "$sum" | awk '{print $8}')
|
|
mbps=$(echo "$sum" | awk '{print $6}')
|
|
total_mbps=$(awk "BEGIN{print $total_mbps + $mbps}")
|
|
else
|
|
bitrate="?"
|
|
retrans="-"
|
|
fi
|
|
printf "%-30s %15s %10s\n" "$flow" "$bitrate" "${retrans:--}"
|
|
done
|
|
printf "%-30s %15s\n" "AGGREGATE (all 4 flows)" "${total_mbps} Mbits/sec"
|
|
|
|
echo ""
|
|
echo "===== CONTEXT ====="
|
|
echo "tsys4: USB cdc_ncm dongle (single 1G link, no bond)"
|
|
echo "tsys5: bond0 broken (1 active slave, no LACP partner) — cable pending"
|
|
echo "tsys6/7: working 2x1G LACP, layer3+4 hash (host side)"
|
|
echo "Cross-rack: 4x1G LACP (pfv-r3-tor-stor → pfv-core-sw01)"
|
|
echo ""
|
|
echo "All logs in: $LOG_DIR/"
|