别再只会用docker system prune了!聊聊Docker磁盘清理的5个隐藏场景与实战命令

Docker作为现代开发与运维的核心工具,其便捷性背后往往隐藏着磁盘管理的复杂性。当docker system prune成为大多数人的清理"万能药"时,真正棘手的磁盘问题却常常出现在那些未被充分讨论的角落——容器日志的失控增长、overlay2目录的神秘膨胀、构建缓存的隐形堆积……这些场景需要的不是一刀切的清理,而是精准的手术刀式操作。

1. 容器日志轮转:从根源解决/var/lib/docker/containers爆满问题

df -h显示/var分区即将爆满时,/var/lib/docker/containers往往是罪魁祸首。默认情况下,Docker容器会将所有标准输出日志以JSON格式存储在此目录,且没有自动轮转机制。我曾见过一个生产环境的Nginx容器在三个月内产生了47GB的日志文件。

1.1 即时清理与日志定位

首先用以下命令快速定位磁盘占用最高的容器日志:

du -d 1 -h /var/lib/docker/containers/ | sort -h

找到目标容器ID后,可以临时清空日志文件(注意:这会丢失所有历史日志):

truncate -s 0 /var/lib/docker/containers/<container_id>/<container_id>-json.log

1.2 配置日志轮转策略

更专业的做法是在容器启动时配置日志驱动和轮转参数:

docker run --log-driver json-file \
           --log-opt max-size=100m \
           --log-opt max-file=3 \
           nginx

关键参数说明:

  • max-size:单个日志文件最大尺寸(支持k,m,g单位)
  • max-file:保留的历史日志文件数量

对于已运行的容器,修改/etc/docker/daemon.json全局配置并重启Docker服务:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}

提示:修改全局配置会影响所有新创建的容器,已存在的容器需要重建才能生效

2. 解剖overlay2:读写层异常增长的诊断与修复

Docker存储驱动采用的overlay2文件系统,会在/var/lib/docker/overlay2下为每个容器创建读写层(merged目录)。当这些目录异常增长时,常规清理往往无效。

2.1 定位问题容器

使用以下命令查看各容器占用的磁盘空间(包括读写层):

docker ps -s --format "table {{.ID}}\t{{.Names}}\t{{.Size}}"

输出示例:

CONTAINER ID   NAMES      SIZE
a1b2c3d4e5f6   mysql_db   2.45GB (virtual 1.2GB)

其中"SIZE"列显示的是读写层大小,"(virtual)"表示镜像基础大小。

2.2 深入分析具体容器

对可疑容器进行深入检查:

docker exec -it <container_id> bash -c "du -h --max-depth=1 / | sort -h"

常见问题根源:

  • 应用程序生成的临时文件未清理
  • 数据库未配置自动清理(如MySQL的binlog)
  • 上传目录缺乏定期清理机制

2.3 针对性清理方案

对于MySQL容器,可以设置自动清理binlog:

SET GLOBAL expire_logs_days = 3;

对于临时文件问题,建议在Dockerfile中加入清理指令:

RUN apt-get update && \
    apt-get install -y package && \
    rm -rf /var/lib/apt/lists/*

3. 构建缓存管理:超越docker builder prune的精细控制

Docker构建缓存虽然加速了构建过程,但长期积累会占用大量空间。docker builder prune虽然简单,但缺乏针对性。

3.1 查看缓存详细组成

docker builder du --verbose

输出示例:

Build Cache: 5.7GB
- 3.2GB FROM alpine:latest
- 1.1GB RUN apk add --no-cache python3
- 0.8GB COPY . /app
- 0.6GB RUN pip install -r requirements.txt

3.2 精准清理特定缓存层

保留基础镜像缓存,仅清理应用层:

docker builder prune --filter type=exec.cachemount

可选过滤器:

  • type=regular:常规Dockerfile指令产生的缓存
  • type=internal:内部使用的缓存
  • type=source.local:本地源文件缓存

3.3 构建时控制缓存行为

在Dockerfile中合理使用--mount=type=cache

RUN --mount=type=cache,target=/var/cache/apt \
    apt-get update && \
    apt-get install -y build-essential

这样既保持了构建速度,又避免了缓存污染主镜像。

4. 镜像深度清理:区分悬空镜像与中间层

docker image prune默认只清理悬空镜像(dangling images),但中间层(intermediate layers)才是真正的空间杀手。

4.1 识别不同类型的镜像

docker images -a --filter dangling=true  # 悬空镜像
docker images -a --format "{{.ID}}\t{{.CreatedSince}}\t{{.Size}}" | sort -k2 -h

4.2 安全清理中间层

先查看可清理的空间预估:

docker system df -v

然后使用带过滤器的清理命令:

docker image prune -a --filter "until=24h"  # 清理24小时前的未使用镜像

4.3 保留关键镜像的策略

给重要镜像打上保留标签:

docker tag my_image:latest my_image:keep

这样在执行prune时可以通过--filter排除:

docker image prune -a --filter="label!=keep"

5. 自动化清理:安全可靠的crontab方案

手动清理终究不是长久之计,但自动化清理需要特别注意安全性。

5.1 基础清理脚本

创建/usr/local/bin/docker-cleanup

#!/bin/bash
# 清理超过7天的已停止容器
docker container prune --force --filter "until=168h"

# 清理未使用的镜像(保留最近3个版本)
docker image prune -a --force --filter "until=72h"

# 清理构建缓存
docker builder prune --force --filter "until=24h"

# 清理网络和卷
docker network prune --force
docker volume prune --force

5.2 安全增强措施

添加资源使用限制和日志记录:

#!/bin/bash
LOG_FILE="/var/log/docker-cleanup.log"
MAX_DISK_USAGE=90

current_usage=$(df -h /var/lib/docker | awk 'NR==2 {print $5}' | tr -d '%')

if [ "$current_usage" -lt "$MAX_DISK_USAGE" ]; then
    echo "$(date) - Disk usage ${current_usage}% below threshold. Skipping cleanup." >> "$LOG_FILE"
    exit 0
fi

echo "$(date) - Starting cleanup (Current usage: ${current_usage}%)" >> "$LOG_FILE"
# ...原有清理命令...

5.3 设置crontab定时任务

sudo crontab -e

添加以下内容(每天凌晨3点执行,且磁盘使用超过85%时):

0 3 * * * /usr/local/bin/docker-cleanup

注意:避免在业务高峰期执行清理,特别是生产环境

更多推荐