Docker 概念
镜像与容器的关系、分层文件系统、容器与虚拟机的差别。
Key Points镜像是多层只读文件系统的叠加,容器在镜像之上加一个可写层;删掉容器,可写层随之丢弃,镜像不受影响。
镜像与容器
An image is the class, a container is the instance. One image can start any number of containers, each with its own writable layer, network and process space. Files written inside a container live only in that layer and vanish with it — persist data with volumes.
Text
镜像(只读,可共享)
├── layer 4 COPY app.jar
├── layer 3 RUN apt-get install -y openjdk-21-jre
├── layer 2 ENV LANG=C.UTF-8
└── layer 1 FROM ubuntu:24.04
容器 = 镜像 + 可写层(container layer)分层与写时复制
Every RUN / COPY / ADD creates a layer keyed by a content hash, shared across machines and images. Reads hit the lower layers directly; a file is copied up to the writable layer only on first write (copy-on-write).
| Concept | Meaning | Practical effect |
|---|---|---|
| Layer | One instruction, one layer | Reordering Dockerfile changes cache hits |
| Copy-on-write | Copy up on first write | Editing a big file copies it whole |
| Immutable image | Layers never change | Same digest = same bytes, everywhere |
容器 vs 虚拟机
| Container | Virtual Machine | |
|---|---|---|
| Isolation | Process-level (namespaces) | Hardware-level (hypervisor) |
| Guest OS | None — shares host kernel | Full OS per VM |
| Startup | Milliseconds | Tens of seconds |
| Size | MB | GB |
| Density | Dozens to hundreds per host | A few per host |
| Isolation strength | Weaker — kernel shared | Strong |
Sharing the kernel means a kernel exploit escapes to the host. For untrusted workloads use a sandboxed runtime such as gVisor or Kata, or just use VMs.
命名空间与 cgroups
pid— 容器内看不到宿主机的其他进程net— 独立的网卡、IP、端口空间mnt— 独立的挂载点视图(rootfs)uts/ipc/user— 主机名、进程间通信、UID 映射cgroups— 限制 CPU、内存、IO,而不是隔离可见性
Bash
# 看容器内的进程号:容器里的 1 号进程在宿主机上是另一个 pid
docker run -d --name demo nginx
docker exec demo ps -ef # 只有 nginx 相关进程
docker top demo # 从宿主机看,pid 不一样镜像与容器
镜像相当于「类」,容器相当于「对象」。一个镜像可以启动任意多个容器,每个容器有自己的可写层、网络、进程空间。容器内写入的文件只存在于该容器的可写层,删除容器即丢失——需要持久化的数据必须挂载卷。
Text
镜像(只读,可共享)
├── layer 4 COPY app.jar
├── layer 3 RUN apt-get install -y openjdk-21-jre
├── layer 2 ENV LANG=C.UTF-8
└── layer 1 FROM ubuntu:24.04
容器 = 镜像 + 可写层(container layer)分层与写时复制
每条 RUN / COPY / ADD 指令生成一层,层之间用内容哈希标识,相同层在多台机器、多个镜像之间共享。读文件时直接从下层读,第一次写某个文件时才把它复制到可写层(copy-on-write)。
| 概念 | 含义 | 实际影响 |
|---|---|---|
| 层 | 一条指令一层 | 调换 Dockerfile 顺序会改变缓存命中 |
| 写时复制 | 首次写才复制 | 改大文件会整体复制 |
| 镜像不可变 | 层永不修改 | 同一 digest 处处一致 |
容器 vs 虚拟机
| 容器 | 虚拟机 | |
|---|---|---|
| 隔离 | 进程级(命名空间) | 硬件级(Hypervisor) |
| 客户系统 | 无 —— 共享宿主内核 | 每台 VM 一个完整系统 |
| 启动 | 毫秒级 | 几十秒 |
| 体积 | MB 级 | GB 级 |
| 密度 | 单机几十到几百 | 单机几台 |
| 隔离强度 | 较弱 —— 内核共享 | 强 |
共享内核意味着:容器里的内核漏洞会波及宿主机,跑不可信代码要用 gVisor / Kata 这类沙箱运行时,或者干脆用 VM。
命名空间与 cgroups
pid—— 容器内看不到宿主机的其他进程net—— 独立的网卡、IP、端口空间mnt—— 独立的挂载点视图(rootfs)uts/ipc/user—— 主机名、进程间通信、UID 映射cgroups—— 限制 CPU、内存、IO,而不是隔离可见性
Bash
# 看容器内的进程号:容器里的 1 号进程在宿主机上是另一个 pid
docker run -d --name demo nginx
docker exec demo ps -ef # 只有 nginx 相关进程
docker top demo # 从宿主机看,pid 不一样