Docker容器本质上是宿主机的进程,Docker通过namespace实现了资源隔离,通过cgroups实现了资源限制,通过写时复制机制(copy-on-write)实现了高效的文件操作
六种namespace
- MNT Namespace
mount namespace 是用来隔离各个进程看到的挂载点视图。在不同namespace中的进程看到的文件系统层次是不一样的。在mount namespace 中调用mount()和umount()仅仅只会影响当前namespace内的文件系统,而对全局的文件系统是没有影响的。
看到这里,也许就会想到chroot()。它也是将某一个子目录变成根节点。但是mount namespace不仅能实现这个功能,而且能以更加灵活和安全的方式实现。
- IPC Namespace
进程间通信(Inter-Process Communication,IPC)涉及的IPC资源包括常见的信号量、消息队列和共享内存。申请IPC资源就申请了一个全局唯一的32位ID,所以IPC namespace中实际上包含了系统IPC标识符以及实现POSIX消息队列的文件系统。在同一个IPC namespace下的进程彼此可见,不同IPC namespace下的进程则互相不可见。
目前使用IPC namespace机制的系统不多,其中比较有名的有PostgreSQL。Docker当前也使用IPC namespace实现了容器与宿主机、容器与容器之间的IPC隔离。
- UTS Namespace
UTS namespace 主要隔离nodename(主机名) 和domainname(域名) 两个系统标识。在UTS namespace里面,每个 namespace 允许有自己的hostname。
- PID Namespace
PS:在容器中默认不能使用systemd作为PID为1的守护进程,因为容器中没有内核
(1)每个PID namespace中的第一个进程“PID 1”,都会像全通Linux中的init进程一样拥有特权,其特殊作用。
(2)一个namespace中的进程,不可能通过kill或ptrace影响父节点或者兄弟节点中的进程,因为其他几点的PID在这个namespace没有任何意义。
(3)如果你在新的PID namespace中重新挂载/proc文件系统,会发现其下只显示同属一个PID namespace中的其他进程。
(4)在root namespace中看到所有的进程,并且递归包含所有子节点中的进程。也可以在Docker外部监控运行程序,就是监控Docker daemon所在的PID namespace下的所有进程及子进程,在进行筛选即可。
dockerd:被client直接访问,其父进程为宿主机的systemd守护进程
docker-proxy:实现容器通信,其父进程为dockerd
containerd:被dockerd进程调用以实现与runc交互
containerd-shim:真正运行容器的载体,其父进程为containerd
- Net Namespace
Network namespace 是用来隔离网络设备,IP地址端口等网络栈的namespace。Network namespace 可以让每个容器拥有自己独立的网络设备(虚拟的),而且容器内的应用可以绑定到自己的端口,每个 namesapce 内的端口都不会互相冲突。在宿主机上搭建网桥后,就能很方便的实现容器之间的通信,而且每个容器内的应用都可以使用相同的端口。
安装bridge-utils,查看网桥:brctl show
- User Namespace
User namespace 主要是隔离用户的用户组ID。也就是说,一个进程的User ID 和Group ID 在User namespace 内外可以是不同的。比较常用的是,在宿主机上以一个非root用户运行创建一个User namespace,然后在User namespace里面却映射成root 用户。这样意味着,这个进程在User namespace里面有root权限,但是在User namespace外面却没有root的权限。从Linux kernel 3.8开始,非root进程也可以创建User namespace ,并且此进程在namespace里面可以被映射成 root并且在 namespace内有root权限。