Docker 核心技术和实现原理

Docker 镜像制作笔记

一、镜像制作的原因

当官方镜像无法满足特定需求时,需要自定义镜像,常见原因包括:

  1. 代码打包
    将自编写的代码打包到镜像中,随镜像发布。
  2. 安全性考虑
    第三方镜像可能存在安全漏洞,需要自制镜像。
  3. 特定功能需求
    官方镜像无法满足功能,如数据库审计、特殊配置。
  4. 公司内部规范
    基于公司内部操作系统或标准构建镜像。

二、镜像制作方式

1. 快照方式(偶尔制作的镜像)

  • 思路:在基础镜像上启动容器,安装所需软件和配置后,创建快照。
  • 命令docker commit
  • 语法
复制代码

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

  • 常用参数
    • -a:提交作者
    • -c:使用 Dockerfile 指令创建镜像,可修改启动指令
    • -m:提交说明
    • -p:提交时暂停容器

2. Dockerfile 构建方式(经常更新的镜像)

  • 思路 :将安装流程写入 Dockerfile,通过 docker build 构建镜像。
  • 特点
    • 可重复构建
    • 易于版本管理
    • 自动化 CI/CD 支持

三、快照方式实战

实战一:C++ HelloWorld 镜像

  1. 准备工作目录
复制代码

mkdir -p /data/maxhou/commitimage

cd /data/maxhou/commitimage

  1. 创建 C++ 源码
复制代码

#include <stdio.h>

int main() {

printf("hello docker!\n");

return 0;

}

  1. 启动 CentOS 容器
复制代码

docker run -it --name mycppcommit centos:7 bash

  1. 替换国内软件源(CentOS 7 已停止更新)
复制代码

sed -i.bak \

-e 's|^mirrorlist=|#mirrorlist=|g' \

-e 's|^#baseurl=http://mirror.centos.org/centos\|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos\|g' \

/etc/yum.repos.d/CentOS-Base.repo

yum makecache

  1. 安装编译器并创建源代码目录
复制代码

yum install -y gcc

mkdir /src/

  1. 拷贝源码到容器
复制代码

docker cp ./demo.c mycppcommit:/src

  1. 编译运行测试
复制代码

cd /src

gcc demo.c -o demo

./demo # 输出 hello docker!

  1. 提交为镜像
复制代码

docker commit mycppcommit mycppimg:v1.0

docker images

  1. 运行镜像验证
复制代码

docker run -it mycppimg:v1.0 ./src/demo

一、Dockerfile 是什么

👉 本质:

  • Dockerfile = 镜像构建脚本
  • 用来描述"镜像每一层怎么生成"

👉 核心思想:

镜像 = 一层一层叠加

Dockerfile = 每一层的构建指令


📌 格式

复制代码

注释

INSTRUCTION arguments

