了解docker的前生 LXC
LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C++中的NameSpace。容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。
Linux Container提供了在单一可控主机节点上支持多个相互隔离的 server container 同时执行的机制。Linux Container有点像 chroot,提供了一个拥有自己进程和网络空间的虚拟环境,但又有别于虚拟机,因为lxc是一种操作系统层次上的资源的虚拟化。
docker并不是LXC替代品,docker底层使用了LXC来实现,LXC将linux进程沙盒化,使得进程之间相互隔离,并且能够控制各进程的资源分配。
在LXC的基础之上,docker提供了一系列更强大的功能。
而docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,这是 LXC 的一种应用,然后发布到任何流行的linux服务器,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类iphone的app),并且容器开销极其低。
Docker 官方文档:docs.docker.com/
Docker理念
开发需要和运维结合的维度,docker主要解决”在我机器上能跑”的问题,确保开发、测试、生产环境一致。
就是将应用程序及其依赖(代码、库、配置文件等)打包到一个标准化的 “容器” 中,确保应用在任何支持 Docker 的环境中(开发机、测试服务器、生产服务器等)都能以相同方式运行,解决 “在我这能跑,在你那跑不了” 的环境一致性问题。
而且实现了资源的高效,容器直接共享主机内核,无需虚拟化整个操作系统,节省内存和 CPU。
高度隔离通过每个容器拥有独立的文件系统、网络和进程空间来实现。

