Docker 镜像瘦身入门:从臃肿镜像到轻量部署
很多项目第一次写 Dockerfile 都很顺利:能跑起来就好。可是镜像一上生产,问题就来了:体积动不动就超过 1GB,拉取慢、部署慢、扫描漏洞多、回滚也慢。瘦身不是为了好看,而是为了更快的交付和更小的攻击面。下面用一个常见的 Node 应用为例,讲四个最实用的办法。
一、先看镜像为什么臃肿
常见原因有三个。第一,基础镜像选得太大,例如直接用默认的完整系统镜像,里面带了编译工具和调试包,运行时根本用不到。第二,每条 RUN 指令都会产生一层,临时文件、缓存和源码都留在了历史层里,即使后面删掉,体积也不会真正缩小。第三,把整个项目目录都 COPY 进去,包括 node_modules、测试文件、本地配置和 .git,镜像自然越来越大。
先用 docker images 和 docker history 看一下现状,再动手改,效果才能量化。建议把优化前后的镜像大小、层数和启动时间都记下来。
二、多阶段构建:编译和运行分开
多阶段构建是最有效的瘦身手段。第一阶段用带工具链的镜像做构建,第二阶段只复制构建产物到一个干净的运行镜像里。这样最终镜像里没有源码、没有编译器,也没有测试依赖。
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
CMD ["node", "dist/index.js"]
这个写法还有一个好处:构建阶段失败不会污染运行镜像,职责更清晰。需要调试时,可以单独进入构建阶段查看,生产镜像保持干净。
三、写好 .dockerignore,不要把什么都拷进去
.dockerignore 和 .gitignore 一样重要。常见应该排除的内容包括 node_modules、.git、测试目录、本地日志、编辑器配置和构建产物。否则每次 COPY . . 都会把这些文件带进镜像,还会破坏缓存。
node_modules
.git
dist
*.log
.env.local
coverage
写完后可以用 docker build 时的上下文大小来验证:如果上下文从几百 MB 降到几 MB,说明已经生效。
四、选对基础镜像,并用好层缓存
如果应用不需要完整系统,优先考虑 alpine 或者 slim 变体,但要注意:alpine 使用 musl,个别依赖原生编译的包可能需要额外处理,上线前一定要跑一次完整测试。不要为了小而牺牲兼容性。
层缓存方面,顺序很关键:先复制 package.json 并安装依赖,再复制源码。这样只有源码变化时才重新构建应用层,依赖层可以复用。同样,尽量把不常变的内容放在前面,把经常变的内容放在后面;合并相关的 RUN 指令,并在同一层里清理缓存。
五、上线前检查清单
第一,确认最终镜像里没有密钥和本地配置文件;第二,用非 root 用户运行应用;第三,固定基础镜像版本,避免 latest 带来不可预期的变化;第四,定期用扫描工具检查漏洞,并把镜像大小纳入发布检查。
瘦身的本质是只把运行时真正需要的东西放进镜像。多阶段构建解决构建残留,.dockerignore 解决上下文过大,基础镜像和层缓存解决体积与速度。把这四点做扎实,大多数臃肿镜像都能明显变轻,部署也会更快、更稳。
- 点赞
- 收藏
- 关注作者
评论(0)