特点:

  • 指令不区分大小写(但必须大写规范写法
  • 从上到下顺序执行
  • 每条指令 = 一层镜像

🧠 类比理解

👉 Dockerfile 就像:

🏠 建房子的施工图纸

  • FROM → 地基
  • RUN → 施工
  • COPY → 搬材料
  • CMD → 房子怎么用

二、为什么用 Dockerfile(比 commit 强在哪)

1️⃣ 可定制

  • 自定义环境 + 代码 + 配置

2️⃣ 自动化

  • 一键 build,不用手动敲命令

3️⃣ 可重复

  • 同一个 Dockerfile → 构建结果一致

4️⃣ 非黑箱(重点)

  • commit = 黑箱 ❌
  • Dockerfile = 可读可维护 ✅

5️⃣ 镜像更小

  • 可清理缓存
  • 支持多阶段构建(高级重点)

三、核心指令(必须掌握)


1️⃣ FROM(基础镜像)

👉 作用:

  • 指定基于哪个镜像构建
复制代码

FROM ubuntu:22.04

📌 特点:

  • 必须是第一条指令(除 ARG 外)
  • 默认 tag = latest
  • 可多次使用(多阶段构建)

2️⃣ LABEL(推荐替代 MAINTAINER)

复制代码

LABEL company="com.bit" app="nginx"

👉 作用:

  • 添加元数据(key-value)

3️⃣ COPY(重点)

复制代码

COPY index.html /data/web/www/

👉 作用:

  • 从宿主机 → 镜像

📌 特点:

  • 不支持下载
  • 不会自动解压
  • 必须在 build 上下文内

4️⃣ ADD(增强版 COPY)

复制代码

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

ADD https://xxx.tar.gz /usr/local/

👉 多出来的能力:

  • ✅ 自动下载 URL
  • ✅ 自动解压 tar

📌 面试点:

能用 COPY 就别用 ADD(更可控)


5️⃣ WORKDIR(重点)

复制代码

WORKDIR /usr/local

👉 作用:

  • 设置工作目录(后续指令都在这里执行)

📌 特点:

  • 默认 /
  • 支持相对路径叠加

6️⃣ ENV(重点)

复制代码

ENV WEB_ROOT=/data/web/www/

👉 作用:

  • 设置环境变量

使用:

复制代码

COPY index.html ${WEB_ROOT}


7️⃣ RUN(核心重点🔥)

复制代码

RUN apt-get update && apt install -y nginx

👉 作用:

  • 构建时执行命令

两种形式:

shell 形式(常用)
复制代码

RUN apt-get update

exec 形式
复制代码

RUN ["apt-get", "update"]

📌 面试重点:

  • shell 会走 /bin/sh -c
  • exec 不支持变量替换

8️⃣ CMD(重点)

👉 作用:

  • 容器启动默认执行命令
复制代码

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

📌 特点:

  • 只能有一个(后面会覆盖前面)

9️⃣ ENTRYPOINT(重点)

👉 作用:

  • 程序入口(更强的 CMD)

📌 区别:

CMD ENTRYPOINT
可被覆盖 不容易被覆盖

四、完整构建流程(Nginx 案例总结🔥)


🧩 整体步骤

1️⃣ 基础镜像
复制代码

FROM ubuntu:22.04


2️⃣ 安装依赖(优化顺序重点)
复制代码

RUN apt-get update -y && apt install -y \

build-essential \

libpcre3 libpcre3-dev \

zlib1g-dev

👉 ⚠️ 为什么放前面?

  • 利用缓存(构建更快)

3️⃣ 设置变量
复制代码

ENV NGINX_VERSION=nginx-1.22.1

ENV WEB_ROOT=/data/web/www/


4️⃣ 拷贝网页
复制代码

COPY index.html ${WEB_ROOT}


5️⃣ 设置工作目录
复制代码

WORKDIR /usr/local


6️⃣ 下载源码
复制代码

ADD https://nginx.org/download/${NGINX_VERSION}.tar.gz ./src


7️⃣ 解压
复制代码

RUN cd ./src && tar zxvf ${NGINX_VERSION}.tar.gz


8️⃣ 编译安装
复制代码

RUN cd ./src/${NGINX_VERSION} \

&& ./configure --prefix=/usr/local/nginx \

&& make && make install


9️⃣ 拷贝配置文件
复制代码

COPY nginx.conf ./nginx/conf


五、Dockerfile 优化(面试高频🔥)


1️⃣ 减少层数

复制代码

RUN apt-get update && apt install -y xxx

✔ 合并命令


2️⃣ 利用缓存(非常重要)

👉 不常变的放前面:

复制代码

RUN apt install ...

COPY .


3️⃣ 使用 .dockerignore

避免把无关文件打包进去


4️⃣ 多阶段构建(高级)

👉 编译和运行分离(减小镜像)


六、Dockerfile vs commit(总结)

对比 Dockerfile commit
自动化
可维护
可复现
体积优化
使用场景 正式环境 临时调试

一、CMD vs ENTRYPOINT(最容易混)

一句话区分

指令 作用 是否可被 docker run 覆盖
CMD 默认启动命令 ✅ 会被覆盖
ENTRYPOINT 容器入口(主程序) ❌ 默认不覆盖

CMD 三种写法(重点)

复制代码
复制代码
复制代码
CMD ["executable","param1","param2"]   ✅ 推荐(exec 形式)
CMD ["param1","param2"]                ✅ 给 ENTRYPOINT 传参
CMD command param1 param2              ❌ shell 形式(不推荐)

最佳实践

  • 想让容器"一直运行":

    复制代码
    复制代码
    复制代码
    ENTRYPOINT ["nginx","-g","daemon off;"]
  • 想提供默认参数:

    复制代码
    复制代码
    复制代码
    CMD ["--help"]

二、RUN vs CMD vs ENTRYPOINT 执行时机

指令 执行阶段 作用
RUN 构建镜像时 安装软件、编译
CMD 容器启动时 默认执行命令
ENTRYPOINT 容器启动时 固定入口程序

📌 口诀

RUN 建镜像,CMD/ENTRYPOINT 跑容器


三、EXPOSE:只是"声明",不是"映射"

核心点

复制代码
复制代码
复制代码
EXPOSE 80/tcp

✅ 作用:

  • 文档说明

  • docker ps能看到端口

  • 配合 -P自动映射

❌ 不会:

  • 自动开放端口

  • 替代 -p

✅ 正确用法:

复制代码
复制代码
复制代码
docker run -p 80:80 web1

📌 面试常问

EXPOSE 是给谁看的?

✅ 给运维 / 使用者看的文档


四、ARG vs ENV(构建 vs 运行)

对比总结

项目 ARG ENV
作用阶段 构建阶段 构建 + 运行
是否可覆盖 ✅ docker build --build-arg ❌ 不可覆盖
是否进容器 ❌ 默认不进 ✅ 进容器

✅ 推荐写法(防空值):

复制代码
复制代码
复制代码
ARG UBUNTU_VERSION=22.04
FROM ubuntu:${UBUNTU_VERSION}

✅ ENV 覆盖 ARG:

复制代码
复制代码
复制代码
ARG VERSION
ENV VERSION=${VERSION:-v1.0}

五、VOLUME:防止数据丢失的"保底机制"

核心理解

复制代码
复制代码
复制代码
VOLUME ["/data"]

✅ 特点:

  • 自动创建匿名卷

  • 容器删除 → 数据还在

  • 用户没 -v也能用

✅ 典型场景:

  • MySQL

  • Nginx 日志

  • 配置文件目录

❌ 不能:

  • 指定宿主机目录

  • 替代 -v

📌 一句话

VOLUME 是"防呆设计",不是"挂载命令"


六、SHELL:切换 shell 解释器

复制代码
复制代码
复制代码
SHELL ["/bin/bash","-cvx"]

✅ 影响:

  • 后续 RUN命令

  • 调试非常有用

✅ 常见用途:

  • 看命令展开

  • 调试 shell 脚本


七、USER:安全最佳实践

复制代码
复制代码
复制代码
USER nginx

✅ 推荐:

  • 不要一直用 root

  • 服务类程序用专用用户

✅ 你例子中的结论是对的:

复制代码
复制代码
复制代码
nginx
mysql

八、HEALTHCHECK:容器"体检"

返回值含义

返回码 含义
0 healthy
1 unhealthy
2 保留

✅ 常见写法:

复制代码
复制代码
复制代码
HEALTHCHECK --interval=5s --timeout=3s \
 CMD curl -f http://localhost/ || exit 1

📌 docker ps会显示:

  • (healthy)

  • (unhealthy)


九、ONBUILD:镜像的"钩子"

复制代码
复制代码
复制代码
ONBUILD RUN echo "in build" >> /tmp/build.txt

✅ 触发条件:

  • 别人 FROM你的镜像

✅ 使用场景:

  • 基础镜像

  • SDK / 构建模板

❌ 不适合:

  • 普通业务镜像

十、STOPSIGNAL:控制容器如何退出

复制代码
复制代码
复制代码
STOPSIGNAL 9
信号 行为
SIGTERM (15) 优雅退出
SIGKILL (9) 强制杀死

✅ 你实验结论非常关键:

  • 有 STOPSIGNAL 9 → 直接消失

  • 默认 → nginx 优雅退出


十一、整体记忆表(面试版)

指令 阶段 是否可覆盖 典型用途
FROM 构建 基础镜像
RUN 构建 安装/编译
CMD 启动 默认命令
ENTRYPOINT 启动 主程序
ARG 构建 构建参数
ENV 构建+运行 环境变量
EXPOSE 文档 端口声明
VOLUME 运行 数据持久
USER 运行 安全
HEALTHCHECK 运行 健康检查
ONBUILD 构建 基础镜像钩子

一、VOLUME到底做了什么?

Dockerfile 中的这一行:

复制代码
复制代码
复制代码
VOLUME ["/data"]

✅ 实际效果只有一句话:

告诉 Docker:这个容器里的 /data目录,在运行时应该挂载一个 volume

⚠️ 注意:

  • 构建阶段

    • 不会创建 volume

    • 只是写进镜像的 metadata

  • 运行阶段docker run):

    • Docker 发现这个声明

    • 自动创建一个匿名 volume

    • 挂载到容器的 /data


