用户说「清理一下」。实测 /opt/agentmail 累计 1.3GB: - 网关旧二进制 33 份 × 24MB ≈ 790MB(每次 redeploy 留一份,从 09-05 起没清过) - 插件快照:opencode 6 份 339MB、dsh 7 份 195MB、zcode 16 份、pi 9 份 - /tmp 里的部署前数据库备份 10 份(tmpfs,占内存) 新增 `deploy/prune-deploy-artifacts.sh`(可复跑,不手敲 rm),两条硬规矩: ① **保留回滚窗口**:网关留最新 3 份、每个插件留最新 3 份快照,`current` 永远保留 (发布纪律要求有回滚目标,所以不是全清); ② ★ **绝不删正在使用的快照**:扫 /proc 的 cmdline 与 cwd,命中就跳过。 执行结果:**/opt/agentmail 1305MB → 302MB(释放 1003MB)**,7 个服务全部 active, `current` 指向未变,`check-deploy-drift` 仍报「四个宿主都在跑当前代码」。 ## 过程中的一处自纠 "在用"闸门第一版有**自我匹配**缺陷:我的测试命令把路径写在命令行里,于是扫 /proc 时 扫到了自己 ⇒ 对不存在的路径也判"在用"(与 `pkill -f` 杀掉自己那条命令同一类)。 已改为排除本进程及其祖先链,并**换正确方式重测**(路径从文件读、不进 cmdline): 在用快照判"在用" ✓、不存在的路径不误判 ✓ —— 判据两侧都验过才敢执行删除。 文档:docs/DEV-TOOLING.md 记了用法与那两条规矩。
108 lines
4.4 KiB
Bash
Executable File
108 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 清理部署残留:旧网关二进制、旧插件快照、/tmp 里的部署前数据库备份。
|
||
#
|
||
# # 为什么要脚本而不是手敲 rm
|
||
#
|
||
# 这些东西**天天在长**:每次 redeploy 留一个 24MB 的网关二进制、每个插件留一份快照
|
||
# (opencode/dsh 的快照含 node_modules,一份几十 MB)。2026-09-14 实测:
|
||
# /opt/agentmail 1.3G,其中旧二进制 ~790MB、插件快照 ~540MB。
|
||
# 手敲的清理不可复现、下次还得再想一遍,而且很容易漏掉"这一份还在被用"的判断。
|
||
#
|
||
# # 两条硬规矩
|
||
#
|
||
# ① **保留回滚窗口**:网关二进制留最新 N 份、每个插件留最新 N 份快照
|
||
# (`current` 永远保留)。发布纪律要求"有回滚目标",所以不是全清。
|
||
# ② ★ **绝不删正在使用的快照**:任何进程的 cmdline 里出现过的快照路径一律跳过。
|
||
# 删掉它 = 进程一重启就找不到自己的代码(而这类事故此前真发生过)。
|
||
#
|
||
# 用法:
|
||
# bash deploy/prune-deploy-artifacts.sh # 干跑,只报告
|
||
# bash deploy/prune-deploy-artifacts.sh --apply # 真删
|
||
# KEEP_GATEWAY=2 KEEP_SNAPSHOTS=2 bash ... --apply # 调保留窗口
|
||
set -uo pipefail
|
||
|
||
ROOT="${AGENTMAIL_ROOT:-/opt/agentmail}"
|
||
KEEP_GATEWAY="${KEEP_GATEWAY:-3}"
|
||
KEEP_SNAPSHOTS="${KEEP_SNAPSHOTS:-3}"
|
||
KEEP_TMP_DB="${KEEP_TMP_DB:-2}"
|
||
APPLY=0
|
||
[ "${1:-}" = "--apply" ] && APPLY=1
|
||
|
||
freed_kb=0
|
||
removed=0
|
||
skipped=0
|
||
|
||
report() { printf ' %s %s\n' "$1" "$2"; }
|
||
|
||
# 收集所有"正在被进程使用"的路径(cmdline + cwd),用来做安全闸。
|
||
# 自己这条命令的 cmdline 里就可能含这个路径(例如调用方把路径写在命令行里),
|
||
# 于是"检查在用"会永远为真 —— 与 `pkill -f` 杀掉自己那条命令是同一个坑。
|
||
# 所以先把本进程及其祖先链排除掉。
|
||
_SELF_CHAIN=" "
|
||
_p=$$
|
||
while [ -n "$_p" ] && [ "$_p" != "1" ] && [ "$_p" != "0" ]; do
|
||
_SELF_CHAIN="$_SELF_CHAIN$_p "
|
||
_p=$(awk '{print $4}' "/proc/$_p/stat" 2>/dev/null || echo "")
|
||
done
|
||
|
||
in_use() {
|
||
local needle="$1" p pid
|
||
for p in /proc/[0-9]*; do
|
||
pid=${p#/proc/}
|
||
[ -r "$p/cmdline" ] || continue
|
||
case "$_SELF_CHAIN" in *" $pid "*) continue;; esac
|
||
if tr '\0' ' ' <"$p/cmdline" 2>/dev/null | grep -qF "$needle"; then return 0; fi
|
||
local cwd; cwd=$(readlink "$p/cwd" 2>/dev/null || true)
|
||
case "$cwd" in *"$needle"*) return 0;; esac
|
||
done
|
||
return 1
|
||
}
|
||
|
||
del() {
|
||
local path="$1"
|
||
local kb; kb=$(du -sk "$path" 2>/dev/null | cut -f1); kb=${kb:-0}
|
||
if [ "$APPLY" = "1" ]; then rm -rf "$path"; fi
|
||
freed_kb=$((freed_kb + kb)); removed=$((removed + 1))
|
||
report " $([ "$APPLY" = 1 ] && echo 删除 || echo 待删)" "$path($((kb/1024))MB)"
|
||
}
|
||
|
||
echo "==> 网关旧二进制(保留最新 $KEEP_GATEWAY 份)"
|
||
mapfile -t bins < <(ls -1t "$ROOT"/agentmail-gateway.bak-* 2>/dev/null)
|
||
for i in "${!bins[@]}"; do
|
||
[ "$i" -lt "$KEEP_GATEWAY" ] && { report " 保留" "${bins[$i]}"; continue; }
|
||
if in_use "${bins[$i]}"; then report " ★跳过(正在被使用)" "${bins[$i]}"; skipped=$((skipped+1)); continue; fi
|
||
del "${bins[$i]}"
|
||
done
|
||
|
||
echo "==> 插件快照(每个插件保留最新 $KEEP_SNAPSHOTS 份,current 永远保留)"
|
||
for plug in "$ROOT"/plugins/*/; do
|
||
[ -d "$plug" ] || continue
|
||
name=$(basename "$plug")
|
||
mapfile -t snaps < <(ls -1dt "$plug"*/ 2>/dev/null | sed 's:/$::')
|
||
for i in "${!snaps[@]}"; do
|
||
s="${snaps[$i]}"
|
||
[ "$(basename "$s")" = "current" ] && continue
|
||
# current 指向的那一份也必须保留
|
||
cur=$(readlink -f "$plug/current" 2>/dev/null || true)
|
||
[ "$cur" = "$(readlink -f "$s")" ] && { report " 保留" "$s(current 指向)"; continue; }
|
||
if [ "$i" -lt "$KEEP_SNAPSHOTS" ]; then report " 保留" "$s"; continue; fi
|
||
if in_use "$s"; then report " ★跳过(正在被使用)" "$s"; skipped=$((skipped+1)); continue; fi
|
||
del "$s"
|
||
done
|
||
done
|
||
|
||
echo "==> /tmp 的部署前数据库备份(保留最新 $KEEP_TMP_DB 份)"
|
||
mapfile -t dbs < <(ls -1t /tmp/agentmail-pre-deploy-*.db 2>/dev/null)
|
||
for i in "${!dbs[@]}"; do
|
||
[ "$i" -lt "$KEEP_TMP_DB" ] && { report " 保留" "${dbs[$i]}"; continue; }
|
||
del "${dbs[$i]}"
|
||
done
|
||
|
||
echo
|
||
if [ "$APPLY" = "1" ]; then
|
||
echo " 已删除 $removed 项,释放约 $((freed_kb/1024))MB;跳过(在用)$skipped 项"
|
||
else
|
||
echo " 干跑:将删除 $removed 项,可释放约 $((freed_kb/1024))MB;跳过(在用)$skipped 项"
|
||
echo " 加 --apply 执行。"
|
||
fi
|