目录

  • 一、问题现象:服务器磁盘突然 95% 告警
  • 二、Docker 磁盘占用的 6 个来源
  • 三、命令一:docker system df——看整体占用
  • 四、命令二:docker system df -v——精确定位大文件
  • 五、命令三:docker image prune——清理悬空镜像
  • 六、命令四:docker builder prune——清理构建缓存
  • 七、命令五:docker system prune -a——一键清理(慎用)
  • 八、实战:一次清理 47GB 的完整过程
  • 九、为什么 CI 环境特别容易磁盘爆炸
  • 十、预防方案:自动清理脚本 + 配置优化
  • 十一、环境与版本

一、问题现象:服务器磁盘突然 95% 告警

上周五下午,CI 服务器磁盘告警了。一台 100GB 系统盘的机器,/ 分区使用率 95%,剩 5GB。Docker 还在不停地 build,眼看就要挂了。

df -h 看了一眼,根分区确实快满了。但代码仓库总共才 2GB,哪来的几十 G?

$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        99G   94G  5.0G  95% /

直觉告诉我是 Docker 干的。一年前遇到过同样的坑,这次记下来完整排查过程,免得下次再踩。

二、Docker 磁盘占用的 6 个来源

先搞清楚 Docker 到底在磁盘上存了什么,才知道该清哪里:

在 CI/CD 环境里,构建缓存(Build Cache)是最大的空间杀手,经常能占 50% 以上。因为每次 docker build 都会产生一层缓存,CI 每天跑几十上百次构建,缓存堆积速度很恐怖。

三、命令一:docker system df——看整体占用

第一条命令,先看 Docker 总共占了多少空间:

$ docker system df

TYPE            TOTAL     ACTIVE    SIZE        RECLAIMABLE
Images          47        8         28.5GB      22.3GB (78%)
Containers      23        5         1.2GB       890MB (74%)
Local Volumes   12        4         3.8GB       2.1GB (55%)
Build Cache     856       0         34.2GB      34.2GB (100%)

四个类型一目了然:

类型说明本次占用可回收
Images所有镜像(含中间层)28.5 GB22.3 GB
Containers所有容器的可写层1.2 GB890 MB
Local Volumes本地数据卷3.8 GB2.1 GB
Build Cache构建缓存34.2 GB34.2 GB

看 RECLAIMABLE 列——总共可以回收 59.5GB。问题找到了。

RECLAIMABLE 显示的是"可安全回收"的空间。这个值高不代表必须清理,但如果磁盘紧张,优先清理 RECLAIMABLE 高的。

四、命令二:docker system df -v——精确定位大文件

加 -v 看详细信息,能定位到具体哪个镜像、哪个卷占了大空间:

$ docker system df -v

# 镜像详情(按大小排序,截取前 5)
Images space usage:
REPOSITORY          TAG       IMAGE ID       SIZE       SHARED
myapp               v1.2      a3b4c5d6e7f8   2.1GB      1.8GB
postgres            16.2      b4c5d6e7f8a3   985MB      620MB
node                20-slim   c5d6e7f8a3b4   420MB      310MB
<none>              <none>   d6e7f8a3b4c5   1.8GB      0       # ← dangling 镜像
<none>              <none>   e7f8a3b4c5d6   1.5GB      0       # ← dangling 镜像

# 构建缓存详情
Build cache usage: 856 entries
CACHE ID        TYPE      SIZE        CREATED        LAST USED
abc123          regular   1.2GB       3 days ago     2 days ago
def456          regular   890MB       5 days ago     5 days ago
ghi789          regular   760MB       1 week ago     1 week ago
...

关键发现:

  • 两个 <none> 镜像占了 3.3GB——这是旧构建产生的 dangling 镜像,没用了
  • 构建缓存 856 条,很多是几天前的,大概率不会再复用

五、命令三:docker image prune——清理悬空镜像

先清理最安全的——dangling 镜像(就是那些 <none>:None 的):

# 只清理 dangling 镜像(最安全)
$ docker image prune
# 会提示:Total reclaimed space: 3.3GB

# 如果要清理所有未被容器使用的镜像(稍激进)
$ docker image prune -a
# 会清理所有没有容器在用的镜像,回收更多空间
docker image prune -a 会删掉所有没在跑的镜像。如果你有些镜像是为了快速启动而缓存的,别用 -a,用不带参数的版本只删 dangling。

六、命令四:docker builder prune——清理构建缓存

这是本次清理的大头——34.2GB 的构建缓存:

# 清理所有构建缓存
$ docker builder prune
# 会提示确认,输入 y
# Total reclaimed space: 34.2GB

# 只清理 24 小时前的缓存(保留最近的,下次构建能命中缓存)
$ docker builder prune --filter "until=24h"

# 只清理未被使用的缓存
$ docker builder prune --filter "unused-for=24h"

--filter "until=24h" 是比较推荐的方式——保留最近 24 小时的缓存(CI 构建还能命中),清理更老的。这样既释放空间又不影响构建速度。