二、谁才是"创建数据卷"的主体?

行为 是谁做的
创建 volume ✅ Docker(运行时)
声明挂载点 ✅ Dockerfile 中的 VOLUME
管理 volume ✅ Docker(docker volume命令)
指定宿主机目录 VOLUME做不到

✅ 真正创建卷的是:

复制代码
复制代码
复制代码
docker run ...

或:

复制代码
复制代码
复制代码
docker volume create

三、VOLUME≠ 数据卷管理

VOLUME不能做的事情

  • 不能指定宿主机目录

  • 不能指定 volume 名字

  • 不能删除 volume

  • 不能设置 volume driver

VOLUME能做的事情

  • 防止用户忘记 -v导致数据丢失

  • 保证容器删除后数据还在

  • 明确"这个目录是数据目录"

📌 一句话总结

VOLUME是"声明",不是"管理"


四、匿名卷 vs 具名卷 vs bind mount

你这个例子非常典型,我们对比一下:

1️⃣ Dockerfile 中 VOLUME

复制代码
复制代码
复制代码
VOLUME ["/data"]

运行时:

复制代码
复制代码
复制代码
docker run myimage

Docker 会:

复制代码
复制代码
复制代码
/var/lib/docker/volumes/<随机ID>/_data  →  /data

匿名卷

  • 名字随机

  • 容器删了,卷还在

  • 需要 docker volume prune清理


