一、简介
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。
容器 = cgroup + namespace + rootfs + 容器引擎
- Cgroup: 资源控制
- namespace: 访问隔离
- rootfs:文件系统隔离。镜像的本质就是一个rootfs文件
- 容器引擎:生命周期控制
- Docker基于容器技术的轻量级虚拟化解决方案- Docker是容器引擎,把Linux的Cgroup、namespace等容器底层技术进行封装抽象,为用户提供了创建和管理容器的便捷界面(包括命令行和API)- 相比其他早起的容器技术,Docker引入了一整套容器管理生态系统,包括分层的镜像模型,容器注册库,友好的Rest API。
1. namespace 机制
Namespace 是将内核的全局资源做封装,使得每个namespace 都有一份独立的资源,因此不同的进程在各自的namespace内对同一种资源的使用互不干扰。
目前,Linux 内核实现了6种 Namespace:
- PID :进程编号
- 隔离进程号,不同namespace 的进程可以使用相同的进程号。当创建一个 PID namespace 时,第一个进程的PID 是1,即 init 进程。它负责回收所有孤儿进程的资源,所有发给 init 进程的信号都会被屏蔽。
- NET :网络设备、网络协议栈、端口等
- 隔离网络资源。每个 namespace 都有自己的网络设备、IP、路由表、/proc/net 目录、端口号等。网络隔离可以保证独立使用网络资源,比如开发两个web 应用可以使用80端口。
- IPC :信号量、消息队列、共享内存
- Linux 提供很多种进程通信机制,IPC namespace 针对 System V 和 POSIX 消息队列,这些 IPC 机制会使用标识符来区别不同的消息队列,然后两个进程通过标识符找到对应的消息队列。IPC namespace 使得 相同的标识符在两个 namespace 代表不同的消息队列,因此两个namespace 中的进程不能通过 IPC 来通信。
- MOUNT :文件系统,挂载点
- 隔离文件挂载点,每个进程能看到的文件系统都记录在/proc/$$/mounts里。在一个 namespace 里挂载、卸载的动作不会影响到其他 namespace。
- UTS :主机名和主机域
- UTS namespace 对主机名和域名进行隔离。为什么要隔离主机名?因为主机名可以代替IP来访问。如果不隔离,同名访问会出冲突。
- USER :操作进程的用户和用户组
| 子系统 | 作用 |
|---|---|
| devices | 设备权限控制 |
| cpuset | 分配指定的CPU和内存节点 |
| CPU | 控制CPU使用率 |
| cpuacct | 统计CPU使用情况 |
| memory | 限制内存的使用上限 |
| freezer | 暂停Cgroup 中的进程 |
| net_cls | 配合流控限制网络带宽 |
| net_prio | 设置进程的网络流量优先级 |
| perf_event | 允许 Perf 工具基于 Cgroup 分组做性能检测 |
| huge_tlb | 限制 HugeTLB 的使用 |
2.1 子系统介绍
1)cpuset 子系统
cpuset 可以为一组进程分配指定的CPU和内存节点。 cpuset 一开始用在高性能计算上,在 NUMA(non-uniform memory access) 架构的服务器上,通过将进程绑定到固定的 CPU 和内存节点上,来避免进程在运行时因跨节点内存访问而导致的性能下降。
cpuset 的主要接口如下:
- cpuset.cpus: 允许进程使用的CPU列表
- cpuset.mems: 允许进程使用的内存节点列表
2)cpu 子系统 cpu 子系统用于限制进程的 CPU 利用率。具体支持三个功能 第一,CPU 比重分配。使用 cpu.shares 接口。 第二,CPU 带宽限制。使用 cpu.cfs_period_us 和 cpu.cfs_quota_us 接口。 第三, 实时进程的 CPU 带宽限制。使用 cpu_rt_period_us 和 cpu_rt_quota_us 接口。
3)cpuacct 子系统 统计各个 Cgroup 的 CPU 使用情况,有如下接口:
- cpuacct.stat: 报告这个 Cgroup 在用户态和内核态消耗的 CPU 时间,单位是 赫兹。
- cpuacct.usage: 报告该 Cgroup 消耗的总 CPU 时间。
- cpuacct.usage_percpu:报告该 Cgroup 在每个 CPU 上的消耗时间。
4)memory 子系统 限制 Cgroup 所能使用的内存上限。
- memory.limit_in_bytes:设定内存上限,单位字节。默认情况下,如果使用的内存超过上限,Linux 内核会试图回收内存,如果这样仍无法将内存降到限制的范围内,就会触发 OOM,选择杀死该Cgroup 中的某个进程。
- memory.memsw,limit_in_bytes: 设定内存加上交换内存区的总量。
- memory.oom_control: 如果设置为0,那么内存超过上限时,不会杀死进程,而是阻塞等待进程释放内存;同时系统会向用户态发送事件通知。
- memory.stat: 报告内存使用信息。
5)blkio 限制 Cgroup 对 阻塞 IO 的使用。
- blkio.weight: 设置权值,范围在[100, 1000],属于比重分配,不是绝对带宽。因此只有当不同 Cgroup 争用同一个 阻塞设备时才起作用
- blkio.weight_device: 对具体设备设置权值。它会覆盖上面的选项值。
- blkio.throttle.read_bps_device: 对具体的设备,设置每秒读磁盘的带宽上限。
- blkio.throttle.write_bps_device: 对具体的设备,设置每秒写磁盘的带宽上限。
- blkio.throttle.read_iops_device: 对具体的设备,设置每秒读磁盘的IOPS带宽上限。
- blkio.throttle.write_iops_device: 对具体的设备,设置每秒写磁盘的IOPS带宽上限。
6)devices 子系统 控制 Cgroup 的进程对哪些设备有访问权限
- devices.list: 只读文件,显示目前允许被访问的设备列表,文件格式为类型[a|b|c] 设备号[major:minor] 权限[r/w/m 的组合]a/b/c 表示 所有设备、块设备和字符设备。
- devices.allow: 只写文件,以上述格式描述允许相应设备的访问列表。
- devices.deny: 只写文件,以上述格式描述禁止相应设备的访问列表。
二、docker技术的优势
1. docker与VM对比
虚拟化技术会虚拟出多个内核,每个虚拟机拥有一个自己的内核,彼此之间隔离性非常好。并且在创建虚拟机之初就定义好了虚拟机的资源限制,如CPU核心数,内存大小,磁盘大小等。
容器技术是直接运行在宿主机之上的,多个容器共用一个内核,因此隔离效果比较差,但是性能更好

| 容器技术 | 虚拟机技术 | |
|---|---|---|
| 占用磁盘空间 | 小,甚至几十KB(镜像层的情况) | 非常大,上GB |
| 启动速度快 | 块,几秒 | 慢,几分钟 |
| 运行形态 | 直接运行与宿主机的内核上,不同容器共享一个Linux内核 | 运行于Hypervisior上 |
| 并发性 | 一台宿主机可以启动成千上百个容器 | 最多几十个虚拟机 |
| 性能 | 接近宿主机本地进程 | 逊于宿主机 |
| 资源利用率 | 高 | 低 |
