From 232409e7980feae850845981b3e5293dcadefc60 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Tue, 26 May 2026 08:06:15 +0000 Subject: [PATCH] =?UTF-8?q?scripts:=20per-VM=20I/O=20cap=20script=20?= =?UTF-8?q?=E2=80=94=20apply-mbps-caps.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Idempotent qm-set script for the per-VM I/O caps on the PVE host's sdc thin pool (2026-05-26 session, beads code-9v2j). Caps protect each Linux VM's share of sdc so a runaway workload (e.g. the 2026-05-23/26 alloy IO storm — memory id=2726) cannot saturate the disk for everyone. Was sitting in /tmp on PVE — moving the source under version control and installing to /usr/local/bin/ alongside the other PVE operational scripts (nfs-mirror, daily-backup, offsite-sync-backup; pattern from memory id=609). Survives PVE host reboots; safe to re-run on any node rebuild to restore the caps. VMIDs covered (Linux only — pfSense 101 and Windows10 300 skipped): 102 devvm 60/60 103 home-assistant 40/40 200 k8s-master 100/60 201 k8s-node1 150/120 202 k8s-node2 150/120 203 k8s-node3 150/120 204 k8s-node4 150/120 220 docker-registry 40/40 --- scripts/apply-mbps-caps.sh | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 scripts/apply-mbps-caps.sh diff --git a/scripts/apply-mbps-caps.sh b/scripts/apply-mbps-caps.sh new file mode 100755 index 00000000..9913f94e --- /dev/null +++ b/scripts/apply-mbps-caps.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Apply per-VM I/O caps via qm set on the PVE host. +# - Reads each VM's current boot-disk options +# - Appends mbps_rd=,mbps_wr= +# - Re-applies via qm set (live, no reboot needed) +# - Verifies with qm config | grep mbps +set -euo pipefail + +# vmid:disk_slot:mbps_rd:mbps_wr (Linux VMs only — skipping 101 pfsense BSD, 300 Windows) +TARGETS=( + "102:scsi0:60:60" # devvm + "103:sata0:40:40" # home-assistant + "200:scsi0:100:60" # k8s-master (alloy storm origin — firmest clip) + "201:scsi1:150:120" # k8s-node1 (GPU + many CSI disks; boots from scsi1) + "202:scsi0:150:120" # k8s-node2 + "203:scsi0:150:120" # k8s-node3 + "204:scsi0:150:120" # k8s-node4 (currently doing write recovery) + "220:scsi0:40:40" # docker-registry +) + +for spec in "${TARGETS[@]}"; do + IFS=: read -r vmid slot rd wr <<<"$spec" + printf '\n=== VMID %s slot=%s rd=%s MB/s wr=%s MB/s ===\n' "$vmid" "$slot" "$rd" "$wr" + + current=$(qm config "$vmid" | awk -v s="$slot:" '$1==s {sub(/^[^ ]+ /, ""); print; exit}') + if [[ -z "$current" ]]; then + echo " ERROR: could not read $slot for vmid $vmid — skipping" + continue + fi + + # Strip any existing mbps_rd / mbps_wr from the current string (idempotent) + cleaned=$(echo "$current" | sed -E 's/,mbps_rd=[0-9]+//g; s/,mbps_wr=[0-9]+//g') + newvalue="${cleaned},mbps_rd=${rd},mbps_wr=${wr}" + + echo " before: $current" + echo " after: $newvalue" + + qm set "$vmid" "--$slot" "$newvalue" + echo " verify: $(qm config "$vmid" | awk -v s="$slot:" '$1==s {print; exit}')" +done + +echo +echo "=== Final verification — mbps on all targets ===" +for spec in "${TARGETS[@]}"; do + IFS=: read -r vmid slot _ _ <<<"$spec" + echo "vmid $vmid: $(qm config "$vmid" | awk -v s="$slot:" '$1==s {print; exit}')" +done