Cursor实战案例-运维监控-101-全自动镜像清理:用Shell脚本定时检测并清除Docker无用挂起镜像与策略数据,把Base URL改到TaoToken
发布时间:2026/10/8 6:29:28 锦皓数字建站

1. 磁盘被 Docker 悄悄吃满运维监控里最容易被忽略的定时清理场景Docker 镜像清理这件事说大不大说小也不小。它属于那种平时没人管、出事就是大事的运维监控盲区。我见过太多服务器跑着跑着df -h一看根分区 95%登上去docker images一列满屏的none:none悬挂镜像加上一堆Exited状态的僵尸容器和没人认领的匿名数据卷几十 GB 就这么无声无息地没了。更麻烦的是这些垃圾不是一次性堆出来的而是每次 CI/CD 构建、每次策略镜像更新、每次调试完忘记--rm一点一点攒出来的。这个场景特别适合用 Cursor 来辅助写 Shell 脚本。原因很简单清理逻辑本身不复杂但安全边界特别多——你得判断 Docker daemon 是否存活、得按创建时间过滤防止误删刚拉取的镜像、得对包含prod、db、postgres这类关键字的卷做白名单保护。这些判断用自然语言描述给 Cursor让它生成带注释的 Bash 代码比手敲快得多而且不容易漏掉边界条件。本文要交付的就是一套可以直接复制到生产环境的方案一个带防误删兜底的docker_cleanup.sh脚本、一份crontab定时配置、执行前后的docker images对比验证方法以及如何把 API 通道统一改到 TaoToken 的 Base URL 上让 Cursor 和后续的自动化脚本共用一套 Key。适合谁看如果你手里有跑 Docker 的 Linux 服务器不管是量化策略节点、CI 构建机还是个人开发机只要磁盘会随着时间推移被镜像和卷蚕食这篇就能直接用。不需要你是 Shell 高手脚本里的每一段我都会解释清楚它在防什么。2. 用 Cursor 生成清理脚本前先把 TaoToken 的 Base URL 配好在动手写脚本之前有个前置动作值得先做把 Cursor 的模型请求通道统一到 TaoToken。这不是必须的但如果你后续想让 Cursor 帮你批量生成运维脚本、或者把清理逻辑接入到自动化 Agent 里统一 Key 和 API 通道会省掉很多切换成本。TaoToken 的官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址后面不加 UTM 参数。具体怎么改Cursor 本身支持自定义 OpenAI 兼容的 Base URL。你可以在 Cursor 的设置里找到模型配置把 Base URL 填成https://taotoken.net/api然后填入你在 TaoToken 控制台生成的 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Key 管理页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你用的是 Claude Code 或者类似的编码 AgentTaoToken 也提供了对应的接入文档地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。这里有个细节要注意改 Base URL 的时候Model ID 也要跟着填对。比如你想用 Claude 系列Model ID 就写claude-sonnet-4-20250514这种完整标识想用 GPT 系列就写对应的模型名。三件套——Base URL、API Key、Model ID——缺一不可少一个就会报 401 或者 model not found。我试过只改 Base URL 忘了改 Model ID结果 Cursor 一直提示reading choices失败排查了半天才发现是模型名对不上。配好之后你可以在 Cursor 里直接对话测试一下问它“帮我写一个检测 Docker daemon 是否存活的 Shell 函数”如果正常返回代码说明通道通了。这一步做完后面让 Cursor 生成完整的清理脚本就顺理成章了。如果你只是想先验证模型能不能通可以到 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 这个模型对话页面直接发一条消息试试不用配任何东西。3. 可复制的 docker_cleanup.sh 与 crontab 定时配置现在进入核心部分。下面这个脚本是我在实际环境里跑过的版本逻辑上做了三层防护第一层检查 Docker daemon 是否响应第二层按时间过滤只清理超过 48 小时的悬挂镜像第三层对匿名卷做关键字白名单保护。你可以直接把这段代码保存为docker_cleanup.sh。#!/bin/bash # # 文件名: docker_cleanup.sh # 用途: 定时清理 Docker 悬挂镜像、退出容器与无主匿名卷 # 运行: chmod x docker_cleanup.sh ./docker_cleanup.sh # LOG_FILE/var/log/docker_cleanup.log CURRENT_TIME$(date %Y-%m-%d %H:%M:%S) RETENTION_HOURS48 log_message() { local level$1 local msg$2 echo [$CURRENT_TIME] [$level] - $msg | tee -a $LOG_FILE } log_message INFO Docker 自动清理开始 # 安全卡点一Docker daemon 存活检测 if ! docker info /dev/null 21; then log_message ERROR Docker daemon 未响应清理终止。 exit 1 fi DISK_BEFORE$(df -h / | awk NR2 {print $5} | sed s/%//) log_message INFO 清理前根分区占用: ${DISK_BEFORE}% # 步骤一清理退出超过 72 小时的容器 log_message INFO 检查已退出容器... exited$(docker ps -a -q -f statusexited) if [ -n $exited ]; then result$(docker container prune -f --filter until72h | grep Total reclaimed space || echo Reclaimed: 0B) log_message INFO 容器清理结果: $result else log_message INFO 无退出容器需要清理。 fi # 步骤二清理悬挂镜像none:none仅清理超过 48 小时的 log_message INFO 检查悬挂镜像... dangling$(docker images -f danglingtrue -q) if [ -n $dangling ]; then result$(docker image prune -f --filter until${RETENTION_HOURS}h | grep Total reclaimed space || echo Reclaimed: 0B) log_message INFO 镜像清理结果: $result else log_message INFO 无悬挂镜像需要清理。 fi # 步骤三清理无主匿名卷带关键字白名单保护 log_message INFO 检查无主匿名卷... unattached$(docker volume ls -q -f danglingtrue) if [ -n $unattached ]; then safe_to_delete for vol in $unattached; do if echo $vol | grep -E -q prod|database|db|postgres|mysql|redis; then log_message WARNING 敏感卷 $vol 已跳过保护。 else safe_to_delete$safe_to_delete $vol fi done if [ -n $safe_to_delete ]; then docker volume rm $safe_to_delete /dev/null log_message INFO 匿名卷清理完成。 else log_message INFO 无符合安全标准的匿名卷。 fi else log_message INFO 无无主匿名卷。 fi DISK_AFTER$(df -h / | awk NR2 {print $5} | sed s/%//) RECLAIMED$((DISK_BEFORE - DISK_AFTER)) log_message INFO 清理后根分区占用: ${DISK_AFTER}% log_message INFO 本次释放: ${RECLAIMED}% log_message INFO Docker 自动清理结束 保存后赋予执行权限chmod x docker_cleanup.sh。然后配置 crontab输入crontab -e在末尾追加一行0 3 * * * /bin/bash /opt/scripts/docker_cleanup.sh /var/log/docker_cleanup_cron.err 21这行配置的意思是每天凌晨 3 点整执行脚本标准输出和错误都追加到/var/log/docker_cleanup_cron.err。为什么选凌晨 3 点因为清理大体积卷时会触发大量磁盘 I/O如果放在业务高峰期可能拖慢数据库写入。凌晨这个时间段大部分服务负载最低出问题也有时间排查。如果你用的是 Cursor 来生成这段脚本可以在对话里直接说“帮我写一个带 daemon 存活检测和卷白名单保护的 Docker 清理脚本日志写到 /var/log”它会给你一个八九不离十的版本你再根据实际路径微调就行。注意脚本里的LOG_FILE路径如果你不是 root 运行/var/log可能没写权限改成./logs/docker_cleanup.log更稳妥。4. 验证清理效果docker images 对比与日志确认脚本写完了别急着挂 crontab先手动跑一次看结果。执行./docker_cleanup.sh控制台会输出类似下面的内容[2026-06-22 10:50:00] [INFO] - Docker 自动清理开始 [2026-06-22 10:50:00] [INFO] - 清理前根分区占用: 74% [2026-06-22 10:50:01] [INFO] - 容器清理结果: Total reclaimed space: 1.24GB [2026-06-22 10:50:02] [INFO] - 镜像清理结果: Total reclaimed space: 4.86GB [2026-06-22 10:50:02] [WARNING] - 敏感卷 prod_postgres_data 已跳过保护。 [2026-06-22 10:50:03] [INFO] - 匿名卷清理完成。 [2026-06-22 10:50:03] [INFO] - 清理后根分区占用: 72% [2026-06-22 10:50:03] [INFO] - 本次释放: 2%看到Total reclaimed space有具体数值说明确实清掉了东西。但光看日志还不够最好用docker images做前后对比。清理前先跑一次docker images -f danglingtrue记下输出的行数。清理后再跑一次同样的命令如果之前有悬挂镜像现在应该返回空。你还可以用docker system df看整体占用变化docker system df这个命令会列出 Images、Containers、Local Volumes、Build Cache 四类的总量、活跃数和可回收空间。清理后RECLAIMABLE那一列应该明显下降。如果RECLAIMABLE还是很大说明有活跃容器正在使用的镜像和卷那些不能动属于正常情况。日志确认方面除了控制台输出检查/var/log/docker_cleanup.log文件是否正常写入。如果文件不存在或者为空多半是权限问题。另外 crontab 执行时环境变量和手动执行不一样docker命令的路径可能找不到建议在脚本开头加上PATH/usr/local/bin:/usr/bin:/bin或者用绝对路径/usr/bin/docker。验证通过后你可以等第二天凌晨看 crontab 是否自动执行。用grep docker_cleanup /var/log/syslog可以查到 cron 的调度记录。如果看到CRON相关的行说明定时任务已经生效。5. 常见报错排查401、local proxy failed 与 reading choices清理脚本本身报错不多但和 Cursor、TaoToken 配合使用时有几个高频错误值得单独说。第一个是401 Unauthorized。这个通常出现在 Cursor 请求模型时原因就三个API Key 填错、Key 过期、或者 Base URL 写成了https://taotoken.net/api/带了多余的斜杠。检查方法很简单到 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 重新生成一个 Key然后确认 Base URL 是https://taotoken.net/api末尾不要加/v1或者/。Model ID 也要和 Key 的权限匹配有些 Key 只开了特定模型的权限填了没权限的模型也会报 401。第二个是local proxy failed。这个报错一般不是 TaoToken 的问题而是本地网络配置或者 Cursor 的代理设置冲突。如果你之前配过系统代理Cursor 可能会走代理导致连接失败。解决办法是在 Cursor 设置里把代理模式改成“直连”或者清空代理地址。另外检查一下防火墙有没有拦截对taotoken.net的 443 端口访问。第三个是reading choices失败。这个错误通常出现在流式响应解析阶段原因可能是 Model ID 写错了比如把claude-sonnet-4-20250514写成了claude-sonnet-4服务端返回的格式和客户端预期对不上。还有一种情况是请求超时网络抖动导致流中断。建议先在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 页面上直接测试同一个 Model ID如果那边正常说明是 Cursor 客户端配置问题。第四个是 OAuth 相关报错。如果你用的是 Claude Code 或者 Codex 这类需要 OAuth 认证的工具报OAuth token expired或者invalid_grant说明令牌过期了。到 TaoToken 控制台重新授权即可。控制台入口是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你用的是 CC Switch 或者 Cline MCP 这类工具配置的时候记得三件套都要填Base URL 填https://taotoken.net/apiAPI Key 填控制台生成的Model ID 填完整模型名。少填任何一个都会导致连接失败。还有一个和清理脚本相关的报错/var/log/docker_cleanup.log: Permission denied。这个前面提过普通用户对/var/log没写权限。两个解决办法要么用 root 跑 cron要么把LOG_FILE改成用户目录下的路径比如$HOME/docker_cleanup.log。我一般推荐后者避免脚本以 root 身份运行带来额外风险。6. 把清理逻辑接入 Coding Plan让运维脚本持续迭代脚本跑起来之后你可能会想加更多功能比如清理前先发个通知、按镜像标签前缀过滤、把清理结果写入 Prometheus 指标。这些迭代如果每次都手动改效率不高。比较顺手的做法是把这套运维脚本纳入 Coding Plan 的管理用统一的 API 通道让 Cursor 或者编码 Agent 持续帮你优化。TaoToken 的 Coding Plan 入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。它的思路是把长期编码任务和 Agent 调用统一到一个通道里你不需要每次重新配 Key 和 Base URL。对于运维脚本这种需要反复调试、逐步加防护逻辑的场景用 Coding Plan 可以保持上下文连续Cursor 能记住你之前写的过滤规则下次让它加功能时不会把白名单逻辑改丢。具体操作上你可以在 Coding Plan 里创建一个“Docker 运维”项目把docker_cleanup.sh作为基础文件然后让 Agent 帮你做这些事把日志格式改成 JSON 方便采集、增加磁盘阈值告警比如占用超过 85% 才触发清理、把敏感卷关键字列表抽成配置文件。每次改动后Agent 会自动跑一遍语法检查你只需要在服务器上验证实际效果。如果你更习惯用 Claude Code 来做这件事TaoToken 也提供了对应的接入方式文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。配置好之后Claude Code 可以直接读取你本地的脚本文件按你的指令修改并生成 diff你确认后再写回磁盘。这种工作流比在网页里复制粘贴代码高效得多尤其适合需要反复调整过滤规则的清理脚本。最后提醒一点不管用哪种方式接入Base URL 始终是https://taotoken.net/apiAPI Key 从控制台生成Model ID 按实际使用的模型填。三件套对齐了剩下的就是让脚本在凌晨 3 点安静地跑你早上起来看日志里那行本次释放: X%就行。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。