Docker实现

152 阅读4分钟

Docker底层的核心技术包括Linux上的命名空间,控制组,Union文件系统和容器样式。 传统的虚拟机通过在宿主机中运行hypervisor来模拟一整套完整的硬件环境给虚拟机的操作系统。虚拟机系统看到的环境是可限制的,也是彼此隔离的。这种直接的做法实现了对资源最完整的封装,但很多时候往往意味着系统资源的浪费。

随着Linux系统对名字空间功能的完善实现,程序员已经可以实现上面的所有需求,让某些进程在彼此隔离的名字空间中运行。大家使用同一个内核和某些运行时环境,但是彼此看不到,都以为系统中只有自己存在。这种机制就是容器,利用名字空间来做权限隔离控制,利用cgroups来做资源分配。

基本架构

Docker采用C/S架构,包括客户端和服务端。Docker daemon作为服务端接受来自客户端的请求,并处理这些请求。客户端和服务端既可以运行在一个机器上,也可以通过socket或者restful API来进行通信,Docker daemon一般在宿主主机后台运行,等待接收来自己客户端的消息。Docker客户端则为用户提供一系列可执行命令,用户用这些命令实现跟Docker daemon交互

图片.png

命名空间

名字空间是Linux内核一个强大的特性。每个容器都有自己单独的名字空间,运行在其中的应用都像是在独立的操作系统中运行一样,名字空间保证了容器之间彼此互不影响。

pid名字空间

不同用户的进程就是通过pid名字空间隔离开的,且不同名字空间中可以有相同pid。所有的LXC进程在Docker中的父进程为Docker进程,每个LXC进程具有不同的名字空间。同时运行嵌套,因此可以很方便实现嵌套的Docker进程。

net名字空间

有了pid名字空间,每个名字空间中的pid能够互相隔离,但是网络端口还是共享host的端口。网络隔离是通过net名字空间实现的,每个net名字空间有独立的网络设备,IP地址,路由表,/proc/net目录。这样每个容器的网络就能隔离开来。Docker默认采用veth的方式,将容器中虚拟网卡同host上的一个Docker网桥docker0连接在一起。

ipc名字空间

容器中进程交互还是采用了Linux常见的进程间交互方法,包括信号量,消息队列和共享内存等。然而同VM不同的是,容器的进程间交互实际上还是host上具有相同的pid名字空间中的进程间交互,因此需要在IPC资源申请时加入名字空间信息,每个IPC资源有一个唯一的32位id

uts名字空间

UTS名字空间允许每个容器拥有独立的hostname和domain name,使其在网络上可以被视作一个独立的节点而非主机上的一个进程。

user名字空间

每个容器可以有不同的用户和组id,也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。

控制组

控制组(cgroups)是linux内核的一个特性,主要用来对共享资源进行隔离,限制审计等。只有能控制分配到容器的资源,才能避免当多个容器同时运行时对系统资源的竞争。

联合文件系统

UnionF是一种分层,轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

联合文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像,可以制作各种具体的应用镜像。

另外,不同Docker容器就可以共享一些基础的文件系统层,同时加上自己独有的改动层,大大提高了存储的效率。

Docker中使用的AUFS就是一种联合文件系统,AUFS支持位每一个成员目录设定只读,读写和写出权限,同时AUFS里有一个类似分层的概念,对只读权限的分支可以逻辑上进行增量地修改。