LV001-Docker简介
一、Docker 简介
这里参考 什么是 Docker?看这一篇干货文章就够了! - 知乎 ,这个例子个人感觉比较容易理解。
1. 容器技术的起源
1.1 环境问题
假设你们公司正在秘密研发下一个“今日头条”APP,我们姑且称为明日头条,程序员自己 从头到尾搭建了一套环境 开始写代码,写完代码后程序员要把代码交给测试同学测试,这时测试同学开始 从头到尾搭建这套环境,测试过程中出现问题程序员也不用担心,大可以一脸无辜的撒娇,“明明在人家的环境上可以运行的”。
测试同学测完后终于可以上线了,这时运维同学又要重新 从头到尾搭建这套环境,费了九牛二虎之力搭建好环境开始上线,糟糕,上线系统就崩溃了,这时心理素质好的程序员又可以施展演技了,“明明在人家的环境上可以运行的”。
从整个过程可以看到,不但我们重复搭建了三套环境还要迫使程序员转行演员浪费表演才华,典型的浪费时间和效率,聪明的程序员是永远不会满足现状的,因此又到了程序员改变世界的时候了,容器技术应运而生。
1.2 容器技术与虚拟机
有的同学可能会说:“等等,先别改变世界,我们有虚拟机啊,VMware 好用的飞起,先搭好一套虚拟机环境然后给测试和运维 clone 出来不就可以了吗?”。其实我们在做 linux 开发的时候,不就是 windows 中装一个 VMware,然后 VMware 中装一个 ubuntu,然后安装 gcc、make、cmake、交叉编译工具链和各种库。
在没有容器技术之前,这确实是一个好办法,只不过这个办法还没有那么好。你可能会想这个办法为什么不好呢?
Tips:先科普一下,现在云计算其底层的基石就是虚拟机技术,云计算厂商买回来一堆硬件搭建好数据中心后使用虚拟机技术就可以将硬件资源进行切分了,比如可以切分出 100 台虚拟机,这样就可以卖给很多用户了。
我们知道和一个单纯的应用程序相比,操作系统是一个很重而且很笨的程序,简称笨重,有多笨重呢?我们知道操作系统运行起来是需要占用很多资源的,大家对此肯定深有体会,刚装好的系统还什么都没有部署,单纯的操作系统其磁盘占用至少几十 G 起步,内存要几个 G 起步。
假设我有一台机器,16G 内存,需要部署三个应用,那么使用虚拟机技术可以这样划分:

在这台机器上开启三个虚拟机,每个虚拟机上部署一个应用,其中 VM1 占用 2G 内存,VM2 占用 1G 内存,VM3 占用了 4G 内存。
我们可以看到虚拟本身就占据了总共 7G 内存,因此 我们没有办法划分出更过虚拟机从而部署更多的应用程序,可是我们部署的是应用程序,要用的也是应用程序而 不是操作系统。
如果有一种技术可以让我们避免把内存浪费在“无用”的操作系统上岂不是太香?这是问题一,主要原因在于操作系统太重了。
还有另一个问题,那就是启动时间问题,我们知道操作系统重启是非常慢的,因为操作系统要从头到尾把该检测的都检测了该加载的都加载上,这个过程非常缓慢,动辄数分钟,因此操作系统还是太笨了。
那么有没有一种技术可以让我们获得虚拟机的好处又能克服这些缺点从而一举实现鱼和熊掌的兼得呢?有的有的,这就是 容器技术。
2. 什么是容器
容器一词的英文是 container,其实 container 还有集装箱的意思,集装箱绝对是商业史上了不起的一项发明,大大降低了海洋贸易运输成本。让我们来看看集装箱的好处:
- 集装箱之间相互隔离
- 长期反复使用
- 快速装载和卸载
- 规格标准,在港口和船上都可以摆放
回到软件中的容器,其实容器和集装箱在概念上是很相似的。现代软件开发的一大目的就是隔离,应用程序在运行时相互独立互不干扰,这种隔离实现起来是很不容易的,其中一种解决方案就是上面提到的虚拟机技术,通过将应用程序部署在不同的虚拟机中从而实现隔离。

