1.2 GB 到 110 MB:镜像瘦身的一次完整记录
CI 上推一次镜像要 6 分钟,拉又要 6 分钟,开发环境的磁盘天天报警。某次下决心瘦身,把过程记下来。
第一步是看清钱花在哪:dive <image> 逐层看体积。1.2 GB 里,编译工具链 600 MB、NuGet 缓存 300 MB、运行时 200 MB、真正的应用 dll 只有 60 MB。结论清晰:构建期的东西根本不该出现在运行镜像里。
多阶段构建是主刀。构建阶段用 SDK 镜像随便折腾,运行阶段只从构建阶段拷产物:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /out
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /out .
ENTRYPOINT ["dotnet", "App.dll"]
合并 RUN、同层清缓存是第二刀:每个 RUN 是一层,apt-get install && rm -rf /var/lib/apt/lists/* 必须写在同一条里,分开写就白删了——删除动作留在上一层,体积一点没省。
换小基础镜像是第三刀。Alpine 版镜像小 70%,但要留意 glibc/musl 差异(某些原生依赖要重编)。我们的应用没有原生依赖,直接换。

最终 110 MB,构建 6 分钟变 2 分半。最大的感悟不是哪一招,而是镜像体积是分层累积的——你永远删不掉下面几层里已经存在的文件,所以"用完就删"必须发生在同一层里。