diff --git a/proxmox/lxc171-claude-agent/README.md b/proxmox/lxc171-claude-agent/README.md index 3e79689..a9c3e6e 100644 --- a/proxmox/lxc171-claude-agent/README.md +++ b/proxmox/lxc171-claude-agent/README.md @@ -3,7 +3,7 @@ **Director:** `proxmox/lxc171-claude-agent/` **VMID:** 171 **IP:** 10.0.20.171 (intern) | 100.95.55.51 (Tailscale) -**Host Proxmox:** pveelite (10.0.20.202) +**Host Proxmox:** pvemini (10.0.20.201) **Rol:** Mediu de dezvoltare izolat pentru Claude Code --- @@ -16,6 +16,7 @@ | `scripts/work.sh` | Meniu interactiv pentru workflow complet | | `scripts/new-task.sh` | Creează branch nou pentru task | | `scripts/finish-task.sh` | Finalizează task (commit + push) | +| `scripts/reap-orphans.sh` | Curăță procese vscode-server orfane + sesiuni zombie (cron, anti-OOM) | --- @@ -27,11 +28,11 @@ | Hostname | claude-agent | | IP intern | 10.0.20.171 | | IP Tailscale | 100.95.55.51 | -| Host Proxmox | pveelite (10.0.20.202) | +| Host Proxmox | pvemini (10.0.20.201) | | User | claude | | Storage | local-zfs (32GB) | -| RAM | 4GB | -| CPU | 2 cores | +| RAM | 16GB (swap 8GB — vezi nota OOM mai jos) | +| CPU | 4 cores | ## Componente Instalate @@ -176,6 +177,84 @@ git push -u origin feature/nume-feature # Creează Pull Request în Gitea ``` +## Memorie & OOM (reaping orfane + zram) + +### Context (incident 2026-06-24) + +Containerul a generat OOM-uri repetate, izolate în cgroup-ul `/lxc/171` (NU la nivel de +host — host-ul avea memorie liberă). Cauzele combinate: + +1. **Swap fictiv:** LXC 171 are `memory: 16384` + `swap: 8192`, dar host-ul **pvemini nu + avea swap deloc** → swap-ul containerului nu era backed de nimic. La atingerea celor + 16GB se intra direct în OOM-kill, fără să poată face swap-out. Victimele tipice erau + procese mici cu `oom_score_adj` ridicat (`systemd`, `dbus-daemon`, `at-spi-bus-laun`, + `sd-pam`) din sesiunile `user@1000`. +2. **Acumulare de fond:** forks `~/.vscode-server` (servere VS Code Remote-SSH) rămase + orfane după ce conexiunea SSH moare — reparentate la init (PPID=1), vechi de zile — plus + sesiuni `systemd-logind` blocate în stare `closing`. Un spike ocazional de + Chromium/Playwright împingea totalul peste 16GB → OOM. + +### Fix 1 — zram swap pe host (pvemini) + +Root-ul pvemini e pe **ZFS**, deci **NU** se folosește swapfile (risc de deadlock). În loc, +zram (swap comprimat în RAM): + +```bash +# pe pvemini (host) +apt install -y zram-tools +cat > /etc/default/zramswap <<'EOF' +ALGO=zstd +SIZE=8192 +PRIORITY=100 +EOF +systemctl restart zramswap.service # enabled persistent la boot +zramctl; swapon --show # verificare: /dev/zram0 8G zstd +``` + +Acum cei 8GB `swap` din configul LXC 171 au backing real → vârfurile tranzitorii fac +swap-out în loc de OOM-kill instant. + +### Fix 2 — reaping orfane (`scripts/reap-orphans.sh`, cron în 171) + +Script conservator care rulează ca root **în interiorul containerului**, la fiecare 6 ore: + +- omoară forks `~/.vscode-server` reparentate la init (PPID=1) **și** mai vechi de 24h + (conexiune SSH moartă); +- termină sesiunile `logind` în stare `closing` cu leader mort (`leader=0`). + +Exclude explicit daemonul de bază `/usr/lib/code-server` (lansat de ttyd) și procesele +Claude Code (pot fi task-uri lungi legitime). Log în `/var/log/reap-orphans.log`. + +```bash +# instalare în container (din host) +pct push 171 reap-orphans.sh /usr/local/sbin/reap-orphans.sh # sau deploy manual +pct exec 171 -- chmod +x /usr/local/sbin/reap-orphans.sh +# cron: /etc/cron.d/reap-orphans -> 0 */6 * * * root /usr/local/sbin/reap-orphans.sh + +# rulare manuală + verificare +pct exec 171 -- /usr/local/sbin/reap-orphans.sh +pct exec 171 -- tail /var/log/reap-orphans.log +``` + +> ⚠️ **NU** folosi `loginctl terminate-session` pe sesiuni `closing` care au `leader != 0` — +> acelea pot fi sesiuni VS Code remote active de lucru. Doar zombiile cu `leader=0` sunt +> sigure (asta verifică și scriptul automat). + +### Diagnostic rapid OOM + +```bash +# pe host: confirmă că OOM e în cgroup-ul containerului, nu pe host +ssh root@10.0.20.201 "journalctl -k --since '1 hour ago' | grep -iE 'oom_memcg|Killed process'" +cat /sys/fs/cgroup/lxc/171/memory.events # oom / oom_kill counters +cat /sys/fs/cgroup/lxc/171/memory.current # consum curent vs memory.max (16G) + +# în container: top consumatori + orfani + sesiuni +pct exec 171 -- bash -c "ps -eo pid,ppid,etimes,rss,comm --sort=-rss | head -20" +pct exec 171 -- loginctl list-sessions +``` + +--- + ## Troubleshooting ### Claude Code nu pornește @@ -367,4 +446,4 @@ ssh claude@ "chmod +x /workspace/*.sh && ln -sf /workspace/start-agent.sh ~/ --- **Data setup:** 2025-12-31 -**Ultima actualizare:** 2026-01-27 +**Ultima actualizare:** 2026-06-24 (zram swap pvemini + reap-orphans cron, anti-OOM) diff --git a/proxmox/lxc171-claude-agent/scripts/reap-orphans.sh b/proxmox/lxc171-claude-agent/scripts/reap-orphans.sh new file mode 100755 index 0000000..7c021dc --- /dev/null +++ b/proxmox/lxc171-claude-agent/scripts/reap-orphans.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# reap-orphans.sh — curăță procese VS Code Remote-SSH orfane + sesiuni logind zombie. +# +# Context: LXC 171 (claude-agent) acumulează forks ~/.vscode-server reparentate la init +# (conexiunea SSH a murit, dar serverul Remote-SSH rămâne) + sesiuni logind blocate în +# stare 'closing'. Acestea umplu treptat cgroup-ul de 16GB; împreună cu spike-uri +# Chromium/Playwright declanșau OOM-kill în /lxc/171 (incident 2026-06-24). +# zram-ul de pe host absoarbe vârfurile, dar nu oprește acumularea de fond — asta o face. +# +# Rulează din cron ca root, ÎN INTERIORUL containerului. Conservator + idempotent. +# +# NU atinge: +# - daemonul de bază /usr/lib/code-server (PPID=1 dar legitim, lansat de ttyd) +# - procese Claude Code / node de workload (pot fi task-uri lungi legitime) +# - sesiuni active sau procese cu părinte viu (arborele SSH e intact) +set -uo pipefail + +THRESH=${REAP_AGE_SECONDS:-86400} # reape doar orfane mai vechi de 24h +LOG=${REAP_LOG:-/var/log/reap-orphans.log} +killed=0 + +log() { echo "[$(date '+%F %T')] $*" >> "$LOG"; } + +# 1) Forks ~/.vscode-server reparentate la init (PPID=1) și vechi => conexiune SSH moartă +while read -r pid ppid etimes args; do + [ "$ppid" = "1" ] || continue + [ "$etimes" -ge "$THRESH" ] || continue + case "$args" in + */usr/lib/code-server*) continue ;; # daemon de bază — păstrează + */.vscode-server/*) : ;; # doar serverele Remote-SSH per-conexiune + *) continue ;; + esac + if kill "$pid" 2>/dev/null; then + log "killed orphan vscode pid=$pid age=${etimes}s :: ${args:0:90}" + killed=$((killed + 1)) + fi +done < <(ps -eo pid,ppid,etimes,args --no-headers) + +# 2) Sesiuni logind 'closing' cu leader mort (leader=0) => zombi siguri de terminat +for s in $(loginctl list-sessions --no-legend 2>/dev/null | awk '{print $1}'); do + st=$(loginctl show-session "$s" -p State --value 2>/dev/null) + lp=$(loginctl show-session "$s" -p Leader --value 2>/dev/null) + if [ "$st" = "closing" ] && [ "$lp" = "0" ]; then + if loginctl terminate-session "$s" 2>/dev/null; then + log "terminated zombie session $s" + killed=$((killed + 1)) + fi + fi +done + +[ "$killed" -gt 0 ] && log "reaped $killed item(s)" +exit 0