Docker 镜像 & Dockerfile 

一、整体认知:镜像是如何诞生的?

Dockerfile → docker build → 镜像(多层只读)→ docker run → 容器(可写层)

核心结论

  • ✅ Dockerfile 是“源代码”

  • ✅ 镜像是“构建产物”

  • ✅ 容器是“运行实例”

  • ✅ 分层结构是 Docker 的灵魂


二、镜像分层结构(结合你发的图)


纯文本

纯文本

┌───────────────┐  ← 容器层(可写,临时)
│  Container    │
├───────────────┤
│  CMD / ENTRY  │  ← 镜像层4(不新增体积)
├───────────────┤
│  Nginx Install│  ← 镜像层3(RUN)
├───────────────┤
│  ADD Source   │  ← 镜像层2(ADD)
├───────────────┤
│  Yum Install  │  ← 镜像层1(RUN)
├───────────────┤
│  CentOS 7     │  ← 基础镜像层(FROM)
└───────────────┘

关键点

特性

说明

每层只读

镜像一旦构建不可更改

层可复用

不同镜像可共享同一层

写时复制

修改才复制到容器层

缓存命中

上层不变,下层直接复用


三、Dockerfile 如何驱动分层构建(逐条对照)

1️⃣ FROM(基础层)


dockerfile

dockerfile

FROM centos:7

✅ 对应:

  • 镜像最底层

  • 所有后续层的基础

📌 原则:

  • 越小越好

  • 越稳定越好


2️⃣ RUN(构建层,核心)


dockerfile

dockerfile

RUN yum install -y gcc gcc-c++ make

✅ 对应:

  • 镜像层 1

  • 每一条 RUN = 一个新层

📌 优化写法:


dockerfile

dockerfile

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

3️⃣ ADD / COPY(文件层)


dockerfile

dockerfile

ADD nginx-1.20.tar.gz /usr/local/

✅ 对应:

  • 镜像层 2

  • 引入外部代码或源码

📌 规则:

  • ADD:自动解压(慎用)

  • COPY:语义清晰(推荐)


4️⃣ RUN(编译安装层)


dockerfile

dockerfile

RUN cd /usr/local/nginx && ./configure && make && make install

✅ 对应:

  • 镜像层 3

  • 构建期执行,不占容器运行时


5️⃣ CMD / ENTRYPOINT(运行定义层)


dockerfile

dockerfile

CMD ["nginx", "-g", "daemon off;"]

✅ 对应:

  • 不生成新镜像层

  • 决定容器启动行为

指令

是否可覆盖

场景

CMD

✅

默认参数

ENTRYPOINT

❌

固定主程序


四、容器运行时发生了什么?

  1. Docker 从镜像复制所有只读层

  2. 在最上层挂载 可写容器层

  3. 执行 CMD / ENTRYPOINT

  4. 容器停止 → 可写层销毁

  5. 镜像始终不变 ✅

📌 这就是 环境一致性​ 的根本原因


五、Dockerfile 核心指令速查表(考试级)

指令

是否建层

作用

建议

FROM

✅

基础镜像

必写

RUN

✅

构建命令

合并写

COPY

✅

拷文件

首选

ADD

✅

拷+解压

少用

CMD

❌

启动命令

可覆盖

ENTRYPOINT

❌

固定入口

生产推荐

ENV

❌

环境变量

必用

WORKDIR

❌

工作目录

代替 cd

EXPOSE

❌

声明端口

仅说明

USER

❌

运行身份

非 root

VOLUME

❌

数据卷

存数据


六、镜像构建三大核心机制(图中红字)

✅ 1. 分层构建

  • 每层对应 Dockerfile 指令

  • 独立、可复用、可缓存

✅ 2. 缓存复用

  • 指令不变 → 层不变 → 秒级构建

  • 改一行代码 → 只重跑该行及之后

✅ 3. 写时复制(CoW)

  • 镜像层共享

  • 修改才复制

  • 节省磁盘 & 内存


七、Dockerfile 最佳实践(实战级)

✅ 使用 .dockerignore

✅ 合并 RUN,清理缓存

✅ 变量抽成 ENV

✅ 代码放最后(利用缓存)

✅ 多阶段构建减小体积

✅ 非 root 用户运行

✅ 明确 tag,不用 latest


八、多阶段构建(进阶,但非常值得)


dockerfile

dockerfile