但是虚拟机技术有上述提到的各种缺点,那么容器技术又怎么样呢?与虚拟机通过操作系统实现隔离不同,容器技术 只隔离应用程序的运行时环境但容器之间可以共享同一个操作系统,这里的运行时环境指的是程序运行依赖的各种库以及配置。

从图中我们可以看到容器更加的 轻量级且占用的资源更少,与操作系统动辄几 G 的内存占用相比,容器技术只需数 M 空间,因此我们可以在同样规格的硬件上 大量部署容器,这是虚拟机所不能比拟的,而且不同于操作系统数分钟的启动时间容器几乎瞬时启动,容器技术为 打包服务栈 提供了一种更加高效的方式,So cool。
3. 什么是 Docker
那么我们该怎么使用容器呢?这就要提到 docker 了。
注意,容器是一种通用技术,docker 只是其中的一种实现。
Docker 是一个开源的应用容器引擎,基于 Go 语言并遵从 Apache2.0 协议开源。它让开发者能够将应用及其依赖打包到一个轻量级、可移植的容器中。然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
也就是说,docker 将程序以及程序所有的依赖都打包到 docker container,这样我们的程序可以在任何环境都会有一致的表现,这里程序运行的依赖也就是容器就好比集装箱,容器所处的操作系统环境就好比货船或港口,程序的表现只和集装箱有关系(容器),和集装箱放在哪个货船或者哪个港口(操作系统)没有关系。
因此我们可以看到 docker 可以屏蔽环境差异,也就是说,只要我们的程序打包到了 docker 中,那么无论运行在什么环境下程序的行为都是一致的,程序员再也无法施展表演才华了,不会再有“在我的环境上可以运行”,真正实现“build once, run everywhere”。
此外 docker 的另一个好处就是 快速部署,这是当前互联网公司最常见的一个应用场景,一个原因在于容器启动速度非常快,另一个原因在于只要确保一个容器中的程序正确运行,那么我们就能确信无论在生产环境部署多少都能正确运行。
- vm : linux centos 原生镜像(一个电脑!) 隔离、需要开启多个虚拟机! 几个 G 几分钟
- docker: 隔离,镜像(最核心的环境 4m + jdk + mysql)十分的小巧,运行镜像就可以了!小巧! 几个 M 秒级启动!
4. 发展历史
2010 年,几个搞 IT 的年轻人,就在美国成立了一家公司 dotcloud。做一些 pass 的云计算服务!LXC(Linux Container 容器)有关的容器技术!他们将自己的技术(容器化技术)命名就是 Docker。
Linux Container 容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。
Docker 刚刚诞生的时候,没有引起行业的注意!(dotCloud 就活不下去)
2013 年,Docker 开源!越来越多的人发现 docker 的优点!火了。Docker 每个月都会更新一个版本!
2014 年 4 月 9 日,Docker1.0 发布!
二、Docker 架构
1. Docker 核心概念
docker 中有这样几个概念:
- 仓库(Repository):仓库是存储和分发镜像的地方,可以包含一个镜像的多个版本,最常用的公共仓库是 Docker Hub。用户可以从 Docker Hub 下载镜像,也可以上传自己的镜像分享给其他人。仓库又可以分为 公有仓库和私有仓库(就和 github、gitee 一样)。
- 镜像(image):是一个只读的模板,包含了运行应用所需的所有内容:代码、运行时、库文件、环境变量和配置文件。另外,dockerfile 就是 image 的源代码,docker 就相当于是 "编译器"。
- 容器(container):器是镜像的运行实例,是一个轻量级、可移植的执行环境。如果镜像是类,那么容器就是对象实例。一个镜像可以创建多个容器,就像一个类可以创建多个对象。
例如,ubuntu:20.04 就是一个镜像,运行 docker run -it ubuntu:20.04,得到一个容器,镜像可以上传到 Docker Hub,方便分享。
实际上我们可以简单的把 image 理解为可执行程序,container 就是运行起来的进程。那么写程序需要源代码,那么“写”image 就需要 dockerfile,dockerfile 就是 image 的源代码,docker 就是 "编译器"。
因此我们只需要在 dockerfile 中指定需要哪些程序、依赖什么样的配置,之后把 dockerfile 交给“编译器”docker 进行“编译”,也就是 docker build 命令,生成的可执行程序就是 image,之后就可以运行这个 image 了,这就是 docker run 命令,image 运行起来后就是 docker container。
2. 架构示意图

