1.容器所在的运行环境统称为宿主机,能够是硬件服务器、虚拟机(其实,docker容器也支持在容器中运行容器)
2.Docker容器本质上是宿主机的进程,经过namespace实现隔离,经过cgroups实现了资源限制,经过写时复制机制(copy-on-write)实现了高效的文件操做。
namespace
3.想要实现一个资源隔离的容器,须要6项隔离,Linux内核提供了这6种namespace隔离的系统调用,在同一个namespace下的进程能够感知彼此的变化,但不了解外界的变化,所以,可让容器中的进程觉得本身在一个独立的系统环境中,达到独立和隔离的目的。
4.四种进行namespace隔离的API操做
-
clone() //在建立新进程的同时建立namespace
-
setns() //加入一个已经存在的namespace
-
unshared() //在原先进程上进行namespace隔离,不须要启动一个新进程
-
/proc下的部分文件 //3.8版本内核开始,能够在/proc/[pid]/ns文件下看到指向不一样namespace号的文件
使用时,须要指定以上六个参数中的一个或者多个,来肯定隔离的是哪六项namespace,经过|(位或)操做来实现
它提供了主机名和域名的隔离,这样每一个Docker容器就能够拥有独立的主机名和域名了,在网络上就能够被视做一个独立的节点,而非宿主机上的一个进程。因此,在Docker中,每一个镜像基本都以自身所提供的服务名称来命名镜像的hostname,且不会对宿主机产生任何影响。
进程间通讯(Inter-Process Communication,IPC)涉及的IPC资源包括常见的信号量、消息队列和共享内存。申请IPC资源就申请了一个全局惟一的32位ID,因此IPC namespace中实际上包含了系统IPC标识符以及实现POSIX消息队列的文件系统。在同一个IPC namespace下的进程彼此可见,不一样IPC namespace下的进程则互不可见。
它对进程PID从新编号,所以两个不一样namespace下的进程能够有相同的PID。每一个PID namespace都有本身的计数程序。内核为全部PID namespace维护了一个树状结构,最顶层的是系统初始建立的,即root namespace,它建立的新PID namespace称为child namespace,原来的就称为parent namespace,从而造成一个层级体系。所属的父节点能够看到子节点中的进程,能够经过信号等方式对子节点产生影响,反之则不行。
-
每一个PID namespace中的第一个进程都会像传统Linux中的init进程同样拥有特权
-
一个namespace中的进程,不可能经过kill或ptrace影响父节点或兄弟节点中的进程,由于其余节点的PID在这个namespace中没有意义
-
若是在新的PID namespace中从新挂载/proc文件系统,会发现其下只显示同属一个PID namespace中的其余进程
-
root namespace中能够看到全部进程,而且递归包含全部子节点中的进程
所以,想要在外部监控Docker中运行程序,能够监控Docker daemon所在的PID namespace下的全部进程及其子进程,再进行筛选。
它经过隔离文件系统挂载点对隔离文件系统提供支持。隔离后,不一样mount namespace中的文件结构发生变化也互不影响。进程在建立mount namespace时,会把当前的文件结构复制给新的namespace,新的namespace中的全部mount操做都只影响自身的文件系统,对外界不会产生任何影响。
主要提供关于网络资源的隔离,包括网络设备、IPv4和IPv6协议栈、IP路由表防火墙、嵌套字等。一个物理的网络设备最多存在于一个network namespace中,能够经过建立虚拟网络设备对在不一样的network namespace间建立通道来通讯。说到network namespace时,指的未必是真正的网络隔离,而是把网络独立出来,给外部用户一种透明的感受,似乎在与一个独立的网络实体进行通讯。容器的经典作法就是建立一个weth pair,一端放置在新的namespace中,一端放在原先的namespace中链接物理网络设备,再把多个设备接入网桥或者进行路由转发来通讯。
主要隔离了安全相关的标识符和属性,包括用户ID、用户组ID、root目录、key(密钥)以及特殊权限。即一个普通用户的进程经过clone()建立的新进程在新的user namespace中能够拥有不一样的用户和用户组。这意味着一个进程在容器外属于一个没有特权的普通用户,可是它建立的容器却属于拥有全部权限的超级用户,为容器提供了极大的自由。(例子:当用户运行容器,容器内部的root用户并不等于宿主机内的root用户,而是映射到宿主上的普通用户。)
-
user namespace被建立后,第一个进程被赋予了该namespace的所有权限,这样该init进程就能够完成全部必要的初始化工做;
-
从namespace内部观察到的UID和GID已经与外部不一样了,默认显示为65534,表示还没有与外部namespace用户映射。咱们须要对user namespace内部的这个初始user和其外部namespace某个用户创建映射,这样能够保证当涉及到一些对外部namespace的操做时,系统能够检验其权限(好比发送一个信号或操做某个文件)。一样用户组也要创建映射;
-
还有一点虽然不能从输出中看出来,可是值得注意。用户在新namespace中有所有权限,可是他在建立他的父namespace中不含任何权限。就算调用和建立他的进程有所有权限也是如此。因此哪怕是root用户调用了clone()在user namespace中建立出的新用户在外部也没有任何权限;
-
最后,user namespace的建立实际上是一个层层嵌套的树状结构。最上层的根节点就是root namespace,新建立的每一个user namespace都有一个父节点user namespace以及零个或多个子节点user namespace,这一点与PID namespace很是类似。