Docker内核知识
Docker容器本质上是宿主机上的进程。Docker通过namespaces实现了资源隔离,通过cgroups实现了资源限制,通过写时复制机制(copy-on-write)实现了高效的文件操作。
namespaces资源隔离
想要实现一个资源隔离的容器,首先,根目录/的挂载点需要切换,即文件系统需要隔离;接着,为了在分布式的环境下进行通信和定位,容器必然要有独立的IP、端口、路由等,即网络需要隔离;同时,容器还需要一个独立的主机名以便在网络中标识自己;有了网络,自然离不开通信,即进程间通信需要隔离;不同用户和用户组之间需要用户权限的隔离;最后,运行在容器中的应用需要有进程号(PID),自然需要与宿主机中的PID进行隔离。
由此,基本上完成了一个容器所需要做的6项隔离,Linux内核中提供了这6种namespaces隔离的系统调用,如图所示:
Linux内核实现namespace的主要目的是轻量级化(容器)服务。在同一个namespace下的进程可以感知彼此的变化,而对外界的进程一无所知。
本文讨论的namespace实现针对的均是Linux内核3.8及以后的版本。接下来首先介绍使用namespace的API,然后对6种namespace进行逐一讲解。
-
进行namespaces API 操作的4种方式
namespaces的API包括clone()、setns()以及unshare(),还有/proc下的部分文件。
-
通过clone()在创建新进程的同时创建namespaces
使用clone()来创建一个独立的namespaces的进程,是最常见的做法,也是Docker使用namespace最基本的方法,它的调用方式如下:
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
-
查看/proc/[pid]/ns文件
如果两个进程指向的namespace编号相同,就说明它们在同一个namespace下。
-
通过setns()加入一个已经存在的namespace
通过setns()系统调用,进程从原先的namespace加入某个已经存在的namespace。
-
通过unshare()在原先进程上进行namespace隔离
调用unshare(),不启动新进程就可以起到隔离的效果,相当于跳出原先的namespace进行操作。
-
-
UTS namespace
UTS(UNIX Time-sharing System) namespace提供主机名和域名的隔离,这样每个docker容器就可以拥有独立的主机名和域名,在网络上可以被视作一个独立的节点,而非宿主机上的进程了。
-
IPC namespace
进程间通信(Inter-Process Cmmunication,IPC)涉及的IPC资源包括常见的信号量、消息队列和共享内存。申请IPC资源就申请了一个全局唯一的32位ID,所以IPC namespace中实际上包含系统IPC标识符以及实现POSIX消息队列的文件系统。在同一个IPC namespace下的进程彼此可见。
-
PID namespace
Linux内核为所有的PID namespace维护了一个树状结构,最顶层的是系统初始时创建的,被称为root namespace。它创建的新的PID namespace称为child namespace(树的子节点),而原先的PID namespace就是新创建的PID namespace的parent namespace(树的父节点)。通过这种方式,不同的PID namespace会形成一个层级体系。所属的父节点可以看到子节点中的进程,并可以通过信号等方式对子节点中的进程产生影响,反过来,子节点却不能看到父节点PID namespace中的任何内容。
-
mount namespace
mount namespace通过隔离文件系统挂载点对隔离文件系统提供支持,隔离后,不同mount namespace中的文件结构发生变化也互不影响。
-
network namespace
network namespace主要提供了关于网络资源的隔离,包括网络设备、IPv4、IPv6协议栈、IP路由表、防火墙、/proc/net目录、/sys/class/net目录、套接字(socket)等。一个物理的网络设备最多存在于一个network namespace中,可以通过创建veth pair在不同的network namespace间创建通道,以达到通信目的。
-
user namespace
user namespace主要隔离了安全相关的标识符(identifier)和属性(attribute),包括用户ID、用户组ID、root目录、key(密钥)以及特殊权限。
cgroups资源限制
-
什么是cgroups?
cgroups是Linux内核提供的一种机制,这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。
本质上来说,cgroups是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的。
-
cgroups的作用
- 资源限制:cgroups可以对任务使用的资源总额进行限制,如任务使用资源超出配额就会发出OOM(out of memory)的提示。
- 优先级分配:通过分配的CPU时间片数量及磁盘IO带宽大小控制任务运行的优先级。
- 资源统计:cgroups可以统计系统的资源使用量,如CPU使用时长、内存用量等。
- 任务控制:cgroups可以对任务执行挂起、恢复等操作。