2️⃣ 具名卷(推荐生产)

复制代码
复制代码
复制代码
docker run -v mydata:/data myimage

✅ 可控、可复用、可管理


3️⃣ bind mount(开发常用)

复制代码
复制代码
复制代码
docker run -v /host/data:/data myimage

✅ 直接绑定宿主机目录


五、为什么 MySQL / Nginx 镜像要用 VOLUME

以 MySQL 为例:

复制代码
复制代码
复制代码
VOLUME /var/lib/mysql

原因只有一个:

防止用户忘记 -v,删容器把数据库全删了

📌 这是 Docker 官方镜像的"安全兜底设计"


六、你实验中的结论是完全正确的 ✅

你做的这个流程:

  1. VOLUME ["/data"]

  2. 容器启动

  3. Docker 自动创建匿名卷

  4. 容器删除

  5. volume 仍然存在

✅ 完全符合 Docker 的设计行为

一、EXPOSE 到底"给谁用"?

✅ 正确理解

EXPOSE是:

写给"人"看的说明信息

  • 写给镜像使用者

  • 写给运维

  • 写给 docker ps/ docker inspect

📌 它不会

  • 打开端口

  • 允许访问

  • 做网络映射

  • 影响容器间通信


二、容器之间通信,根本不需要 EXPOSE ✅

这是你最关心的点

容器之间能不能互通,和 EXPOSE 完全无关

例子
复制代码
复制代码
复制代码
docker network create app-net

docker run -d --name db --network app-net mysql
docker run -d --name web --network app-net nginx

web容器里:

复制代码
复制代码
复制代码
ping db
curl db:3306

只要:

  • 在同一 network

  • 容器内服务真的在监听端口

不需要:

  • EXPOSE

  • -p

📌 容器间通信 = Docker 网络 + 端口监听


三、那 EXPOSE 有什么实际作用?

✅ 1️⃣ 给 docker ps

复制代码
复制代码
复制代码
docker ps

你会看到:

复制代码
复制代码
复制代码
PORTS
80/tcp

这个信息来自:

复制代码
复制代码
复制代码
EXPOSE 80

✅ 2️⃣ 给 -P用(自动映射)

复制代码
复制代码
复制代码
docker run -P nginx

Docker 会:

  • 读取 EXPOSE 的端口

  • 自动映射到宿主机随机端口

📌 没有 EXPOSE:

复制代码
复制代码
复制代码
docker run -P

👉 什么都映射不出来


✅ 3️⃣ 给镜像使用者"提示"

复制代码
复制代码
复制代码
EXPOSE 80 443

等于在说:

"这个镜像默认会监听 80 和 443"


四、EXPOSE 在内网 / 外网中的真实角色

场景 是否需要 EXPOSE
容器 ↔ 容器 ❌ 不需要
容器 ↔ 宿主机 ❌ 不需要
docker run -p ❌ 不需要
docker run -P ✅ 需要
文档说明 ✅ 需要

五、一个非常重要的对比(必记)

项目 EXPOSE -p
是否真正开放端口
是否影响容器通信
是否影响宿主机访问
是否只是声明

六、你为什么会觉得"EXPOSE 是给内网用的"?

这是一个非常常见的误解来源

Docker 官方文档 + 很多教程

会说:

"EXPOSE 用于声明容器监听的端口"

然后大家就误以为:

"哦,这是给内网容器访问用的"

❌ 实际是:

"声明" ≠ "生效"


七、生产环境正确姿势 ✅

