feat: add docker image cleanup script for benchmark images
This commit is contained in:
parent
ba2b5c1772
commit
0d7608c867
205
bash/cleanup_docker_images.sh
Executable file
205
bash/cleanup_docker_images.sh
Executable file
@ -0,0 +1,205 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# 清理评测后遗留的 Docker 镜像
|
||||
#
|
||||
# 用途:
|
||||
# SWE-bench、Terminal-Bench 等 benchmark 会拉取大量 Docker 镜像,
|
||||
# 评测完后很占磁盘。本脚本按命名空间批量删除这些镜像。
|
||||
#
|
||||
# 用法:
|
||||
# # 默认只预览,不会真的删除
|
||||
# bash bash/cleanup_docker_images.sh --preset swe
|
||||
#
|
||||
# # 确认删除
|
||||
# bash bash/cleanup_docker_images.sh --preset swe --yes
|
||||
#
|
||||
# # 删除 terminal-bench 镜像
|
||||
# bash bash/cleanup_docker_images.sh --preset terminal --yes
|
||||
#
|
||||
# # 删除所有常见 benchmark 镜像
|
||||
# bash bash/cleanup_docker_images.sh --preset all-bench --yes
|
||||
#
|
||||
# # 自定义匹配规则(支持多个,逗号分隔)
|
||||
# bash bash/cleanup_docker_images.sh --pattern 'swebench/*,alexgshaw/*' --yes
|
||||
#
|
||||
# # 同时清理 dangling 镜像和未使用卷
|
||||
# bash bash/cleanup_docker_images.sh --preset all-bench --yes --prune
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# 预设的 benchmark 镜像命名空间
|
||||
PRESET_SWE=('swebench/*')
|
||||
PRESET_TERMINAL=('alexgshaw/*')
|
||||
PRESET_ALL_BENCH=()
|
||||
PRESET_ALL_BENCH+=("${PRESET_SWE[@]}")
|
||||
PRESET_ALL_BENCH+=("${PRESET_TERMINAL[@]}")
|
||||
|
||||
DRY_RUN=1
|
||||
PRESET=''
|
||||
PATTERNS=''
|
||||
PRUNE=0
|
||||
|
||||
usage() {
|
||||
sed -n '7,34p' "$0"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--preset)
|
||||
PRESET="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--pattern)
|
||||
PATTERNS="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--yes)
|
||||
DRY_RUN=0
|
||||
shift
|
||||
;;
|
||||
--prune)
|
||||
PRUNE=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 解析出要删除的 pattern 列表
|
||||
PATTERN_LIST=()
|
||||
if [[ -n "$PRESET" ]]; then
|
||||
case "$PRESET" in
|
||||
swe)
|
||||
PATTERN_LIST+=("${PRESET_SWE[@]}")
|
||||
;;
|
||||
terminal)
|
||||
PATTERN_LIST+=("${PRESET_TERMINAL[@]}")
|
||||
;;
|
||||
all-bench)
|
||||
PATTERN_LIST+=("${PRESET_ALL_BENCH[@]}")
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown preset '$PRESET'. Available: swe, terminal, all-bench"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [[ -n "$PATTERNS" ]]; then
|
||||
IFS=',' read -ra CUSTOM_PATTERNS <<< "$PATTERNS"
|
||||
PATTERN_LIST+=("${CUSTOM_PATTERNS[@]}")
|
||||
fi
|
||||
|
||||
if [[ ${#PATTERN_LIST[@]} -eq 0 ]]; then
|
||||
echo "ERROR: 请指定 --preset 或 --pattern"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "============================================================"
|
||||
echo "清理目标 pattern:"
|
||||
for p in "${PATTERN_LIST[@]}"; do
|
||||
echo " - $p"
|
||||
done
|
||||
echo "============================================================"
|
||||
|
||||
# 收集所有匹配且没有被运行中容器使用的镜像 ID
|
||||
MATCHED_IMAGES=()
|
||||
RUNNING_IMAGES=$(docker ps --format '{{.Image}}' | sort -u)
|
||||
|
||||
for pattern in "${PATTERN_LIST[@]}"; do
|
||||
while IFS= read -r img; do
|
||||
[[ -z "$img" ]] && continue
|
||||
repo="${img%:*}"
|
||||
# 检查是否有运行中容器在使用该镜像
|
||||
if echo "$RUNNING_IMAGES" | grep -qx "$img"; then
|
||||
echo "SKIP (running container uses): $img"
|
||||
continue
|
||||
fi
|
||||
# 避免重复
|
||||
if [[ ! " ${MATCHED_IMAGES[*]} " =~ " ${img} " ]]; then
|
||||
MATCHED_IMAGES+=("$img")
|
||||
fi
|
||||
done < <(docker images --format '{{.Repository}}:{{.Tag}}' --filter=reference="$pattern")
|
||||
done
|
||||
|
||||
if [[ ${#MATCHED_IMAGES[@]} -eq 0 ]]; then
|
||||
echo "没有匹配到可删除的镜像"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "匹配到 ${#MATCHED_IMAGES[@]} 个镜像:"
|
||||
for img in "${MATCHED_IMAGES[@]}"; do
|
||||
size=$(docker images --format '{{.Size}}' "$img" | head -1)
|
||||
echo " $img ($size)"
|
||||
done
|
||||
|
||||
# 计算标记总大小(注意共享层会被多次计算,仅作参考)
|
||||
TOTAL_SIZE_GB=$(docker images --format '{{.Size}} {{.Repository}}:{{.Tag}}' \
|
||||
| awk -v img_list="${MATCHED_IMAGES[*]}" '
|
||||
BEGIN {
|
||||
n = split(img_list, imgs, " ");
|
||||
for (i=1; i<=n; i++) target[imgs[i]] = 1;
|
||||
}
|
||||
{
|
||||
size = $1;
|
||||
img = $2;
|
||||
if (!(img in target)) next;
|
||||
unit = substr(size, length(size)-1);
|
||||
val = substr(size, 1, length(size)-2);
|
||||
if (unit == "GB") sum += val * 1024 * 1024 * 1024;
|
||||
else if (unit == "MB") sum += val * 1024 * 1024;
|
||||
else if (unit == "kB") sum += val * 1024;
|
||||
}
|
||||
END {
|
||||
printf "%.2f", sum / 1024 / 1024 / 1024;
|
||||
}')
|
||||
|
||||
echo ""
|
||||
echo "标记总大小约: ${TOTAL_SIZE_GB} GB(含共享层,实际释放会小于该值)"
|
||||
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
echo ""
|
||||
echo "当前是预览模式,不会删除。如需删除请加上 --yes"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "开始删除..."
|
||||
FAILED=0
|
||||
for img in "${MATCHED_IMAGES[@]}"; do
|
||||
if docker rmi -f "$img" >/dev/null 2>&1; then
|
||||
echo " DELETED $img"
|
||||
else
|
||||
echo " FAILED $img"
|
||||
FAILED=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$PRUNE" -eq 1 ]]; then
|
||||
echo ""
|
||||
echo "清理 dangling 镜像和未使用卷..."
|
||||
docker image prune -f
|
||||
docker volume prune -f
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
if [[ "$FAILED" -eq 0 ]]; then
|
||||
echo "清理完成"
|
||||
else
|
||||
echo "部分镜像删除失败"
|
||||
fi
|
||||
echo "============================================================"
|
||||
Loading…
x
Reference in New Issue
Block a user