七、命令五:docker system prune -a——一键清理(慎用)

如果你想一步到位,用这条命令:

# 清理所有未使用资源(镜像 + 容器 + 网络 + 构建缓存)
$ docker system prune -a

# 加 --volumes 连数据卷一起清
$ docker system prune -a --volumes
# ↑ 这个会删数据,确认没有重要数据卷再加这个参数

docker system prune -a 会清理:

  • 所有停止的容器
  • 所有没有被容器使用的网络
  • 所有没有标签的镜像(dangling)
  • 所有没有被容器引用的镜像(-a 的作用)
  • 所有构建缓存
-a 参数很激进。如果你有 docker pull 下来准备用的镜像,但没有容器在跑,会被一起删掉。生产环境慎用。

八、实战:一次清理 47GB 的完整过程

回到开头那个 95% 磁盘告警的服务器,完整清理过程:

步骤 1:确认 Docker 占用

$ docker system df
# Build Cache: 34.2GB, Images: 28.5GB (22.3GB reclaimable)

步骤 2:清理 dangling 镜像

$ docker image prune
# Total reclaimed space: 3.3GB

步骤 3:清理旧构建缓存(保留 24h)

$ docker builder prune --filter "until=24h"
# Total reclaimed space: 28.7GB

步骤 4:清理停止的容器

$ docker container prune
# Total reclaimed space: 890MB

步骤 5:清理未使用的数据卷

$ docker volume prune
# Total reclaimed space: 2.1GB

步骤 6:确认结果

$ df -h /
Filesystem      Size  Used Avail Use%
/dev/vda1        99G   47G   52G  47% /    # ← 从 95% 降到 47%
清理项回收空间耗时
dangling 镜像3.3 GB2 秒
构建缓存(24h 前)28.7 GB5 秒
停止的容器890 MB1 秒
未使用的数据卷2.1 GB1 秒
合计34.99 GB~10 秒

磁盘使用率从 95% 降到 47%,总共回收了约 35GB。

九、为什么 CI 环境特别容易磁盘爆炸

CI 环境的磁盘爆炸有规律可循,根本原因是三个:

1. 频繁构建产生大量缓存——每次 docker build 都会产生 Build Cache。一天 50 次构建,一个月就是 1500 条缓存,轻松几十 G。

2. 旧镜像不清理——CI 每次构建都打新 tag,旧 tag 的镜像没人用但也不删。myapp:v1.2myapp:v1.3myapp:v1.4... 每个都 1-2GB。

3. 容器日志无限增长——默认的 json-file 日志驱动没有大小限制,一个跑了几个月的服务日志能涨到好几个 G。

十、预防方案:自动清理脚本 + 配置优化

方案一:定期清理脚本

#!/bin/bash
# docker-cleanup.sh,放到 crontab 每天跑一次

# 清理 24 小时前的构建缓存
docker builder prune -f --filter "until=24h"

# 清理 dangling 镜像
docker image prune -f

# 清理停止的容器
docker container prune -f

# 清理未使用的数据卷(确认安全后取消注释)
# docker volume prune -f

echo "Cleanup done at $(date)"
# 加到 crontab,每天凌晨 3 点执行
0 3 * * * /opt/scripts/docker-cleanup.sh >> /var/log/docker-cleanup.log 2>&1

方案二:限制容器日志大小

修改 /etc/docker/daemon.json,限制单个容器日志文件最大 100MB,保留 3 个文件:

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

改完重启 Docker:

$ sudo systemctl restart docker
这个配置只对新建的容器生效。已有容器需要重建才会应用新的日志限制。

方案三:CI 流水线构建后自动清理

在 CI pipeline 的最后加一步清理:

# GitLab CI 示例
after_script:
  - docker image prune -f
  - docker builder prune -f --filter "until=1h"

# GitHub Actions 示例
- name: Cleanup Docker
  if: always()
  run: |
    docker image prune -f
    docker builder prune -f --filter "until=1h"

三种方案对比

方案效果适用场景风险
定时清理脚本稳定保持磁盘可用所有 Docker 环境
日志大小限制防止日志无限增长长期运行的服务低(需重建容器生效)
CI 后清理每次构建后即时清理CI/CD 环境

三个方案建议同时用,基本能彻底解决 Docker 磁盘爆炸的问题。

十一、环境与版本

组件版本说明
Docker Engine26.1.4docker system df 从 17.04 开始支持
BuildKit内置(26.x)构建缓存管理依赖 BuildKit
操作系统Ubuntu 24.04 LTS
存储驱动overlay2默认推荐驱动
本文命令在 Docker 20.10+ 上均适用。docker builder prune 从 Docker 23.0 开始默认使用 BuildKit,低版本可能需要手动启用。

如果这篇文章帮你清理了磁盘空间,点个赞让更多被 Docker 磁盘问题困扰的人看到。有其他清理技巧欢迎评论区补充。

更多推荐