1. 服务器Docker磁盘爆满的典型场景
每次登录服务器看到"no space left on device"的报错时,我的血压都会瞬间飙升。作为长期与Docker打交道的运维人员,我见过太多因为容器日志、缓存镜像导致的磁盘灾难。上周刚处理的一台生产服务器,/var/lib/docker目录竟然占用了98%的磁盘空间,导致关键服务崩溃。
这种情况通常由三类"磁盘杀手"引起:
- 悬空镜像:那些没有标签的中间镜像层(显示为
<none>),在多次build后可能堆积数GB - 停止的容器:即使容器已停止,其写入层仍然占用空间
- 日志文件:特别是未配置日志轮转的容器,单个日志文件就能撑爆磁盘
经验之谈:我曾遇到一个Java应用的容器,仅一周就产生了35GB的GC日志,而开发者甚至不知道默认启用了-XX:+PrintGCDetails参数
2. 手动清理的常规操作与痛点
临时救急时,我们通常会执行以下命令组合:
# 清理停止的容器、未使用的网络和悬空镜像 docker system prune -f # 针对性地清理悬空镜像 docker image prune -a --filter "until=24h" # 查看各容器日志大小 find /var/lib/docker/containers -name "*.log" -exec ls -lh {} \;但手动清理存在明显缺陷:
- 操作滞后性:往往在磁盘报警后才处理
- 心理负担:需要记住复杂的prune参数组合
- 风险不可控:误删正在使用的镜像可能导致服务中断
3. 自动化清理方案设计
3.1 核心清理策略
基于生产环境经验,我设计了三层清理机制:
每日轻量清理(低风险):
- 删除24小时前的悬空镜像
- 清理已停止超过3天的容器
每周深度清理(中风险):
- 删除7天前未使用的镜像
- 轮转超过100MB的容器日志
每月核验(安全网):
- 检查/var/lib/docker目录大小
- 发送磁盘使用报告到邮箱
3.2 完整Shell脚本实现
创建/usr/local/bin/docker-cleanup:
#!/bin/bash # 全局配置 LOG_DIR="/var/log/docker-cleanup" MAX_LOG_SIZE="100M" DAYS_TO_KEEP_IMAGES=3 DAYS_TO_KEEP_CONTAINERS=3 # 初始化日志目录 mkdir -p "$LOG_DIR" # 定义日志函数 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_DIR}/cleanup-$(date +%Y%m%d).log" } # 磁盘检查函数 check_disk() { local threshold=85 local usage=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%') if [ "$usage" -ge "$threshold" ]; then log "警告:根分区使用率已达${usage}%,启动紧急清理模式" docker system prune -af --volumes return 1 fi return 0 } # 常规清理流程 normal_cleanup() { log "=== 开始常规Docker资源清理 ===" # 清理悬空镜像(3天前) log "清理悬空镜像..." docker image prune -f --filter "dangling=true" --filter "until=${DAYS_TO_KEEP_IMAGES}h" # 清理停止的容器(3天前) log "清理停止的容器..." docker container prune -f --filter "until=${DAYS_TO_KEEP_CONTAINERS}h" # 清理未使用的网络 log "清理未使用的网络..." docker network prune -f # 日志轮转 log "轮转大尺寸容器日志..." find /var/lib/docker/containers -name "*.log" -size +${MAX_LOG_SIZE} -exec truncate -s 0 {} \; log "=== 常规清理完成 ===" } # 主流程 main() { if check_disk; then normal_cleanup fi # 每周日执行深度清理 if [ "$(date +%u)" -eq 7 ]; then log "=== 启动每周深度清理 ===" docker system prune -af --filter "until=168h" # 7天前 log "=== 深度清理完成 ===" fi } main "$@"关键安全设计:
- 使用
--filter参数避免删除新建资源 - 先检查磁盘使用率再决定清理强度
- 所有操作记录详细日志
4. 定时任务配置技巧
4.1 Crontab最佳实践
编辑root用户的crontab:
# 每天凌晨3点执行清理(避免业务高峰) 0 3 * * * /usr/local/bin/docker-cleanup >/dev/null 2>&1 # 每月1号发送磁盘报告 0 6 1 * * df -h | mail -s "Monthly Disk Report" admin@example.com4.2 系统级配置建议
更规范的部署方式:
- 将脚本放入
/etc/cron.daily/目录 - 创建systemd服务单元:
# /etc/systemd/system/docker-cleanup.service [Unit] Description=Docker Cleanup Service After=docker.service [Service] Type=oneshot ExecStart=/usr/local/bin/docker-cleanup # /etc/systemd/system/docker-cleanup.timer [Unit] Description=Run docker cleanup daily [Timer] OnCalendar=*-*-* 03:00:00 Persistent=true [Install] WantedBy=timers.target5. 生产环境注意事项
5.1 资源保留策略
对于关键容器需要设置例外:
# 给重要容器打标签避免被清理 docker run --label "do_not_prune=true" nginx # 修改清理脚本的find命令 find /var/lib/docker/containers -name "*.log" ! -path "*do_not_prune*" -size +100M -exec truncate -s 0 {} \;5.2 监控与告警
建议配合监控系统设置阈值告警:
- Prometheus示例配置:
alert: DockerDiskUsageCritical expr: 100 - (node_filesystem_avail_bytes{mountpoint="/var/lib/docker"} / node_filesystem_size_bytes{mountpoint="/var/lib/docker"} * 100) > 90 for: 10m labels: severity: critical annotations: summary: "Docker 磁盘即将爆满 ({{ $value }}%)"5.3 性能影响测试
在实施前建议进行压力测试:
# 模拟高负载时清理的影响 stress-ng --vm 4 --vm-bytes 2G -t 60s & /usr/local/bin/docker-cleanup6. 进阶优化方案
对于大型K8s集群,可以考虑:
日志收集架构改造:
- 所有容器日志直接输出到ELK或Loki
- 禁用本地日志存储(配置docker daemon.json):
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
分布式存储方案:
- 为/var/lib/docker挂载单独磁盘
- 使用LVM实现动态扩容
镜像仓库管理:
- 定期清理私有仓库中的旧镜像
- 使用Harbor的自动回收策略
这套方案在我管理的50+服务器上稳定运行了两年,将磁盘紧急事件减少了90%。最关键的技巧是:永远给清理操作加上时间过滤器,这样既能保持系统清洁,又不会误伤正在使用的资源。