何为”一次镜像,处处运行”
Docker 将 “镜像(Image)” 定义为应用的 “标准化封装体”——包含应用运行所需的全部内容(代码、依赖库、配置文件、运行时环境甚至系统工具),且这个封装体具有 “不可修改性”(只读)。
“一次镜像,处处运行” 的本质是:一旦构建出合格的镜像,它就可以在任何支持 Docker 的环境中(开发机、测试服务器、生产集群、云平台等)以完全一致的方式启动和运行,无需针对不同环境重复配置依赖或调整参数。
在 Docker 出现前,开发和部署中最常见的问题就是 “环境差异”,具体表现为:
- 开发者 A 的本地电脑:应用依赖 Python 3.8 + MySQL 5.7,运行正常;
- 开发者 B 的本地电脑:默认安装 Python 3.9 + MySQL 8.0,运行时出现语法错误或数据库兼容性问题;
- 测试服务器:运维手动配置时少装了一个系统库(如
libssl1.0
),导致应用启动失败; - 生产服务器:操作系统是 CentOS,而开发环境是 Ubuntu,因系统工具差异导致脚本执行报错。
这些问题的根源是:应用的运行依赖 “应用代码 + 外部环境” 的组合,而外部环境(操作系统、库版本、配置等)在不同场景下难以保持一致。 而 “一次镜像,处处运行” 的理念,通过将 “应用 + 依赖环境” 打包成镜像,直接消除了 “环境差异”—— 镜像内部的环境是固定的,无论在哪里运行,镜像本身不变,应用行为自然一致。
Docker三要素
Docker 的 “三要素” 是其设计的核心抽象,三者紧密配合,构成了 Docker 的基本工作模型。分别是:镜像(Image)、容器(Container)、仓库(Repository)。
- 镜像
- 定义应用的 “静态状态”,是容器运行的 “蓝图”。包含应用运行所需的所有依赖(代码、库、环境变量、配置文件等),且内容不可修改(只读)。
- 可以理解为操作系统的 “ISO 镜像文件”—— 一个 ISO 文件是安装系统的模板,同理,Docker 镜像就是启动容器的模板。
- 容器
- 镜像的 “动态运行实例”,是应用实际运行的载体。基于镜像创建,在镜像的只读层上增加一个可写层,所有运行时的操作(如修改文件、运行进程)都在可写层中进行。
- 如果镜像是 “类(Class)”,那么容器就是 “对象(Object)”—— 一个类可以创建多个对象,一个镜像也可以启动多个容器。
- 仓库
- 集中存储和分发镜像的 “仓库”,实现镜像的共享和版本管理。开发者构建镜像后推送到仓库,运维或其他开发者从仓库拉取镜像使用。
- 分类
- 公共仓库(如 Docker
Hub):免费提供大量官方和社区镜像(如
ubuntu
redis
)。 - 私有仓库(如 Harbor):企业内部使用,用于存储业务镜像,确保安全性和私密性。
- 公共仓库(如 Docker
Hub):免费提供大量官方和社区镜像(如
- 类似代码仓库(如 GitHub)—— 开发者提交代码到 GitHub,其他人从 GitHub 拉取代码;同理,开发者推送镜像到 Docker 仓库,其他人从仓库拉取镜像。
Docker 工作原理和架构
Docker 架构是基于客户端-服务器模式的,其中包括多个关键组件,确保容器化应用的高效构建、管理和运行。
Docker 的架构设计使得开发者能够轻松地将应用程序与其所有依赖封装在一个可移植的容器中,并在不同的环境中一致地运行。
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器。
Docker 容器通过 Docker 镜像来创建。

总体架构


从上图不难看出,用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。
而Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;而后Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。
Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;当需要为Docker创建网络环境时,通过网络管理驱动networkdriver
创建并配置Docker容器网络环境;当需要限制Docker容器运行资源或执行用户指令等操作时,则通 execdriver 来完成。
而 libcontainer 是一项独立的容器管理包,networkdriver以及execdriver都是通过libcontainer来实现具体对容器进行的操作。当执行完运行容器的命令后,一个实际的Docker容器就处于运行状态,该容器拥有独立的文件系统,独立并且安全的运行环境等。
graph TD
%% 定义样式(可选,用于美化)
classDef coreFill fill:#EAF7FF,stroke:#66B2FF,stroke-width:2px,rx:6
classDef pluginFill fill:#FFF9E6,stroke:#FFC861,stroke-width:2px,rx:6
classDef kernelFill fill:#F2F2F2,stroke:#999,stroke-width:2px,rx:6
%% 顶层模块
subgraph VolumeModule[Volume]
direction TB
VOLUME(volume)
VOLUMEDRIVER(volumedriver)
LOCALPLUGIN(local<br/>other plugin..)
VOLUME --> VOLUMEDRIVER --> LOCALPLUGIN
class VolumeModule coreFill
end
subgraph DockerDaemon[Docker daemon]
direction TB
DAEMON(Docker daemon)
API(api)
EXECDRIVER(execdriver)
NETWORK(network)
GRAPHDRIVER(graphdriver)
DAEMON --> API
API --> EXECDRIVER
API --> NETWORK
API --> GRAPHDRIVER
class DockerDaemon coreFill
end
subgraph ImageManagement[image management]
direction TB
DISTRIBUTION(distribution)
LAYER(layer)
IMAGE(image)
REGISTRY(registry)
REFERENCE(reference)
DISTRIBUTION --> LAYER
DISTRIBUTION --> IMAGE
DISTRIBUTION --> REGISTRY
DISTRIBUTION --> REFERENCE
class ImageManagement coreFill
end
%% 中间依赖模块
LIBCONTAINER(libcontainer)
LIBNETWORK(libnetwork)
UNIONFS(union FS、<br/>Device mapper)
%% 底层内核
subgraph LinuxKernel[Linux Kernel]
direction TB
KERNEL(Linux Kernel)
NAMESPACE(namespace)
CGROUP(cgroup)
KERNEL --> NAMESPACE
KERNEL --> CGROUP
KERNEL --> UNIONFS
class LinuxKernel kernelFill
end
%% 连接关系
DockerDaemon -->|execdriver 依赖| LIBCONTAINER --> LinuxKernel
DockerDaemon -->|network 依赖| LIBNETWORK --> LinuxKernel
DockerDaemon -->|graphdriver 依赖| UNIONFS
VolumeModule --> LinuxKernel
DockerDaemon -->|交互| ImageManagement
核心组件
Docker Client(客户端)
Docker 客户端是用户与 Docker 守护进程交互的命令行界面(CLI)。它是用户与 Docker 系统的主要交互方式,用户通过 Docker CLI 发出命令,这些命令被发送到 Docker 守护进程,由守护进程执行相应的操作。
- 作用:用户操作 Docker 的入口,通过
docker
命令(如docker build
/pull
/run
)发送请求到 Docker 守护进程。 - 类比:像手机 App 的操作界面,你点 “打开”“下载”,它帮你传递指令。
- 常用命令:
docker run
:运行容器。docker ps
:列出正在运行的容器。docker build
:构建 Docker 镜像。docker exec
:在容器中执行命令。
Docker Daemon(守护进程,图中
Docker daemon
)
Docker 架构的核心,负责管理容器生命周期、构建镜像、分发镜像等任务。
- 作用:Docker 守护进程监听来自 Docker 客户端的请求,并且通过 Docker API 执行这些请求。守护进程将负责容器、镜像等 Docker 对象的管理,并根据请求的参数启动容器、删除容器、修改容器配置等。
- 地位:Docker 架构的核心,所有资源操作都由它调度。
Docker 引擎 API(Docker Engine API)
Docker 引擎 API 是 Docker 提供的 RESTful 接口,允许外部客户端与 Docker 守护进程进行通信。通过这个 API,用户可以执行各种操作,如启动容器、构建镜像、查看容器状态等。API 提供了 HTTP 请求的接口,支持跨平台调用。
- 功能:
- 向 Docker 守护进程发送 HTTP 请求,实现容器、镜像的管理。
- 提供 RESTful 接口,允许通过编程与 Docker 进行交互。
Images(镜像)
- 作用:应用的
“只读模板”,包含运行应用的完整环境(代码、依赖、配置等)。比如
nginx
镜像,拿到它就等于拿到了 “能跑 Nginx 服务的所有必要内容”。 - 特性:分层存储(可复用、体积小)、不可修改(修改会生成新镜像层 )。
Docker Containers(容器)
容器是 Docker 的执行环境,它是轻量级、独立且可执行的软件包。容器是从 Docker 镜像启动的,包含了运行某个应用程序所需的一切——从操作系统库到应用程序代码。容器在运行时与其他容器和宿主机共享操作系统内核,但容器之间的文件系统和进程是隔离的。
- 作用:镜像的 “可运行实例”。基于镜像启动容器,就像用 “模板” 生成一个 “沙盒环境”—— 容器里跑着你的应用,和宿主机 / 其他容器隔离,互不干扰。
- 关键:容器 = 镜像 + 可写层(运行时的临时修改存在这里,容器删除则数据消失,需持久化存储解决)。
Registry(镜像仓库)
Docker 仓库是用来存储 Docker 镜像的地方,最常用的公共仓库是 Docker Hub。用户可以从 Docker Hub 下载镜像,也可以上传自己的镜像分享给其他人。除了公共仓库,用户也可以部署自己的私有 Docker 仓库来管理企业内部的镜像。
- 作用:镜像的 “存储中心”,类似代码仓库(如 GitHub)。分为:
- 公共仓库(如 Docker Hub):存官方 /
社区镜像(
nginx
/mysql
等),方便直接下载。 - 私有仓库(如 Harbor):企业内部用,存自己的业务镜像,保证安全。
- 公共仓库(如 Docker Hub):存官方 /
社区镜像(
Docker Compose
- 作用:Docker Compose 是一个用于定义和运行多容器
Docker 应用的工具。通过 Compose,用户可以使用一个
docker-compose.yml
配置文件定义多个容器(服务),并可以通过一个命令启动这些容器。Docker Compose 主要用于开发、测试和部署多容器的应用。
Docker的工作流程
- 构建镜像:使用
Dockerfile
创建镜像。 - 推送镜像到注册表:将镜像上传到 Docker Hub 或私有注册表中。
- 拉取镜像:通过
docker pull
从注册表中拉取镜像。 - 运行容器:使用镜像创建并启动容器。
- 管理容器:使用 Docker 客户端命令管理正在运行的容器(例如查看日志、停止容器、查看资源使用情况等)。
- 网络与存储:容器之间通过 Docker 网络连接,数据通过 Docker 卷或绑定挂载进行持久化。
以最常用的 docker run
启动一个 Nginx
容器 为例,流程如下(对应图中箭头方向):
客户端发指令: 你在终端执行
docker run nginx
→ Client 把 “启动 Nginx 容器” 的请求,通过 API 发给 Docker Daemon。检查本地镜像:
Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server 的功能使其可以接收 Docker Client 的请求
Daemon 先看本地有没有
nginx
镜像:- 如果有 → 直接用本地镜像启动容器。
- 如果没有 → 去 Registry(如 Docker
Hub)拉取镜像(类似
docker pull
的逻辑)。
拉取镜像(若需要):
Docker Engine 执行 Docker 内部的一系列工作,没项工作都是以一个job的形式存在,job的运行过程中,当需要容器镜像时候,Daemon 会访问 Registry,下载
nginx
镜像到本地 Images 存储区。并且通过镜像管理驱动 Graph driver 将下载镜像以 Graph 的形式存储。启动容器: Daemon 基于
nginx
镜像,创建一个容器实例(在 Containers 区生成):- 给容器分配隔离的资源(网络、进程、文件系统等)。当需要为 Docker 创建网络环境的时候,通过网络管理驱动 Network driver 创建并配置 Docker 容器网络环境,当需要 Docker 容器运行资源或执行用户指令等操作时,通过 Exec driver 来完成,
- Libcontainer 是一项独立的容器管理包, Exec driver 和 Network driver都是通过 Libcontainer 来实现具体对容器的操作
- 启动后,你就能通过浏览器访问 Nginx 服务(容器暴露的端口)。
Docker 架构通过 Client 发指令 → Daemon 调度资源 → 从 Registry 拉取 / 管理镜像 → 启动容器运行应用,实现了 “镜像标准化、容器隔离化、分发中心化” 的完整闭环,让应用部署和管理变得高效、一致 。
简单说:Client 是 “遥控器”,Daemon 是 “管家”,Registry 是 “仓库”,镜像像 “模板”,容器像 “即用即走的沙盒” ,一起协作让应用跑起来。
容器在内核中支持2种重要技术
Docker 引擎(Docker
daemon)是运行在宿主机上的一个后台进程,以守护进程的形式存在。从操作系统的角度来看,它和其他普通进程一样,受到操作系统的调度和管理。当我们通过
Docker 命令(如 docker run
)创建并启动一个容器时,Docker
守护进程会根据配置和镜像信息,在宿主机上创建一系列进程,这些进程就是容器内应用运行的载体。
虽然容器给人一种独立运行环境的感觉,但实际上容器内的进程都是宿主机上的进程,只不过通过一些技术手段实现了资源隔离和限制,让它们看起来像是在一个独立的环境中运行。
namespaces 名称空间
Namespace 是 Linux 内核提供的一种内核级别的资源隔离机制,Docker 利用它实现了不同容器之间以及容器与宿主机之间的资源隔离,具体包括以下几种类型:
UTS (Unix Timesharing System) namespace:隔离主机名和域名。每个容器可以有自己独立的主机名和域名,互不干扰。比如,容器 A 的主机名是
app1
,容器 B 的主机名是app2
,在容器内部进行网络通信时,会根据各自容器内设置的主机名进行解析,不会影响到其他容器。IPC (Inter-Process Communication) namespace:隔离进程间通信。不同容器中的进程无法直接进行 IPC 通信,确保了容器内进程的独立性和安全性。例如,容器 A 中的进程不能通过共享内存、信号量等方式与容器 B 中的进程进行通信。
PID (Process ID) namespace:隔离进程 ID。每个容器都有自己独立的进程编号空间,容器内的进程 ID 从 1 开始计数,就像在一个独立的操作系统中一样。这样可以避免不同容器中的进程 ID 冲突,并且可以对容器内的进程进行独立管理。
NET (Network) namespace:隔离网络资源。每个容器都有自己独立的网络栈,包括网络接口、路由表、iptables 规则等。容器可以配置自己的 IP 地址、端口等网络信息,容器之间的网络通信需要通过网络桥接等方式进行转发。比如,容器 A 监听 80 端口,容器 B 也可以监听 80 端口,它们之间不会产生端口冲突。
MOUNT namespace:隔离文件系统挂载点。每个容器都有自己独立的文件系统视图,可以对文件系统进行独立的挂载、卸载操作。这意味着容器可以看到自己特定的文件系统结构,而不会受到宿主机或其他容器文件系统的影响。
control Group 控制组
cgroup(Control Groups)也是 Linux 内核提供的一种资源管理机制,它主要用于限制进程组对资源的使用,Docker 利用它来控制容器对 CPU、内存、磁盘 I/O、网络带宽等资源的占用,具体如下:
- CPU 资源限制:可以通过 cgroup 为容器分配特定的 CPU 时间片或 CPU 核心。例如,我们可以设置一个容器只能使用宿主机 50% 的 CPU 资源,或者将容器绑定到特定的 CPU 核心上运行,从而避免容器过度占用 CPU 资源,影响宿主机上其他进程的运行。
- 内存资源限制:可以限制容器能够使用的最大内存量。当容器使用的内存达到设定的上限时,系统会根据配置采取相应措施,如终止容器内的进程,以防止容器因为内存不足而导致宿主机出现问题,同时也保证了多个容器之间内存资源的合理分配。
- 磁盘 I/O 限制:可以控制容器对磁盘的读写速度,例如限制容器每秒对磁盘的读取次数、写入数据量等。这对于一些对磁盘 I/O 敏感的应用,如数据库应用,非常重要,可以避免多个容器在同时进行大量磁盘操作时互相干扰。
- 网络带宽限制:虽然不是 cgroup 的原生功能,但结合其他工具(如 tc,Traffic Control),可以实现对容器网络带宽的限制,保证容器在网络传输方面不会占用过多资源,影响宿主机或其他容器的网络通信。
通过写时复制技术(copy-on-write)实现高效的文件操作
写时复制技术是 Docker 实现高效镜像管理和容器文件系统操作的关键技术。在 Docker 中,镜像由多个只读层组成,容器是在镜像的基础上添加一个可写层来运行。
- 镜像层共享:当多个容器基于同一个镜像创建时,它们会共享镜像的只读层。例如,有
10 个容器都基于
ubuntu
镜像创建,这些容器不会各自复制一份完整的ubuntu
镜像文件系统,而是共享镜像的只读层数据,大大节省了磁盘空间。 - 写时复制机制:当容器需要修改文件时,在写操作发生之前,文件实际上还是共享只读层的。只有当容器对文件进行写入操作时,Docker 才会将该文件从只读层复制到容器的可写层,然后在可写层上进行修改。这样既保证了镜像的不可变性,又能高效地实现容器对文件的个性化修改,同时减少了磁盘 I/O 操作和存储空间的占用。这和虚拟机磁盘分配类似,虚拟机分配较大的虚拟磁盘空间,但实际只有在写入数据时才会逐渐占用物理磁盘空间,而不是一开始就完全占用分配的磁盘容量。
为什么 Docker 会比 VMware 快
Docker 和 VMware 都是常见的虚拟化技术,不过 Docker 在运行速度上通常比 VMware 更快
虚拟化实现方式不同
- Docker:Docker 利用 Linux 内核的 Namespace 和 Cgroup 技术实现容器级别的虚拟化。Namespace 提供资源隔离,像进程 ID、网络、文件系统挂载点等资源,在不同容器间相互隔离;Cgroup 负责资源限制,对 CPU、内存、磁盘 I/O 等资源的使用进行约束。由于 Docker 是在操作系统内核层面进行的虚拟化,启动容器就相当于在宿主机上启动一个进程,因此启动速度非常快,一般在秒级甚至毫秒级。
- VMware:VMware 是基于硬件的虚拟化技术,它通过 Hypervisor(虚拟机监视器)在物理硬件和虚拟机之间建立一个抽象层,模拟出完整的硬件环境,包括 CPU、内存、硬盘、网卡等。虚拟机需要加载自己的操作系统,这个过程涉及到引导操作系统内核、初始化硬件驱动等一系列复杂操作,所以启动时间较长,通常需要几十秒到几分钟不等。
资源占用情况
- Docker:容器共享宿主机的操作系统内核,容器本身只包含应用程序及其依赖,不需要额外的操作系统镜像,因此占用的内存、CPU 等资源较少。比如一个简单的 Web 应用容器,可能只需要几十兆的内存就能运行起来。这使得在同一台物理机上可以同时运行大量的 Docker 容器,并且资源利用率较高,容器之间的切换也相对迅速。
- VMware:每个虚拟机都运行着完整的操作系统,除了要运行应用程序外,还需要为操作系统内核、驱动程序等分配资源。例如,一个安装了 Windows 操作系统的虚拟机,即使没有运行任何应用程序,也会占用几百兆甚至上 GB 的内存,同时还会占用一定的 CPU 资源来模拟硬件环境。这就导致在相同的硬件条件下,能运行的虚拟机数量有限,并且虚拟机之间的切换也会比较耗时。
镜像和存储机制
- Docker:采用分层的镜像存储机制,多个容器可以共享同一个镜像层,只有在对文件进行写入操作时,才会将文件从只读层复制到可写层(写时复制技术,copy-on-write)。这种机制不仅节省了磁盘空间,而且在创建和启动容器时,由于不需要重复复制大量文件,大大提高了操作速度。
- VMware:虚拟机的镜像通常是整个虚拟磁盘的完整副本,每次创建新的虚拟机都需要复制或分配一定大小的磁盘空间。即使多个虚拟机使用相同的操作系统,也无法像 Docker 那样共享镜像层,这就导致存储成本较高,并且在创建和启动虚拟机时,需要花费更多时间来处理磁盘镜像。
应用部署和启动流程
- Docker:部署应用时,只需将打包好的容器镜像快速分发到目标环境,然后通过简单的命令即可启动容器,应用就能立即运行。由于容器启动速度快,整个部署流程非常高效,适合快速迭代的开发和持续集成 / 持续部署(CI/CD)场景。
- VMware:部署虚拟机时,需要先创建虚拟机实例,然后安装操作系统,再进行各种配置和软件安装,整个过程较为繁琐。而且虚拟机的启动过程相对漫长,这使得应用从部署到可用的时间大大增加,不太适合对响应速度要求较高的场景。
所以说,在 DevOps 的大浪潮下,Docker 的出现是必然的。
hello world 分析介绍
启动后台容器,运行 hello world镜像
1 | ergoutree@ergoutree-virtual-machine:~$ docker pull hello-world |
1 | ergoutree@ergoutree-virtual-machine:~$ docker run hello-world |
首先,这部分
1 | Using default tag: latest |
大意是本地没有 hello-world 镜像,所以拉取就会下载一个 hello-world 镜像,并在容器内运行
其中
Using default tag: latest
:默认拉取latest
标签的镜像(最新版本)。latest: Pulling from library/hello-world
:从 Docker 官方仓库(library
是官方镜像库)拉取镜像。
1 | e6590344b1a5: Pull complete |
e6590344b1a5: Pull complete
:镜像的分层文件拉取完成(hello-world
镜像非常小,只有一层)。Status: Downloaded newer image
:确认镜像已成功下载到本地
之后是 run 命令启动 hello-world 镜像
1 | Hello from Docker! |
- 容器启动后,输出了
Hello from Docker!
及详细说明,这是hello-world
镜像的预设行为 —— 通过一个简单的可执行文件输出验证信息。 - 输出之后,这个容器实例就会把自己回收掉。
1 | To generate this message, Docker took the following steps: |
- 说明中清晰列出了 Docker 工作的核心步骤:客户端与守护进程通信 → 拉取镜像 → 创建容器 → 运行程序 → 输出结果,直观展示了 Docker 的工作流程。
这其中,run 命令做了如下事情
flowchart TD
A([开始]) --> B{Docker在本机中寻找该镜像}
B -->|是| C[以该镜像为模板生产容器实例运行]
B -->|否| D[去Docker Hub上查找该镜像]
D --> E{Hub能否找到}
E -->|是| F[下载该镜像到本地]
F --> C
E -->|否| G[返回失败错误,查不到该镜像]