✅ 推荐写法

复制代码
复制代码
复制代码
EXPOSE 80

✅ 推荐运行方式

复制代码
复制代码
复制代码
docker run -p 80:80 myimage

❌ 不推荐依赖

复制代码
复制代码
复制代码
docker run -P

八、一句话终极记忆(强烈推荐)

**EXPOSE 是"说明书",不是"开关"**​

容器通不通,看网络;宿主机能不能访问,看 -p

一、HEALTHCHECK 是干什么的?

检测容器内部服务是否"健康"

注意:

  • ✅ 容器 进程还在 ≠ 服务正常

  • ✅ 比如:

    • nginx 进程在

    • 但 80 端口不响应

    • 或返回 500

HEALTHCHECK就是干这个的。


二、Dockerfile 中的基本写法

✅ 标准格式

复制代码
复制代码
复制代码
HEALTHCHECK [OPTIONS] CMD command

或关闭继承的健康检查:

复制代码
复制代码
复制代码
HEALTHCHECK NONE

三、OPTIONS 参数(非常重要)

参数 默认值 含义
--interval=DURATION 30s 多久检查一次
--timeout=DURATION 30s 单次检查超时
--start-period=DURATION 0s 启动宽限期
--retries=N 3 连续失败几次算 unhealthy

✅ 示例:

复制代码
复制代码
复制代码
HEALTHCHECK --interval=5s --timeout=3s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

四、CMD 的返回值(核心)

Docker 只看 exit code

返回值 含义
0 ✅ healthy
1 ❌ unhealthy
2 ⚠️ 保留(不要用)

📌 非 0 都是不健康


五、最常见的几种写法(实战)

✅ 1️⃣ HTTP 服务(最常用)

复制代码
复制代码
复制代码
HEALTHCHECK CMD curl -f http://localhost/ || exit 1

或:

复制代码
复制代码
复制代码
HEALTHCHECK CMD wget -q --spider http://localhost/ || exit 1

✅ 2️⃣ TCP 端口检测

复制代码
复制代码
复制代码
HEALTHCHECK CMD nc -z localhost 80 || exit 1

✅ 3️⃣ 只检查进程是否存在(不推荐)

复制代码
复制代码
复制代码
HEALTHCHECK CMD pgrep nginx || exit 1

⚠️ 进程存在 ≠ 服务正常


✅ 4️⃣ 禁用健康检查

复制代码
复制代码
复制代码
HEALTHCHECK NONE

六、Dockerfile 中完整示例(nginx)

复制代码
复制代码
复制代码
FROM nginx:1.22

RUN apt-get update && apt-get install -y curl

HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

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

七、构建 & 运行后怎么看?

✅ 1️⃣ docker ps

复制代码
复制代码
复制代码
docker ps

你会看到:

复制代码
复制代码
复制代码
STATUS
Up 10 seconds (healthy)

或:

复制代码
复制代码
复制代码
Up 10 seconds (unhealthy)

✅ 2️⃣ docker inspect(最详细)

复制代码
复制代码
复制代码
docker inspect <container_id>

重点字段:

复制代码
复制代码
复制代码
"State": {
  "Health": {
    "Status": "healthy",
    "FailingStreak": 0,
    "Log": [...]
  }
}

✅ 3️⃣ 查看失败原因

复制代码
复制代码
复制代码
docker inspect --format='{{json .State.Health}}' <container>

八、HEALTHCHECK 在 docker run 中也能用

复制代码
复制代码
复制代码
docker run \
  --health-cmd="curl -f http://localhost/ || exit 1" \
  --health-interval=5s \
  --health-timeout=3s \
  --health-retries=3 \
  nginx

📌 docker run 的优先级 > Dockerfile


九、在 Docker Compose 中怎么用?

复制代码
复制代码
复制代码
services:
  web:
    image: nginx
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/"]
      interval: 5s
      timeout: 3s
      retries: 3

Docker 镜像制作命令:docker build

功能

  • 使用 Dockerfile 创建镜像。

语法

复制代码

docker build [OPTIONS] PATH | URL | -

  • PATH:Dockerfile 所在的目录路径
  • URL:远程 Git 仓库地址
  • -:从标准输入读取 Dockerfile

关键参数

参数 说明
--build-arg=[] 设置镜像创建时的变量
-f 指定 Dockerfile 路径
--label=[] 设置镜像元数据(标签信息)
--no-cache 构建镜像时不使用缓存
--pull 尝试更新基础镜像到新版本
--quiet, -q 安静模式,只输出镜像 ID
--tag, -t 指定镜像名称和标签,例如 name:tag,可为一个镜像设置多个标签
--network 构建期间设置 RUN 指令的网络模式,默认 default

