Docker 技术原理浅析
Docker 技术原理浅析
1.简介
简单介绍Docker使用的核心技术
- 利用Linux的
Namespace
功能,实现资源的隔离
- 利用Linux的
cgroups
功能,实现资源的限制
- 利用AUFS栈式叠加的文件系统(联合挂载),实现docker的分层结构和增量更新等能力
以下一切都基于操作系统资源分配的最小单位:进程 进行讨论
2.NameSpace
Linux Namespace是Linux提供的一种内核级别的环境隔离方法,可以实现不同资源的隔离。
Linux提供的不同Namespace有六种:
- Mount Namespace:用于隔离文件系统的挂载点
- UTS Namespace:用于隔离hostname主机名和DomianName域名
- IPC Namespace:用于隔离进程间通信
- PID Namespace:用于隔离进程ID
- NetWork Namespace:用于隔离网络
- User Namespace:用于隔离用户和用户组 UID/GID
每种Namespace都有一个默认值代表不同的命名空间,通过这些默认值,我们在创建新进程时设置新进程应该在哪些资源上与宿主机器进行隔离,比如Linux Namespace 主要的三个API:
- clone 创建一个新进程,需要传入一个namespace的默认值,用于指定namespace
- setns 允许进程重新加入一个已经存在的namesapce
- unshare 将调用进程移动到新的namesapce
正是这三个namespace的API,使得docker可以在进程级别有实现’独立环境‘的能力,这样docker容器内部的任意进程都对宿主机器的进程一无所知。
Docker容器与容器,容器与宿主机之间的通信是如何实现的?
使用iptables进行数据包转发,让docker容器可以优雅的为宿主机器或其他容器提供服务。
具体实现:
docker默认提供的网络模式是网桥模式
在这种模式下,除了分配隔离的网络命名空间外,docker还会为所有容器设置ip地址
当docker服务器在主机上启动之后,会创建新的虚拟网桥docker0,随后在该主机上启动的全部服务都默认与该网桥相连
在默认的情况下,每一个容器在创建时,都会创建一对虚拟网卡,两个虚拟网卡组成了数据通道,其中一个网卡放在创建的容器中,另一个放到虚拟网桥docker0中
这样的话,docker0会为每一个容器分配一个新的ip地址并且将docker0的ip地址设置为默认的网关,网桥docker0通过iptables中的配置与宿主机上的网卡相连,所有符合条件的请求都会通过iptables转发到docker0然后分发给对应的容器
“当有docker服务需要暴露给宿主机时,就会为容器分配一个ip地址,同时向iptables追加一条心规则”
Docker如何实现文件系统的隔离?
挂载点,在新的进程中创建隔离的挂载点命名空间需要在clone函数中传入CLONE_NENNS,这样子进程就能得到父进程挂载点的拷贝,如果不传入这个参数,子进程对文件系统的读写都会同步回父进程以及整个主机的文件系统,为了保证当前容器进程没有办法访问宿主机上的目录,还需要通过libcontainer提供的pivot_port或change root函数改变进程能够访问的文件目录的根节点
change root:Linux中,默认文件路径都是以/开头,chroot的使用能够改变当前系统根目录结构,通过改变系统的根目录,我们能够限制用户的权利,在新的目录下并不能够访问旧系统根目录的文件,这样就是实现了和原系统隔离的目录结构
3.CGroups
虽然Namespace为我们新创建的进程隔离了文件系统,网络,但是其并不能够为我们提供物理资源上的隔离,比如CPU和内存,而control groups,简称cgroups,能够隔离宿主机上的物理资源,比如cpu,内存,磁盘IO和网络带宽。
每一组CGroup都是一组被相同标准和参数限制的进程,不同的CGroup之间是有层级关系的,也就是说他们之间可以从父类继承一些用于限制资源使用的标准和参数
Linux的CGroup能够为一组进程分配资源,比如cpu,内存,网络带宽等
在CGroup中,所有的任务就是一个系统的一个进程,而CGroup就是一组按照某种标准划分的进程,在CGroup这种机制中,所有的资源控制都是以CGroup为单位实现的,每一个进程都可以随时加入一个CGroup,也可以随时退出一个CGroup
那么,CGroups又是如何实现的呢?
Linux使用文件系统来实现CGroups,比如想创建的一个新的CGroup,只需要在想要分配或者限制资源的子系统下创建一个文件夹,然后这个文件夹下就会自动出现很多内容,每一个cgroups下面都有一个tasks文件,其中存储着属于cgroup的所有进程的pid,
作为负责 cpu 的子系统,
cpu.cfs_quota_us
文件中的内容能够对 CPU 的使用作出限制,如果当前文件的内容为 50000,那么当前控制组中的全部进程的 CPU 占用率不能超过 50%
当我们使用 Docker 关闭掉正在运行的容器时,Docker 的子控制组对应的文件夹也会被 Docker 进程移除,Docker 在使用 CGroup 时其实也只是做了一些创建文件夹改变文件内容的文件操作,不过 CGroup 的使用也确实解决了我们限制子容器资源占用的问题,系统管理员能够为多个容器合理的分配资源并且不会出现多个容器互相抢占资源的问题。
4.UnionFS
栈式的文件叠加系统,解决镜像文件问题
Docker 中的每一个镜像都是由一系列只读的层组成的,Dockerfile 中的每一个命令都会在已有的只读层上创建一个新的层
当镜像被 docker run
命令创建时就会在镜像的最上层添加一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改。
容器和镜像的区别就在于,所有的镜像都是只读的,而每一个容器其实等于镜像加上一个可读写的层,也就是同一个镜像可以对应多个容器。
UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。而 AUFS 即 Advanced UnionFS 其实就是 UnionFS 的升级版,它能够提供更优秀的性能和效率。
AUFS 作为联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 AUFS 中称作分支,整个『联合』的过程被称为*联合挂载(Union Mount)