dockerfile-构建原理--镜像分层优化

Dockerfile 构建原理 + 镜像分层优化

·6 分钟阅读·2178 字
📋 目录

一、Dockerfile核心原理:基于UnionFS分层构建

1.构建机制

docker build 逐条解析Dockerfile指令,每条指令生成一个镜像层(Layer),下层只读,上层增量;
Docker内置构建缓存机制

  1. 从上往下逐条执行,指令不变、依赖文件无变化,复用本地缓存层,不重新构建
  2. COPY/ADD会校验文件checksum,文件改动则缓存失效,后续全部重新构建。

本质:每一条RUN/COPY/ADD/ENV都是一次镜像提交commit,堆叠成最终镜像。

2.构建整体流程

  1. docker客户端把Dockerfile+上下文目录(context)打包发送dockerd;
  2. dockerd初始化基础镜像层(From);
  3. 按顺序执行指令,生成新镜像层;
  4. 最后生成镜像配置层(CMD/ENTRYPOINT/EXPOSE等元数据,不生成存储层)。

上下文误区:.dockerignore剔除无用文件,减少打包传输体积,加快build。

二、常用指令分层规则

指令分层规则说明
FROM第一层基础镜像,所有层的lower底层
RUN生成新层执行shell命令,写入容器upper,产生数据变更层
COPY/ADD生成新层复制宿主机文件进镜像,文件变化缓存失效
ENV/ARG元数据层环境变量,仅修改镜像配置,不占用存储层
WORKDIR/USER元数据仅修改运行配置,无数据写入
EXPOSE元数据仅声明端口,不实际打开端口
CMD/ENTRYPOINT元数据容器启动命令,镜像配置,无分层存储

关键:只有会修改文件系统的指令才产生存储层,配置类指令只改镜像json信息。

三、镜像臃肿根源&优化方案(生产高频)

1.坑1:多条RUN拆分,产生多余分层

错误写法:

RUN yum install -y gcc
RUN yum install -y make
RUN rm -rf /var/cache/yum/*

产生3层:安装gcc层、安装make层、清理缓存层,缓存文件依然存在前两层,镜像变大。

正确写法(同一条RUN合并,安装+清理在同一层):

RUN yum install -y gcc make \
    && rm -rf /var/cache/yum/*

安装和清理在同一个读写层,临时文件直接在本层删除,不会遗留到镜像。

2.坑2:构建依赖与运行依赖共存(多阶段构建Multi-stage重点)

场景:编译Java/Golang项目,编译需要gcc/jdk/maven,运行只需要JRE/二进制程序。
多阶段构建:编译环境在第一阶段镜像,只拷贝产物到最终镜像,抛弃编译依赖。

示例Go多阶段:

#阶段1:编译镜像(临时镜像,不会打包进最终产物)
FROM golang:1.21 AS build
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o app

#阶段2:最终运行镜像(空alpine,仅拷贝二进制)
FROM alpine:3.19
COPY --from=build /app/app /usr/local/bin/
CMD ["/usr/local/bin/app"]

优势:最终镜像只有几十MB,不带编译器、源码、依赖包。

3.坑3:COPY多余文件,利用.dockerignore

# .dockerignore
.git/
node_modules/
*.log
README.md

排除构建无用资源,缩小build上下文,避免多余文件打入镜像层。

4.坑4:指令顺序不合理,缓存频繁失效

原则:易变指令放后面,固定指令放前面
固定:FROM、ENV、RUN安装依赖
易变:COPY源码
错误:先COPY源码再装依赖→每次改源码全量重新构建
正确:先安装依赖→后COPY源码,依赖不变可复用缓存。

FROM centos
RUN yum install -y nginx 固定前置,缓存常驻
COPY ./html /usr/share/nginx/html 频繁改动后置

四、ENTRYPOINT与CMD区别(面试必考点)

  1. CMD:容器默认启动参数,可被docker run 新命令直接覆盖
CMD ["nginx","-g","daemon off;"]

docker run xxx ls -l → 执行ls,替换原CMD
2. ENTRYPOINT:固定入口脚本,CMD变为ENTRYPOINT的参数

ENTRYPOINT ["/start.sh"]
CMD ["default"]

docker run xxx abc → /start.sh abc

五、.dockerignore作用

build打包上下文时忽略匹配文件,1.缩短构建传输时间;2.避免机密文件、无用依赖打包进镜像。


关联文档