示例

复制代码

docker build -t mynginx:v1 .

  • 在当前目录下使用 Dockerfile 构建镜像
  • 镜像命名为 mynginx,标签为 v1

Dockerfile 编写优秀实践

1. 善用 .dockerignore 文件

  • 在执行 docker build 时,忽略不必要的路径和文件
  • 优点:
    • 减少发送到 Docker 守护进程的数据量
    • 加快镜像构建速度

2. 镜像的多阶段构建(Multi-stage Build)

  • 编译运行 分开
  • 仅保留最终镜像所需环境
  • 优势:
    • 镜像体积更小
    • 保持清晰的构建流程
  • 注意:单独维护多个 Dockerfile 也可实现类似效果,但更复杂

3. 合理使用缓存

  • 利用 Docker 的 缓存机制,提高构建速度
  • 建议:
    • 内容不变的指令尽量放在前面
    • 减少 COPY 目录下的文件数量
    • 将不经常改变的依赖先处理

4. 基础镜像选择

  • 优先使用官方镜像
  • 尽量选 小体积镜像
    • 应用镜像:node:slim
    • 系统镜像:alpinebusyboxdebian
  • 避免使用过大的基础镜像(如完整 Ubuntu),减少最终镜像臃肿

5. 减少镜像层数

  • 尽量合并 RUNADDCOPY 指令
  • 示例:
复制代码

RUN apt-get update && apt-get install -y \

package1 package2 package3 \

