1. 当Docker突然罢工:磁盘爆满的紧急诊断

那天凌晨3点,我的手机突然疯狂震动——监控系统报警显示生产环境服务器磁盘使用率100%。连SSH都卡成幻灯片,好不容易登上去用df -h一看,/var/lib/docker/overlay2这个目录吃掉了95%的空间。这种场景就像你家的水管突然爆裂,必须立即找到漏水点。

第一步要像外科手术般精准定位问题源。进入/var/lib/docker目录后,我用这个命令快速扫描各子目录占用情况:

du -h --max-depth=1 | sort -rh

发现三个"罪魁祸首":

  • containers目录下堆积了23GB的*-json.log日志文件
  • volumes目录里某个Java应用的日志卷占了18GB
  • overlay2存储驱动层积压了40GB废弃数据

这里有个实用技巧:用ncdu工具可以交互式查看目录大小(需提前安装)。相比du命令,它能实时导航目录树,特别适合在复杂的目录结构中快速定位大文件:

ncdu /var/lib/docker

2. 救火队员的紧急清理手册

2.1 清理容器日志的黄金三连

面对还在运行的容器,直接删除日志文件可能导致程序异常。我推荐这个安全清理脚本:

#!/bin/bash
echo "==== 开始清理容器日志 ===="
find /var/lib/docker/containers -name "*.log" -exec sh -c 'echo "清理 {}"; > {}' \;
echo "==== 清理完成 ===="

保存为clean_logs.sh后,记得给执行权限:

chmod +x clean_logs.sh

重要警告:如果遇到"设备空间不足"错误,说明磁盘真的被塞满了。这时需要先手动删除部分文件腾出空间:

# 找出最大的5个日志文件并清空
find /var/lib/docker -type f -name "*.log" -exec ls -lh {} + | sort -k5 -rh | head -5 | awk '{print $9}' | xargs -I {} sh -c '> {}'

2.2 彻底重置Docker存储

当需要核弹级清理时(慎用!会删除所有容器和镜像):

docker system prune -af --volumes

这个命令会:

  1. 停止所有运行中的容器
  2. 删除所有停止的容器
  3. 删除所有未被使用的网络
  4. 删除所有未被任何容器引用的卷
  5. 删除所有悬空和未使用的镜像

3. 为什么Overlay2会成为磁盘杀手?

3.1 存储驱动的层叠魔法

Overlay2就像千层蛋糕:

  • lowerdir:只读的基础镜像层(相当于蛋糕胚)
  • upperdir:容器的可写层(相当于奶油装饰)
  • merged:最终呈现的统一视图(成品蛋糕)

每次docker commit都会产生新层。我曾遇到过一个反复提交的镜像,竟然堆积了120+层,占用空间是基础镜像的10倍!

3.2 镜像体积的隐形膨胀

通过docker history查看镜像构建历史时,经常会发现这种情况:

IMAGE          CREATED        SIZE      COMMENT
a1b2c3d4e5f6   2 weeks ago    1.2GB    apt-get install unnecessary-package

很多开发者喜欢在Dockerfile里用apt-get upgrade,这会导致镜像包含所有旧版本软件包。正确的做法是:

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

4. 构建防爆仓的Docker环境

4.1 日志管理的三重防护

第一重:全局日志限制 编辑/etc/docker/daemon.json(没有就新建):

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

然后重启Docker服务:

systemctl restart docker

第二重:Compose文件级控制docker-compose.yml中为每个服务单独设置:

services:
  my_app:
    logging:
      driver: json-file
      options:
        max-size: 50m
        max-file: 2

第三重:应用层日志轮转 对于Java应用,在logback.xml中配置:

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>/var/log/myapp.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>/var/log/myapp.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
        <maxFileSize>50MB</maxFileSize>
        <maxHistory>7</maxHistory>
    </rollingPolicy>
</appender>

4.2 存储空间的智能监控

我习惯用这个脚本做每日检查,保存为/usr/local/bin/check_docker_storage

#!/bin/bash
THRESHOLD=80
CURRENT=$(df /var/lib/docker | awk 'NR==2 {print $5}' | sed 's/%//')

if [ "$CURRENT" -gt "$THRESHOLD" ]; then
    echo "警告:Docker存储使用率 ${CURRENT}%"
    echo "Top 5大容器:"
    docker ps --format "{{.Names}}" | xargs -I {} sh -c \
    'echo {}: $(du -sh $(docker inspect {} --format="{{.GraphDriver.Data.MergedDir}}") | cut -f1)'
else
    echo "当前使用率: ${CURRENT}%"
fi

然后添加到crontab:

0 9 * * * /usr/local/bin/check_docker_storage >> /var/log/docker_storage.log

5. 高级玩家的存储优化技巧

5.1 使用外挂存储设备

如果服务器支持,最好把Docker数据目录迁移到独立磁盘:

# 假设新磁盘挂载在/mnt/data
systemctl stop docker
rsync -a /var/lib/docker /mnt/data/
mv /var/lib/docker /var/lib/docker.bak
ln -s /mnt/data/docker /var/lib/docker
systemctl start docker

5.2 选择更适合的存储驱动

对于高IOPS需求的场景,可以考虑devicemapperzfs驱动。比如在CentOS上切换为devicemapper:

# 编辑/etc/docker/daemon.json
{
  "storage-driver": "devicemapper",
  "storage-opts": [
    "dm.basesize=20G",
    "dm.fs=ext4"
  ]
}

不过要注意,不同驱动有各自的适用场景,变更前务必做好测试。

那次事故后,我给团队制定了新的Docker使用规范:所有生产环境容器必须配置日志轮转,CI/CD流水线中加入镜像层数检查,每周例行执行存储健康检查。现在两年过去了,再没出现过半夜被磁盘告警吵醒的情况。

更多推荐