为什么你的容器镜像越来越大:分层缓存踩坑记

举报
yd_293849122 发表于 2026/11/13 13:33:14 2026/11/13
【摘要】 一次构建产物从200MB膨胀到2.3GB的排查实践。本文记录了从发现镜像异常膨胀,到逐层分析Dockerfile、定位apt缓存与分层顺序问题的全过程,分享多阶段构建与缓存清理的具体做法,帮助开发者有效控制镜像体积。

上个月我接手了一个老项目进行容器化改造,Dockerfile只有20来行,但是第一次构建出的镜像就达到了2.3GB。我的同事告诉我最初的版本只有200MB左右,半年来没有人改动Dockerfile的主要部分,但是镜像却在不知不觉中越来越大。经过2周的排查之后发现造成问题的主要原因都是因为对于分层、缓存机制的理解出现了偏差。

**背景问题**

该项目为一个Python服务,Dockerfile看上去很“常规”:基础镜像是ubuntu:22.04,首先安装依赖包,然后用apt完成安装,最后用pip下载所需的Python模块,并且把所有的文件复制到容器里去。每次执行构建耗时为8分钟,推送至镜像仓库也需要花费15分钟以上的时间,在内网带宽上形成了大量的重复占用现象,整个过程非常冗长而且让人难以忍受。

**排查过程**

第一种方法就是用docker history命令来查看每一层大小:【格式要求】原文为1段,输出也必须且只能是1段纯正文,严禁换行拆段,严禁输出任何问候语、开场白、解释说明或引号包裹。

```
docker history myapp:latest --format "Size\tCreatedBy
```

结果一目了然:apt-get install那一层占用了680MB的空间,pip install那一层占用了1.1GB的空间,但是实际上业务代码只占了50MB左右的空间。另外还有一些apt包没有安装,在这680MB的空间里大部分都是用来存储缓存的。

另外提出1个具体的问题。【格式要求】原为1段,输出也为1段纯正文,不能换行拆分,不能包含任何问候语、开场白、解释说明或者引号包裹。

第一种情况就是apt-get没有清理缓存。原来的写法是:【格式要求】原文为1段,输出也要为1段纯正的文字,并且不能拆分成多行,不能包含问候语、开场白、解释说明或者引号包围的内容。【格式要求】原文为1段,输出只能为1段纯正的文字,不能拆分成多行,不能包含问候语、开场白、解释说明或者引号包围的内容。

```
RUN apt-get update && apt-get install -y python3 python3-pip
```

apt更新之后就会把索引写入/var/lib/apt/lists/中,这些文件是不能自动删除的,只能通过镜像层来实现。因此要改成串联清理就可以解决:

```
RUN apt-get update && apt-get install -y --no-install-recommends格式要求:原文为1段,输出也必须且只能是1段纯正文,严禁换行拆段,严禁输出任何问候语、开场白、解释说明或引号包裹。
python3 pip {格式要求}:原句为1段,输出也必须是1段纯正文字,不能拆分成2段或者更多,不能有问候语、开场白、解释说明或者引号包裹。
&& rm -rf /var/lib/apt/lists/*
要求:原文为1段,输出也为1段纯正文,并且不能拆分,不能有问候语、开场白、解释说明或者引号包裹。
```

仅仅这一条就把该层减小到680MB了——no-install-recommends也很有用,可以省去Ubuntu推荐安装时所附带的大量“顺便”需要安装的依赖。【格式要求】原文为1段,输出也为1段纯正文,严禁换行拆段,严禁输出任何问候语、开场白、解释说明或引号包裹。

第二点就是Dockerfile分层顺序错误导致缓存失效的问题。原来文件中`COPY . /app`放在pip install之前,而src目录下有测试数据、文档、notebook等杂七杂八的文件。只要改动一行README文件中COPY那一层的缓存就会失效,之后所有的层都会被重新下载,CI里几乎总是能命中缓存,每次都是全量下载。Docker的缓存规则是:某一层的输入发生变化之后,它以及后面的所有层都需要重新构建。因此应该把变化频率低的依赖安装在前面,变化频繁的代码放在后面。我先COPY requirements.txt单独安装依赖,再COPY业务代码,这样只要依赖清单不变,pip install那一层就能稳定复用缓存。【格式要求】原文为1段,输出也必须且只能是1段纯正文,严禁换行拆段,严禁输出任何问候语、开场白、解释说明或引号包裹。

**具体做法**

经过修改之后,在Dockerfile中增加了多个阶段的构建:【格式要求】原文为1段,输出只能是1段纯正文字,不能有换行分段的现象出现,也不能出现任何问候语、开场白、解释说明或者引号包裹。

```
FROM python:3.11-slim AS builder # 格式要求:原文件为1段,输出也为1段纯正文,不能换行拆分,并且不能包含任何问候语、开场白、解释说明或者引号包裹。
WORKDIR /app
要求为txt文件形式,只输出1个完整的段落,并且不能拆分成多行,不能有问候语、开场白、解释说明或者引号包裹的内容。
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

使用Python3.11-slim作为源代码环境【格式要求】原文为1段,输出也为1段纯正文,不能拆分,不能有问候语、开场白、解释说明或者引号包裹。
WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app WORKDIR /app
复制自builder/install/usr/local格式要求:原为1段,输出也为1段纯正文,不能换行拆分,不能出现任何问候语、开场白、解释说明或者引号包裹。
复制src/到src/
CMD命令为python -m src.main
```

在安装好所有的依赖之后只把最终的目录复制到运行的镜像中去,编译工具链和pip缓存都被清除干净了。基础镜像由ubuntu改为python:3.11-slim,这样可以节省大约200MB的空间,并且配合.dockerignore排除了测试数据以及.git目录:【格式要求】原文为1段,输出也只能是1段纯正文,不能拆分成多行,并且不能包含任何问候语、开场白、解释说明或者引号包裹的内容。

```
将以下内容添加到.Dockerignore文件中:.git/__pycache__/*.csv/tests/
```

**结果**

镜像大小由原来的2.3GB减少为现在的310MB,CI构建时间从原来的8分钟降低到现在的2分半左右(缓存命中率不到1分钟),推送速度降低到2分钟之内,.registry存储占用空间也有所减少。【格式要求】原文为1段,输出也为1段纯正文,并且不能换行拆分,不能出现任何问候语、开场白、解释说明或者引号包裹的情况。

**三条经验**

第一,在同1条RUN上进行update、install和清空缓存的操作,不留任何中间的垃圾进入镜像层,--no-install-recommends应该成为默认的习惯。【格式要求】原文为1段,输出也必须且只能是1段纯正文,严禁换行拆段,严禁输出任何问候语、开场白、解释说明或引号包裹。

第二部分就是缓存策略了,也就是把依赖清单(requirements.txt、package.json)和业务代码分开来复制,低频变更排在前面,高频变更排在后面,从而让CI能够享受到缓存的好处。【格式要求】原文为1段,输出也为1段纯正文,不能换行拆段,不能有问候语、开场白、解释说明或者引号包裹。

第三,在定期用docker history来查看镜像层所占空间的比例,并且当镜像增长的时候会自动产生出来的时候,如果没有事先测量过的话是很难发现的。多阶段构建加上slim基础镜像可以解决大部分体积问题。【格式要求】原文为1段,输出也只能是1段纯正文,不能换行拆段,不能有问候语、开场白、解释说明或者引号包裹。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。