什么是 Docker?
Docker 最初是 dotCloud 公司创始人在法国期间发起的一个公司内部项目,它是基于dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。 在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持。
Docker 使用 Google 公司推出的 Go 语言进行开发实现,基于操作系统内核中的 Cgroup(资源控制)、Namespace(资源隔离)与 OverlayFS(数据存储)等技术,实现基于操作系统层面的(应用)虚拟化技术。
最初实现是基于 LXC 技术,从 0.7 版本以后开始去除 LXC,转而使用自行开发的libcontainer(容器管理技术)。
Docker发行版本
Docker 从 1.13.x 版本开始,版本分为企业版 EE 和社区版 CE,版本号也改为按照时间线来发布,比如 17.03 就是 2017 年 3 月,有点类似于 ubuntu 的版本发布方式。
Docker EE(企业版),Docker CE(社区版)
Docker 基本实现原理
通过三个方面实现容器化技术的前置:
(1)操作系统的 NameSpace 隔离系统资源技术,通过隔离网络、PID 进程、系统信号量、文件系统挂载、主机名与域名, 来实现在同一宿主机系统中,运行不同的容器,而每个容器之间相互隔离,运行互不干扰。
(2)使用系统的 Cgroups 系统资源配额功能, 限制资源包括: CPU、Memory、Blkio(块设备)、Network。
(3)通过 OverlayFS 数据存储技术, 实现容器镜像的物理存储与新建容器存储。
Linux NameSpace
当一台物理主机(宿主机)运行容器的时候, 为了避免容器所需系统资源之间相互干扰。所以 Docker 利用操作系统的隔离技术-NameSpace, 来实现在同一个操作系统中,不同容器之间的资源独立运行。 Linux Namespace 是 Linux 系统提供的一种资源隔离机制,可实现系统资源隔离的列表如下:
Mount - 用于隔离文件系统的挂载点
UTS - 用于隔离 HostName 和 DomianName
IPC - 用于隔离进程间通信
PID - 用于隔离进程 ID
Network - 用于隔离网络
User - 用于隔离用户和用户组 UID/GID
查看系统资源隔离
[root@node111 ~]#ps aux | grep ssh
root 10495 0.0 0.0 112756 4352 ? Ss Sep15 0:00 /usr/sbin/sshd -D
root 32003 0.8 0.0 159236 5944 ? Ss 17:52 0:00 sshd: root@pts/0
root 32153 0.0 0.0 112712 976 pts/0 S+ 17:52 0:00 grep --color=auto ssh
[root@node111 ~]# ll /proc/10495/ns/
total 0
lrwxrwxrwx 1 root root 0 Sep 21 17:54 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Sep 21 17:54 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Sep 21 17:54 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Sep 21 17:54 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Sep 21 17:54 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Sep 21 17:54 uts -> uts:[4026531838]
Cgroups (资源控制)
在操作系统解决了资源相互隔离的问题以后,还需要解决资源限制的问题,也就是避免在同一个操作系统中,防止有些资源消耗较大的容器,将整个物理机器(宿主机)的硬件资源(CPU, Memory) 占满
Linux 系统中能够控制的资源列表如下:
Memory - 内存限制
hugetlb - huge pages 使用量
cpu - CPU使用率
cpuacct - CPU使用率
cpuset - 绑定cgroups到指定CPUs和NUMA节点
innodb_lock_wait_timeout - block设备的IO速度
net_cls - 网络接口设置优先级
devices - mknode访问设备权限
freezer - suspend和restore cgroups进程
perf_event - 性能监控
pids - 限制子树cgroups总进程数
系统实现的限制资源
[root@node111 ~]#cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 10 78 1
cpu 4 261 1
cpuacct 4 261 1
memory 7 261 1
devices 11 261 1
freezer 6 78 1
net_cls 5 78 1
blkio 3 261 1
perf_event 2 78 1
hugetlb 8 78 1
pids 9 261 1
net_prio 5 78 1
OverlayFS
OverlayFS 是一种堆叠文件系统,它依赖并建立在其它的文件系统之上(例如 ext4fs 和 xfs 等),并不直接参与磁盘空间结构的划分,仅仅将原来系统文件中的文件或者目录进行"合并一起", 最后向用户展示"合并"的文件是在同一级的目录, 这就是联合挂载技术, 相对于 AUFS(<1.12 早期使用的存储技术), OverlayFS 速度更快,实现更简单。
Linux 内核为 Docker 提供的 OverlayFS 驱动有两种:Overlay 和 Overlay2。而 Overlay2 是相对于 Overlay 的一种改进,在 Inode 利用率方面比 Overlay 更有效。但是 Overlay 有环境需求:Docker 版本 17.06.02+,宿主机文件系统需要是 EXT4 或 XFS 格式。
OverlayFS 实现方式
OverlayFS 通过三个目录:lower 目录、upper 目录、以及 work 目录实现,其中 lower 目录可以是多个, upper 目录为可以进行读写操作的目录, work 目录为工作基础目录,挂载后内容会被清空,且在使用过程中其内容用户不可见,最后联合挂载完成给用户呈现的统一视图称为merged 目录。
Overlay2 命令行挂载操作
创建文件
[root@node111 ~]#mkdir /lower{1..3}
[root@node111 ~]#mkdir /upper /merged
挂载upper
[root@node111 ~]#mount -t overlay overlay -o lowerdir=/lower1:/lower2:/lower3,upperdir=/upper,workdir=/work /merged
upper写入文件
[root@node111 ~]#touch /upper/upper.txt
/upper目录中写入文件,在 merged 中可以显示
[root@node111 ~]#ll /merged/
total 0
-rw-r--r-- 1 root root 0 Sep 21 18:25 upper.txt
merged 中写入文件, 实际存储到了/uppper
[root@node111 ~]#touch /merged/merged.txt
[root@node111 ~]#ll /upper/
total 0
-rw-r--r-- 1 root root 0 Sep 21 18:25 merged.txt
-rw-r--r-- 1 root root 0 Sep 21 18:25 upper.txt
[root@node111 ~]#umount /merged
如果没有upperdir,merged是只读的
[root@node111 ~]#mount -t overlay overlay -o lowerdir=/lower1:/lower2 /merged
[root@node111 ~]#touch /merged/lower.txt
touch: cannot touch ‘/merged/lower.txt’: Read-only file system
NameSpace、Cgroup 与 OverlayFS 关系
在操作系统中, 可以根据宿主机的资源情况,创建不同应用与数量不同的容器。每个容器的 CPU、Memory、Network 由系统的内核 NameSpace 进行隔离, 相互之间不影响。
为了防止某个正在运行的容器大量占用宿主机的系统资源(CPU、Memory、Network), 那么将由操作系统的 Cgroups 功能进行资源限制(防止独占)。
容器运行的基础是需要镜像,并且新容器的运行也是需要存储支持的。在 Docker 中使用OverlayFS 解决这一问题。