&& rm -rf /var/lib/apt/lists/*

  • 优点:
    • 镜像层少
    • 镜像体积更小

6. 精简镜像用途

  • 每个镜像尽量 功能单一
  • 避免构造大而复杂、多用途的镜像
  • 有利于维护和升级

7. 减少外部源干扰

  • 如果必须从外部下载数据:
    • 指定稳定 URL
    • 带版本信息
  • 保证镜像可复现,其他人使用不出错

8. 减少不必要的包安装

  • 只安装运行应用所需的依赖包
  • 避免安装多余工具
  • 直接降低镜像体积,提高安全性

Docker 镜像制作常见问题整理

1. ADD 与 COPY 的区别

指令 功能 使用场景
ADD 可将本地文件/目录或远程 URL 对应的资源复制到镜像 需要下载远程资源或解压归档文件
COPY 仅能将本地文件/目录复制到镜像 仅需要拷贝本地文件或压缩包到镜像

建议:如果只是简单拷贝本地文件,使用 COPY 即可。


2. CMD 与 ENTRYPOINT 的区别

指令 功能 特点
ENTRYPOINT 指定容器启动后执行的命令 容器行为像一个可执行程序,docker run 参数会传给 ENTRYPOINT;Dockerfile 中只能有一个 ENTRYPOINT
CMD 指定默认的运行命令或参数 可被 docker run 后的命令覆盖

组合使用

  • ENTRYPOINT:指定默认命令
  • CMD:指定默认参数
  • 实现可灵活覆盖参数又保持默认命令的效果

3. 多个 FROM 指令的使用(多阶段构建)

  • 每条 FROM 指令都是一个构建阶段
  • 最终镜像以最后一条 FROM 为准
  • 前置阶段可提供文件给后续阶段,常用于:
    • 编译环境与运行环境分离
    • 只保留运行环境的最小镜像,提高效率和安全性

4. 快照与 Dockerfile 制作镜像的区别

  • 快照:手动操作生成的镜像,难以复现和管理
  • Dockerfile:声明式定义镜像,可版本控制、自动化、可复现

原因:使用 Dockerfile 可以更好地管理和重建镜像


5. 空悬镜像(dangling image)

  • 特征:仓库名、标签均为 <none>
  • 原因:
    1. 镜像更新后,旧镜像名被覆盖
    2. Dockerfile 构建时重复生成同名镜像
  • 查看空悬镜像:
复制代码

docker image ls -f dangling=true

  • 可安全删除,因为一般已经失去存在价值

6. 中间层镜像

  • 用途:加速镜像构建、重复利用资源
  • 特征:
    • Docker 构建每条指令产生一层 image
    • 中间层镜像可能无标签,但被上层镜像依赖
  • 查看中间层镜像:
复制代码

docker image ls -a

  • 不建议删除,否则上层镜像可能出错
  • 不会占用额外存储,相同层只存一份

Docker 镜像原理笔记

一、操作系统基础

  • 操作系统由以下子系统组成:
    • 进程调度子系统
    • 进程通信子系统
    • 内存管理子系统
    • 设备管理子系统
    • 文件管理子系统
    • 网络通信子系统
    • 作业控制子系统
  • Linux 文件管理子系统由 bootfsrootfs 组成:
    1. bootfs
      • 包含 bootloader 和 kernel
      • 功能:引导加载内核,启动时加载,加载完成后被卸载
      • Docker 镜像底层对应 bootfs
    2. rootfs
      • 包含 Linux 标准目录 /dev, /proc, /bin, /etc
      • 对应不同 Linux 发行版(Ubuntu、CentOS 等)

二、Union FS(联合文件系统)

  • 将多个目录合并挂载到一个虚拟目录下
  • 特性:
    • 支持只读和可读写目录合并
    • 写时复制(Copy-on-Write,CoW)
  • 写时复制原理
    • 资源初次修改才创建新副本
    • 未修改资源共享,节省存储和内存
    • 修改操作只在新副本上发生

三、Docker 镜像概念

  • 镜像 = 多层 Union FS 文件系统
  • 每一层称为 layer
  • 镜像特点:
    1. 共享宿主机内核
    2. Base 镜像是最小 Linux 发行版
    3. 同一 Docker 主机支持不同 Linux 发行版
    4. 分层结构,可上层引用下层,实现资源最大化共享
    5. 容器层可写,采用 CoW 技术,仅保存变化部分
    6. 容器层以下都是只读
    7. Docker 从上到下查找文件

四、Docker 分层存储实现原理

1. 支持的 Union FS 类型

  • AUFS、overlay、overlay2、DeviceMapper、VSF
  • 各发行版使用:
    • CentOS: overlay2 / overlay
    • Debian: aufs
    • RedHat: devicemapper
  • 推荐:overlay2(稳定,Docker 默认)

2. Union FS 原理

  • 镜像 = 多个只读层叠加
  • 容器启动时:
    1. 加载镜像只读层
    2. 在顶部加 读写层
    3. 文件修改:
      • Copy-up 到读写层
      • 原只读层保持不变
    4. 文件删除:
      • 在读写层创建 whiteout 文件
      • 屏蔽下层文件,但下层文件不删除

3. overlay2 层级结构

作用
lowerdir 镜像只读层,包括 bootfs/rootfs 及 Dockerfile 构建的镜像层
upperdir 容器读写层,使用 CoW,存储文件修改
workdir 临时中间层,修改操作先进入 workdir,再移动到 upperdir
merged 用户看到的统一目录,展示 upperdir 或 lowerdir 的文件内容

4. 文件操作机制

  • 读取
    • 文件在 upperdir → 直接读取
    • 文件不在 upperdir → 从 lowerdir 读取
  • 写入
    • 首次写入 → copy-up 到 upperdir
    • 再次写入 → 修改 upperdir 副本
  • 删除
    • 创建 whiteout 文件隐藏下层文件
    • lowerdir 不受影响

注意:容器层文件删除只是"遮挡",image 层不会改变


五、Docker 镜像加载原理

  • 启动流程:
    1. bootfs 加载 → 内核加载完成后卸载
    2. rootfs 加载 → 只读检查
    3. 利用 union mount 将可写层挂载到 readonly rootfs 上
  • 运行时:
    • 所有只读镜像层共享
    • 容器最顶层可读写
    • 利用命名空间、控制组、rootfs 实现容器隔离与组装

六、核心概念总结

  1. 镜像是多层只读文件系统叠加
  2. 容器层为可写层,采用 CoW 技术
  3. 文件删除通过 whiteout 实现,原镜像不变
  4. overlay2 是官方推荐,稳定高效
  5. 镜像分层结构实现资源共享和存储节省

实战一:镜像分层存储实战

一、先用一句人话理解整个实战

👉 Docker 镜像本质就是:

一层一层"文件夹"叠起来,最后拼成一个完整的 Linux 系统

而 overlay2 做的事就是:

把多个目录"叠加"成一个目录给你看


二、先建立一个"脑子里的模型"(最重要)

想象:

复制代码

第3层(你修改的) ← 可写

第2层(nginx配置)

第1层(系统文件)

第0层(Debian基础)

Docker 实际干的事:

👉 把这些层 叠起来

你看到的是:

复制代码

一个完整的 Linux 系统(假的,但看起来真的)


三、磁盘上到底长什么样?(重点)

路径:

复制代码

/var/lib/docker/overlay2/

结构👇

复制代码

overlay2/

├── abc123/

│ ├── diff ← 这一层的真实文件

│ ├── lower ← 它的"爸爸层"

│ ├── link ← 短名字

│ └── work

├── def456/

├── ghi789/

├── l/ ← 所有层的"快捷方式"


四、逐个解释(这是核心)

1️⃣ diff ------ 真正存文件的地方

👉 你可以理解为:

每一层就是一个"文件夹快照"

比如:

复制代码

.../diff/usr/sbin/nginx

说明:

👉 nginx 就存在这一层里

📌 重点:

  • 每一层只存"自己新增或修改的文件"
  • 不重复存

2️⃣ lower ------ 层之间的"族谱"

复制代码

cat lower

输出:

复制代码

l/xxx:l/yyy

👉 意思是:

我这一层,是基于 xxx 和 yyy 这两层的

📌 本质:

👉 层与层之间是链式关系


复制代码

cat link

得到:

复制代码

QJTI7BHG2F...

然后你去:

复制代码

overlay2/l/QJTI7BHG2F...

会发现:

👉 它是一个软链接 → 指向 diff

📌 作用:

👉 缩短路径(不然太长)


4️⃣ l 目录 ------ 所有层的"快捷方式集合"

复制代码

overlay2/l/

里面全是:

复制代码

短ID → 指向某个 diff

👉 类似 Windows 快捷方式


五、重点来了:容器启动前 vs 启动后

❌ 没启动容器时

只有:

复制代码

diff(镜像层)

lower(关系)

👉 没有:

  • upper
  • merged

✅ 启动容器后

复制代码

docker run -d nginx

系统帮你创建:

复制代码

upperdir ← 你可以写

merged ← 给你看的"完整系统"

workdir ← 临时用


六、最关键:merged 到底是什么?

👉 merged 就是:

把所有层 + 你的修改 拼起来的"最终视图"

你进去看:

复制代码

cd merged

ls

看到:

复制代码

/bin

/etc

/usr

📌 这就是:

👉 你以为的 Linux 系统


七、读文件过程(超级重要)

当你访问:

复制代码

/usr/sbin/nginx

Docker 怎么找?

👉 从上往下找:

复制代码
  1. upper(你改过?)

  2. layer3

  3. layer2

  4. layer1

👉 找到就停


八、写文件过程(COW核心)

假设你修改 nginx:

第一次修改:

复制代码
  1. 从下层复制到 upper(copy_up)

  2. 修改 upper 中的副本

👉 原来的不动!


第二次修改:

复制代码

直接改 upper


📌 结论:

👉 所有修改都在 upper 层


九、删除文件(非常容易考)

你删除:

复制代码

rm /usr/sbin/nginx

实际发生:

❌ 不会删底层文件

✅ 创建一个:

复制代码

whiteout 文件(遮罩)

👉 效果:

复制代码

你看不到 nginx

但其实还在


十、为什么 docker commit 会越来越大?

因为:

复制代码

删除 ≠ 真删除

只是遮住

👉 所以:

  • 层越来越多
  • 数据越来越大

十一、把整个流程串起来(最重要总结)

镜像构建:

复制代码

Dockerfile 每一行 → 一个 layer(diff)


镜像存储:

复制代码

diff 存内容

lower 存关系

l 存快捷方式


容器运行:

复制代码

镜像层(只读)

  • upper(可写)

= merged(你看到的系统)


十二、最简单理解版本(一定要记)

👉 Docker 做的事其实就一句话:

用 overlay2 把多个文件夹叠起来,再给你一个"假的完整系统"

相关推荐
风酥糖2 小时前
chroot的Linux服务配置-当云服务器真正用起来
linux·运维·服务器
木鱼布2 小时前
安装arm虚机启动失败
linux·运维·arm开发
feng14562 小时前
稳定性-从端到端观测开始
运维
Tom Ma.2 小时前
Docker 安装 OpenClaw
运维·docker·容器
姜太小白2 小时前
【Linux】麒麟V10SP3解决网络设备名不匹配问题
linux·运维·服务器
开开心心_Every2 小时前
内存清理软件灵活设置,自动阈值快捷键清
运维·服务器·pdf·web3·电脑·excel·共识算法
运维老郭2 小时前
MySQL 主从延迟根因诊断法:从现象到本质的全链路排查指南
运维·mysql
泛黄的咖啡店3 小时前
KVM 虚拟化物理机
运维
xcbeyond3 小时前
Linux 磁盘挂载
linux·运维·服务器