FROM gcc AS builder
RUN gcc main.c -o app

FROM alpine
COPY --from=builder /app /app
CMD ["/app"]

常见面试题:

1️⃣ 有没有自己写过镜像?写过哪些?怎么写的?

✅ 写过,实际生产中使用过

常见写过的基础镜像:

  • 业务应用镜像

    • Java(Spring Boot)

    • Python(Flask / FastAPI / Django)

    • Node.js(Express / NestJS)

  • 中间件镜像

    • Nginx(自定义配置)

    • Redis(修改配置、加密码)

  • CI/CD 工具镜像

    • Maven + JDK

    • Node + Yarn + Build 工具

  • 基础运行镜像

    • Alpine + 必要依赖(精简运行时)

怎么写的(标准流程):

  1. 编写 Dockerfile

  2. 使用多阶段构建(Multi-stage build)

  3. 选择合适的基础镜像(alpine / distroless)

  4. 拷贝代码 / 依赖

  5. 暴露端口

  6. 定义启动命令(CMD / ENTRYPOINT)

  7. 构建镜像

    
    

    bash

    bash

    docker build -t myapp:1.0 .
  8. 推送镜像仓库

    
    

    bash

    bash

    docker push myapp:1.0

✅ 示例(Spring Boot):


dockerfile

dockerfile

FROM maven:3.9-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests

FROM openjdk:17-jdk-alpine
COPY --from=build /app/target/app.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]

2️⃣ Dockerfile 常用指令有哪些?

指令

作用

FROM

指定基础镜像(必须)

MAINTAINER

维护者信息(已不推荐)

LABEL

镜像元数据(推荐)

RUN

构建时执行命令

CMD

容器启动时默认命令

ENTRYPOINT

容器主进程

EXPOSE

声明端口

ENV

设置环境变量

ARG

构建参数

ADD

复制文件(支持 URL、自动解压)

COPY

复制文件(推荐)

VOLUME

定义挂载点

WORKDIR

设置工作目录

USER

指定运行用户

HEALTHCHECK

健康检查

ONBUILD

子镜像触发指令


3️⃣ 常见易混指令区别(高频考点)

✅ ADD vs COPY

对比项

ADD

COPY

复制本地文件

✅

✅

支持 URL

✅

❌

自动解压 tar

✅

❌

推荐使用

❌(复杂)

✅(简单明确)

✅ 官方建议:

能用 COPY就不要用 ADD


✅ CMD vs ENTRYPOINT

对比项

CMD

ENTRYPOINT

作用

默认参数

主程序

是否可被覆盖

✅ 容易被 docker run覆盖

❌ 不易被覆盖

组合使用

常与 ENTRYPOINT 搭配

定义容器主进程

常见用法

默认值

固定启动命令

✅ 推荐写法(黄金组合):


dockerfile

dockerfile

ENTRYPOINT ["nginx"]
CMD ["-g","daemon off;"]

✅ 覆盖行为:


bash

bash

docker run nginx -t
# CMD 被替换,ENTRYPOINT 不变

4️⃣ Docker 镜像怎么优化?(非常重要)

✅ 1. 使用更小的基础镜像

  • alpine(最小)

  • distroless(最安全)

  • 避免 latest


dockerfile

dockerfile

FROM node:18-alpine

✅ 2. 多阶段构建(Multi-stage Build)

✅ 减少最终镜像体积


dockerfile

dockerfile

FROM golang:1.21 AS builder
WORKDIR /app
RUN go build -o app

FROM alpine
COPY --from=builder /app/app .
CMD ["./app"]

✅ 3. 合并 RUN 指令(减少层数)

❌ 不好:


dockerfile

dockerfile

RUN apt update
RUN apt install -y curl

✅ 好:


dockerfile

dockerfile

RUN apt update && apt install -y curl \
    && rm -rf /var/lib/apt/lists/*

✅ 4. 清理缓存 & 临时文件


dockerfile

dockerfile

rm -rf /var/cache/apk/*
rm -rf ~/.cache

✅ 5. 合理使用 .dockerignore

避免把以下内容打进镜像:


纯文本

纯文本

.git
node_modules
target
*.log

✅ 6. 非 root 用户运行


dockerfile

dockerfile

RUN addgroup -S app && adduser -S app -G app
USER app

✅ 7. 镜像分层设计(复用性)

  • 不常变的内容放前面

  • 经常变的放后面

更多推荐