Docker 核心概念详解:容器 vs 虚拟机,镜像、容器、仓库与守护进程
从零开始理解 Docker:深入讲解容器与虚拟机的本质区别(隔离层次、性能开销、启动时间对比),以及 Docker 四大核心概念——镜像(Image)、容器(Container)、仓库(Registry)、守护进程(Daemon)的关系与工作原理,帮助运维工程师建立正确的容器认知模型。
Docker 是当前最主流的容器技术,但很多初学者在“会用”之前缺少一个清晰的概念模型。本文不写命令,专门讲Docker 是什么、为什么、怎么运作,帮你在动手之前建立正确的认知。
容器 vs 虚拟机:本质区别
这是理解 Docker 的第一道门槛。两者都能隔离应用,但隔离的层次完全不同。
虚拟机(VM)架构:
┌────────────┐ ┌────────────┐ ┌────────────┐
│ App A │ │ App B │ │ App C │
├────────────┤ ├────────────┤ ├────────────┤
│ Guest OS │ │ Guest OS │ │ Guest OS │
│ (Linux) │ │ (Windows) │ │ (Linux) │
├────────────┴──┴────────────┴──┴────────────┤
│ Hypervisor(虚拟化层) │
├────────────────────────────────────────────┤
│ Host OS(宿主机操作系统) │
├────────────────────────────────────────────┤
│ 物理硬件 │
└────────────────────────────────────────────┘
容器(Docker)架构:
┌────────────┐ ┌────────────┐ ┌────────────┐
│ App A │ │ App B │ │ App C │
│ + 依赖库 │ │ + 依赖库 │ │ + 依赖库 │
├────────────┴──┴────────────┴──┴────────────┤
│ Docker Engine(容器引擎) │
├────────────────────────────────────────────┤
│ Host OS(宿主机操作系统) │
├────────────────────────────────────────────┤
│ 物理硬件 │
└────────────────────────────────────────────┘
关键区别:
| 维度 | 虚拟机 | Docker 容器 |
|---|---|---|
| 隔离层次 | 硬件级(每个VM有自己的内核) | 进程级(共享宿主机内核) |
| 启动时间 | 分钟级 | 秒级甚至毫秒级 |
| 磁盘占用 | GB 级(包含完整 OS) | MB 级(只含应用和依赖) |
| 内存开销 | 每个 VM 需要独立内存 | 共享内核,开销极低 |
| 安全隔离 | 强(内核完全隔离) | 较弱(共享内核,依赖 namespace/cgroup) |
| 适用场景 | 运行不同操作系统、强安全要求 | 同一 OS 下隔离应用、快速部署 |
Docker 的隔离原理
容器不是魔法,它依赖 Linux 内核的两个核心机制:
Linux Namespace(命名空间隔离)
Namespace 让每个容器“以为”自己有独立的系统资源:
Namespace 类型 隔离的内容
─────────────────────────────────────────
PID namespace 进程 ID(容器内的 PID 1 是容器的初始进程)
NET namespace 网络接口、IP、路由表、防火墙规则
MNT namespace 文件系统挂载点(容器看到自己的文件系统)
UTS namespace 主机名和域名
IPC namespace 进程间通信(信号量、消息队列)
USER namespace 用户 ID(容器内的 root ≠ 宿主机的 root)
Linux Cgroups(资源限制)
Namespace 解决“看到什么”,Cgroups 解决“能用多少”:
Cgroup 控制的资源:
cpu → 限制 CPU 使用时间(如最多使用 0.5 核)
memory → 限制内存用量(如最多 512MB,超出被 OOM Kill)
blkio → 限制磁盘 I/O 带宽
net_cls → 标记网络包,配合 tc 限速
pids → 限制容器内的进程数
# 实际效果演示(了解即可,不需要手动操作)
# 启动一个限制 CPU 和内存的容器:
docker run --cpus=0.5 --memory=256m nginx
# Docker 会在宿主机上创建对应的 cgroup:
# /sys/fs/cgroup/memory/docker/<container_id>/memory.limit_in_bytes
# → 值为 268435456(256 × 1024 × 1024 bytes)
Docker 四大核心概念
1. 镜像(Image)
镜像是只读的文件系统快照,包含运行应用所需的一切:代码、运行时、依赖库、配置文件。
镜像的分层结构:
┌──────────────────────────────┐ ← 层4:COPY 应用代码(可写层,构建时)
├──────────────────────────────┤ ← 层3:RUN pip install requirements
├──────────────────────────────┤ ← 层2:RUN apt-get install python3
├──────────────────────────────┤ ← 层1:ubuntu:22.04 基础镜像
└──────────────────────────────┘
每一层都是只读的,层与层之间用内容寻址(SHA256)标识
多个镜像可以共享相同的底层(节省磁盘空间)
类比: 镜像类似于虚拟机的“快照”或光盘镜像,但更轻量,且是分层可复用的。
# 镜像命名规则:
[registry/][namespace/]name[:tag]
docker.io/library/nginx:1.25 # Docker Hub 官方镜像
registry.cn-hangzhou.aliyuncs.com/library/nginx:1.25 # 阿里云镜像
harbor.company.com/backend/api:v1.2.3 # 私有仓库
2. 容器(Container)
容器是镜像的运行实例。Docker 在镜像顶部添加一个可写层,所有运行时的文件修改都写入这一层。
容器运行时结构:
┌──────────────────────────────┐ ← 可写层(Container Layer)
│ 运行时写入的文件/日志 │ 容器停止后这层默认消失
├──────────────────────────────┤
│ 镜像层(只读) │ 多个容器共享同一镜像层
│ Image Layer 1-N │ 节省大量磁盘空间
└──────────────────────────────┘
类比:
镜像 = 程序安装包(.exe 或 .dmg)
容器 = 运行中的程序实例(可以同时运行多个)
同一个镜像可以启动多个容器,互不干扰:
# 3 个 nginx 容器,共享同一个镜像层
docker run -d -p 8081:80 --name web1 nginx
docker run -d -p 8082:80 --name web2 nginx
docker run -d -p 8083:80 --name web3 nginx
# 3 个容器运行中,但磁盘上镜像只存一份
docker images nginx # 只有 1 行
docker ps # 3 行
3. 仓库(Registry)
仓库是镜像的存储和分发中心,类似于 Git 的 GitHub。
常见仓库:
公共仓库:
Docker Hub → hub.docker.com(默认)
Quay.io → quay.io(Red Hat 维护)
国内加速镜像:
阿里云 ACR → registry.cn-hangzhou.aliyuncs.com
腾讯云 TCR → ccr.ccs.tencentyun.com
私有仓库(自建):
Harbor → 企业最常用
GitLab Registry → 与 GitLab CI 集成
JFrog Artifactory
仓库结构:
Registry(仓库服务器)
└── Repository(镜像仓库,如 nginx)
├── Tag: 1.25
├── Tag: 1.24
└── Tag: latest
4. Docker 守护进程(Daemon)
Docker Daemon(dockerd)是运行在宿主机上的后台服务,负责管理所有容器、镜像、网络和存储。
Docker 架构(C/S 模型):
用户(你)
↓ docker 命令(CLI)
↓ 通过 /var/run/docker.sock(Unix Socket)
Docker Daemon(dockerd)
├── 镜像管理(pull/build/push)
├── 容器管理(create/start/stop/rm)
├── 网络管理(bridge/overlay)
└── 存储管理(volume/bind mount)
↓
containerd(实际的容器运行时)
↓
runc(底层容器创建工具)
↓
Linux Namespace + Cgroups
# 查看 Docker Daemon 状态
systemctl status docker
# Docker 架构组件
docker version
# Client: Docker Engine 版本
# Server: dockerd 版本(运行在后台)
# 查看 daemon 的 socket 文件
ls -la /var/run/docker.sock
# srw-rw---- 1 root docker → socket 文件,docker 组的用户可以访问
核心概念关系图
用户操作流程:
docker pull nginx # 从 Registry 拉取 Image
docker build -t myapp . # 用 Dockerfile 构建 Image
↓
Image(镜像)
┌─────────────────────┐
│ layer4: 应用代码 │
│ layer3: 依赖 │
│ layer2: 运行时 │
│ layer1: 基础 OS │
└─────────────────────┘
↓ docker run
Container(容器) Container(容器)
┌───────────────────┐ ┌───────────────────┐
│ 可写层(运行时) │ │ 可写层(运行时) │
├───────────────────┤ ├───────────────────┤
│ 共享镜像层 │ │ 共享镜像层 │
└───────────────────┘ └───────────────────┘
docker push myapp # 把 Image 推送到 Registry
为什么要用 Docker
不是“Docker 是什么”,而是“Docker 解决了什么痛点”:
痛点1:"在我机器上能跑"
问题:开发环境和生产环境依赖版本不一致
解决:镜像将应用和依赖打包在一起,环境一致
痛点2:部署流程复杂
问题:每次部署需要手动安装依赖、配置环境
解决:docker run 一条命令完成部署
痛点3:多应用隔离
问题:同一台机器上多个应用依赖冲突(Python2 vs Python3)
解决:每个容器有独立的依赖环境
痛点4:资源利用率低
问题:VM 独占资源,空闲时浪费
解决:容器共享内核,资源按需使用
痛点5:扩缩容慢
问题:VM 启动需要几分钟
解决:容器秒级启动,快速水平扩展
小结
Docker 的核心认知模型:
镜像是模板,容器是实例。 镜像是静态的只读文件,容器是镜像的运行态,有自己的进程、网络和文件系统(可写层)。
容器不是虚拟机,但也不是裸进程。 它是通过 Linux Namespace 和 Cgroups 实现的“轻量级隔离进程”,与宿主机共享内核。
Docker Daemon 是管控中枢。 所有 docker 命令都是发给 Daemon 的请求,Daemon 再调用 containerd/runc 完成实际操作。
建立了这个认知模型后,后续所有 Docker 操作都会变得自然而然。下一篇我们将动手安装 Docker,并运行第一个容器。
