Docker镜像瘦身,减小建站环境镜像体积
为什么你的建站镜像越用越大
很多站长发现,一台服务器上跑几个网站后,Docker镜像动辄 1GB 以上,拉取慢、部署卡、磁盘告警。Docker镜像瘦身的核心思路只有一句话:只把运行时真正需要的东西放进最终镜像。
本文从零基础出发,用 5 个可验证的步骤,帮你把典型 LNMP 建站环境镜像从 1GB+ 压缩到 200MB 以内,并给出每一步的验证命令。
先看清镜像里到底装了什么
动手之前,先知道空间被谁占了。
推荐用 dive 工具分析镜像每一层:
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
wagoodman/dive:latest your-image:tag
左侧是层列表,右侧是文件树。
按 Ctrl+U 可以只看未使用的层。
常见浪费来源:编译工具链、缓存文件、日志、测试依赖、重复安装的包。
另一个快速检查方式:
docker history your-image:tag --no-trunc
输出里 SIZE 列超过 100MB 的层,基本都有优化空间。
五个直接可用的瘦身步骤
步骤1:把基础镜像换成 Alpine 或 slim 版本
Ubuntu 基础镜像约 77MB,Debian slim 约 30MB,Alpine 仅约 5MB。
以 Nginx 为例:
# 优化前
FROM nginx:latest
# 优化后
FROM nginx:1.25-alpine
Alpine 使用 musl libc,部分二进制可能不兼容。
如果遇到 not found 报错,改用 debian:bookworm-slim 作为折中方案。
步骤2:用多阶段构建隔离编译环境
编译 PHP 扩展、Node 依赖时,编译器和头文件只在构建阶段需要。
多阶段构建让最终镜像只保留产物:
# 阶段1:构建
FROM php:8.2-fpm AS builder
RUN apt-get update && apt-get install -y libzip-dev \
&& docker-php-ext-install zip
# 阶段2:运行
FROM php:8.2-fpm-alpine
COPY --from=builder /usr/local/lib/php/extensions/ \
/usr/local/lib/php/extensions/
COPY --from=builder /usr/local/etc/php/conf.d/ \
/usr/local/etc/php/conf.d/
验证方式:docker images 查看新镜像大小,通常能减少 50%-70%。
步骤3:合并 RUN 指令并清理缓存
Dockerfile 中每条 RUN 都会产生一层。
把安装、使用、清理写在同一层:
# 错误写法:产生3层,缓存留在镜像里
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# 正确写法:1层完成
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
关键点:--no-install-recommends 避免安装推荐包,rm -rf 必须和安装在同一层才有效。
步骤4:配置 .dockerignore 排除无关文件
在项目根目录创建 .dockerignore:
.git
node_modules
*.log
*.md
tests/
.env
COPY . . 会把这些文件全部打包进镜像。
加一行 .dockerignore,通常能省几十 MB。
步骤5:清理无用镜像和构建缓存
构建过程中产生的悬空镜像和缓存也占磁盘:
docker image prune -f
docker builder prune -f
如果想更彻底:
docker system df
docker system prune -a --volumes
注意:prune -a 会删除所有未使用的镜像,生产环境请先确认没有正在运行的容器依赖它们。
避坑指南:这些操作反而会让镜像变大
- 不要在 RUN 里用
apt-get upgrade:会更新所有包,产生大量新层,且破坏可复现性。 - 不要先 COPY 再 RUN 安装依赖:代码变动会导致依赖层缓存失效,每次重新安装。正确顺序是先 COPY 依赖清单文件(如
package.json、composer.json),安装后再 COPY 源码。 - 不要把数据卷内容打进镜像:数据库文件、上传目录应通过 volume 挂载,不应出现在镜像里。
- Alpine 不是万能药:某些需要 glibc 的二进制(如部分 Java 应用、Oracle 客户端)在 Alpine 上无法运行,此时应选择 slim 镜像而非强行换 Alpine。
怎么确认瘦身真的生效了
完成上述步骤后,执行以下命令对比:
docker images | grep your-image
记录优化前后的 SIZE 列。
同时验证容器能正常启动:
docker run -d --name test your-image:tag
docker logs test
docker exec test nginx -t # 以 Nginx 为例验证配置
如果服务正常、日志无报错,说明瘦身没有破坏运行时依赖。
最后用 dive 再看一次,确认没有超过 50MB 的无效层。
常见疑问
Q:换成 Alpine 后 PHP 扩展装不上怎么办?
Alpine 的包管理是 apk,不是 apt。
安装 PHP 扩展需要用 apk add 或 docker-php-ext-install 配合 apk add --no-cache 安装编译依赖。
如果扩展依赖 glibc,建议退回 php:8.2-fpm-slim。
Q:多阶段构建后镜像启动报“找不到文件”怎么排查?
说明 COPY 路径没覆盖运行时需要的文件。
用 docker run --rm -it your-image sh 进入容器,手动执行启动命令,看具体缺哪个文件,再回到 Dockerfile 补 COPY。
Q:瘦身之后构建速度变慢了,正常吗?
多阶段构建会增加构建步骤,首次构建可能稍慢。
但合理利用缓存(先 COPY 依赖清单再安装)后,日常构建速度不会明显下降。
镜像小反而让推送和拉取更快。