容器技术发展史与Docker核心架构
容器技术发展史与 Docker 核心架构
📑 目录
- 一、容器技术发展史
- 二、什么是虚拟化、容器化
- 三、为什么要虚拟化、容器化
- 四、虚拟化的实现方式
- 五、容器虚拟化的底层基础
- 六、Docker 是什么
- 七、Docker 架构
- 八、Docker 生态:为什么要设计镜像和仓库
一、容器技术发展史
容器不是一个新概念或者新技术,很早就有了,只是近几年遇到了云计算,整个技术被彻底引爆了。下面按时间线梳理它的演进脉络。
1. Jail 时代:进程隔离的起源
| 时间 | 事件 | 核心贡献 |
|---|---|---|
| 1979 年 | 贝尔实验室发明 chroot | 进程隔离的鼻祖,将进程及其子进程的根目录更改为文件系统中的新位置,隔离后该进程无法访问外面的文件,被称为 Chroot Jail(监狱) |
| 2000 年 | FreeBSD 4.0 发行 FreeBSD Jail | 不仅有了 chroot 的文件系统隔离,还扩充了独立的进程和网络空间,能为每个系统分配 IP 地址 |
| 2001 年 | Linux VServer 发行 | 对文件系统、网络地址、内存等资源进行分区 |
| 2004 年 | Solaris Containers 发行 | 结合系统资源控制和区域进行隔离,并添加了快照和克隆能力 |
这个时期的进程隔离技术大多以 Jail 模式为核心,基本实现了进程相关资源的隔离操作,但没有更大的应用场景,发展有限。
2. 云时代:从隔离到资源控制
2006 年 Google 101 计划提出云的概念。云计算需要处理海量数据、超高并发、快速扩展等问题,此时不仅仅需要隔离,还需要能够对资源进行控制和调配。
| 时间 | 事件 | 要点 |
|---|---|---|
| 2006 年 | Google 推出 Process Container | 限制、统计和隔离一组进程的资源使用(CPU、内存、磁盘 I/O、网络),一年后更名为 Control Groups(cgroups),最终合并到 Linux 内核 2.6.24 |
| 2008 年 | LXC 推出 | Linux 容器管理器的第一个、最完整的实现,使用 cgroups 和 Linux 命名空间实现,无需内核补丁。同年 Google 推出 GAE,并在其中使用 Borg(Kubernetes 的前身) 来对容器进行编排和调度 |
| 2011 年 | CloudFoundry 推出 Warden | 早期使用 LXC,后来替换为自己的实现,直接对 Cgroups 及 Linux Namespace 操作 |
| 2013 年 | LMCTFY 启动 | Google 容器堆栈的开源版本;后因 Google 与 Docker 合作转向 libcontainer,于 2015 年停止 |
| 2013 年 | Docker 推出风靡全球 | 最初是 dotCloud 公司的内部项目,初期使用 LXC,之后采用自研的 libcontainer |
Docker 与其他只做容器的项目不同,它引入了一整套管理容器的生态系统:高效、分层的容器镜像模型,全局和本地的容器注册库,清晰的 REST API、命令行等。Docker 不仅解决了容器化问题,还解决了分发问题,很快被各大厂商选择变成了云基础设施。
3. 云原生时代:Docker 与 K8s 的竞争与标准确立
Docker 生态扩张后,与 CoreOS 的布局产生直接竞争,容器江湖逐渐分为 Google 派系和 Docker 派系,行业标准的诉求越来越强烈。
| 时间 | 事件 | 要点 |
|---|---|---|
| 2014 年 6 月 | Google 发布开源容器编排引擎 Kubernetes(K8S) | Google 内部调度系统 Borg 拥有 10 多年容器经验;K8S 解决容器编排、网络、负载均衡、监控、部署、更新等问题 |
| 2014 年 12 月 | CoreOS 发布开源容器引擎 Rocket(rkt) | 与 Docker 正式分开发展 |
| 2015 年 | Docker 推出容器集群编排组件 Swarm | 在 Docker 1.12 及更高版本中与 Docker 引擎集成 |
| 2015 年 6 月 | Docker 成立 OCI | Docker 将 Libcontainer 捐出并改名 RunC,交由中立基金会管理;OCI 制定容器镜像格式和容器运行时规范(Runtime Spec / Image Spec / Distribution Spec),解决容器的构建、分发和运行问题 |
| 2015 年 7 月 | Google 带头成立 CNCF | 旨在构建云原生基础设施,K8S 是第一个纳入的项目(后续 Prometheus、ETCD 等也加入),解决应用管理及容器编排问题 |
此后 K8s 逐步成为云原生事实标准,一系列标准相继确立:
- 2016 年发布 CRI 标准:Container Runtime Interface,K8s 定义的一组与容器运行时交互的接口,只要实现这套接口就能对接 K8s。由于 Docker 仍是事实标准,产生了 dockershim(K8s 对接 Docker 的 CRI 实现)。
- 2016 年 Docker 捐献 containerd:从 Docker Engine 中剥离出来捐献给 CNCF;Google 为将 containerd 加入 CRI 标准开发了 cri-containerd。
- 2016 年 CRI-O 发布:让 K8s 可以不依赖传统容器引擎(如 Docker)也能管理容器化工作负载。
- 2017 年 containerd 确定作为标准 CRI:各大厂商(AWS、Microsoft Azure、VMware 等)纷纷拥抱 K8s,K8s 成为容器编排领域的绝对标准,Docker 成为容器事实标准。
💡 结论:Kubernetes 已成容器编排领域的绝对标准,Docker 已成容器事实标准。
4. 编排与容器的技术演进之路
围绕"容器运行时如何与 K8s 对接"这一问题,技术栈经历了如下演进:DockerClient → RUNC & Shim → CRI-Containerd → CRI-O → Containerd。其本质是不断剥离 Docker 一家独大的情况,确保各厂商都能搭建自己的容器平台。
以腾讯 TKE(腾讯商用 K8S 产品)为例,生产集群支持选择 containerd 和 docker 两种运行时模式,选择建议如下:
- containerd:调用链更短,组件更少,更稳定,占用节点资源更少,建议选择 containerd。
- 以下情况仍需用 docker:使用
docker build/push/save/load等命令;调用 docker API;需要 docker compose 或 docker swarm。
二、什么是虚拟化、容器化
在理解 Docker 之前,先弄清三个基础概念。
- 物理机:实际的服务器或计算机,相对于虚拟机而言的实体计算机的称呼。它提供给虚拟机以硬件环境,有时也称为"宿主"或"寄主"。
- 虚拟化:通过虚拟化技术将一台计算机虚拟为多台逻辑计算机,每个逻辑计算机可运行不同的操作系统,且应用程序可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。
- 容器化:一种虚拟化技术,又称操作系统层虚拟化(Operating system level virtualization),将操作系统内核虚拟化,允许用户空间软件实例被分割成几个独立的单元在内核中运行。这个软件实例也被称为一个容器(containers)。对每个实例的拥有者来说,服务器程序看起来就像是自己专用的。
生活类比:
- 物理机像一个庄园,独立占用一块土地,花园都是自己的,其他人无法共享。
- 虚拟机相当于开发商的一个楼盘,一栋楼一套房子一户人家,共享宅基地、小区花园和游乐设施。
- 容器相当于在一间房子里开辟出一个又一个胶囊公寓,共享卫生间、厨房、WiFi,只有衣服、电脑等私人物品是自己的。
三、为什么要虚拟化、容器化
虚拟化和容器化的最主要目的就是资源隔离,随着资源隔离的实现,逐渐带来了更大的收益。
| 收益 | 说明 |
|---|---|
| 资源利用率高 | 将利用率较低的服务器资源进行整合,用更少硬件资源运行更多业务,降低 IT 支出和运维管理成本 |
| 环境标准化 | 一次构建,随处执行。Docker 镜像提供除内核外完整的运行时环境,确保应用运行环境一致性,不再出现"这段代码在我机器上没问题啊"这类问题 |
| 资源弹性伸缩 | 根据业务情况动态调整计算、存储、网络等资源,如双 11 扩容 100 个、过后收回 |
| 差异化环境提供 | 同时提供多套差异化执行环境,如一个服务依赖 Ubuntu、另一个依赖 CentOS,无需购买两台物理机 |
| 沙箱安全 | 在虚拟执行环境中运行不安全或不稳定软件,不影响宿主系统,如在容器里执行 rm -rf /* 不会搞死整个服务器 |
| 更轻量、启动更快 | Docker 容器直接运行于宿主内核,无需启动完整操作系统,可做到秒级甚至毫秒级启动 |
| 维护和扩展容易 | 分层存储与镜像技术使重复部分复用更容易,Docker Hub 提供大量官方基础镜像,可直接使用或进一步定制 |
四、虚拟化的实现方式
应用程序的执行环境可分为三层:硬件层(提供硬件抽象,包括指令集架构、硬件设备及访问接口)、操作系统层(提供系统调用接口,管理硬件资源)、程序库层(提供数据结构定义及函数调用接口)。
基于这三分层,常见虚拟化类别有:
| 类别 | 定位 | 原理 |
|---|---|---|
| 虚拟机 | 硬件层与操作系统层之间 | 通过"伪造"硬件抽象接口,将操作系统及以上的层嫁接到硬件上,实现接近物理机的功能。如在一台 Windows 电脑上运行 Android 虚拟机 |
| 容器 | 操作系统层与函数库层之间 | 通过"伪造"操作系统接口,将函数库层以上的功能置于操作系统上。以 Docker 为例,基于 Linux 的 Namespace 和 Cgroup 实现隔离容器。容器把一个个应用单独封装隔离,体积比虚拟机小很多 |
| JVM 之类的虚拟机 | 函数库层与应用程序之间 | 在应用层与函数库层之间建立抽象层,对下适配不同操作系统函数库,对上提供统一运行环境 |
主机虚拟化(虚拟机)根据虚拟化层是否直接位于硬件之上,分为两种:
- Type1:Hypervisor 直接运行在硬件之上,没有宿主操作系统,直接控制硬件资源和客户机。典型如 Xen、VMware ESX。
- Type2:Hypervisor 运行在宿主操作系统之上,作为其中一个应用程序,客户机是宿主机上的一个进程。如 VMware Workstation。
五、容器虚拟化的底层基础
容器虚拟化有别于主机虚拟化,是操作系统层的虚拟化,通过 namespace 进行各程序的隔离,加上 cgroups 进行资源的控制,以此来实现虚拟化。这两个能力都是 Linux 内核提供的,而非 Docker 提供的。
1. Namespace(命名空间)
namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,两拨进程根本感觉不到对方的存在。改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。
Linux 提供了三个 API 来操作 namespace:clone()、setns() 和 unshare(),通过指定调用参数来确定隔离哪项 namespace。常用隔离参数及资源如下:
| namespace | 系统调用参数 | 被隔离的全局系统资源 | 引入内核版本 |
|---|---|---|---|
| UTS | CLONE_NEWUTS | 主机名和域名 | 2.6.19 |
| IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存(进程间通信) | 2.6.19 |
| PID | CLONE_NEWPID | 进程编号 | 2.6.24 |
| Network | CLONE_NEWNET | 网络设备、网络栈、端口等 | 2.6.29 |
| Mount | CLONE_NEWNS | 文件系统挂载点 | 2.4.19 |
| User | CLONE_NEWUSER | 用户和用户组 | 3.8 |
这些 namespace 在容器环境下的隔离效果:UTS 让每个容器拥有独立主机名;IPC 使不同 IPC namespace 之间不能通信;PID 让每个容器有其 PID 为 1 的 root 进程;Network 让每个容器有独立的网络设备、IP、路由表、端口号;Mount 让每个容器看到不同的文件系统层次结构;User 让每个容器有不同的 user 和 group id。
2. cgroups(控制组)
cgroups(Control Groups)是 Linux 内核提供的一种机制,可以把一系列系统任务及其子任务整合到按资源划分等级的不同组内,从而为系统资源管理提供一个统一框架。简单说,cgroups 可以限制、记录任务组所使用的物理资源。本质上,cgroups 是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度触发相应钩子,达到资源追踪和限制的目的。
Docker 及 K8s 中的 Pod 就使用了 cgroups 提供的资源限制能力。其主要用途包括:Resource limitation(限制资源使用)、Prioritization(优先级控制)、Accounting(审计统计)、Control(挂起/恢复进程)。可控制的子系统有:
| 子系统 | 作用 |
|---|---|
| blkio | 对块设备的 IO 进行限制 |
| cpu | 限制 CPU 时间片的分配 |
| cpuacct | 生成 cgroup 中任务占用 CPU 资源的报告 |
| cpuset | 分配独立的 CPU 和内存节点 |
| devices | 限制设备文件的创建和读写 |
| freezer | 暂停/恢复 cgroup 中的任务 |
| memory | 限制可用内存并生成资源占用报告 |
| perf_event | 允许 perf 观测 cgroup 中的 task |
| net_cls | 对数据报文打类别标识符,配合 tc 进行网络限制 |
| hugetlb | 限制内存页数量 |
| pids | 限制任务数量 |
| rdma | 限制 RDMA 资源 |
3. LXC
LXC(LinuX Containers)是一种操作系统层虚拟化技术,为 Linux 内核容器功能提供用户空间接口,将应用打包成软件容器,内含应用代码以及所需操作系统核心和库。它是最早一批真正把完整容器技术用一组简易工具和模板来简化使用的方案。
但 LXC 比起直接通过内核调用使用容器技术,复杂程度并没有多大降低——必须学会一组 LXC 命令工具,通过批量命令实现数据迁移并不容易,隔离性也没有虚拟机那么强大。后来就出现了 Docker,从一定程度上来说,Docker 就是 LXC 的增强版。
📌 Namespace、cgroups、LXC 的实战操作会在本系列第 7 篇详细演示,此处先建立概念认知。
六、Docker 是什么
1. Docker 的本质
Docker 本质其实是 LXC 之类的增强版,它本身不是容器,而是容器的易用工具。容器是 Linux 内核中的技术,Docker 只是把这种技术在使用上简易普及了。Docker 早期版本的核心就是 LXC 的二次封装发行版。
Docker 是基于 Go 语言实现的一个开源项目,主要目标是 “Build, Ship and Run Any APP, Anywhere”,即通过对组件的封装、分发、部署、运行等生命周期的管理,使用户的应用及其运行环境能够做到"一次封装,到处运行"。
早期 Docker 利用 LXC 做容器管理引擎,但创建容器时不再使用模板安装,而是通过镜像技术——把操作系统用户空间所需组件事先编排好并整体打包成一个镜像文件(image),集中放在仓库中。需要创建容器时,Docker 调用 LXC 的 lxc-create 工具,连接到镜像服务器下载匹配的镜像文件,基于镜像启动容器。此后创建启动容器只需要 docker run、docker stop 即可。
2. Docker 的引擎迭代
| 阶段 | 引擎 | 说明 |
|---|---|---|
| 早期 | LXC | 基于 LXC 容器管理引擎实现 |
| 成熟期 | libcontainer | Docker 自建的容器引擎,从 0.9 版本开始替代 LXC,1.10 版本彻底去除 LXC |
| 标准化 | runC | CNCF 介入后研发的工业化标准容器引擎,libcontainer 成为 runC 的核心功能模块,目前新版 Docker 使用的就是 runC |
3. Docker 与虚拟机的区别
| 对比项 | 传统虚拟机 | Docker 容器 |
|---|---|---|
| 磁盘占用 | 几个 GB 到几十个 GB | 几十 MB 到几百 MB |
| CPU/内存占用 | 虚拟操作系统非常占用,需通过虚拟层调用,占用率高 | Docker 引擎占用资源极低,直接作用于硬件资源 |
| 启动速度 | 几分钟 | 几秒 |
| 安装管理 | 需要专门的运维技术 | 安装、管理方便 |
| 应用部署 | 手动部署,速度慢 | 体系化部署,可自动化,速度快 |
| 隔离性 | 系统级别 | 进程级别 |
| 封装程度 | 打包整个操作系统 | 打包项目代码和依赖信息 |
4. Docker 为什么比虚拟机资源利用率高、启动快
资源利用率高:Docker 有比虚拟机更少的抽象层,不需要 Hypervisor 实现硬件资源虚拟化,运行在 Docker 容器上的程序直接使用实际物理机的硬件资源;Docker 利用的是宿主机的内核,而不需要 Guest OS,节省了 Guest OS 占用的资源。
启动快:Docker 不需要 Guest OS,创建容器时不需要像虚拟机一样重新加载一个操作系统内核,从而避免了寻址、加载操作系统内核返回时耗时耗资源的过程。新建虚拟机是分钟级别的,而新建 Docker 容器只需要几秒钟。
5. Docker 与 JVM 虚拟化的区别
| 对比项 | JVM | Docker 容器 |
|---|---|---|
| 性能 | JVM 需要占用一定的 CPU 和内存 | 基本没有损失 |
| 虚拟层面 | 基于 JVM 虚拟机,更加上层 | 基于操作系统,更加通用 |
| 代码无关性 | 特定代码的执行平台,运行时才存在,只能支撑特定代码在 JVM 进程内执行 | 模拟了整个操作系统,静态存在,可支撑任何相同平台的应用程序 |
| 主机隔离性 | JVM 不隔离主机 | 通过命名空间实现隔离 |
6. Docker 的版本
| 版本 | 说明 |
|---|---|
| lxc | 最早的 Linux 容器技术,早期 Docker 直接使用 LXC 实现底层功能 |
| libcontainer | Docker 从 0.9 版本自研,替代 LXC;1.11 版本拆分出 runC 后成为其核心模块 |
| moby | Docker 公司发起的开源项目,是 dockerd 目前使用的开源项目名称,使用 containerd 作为运行时标准 |
| docker-ce | Docker 开源版本(Community Edition),组件来自 moby、containerd 等项目。学习和使用通常指这个版本 |
| docker-ee | Docker 收费版本(Enterprise Edition),基础组件与 ce 相同,附加其他组件和功能 |
七、Docker 架构
Docker 使用客户端-服务器(C/S)架构模式,使用远程 API 来管理和创建 Docker 容器,Docker 容器通过 Docker 镜像来创建。核心组件如下:
| 组件 | 说明 |
|---|---|
| Docker 仓库(Registry) | 保存镜像,可理解为代码控制中的代码仓库。Docker Hub 提供庞大的镜像集合 |
| Docker daemon | 服务器组件,Docker 最核心的后台进程,也称为守护进程 |
| Docker 客户端(Client) | 通过命令行或其他工具使用 Docker API 与守护进程通信 |
| Docker 主机(Host) | 一个物理或虚拟的机器,用于执行 Docker 守护进程和容器 |
| Docker 镜像(Images) | 用于创建 Docker 容器的模板 |
| Docker 容器(Container) | 独立运行的一个或一组应用 |
生活案例(酒店入住):
- 一家人去旅游入住酒店,"我们"就是 Docker Client。
- 酒店前台提供办理入住、退房、缴费等服务,就是 Docker Daemon(核心服务端)。
- 酒店的宅基地和大楼是实际的服务器,即 Docker Host。
- 酒店有 1000 多个房间,有标间、大床房、家庭房等,这就是 Docker 镜像仓库。
- 标准的豪华大床房和双人标间是 Docker 镜像,客户无法修改。
- 办理入住后把个人物品带到具体房间(如 9527),这个可使用的房间就是 Docker Container。朋友也开了同样的豪华大床房,但携带物品、作息时间都不同——镜像相同,容器实例各自独立。
- 退房搬出后,酒店把房间恢复成镜像原来的样子——容器销毁。
八、Docker 生态:为什么要设计镜像和仓库
云时代对软件提出了新的诉求,Docker 的镜像和仓库设计正是针对这些诉求的解决方案。
1. 时代诉求
数据量疯狂增长:据 IDC 预测,全球数据量将从 2018 年的 33ZB 增至 2025 年的 175ZB,增长超过 5 倍。处理能力快速增加:腾讯云全球服务器数量 100 万+,阿里云在全球部署了上百个数据中心,服务器总规模接近 200 万台。软件需求爆发式增长:研发模式从瀑布开发变为敏捷开发,发布频繁且需要快速回滚;软件需要方便地共享给他人;环境搭建复杂、技术种类繁多,每个项目组语言不同,从 yum 开始一个个部署,各种奇怪问题,运维成本很高。
2. Docker 的解决方案
| 诉求 | Docker 方案 |
|---|---|
| 处理海量数据 | 购买大量服务器并研发对应软件 |
| 代码快速分发到成百上千台服务器、共享软件 | 搞一个中心仓库让各服务器去下载——Docker 设计了镜像仓库,Docker Hub 是公共托管仓库 |
| 快速安装启动、有问题回滚 | 将 Docker 所需的所有信息设计一套软件格式,把所有依赖搞进去并打上版本标签——Docker 设计了镜像 |
| 不同开发环境搭建(Java、C++ 等) | 镜像里存放运行环境,像 iPhone 内置 iOS、华为 mate 50 内置鸿蒙一样,一条命令完成环境搭建 |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)