一般工作流程如下:
- 构建镜像:使用 Dockerfile 创建镜像。
- 推送镜像到注册表:将镜像上传到 Docker Hub 或私有注册表中。
- 拉取镜像:通过 docker pull 从注册表中拉取镜像。
- 运行容器:使用镜像创建并启动容器。
- 管理容器:使用 Docker 客户端命令管理正在运行的容器(例如查看日志、停止容器、查看资源使用情况等)。
- 网络与存储:容器之间通过 Docker 网络连接,数据通过 Docker 卷或绑定挂载进行持久化。
2.1 Docker Client
主要功能如下:
- 用户与 Docker 交互的主要方式
- 接收用户命令并发送给 Docker Daemon
- 可以与远程 Docker Daemon 通信
docker run # 运行容器
docker build # 构建镜像
docker pull # 拉取镜像
docker ps # 查看容器状态2.2 Docker Daemon
主要功能如下:
- Docker 的核心服务进程
- 管理镜像、容器、网络和存储卷
- 监听 Docker API 请求并处理
主要职责:
- 镜像管理(构建、存储、分发)
- 容器生命周期管理
- 网络管理
- 数据卷管理
- 与 Registry 通信
2.3 Docker Engine
组成:
- Docker Client + Docker Daemon + REST API
- 是 Docker 的核心组件
三、Docker 与虚拟机
1. 架构对比
| 特性 | 虚拟机 | Docker 容器 |
|---|---|---|
| 隔离级别 | 硬件级别虚拟化 | 操作系统级别虚拟化 |
| 操作系统 | 每个 VM 需要完整 OS | 共享宿主机 OS 内核 |
| 资源占用 | 重量级,占用较多资源 | 轻量级,资源占用少 |
| 启动时间 | 分钟级别 | 秒级别 |
| 性能开销 | 较大 | 接近原生性能 |
| 镜像大小 | GB 级别 | MB 级别 |
2. 容器 VS 虚拟机架构

docker容器为什么启动这么快呢???从上面的架构对比中可以清楚的看到,VM比docker多了Hypervisor 和 Guest OS的过程,也正是省略了这些过程使docker技高一筹,问题又来了,为什么docker可以省略这些过程呢?
Hypervisor:主要作用是实现硬件资源虚拟化;因为docker容器上程序直接使用的都是物理机的硬件资源,所以不需要资源虚拟化的过程,也因此在CPU、内存利用率上docker将会在效率上明显提高
Guest OS:主要作用加载操作系统内核;因为docker利用的是宿主机的内核,所以在启动一个容器时,不需要像VM一样重新加载一个操作系统内核,也因此大大节约了启动时间。
以下是官网提供的容器启动过程图:

3. 使用场景对比
虚拟机适用场景:
- 需要完全隔离的环境
- 运行不同操作系统的应用
- 需要硬件级别的安全隔离
Docker 容器适用场景:
- 微服务架构
- CI/CD 流水线
- 应用快速部署和扩展
- 开发环境标准化
四、技术底座
Linux 命名空间(Namespace)、控制组(cgroups )和 UnionFS 三大技术支撑了目前 Docker 的实现,也是 Docker 能够出现的最重要原因。
1. namespace —— 命名空间
在Linux系统中,Namespace是在内核级别以一种抽象的形式来封装系统资源,通过将系统资源放在不同的Namespace中,来实现资源隔离的目的。不同的Namespace程序,可以享有一份独立的系统资源。
Linux 的命名空间机制提供了以下七种不同的命名空间,包括 :CLONE_NEWCGROUP、CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER 和CLONE_NEWUTS。
通过这七个选项, 我们能在创建新的进程时, 设置新进程应该在哪些资源上与宿主机器进行隔离。具体如下:
| Namespace | Flag | Page | Isolates |
|---|---|---|---|
| Cgroup | CLONE_NEWCGROUP | cgroup_namespaces | Cgroup root directory |
| IPC | CLONE_NEWIPC | ipc_namespaces | System V IPC,POSIX message queues 隔离进程间通信 |
| Network | CLONE_NEWNET | network_namespaces | Network devices,stacks, ports, etc. 隔离网络资源 |
| Mount | CLONE_NEWNS | mount_namespaces | Mount points 隔离文件系统挂载点 |
| PID | CLONE_NEWPID | pid_namespaces | Process IDs 隔离进程的ID |
| Time | CLONE_NEWTIME | time_namespaces | Boot and monotonic clocks |
| User | CLONE_NEWUSER | user_namespaces | User and group IDs 隔离用户和用户组的ID |
| UTS | CLONE_NEWUTS | uts_namespaces | Hostname and NIS domain name 隔离主机名和域名信息 |
docker也使用了命名空间,它是容器隔离的基础,保证 A 容器看不到 B 容器。
2. cgroups —— 控制组
cgroups(Control Group) 的缩写,即控制组。
实际上 Docker 是使用了很多 Linux 的隔离功能,让容器看起来像一个轻量级虚拟机在独立运行,容器的本质是被限制了的 Namespaces,cgroup,具有逻辑上独立文件系统,网络的一个进程。
3. UnionFS —— 联合文件系统
3.1 简介
联合文件系统(Union File System,Unionfs)是一种分层的轻量级文件系统,它可以把多个目录内容联合挂载到同一目录下,从而形成一个单一的文件系统,这种特性可以让使用者像是使用一个目录一样使用联合文件系统。
那联合文件系统对于 Docker 是一个怎样的存在呢?它可以说是 Docker 镜像和容器的基础,因为它可以使 Docker 可以把镜像做成分层的结构,从而使得镜像的每一层可以被共享。例如两个业务镜像都是基于 CentOS 7 镜像构建的,那么这两个业务镜像在物理机上只需要存储一次 CentOS 7 这个基础镜像即可,从而节省大量存储空间。
联合文件系统在主机上使用多层目录存储,但最终呈现给用户的则是一个普通单层的文件系统,我们把多层以单一层的方式呈现出来的过程叫作联合挂载。
3.2 有哪些 UFS?
联合文件系统只是一个概念,真正实现联合文件系统才是关键,那如何实现呢?其实实现方案有很多,Docker 中最常用的联合文件系统有三种:AUFS、Devicemapper 和 OverlayFS。
AUFS 目前并未被合并到 Linux 内核主线,因此只有 Ubuntu 和 Debian 等少数操作系统支持 AUFS。它在主机上使用多层目录存储。
相比对文件系统加锁的机制,Devicemapper 工作在块级别,因此可以实现同时修改和读写层中的多个块设备,比AUFS文件系统效率更高。
通常情况下, overlay2 会比 AUFS 和 Devicemapper 性能更好,而且更加稳定,因为 overlay2 在 inode 优化上更加高效。因此在生产环境中推荐使用 overlay2 作为 Docker 的文件驱动。
3.3 为什么 docker 用 UFS?
(1)共享和复用:UFS 允许多个容器共享相同的基础镜像层,这样可以节省存储空间,并减少容器的创建和启动时间。当多个容器共享相同的文件系统层时,这些层中的文件和资源只需存储一次,而不是每个容器都存储一份。这和代码是一样的,封装的方法拆分的越小,可复用性就会更好。
(2)写时复制(Copy-on-Write):UFS 允许容器在运行时进行读写操作,同时保留了镜像的不可变性。当容器对文件系统进行修改时,UFS 会将修改的内容写入新的层中,而不会直接修改原始镜像层,这样可以确保容器之间的隔离性,并且不会影响其他容器或原始镜像。
(3)快速启动和部署:由于 UFS 允许容器共享文件系统层并利用写时复制机制,因此容器的创建和启动速度非常快。Docker 可以快速地在现有镜像的基础上创建新的容器实例,并立即启动运行,这对于实现快速部署和横向扩展至关重要。
(4)轻量级:UFS 提供了一种轻量级的文件系统封装方式,它不需要完整地复制整个文件系统,而是在需要修改文件时才进行复制和修改,这样可以节省存储空间并减少资源消耗。
参考资料:
docker文件系统分层存储原理 - 塔克拉玛攻城狮 - 博客园
Docker 中文文档 - Docker 入门教程、安装配置、实战案例