scripts/container-gc.sh
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bash
# 兜底回收异常容器(技术方案 §6 部署):宿主机 cron / systemd timer 周期执行。
#
# 职责:
# 1. 清理已退出(exited/dead)的 mao-user-* 容器(Registry 正常回收只 stop 不 rm,
# 异常退出残骸由本脚本兜底,挂载数据在宿主机目录不受影响);
# 2. 清理无容器对接的残留 socket 文件(容器被 rm 后 socket 已无意义)。
set -euo pipefail
CHANNEL_DIR="${MAO_CONTAINER_CHANNEL_DIR:-/opt/mao-data/runtime/channels}"
if ! command -v docker >/dev/null 2>&1; then
echo "container-gc: docker 不可用,跳过" >&2
exit 0
fi
# 1. 退出态容器清理
EXITED="$(docker ps -a --filter 'name=mao-user-' --filter 'status=exited' --filter 'status=dead' -q || true)"
if [ -n "$EXITED" ]; then
# shellcheck disable=SC2086
docker rm $EXITED >/dev/null
echo "container-gc: removed exited containers: $(echo "$EXITED" | wc -l | tr -d ' ')"
fi
# 1.5 旧命名孤儿容器清理(2026-11-07 复合容器方案 §8):容器键改为 (userId, agentId) 后,
# 升级前遗留的 mao-user-{userId}(无 -agent- 段)在 Registry 重启后脱离管控,运行中的
# 不会被上面的 exited 规则回收,会一直占资源。该命名模式升级后必为孤儿(新容器恒含
# -agent- 段),按名字精确匹配清理无误伤。
LEGACY_RUNNING="$(docker ps --filter 'name=mao-user-' --format '{{.Names}}' | grep -E '^mao-user-[0-9]+$' || true)"
if [ -n "$LEGACY_RUNNING" ]; then
# shellcheck disable=SC2086
docker stop -t 10 $LEGACY_RUNNING >/dev/null
docker rm $LEGACY_RUNNING >/dev/null
echo "container-gc: removed legacy orphan containers: $(echo "$LEGACY_RUNNING" | wc -l | tr -d ' ')"
fi
# 2. 残留 socket 清理:目录下没有 running 状态容器对应 socket 时移除 runtime.sock
# 2026-11-07 起目录为 {channelDir}/{userId}/{agentId}/ 两级(复合容器键),兼容旧一级布局
if [ -d "$CHANNEL_DIR" ]; then
for sock in "$CHANNEL_DIR"/*/runtime.sock "$CHANNEL_DIR"/*/*/runtime.sock; do
[ -S "$sock" ] || continue
# socket 有活跃连接(lsof 可见)则跳过;lsof 缺失时保守跳过清理
if command -v lsof >/dev/null 2>&1; then
if lsof "$sock" >/dev/null 2>&1; then
continue
fi
rm -f "$sock"
fi
done
fi
echo "container-gc: done"