docker之namespace与cgroup简介

前言

当谈论docker时,常常会聊到docker的实现方式。很多开发者都知道,docker容器本质上是宿主机的进程,Docker通过namespace实现了资源隔离,通过cgroups实现了资源限制,通过写时复制机制(copy-on-write)实现了高效的文件操作。

对于Linux容器的最小组成,可以由下面公式来表示

容器=namespace+cgroup+rootfs+容器引擎(用户态工具)

其中各项的功能分别为:

  • Namespace:访问隔离
  • Cgroup:资源控制
  • rootfs:文件系统隔离
  • 容器引擎:生命周期控制

目前市场上所有的Linux容器都包含了以上组件,一般来说,容器技术主要包括NamespaceCroup这两个内核特性

容器创建过程

下面通过三段代码来简要说明容器的创建过程

代码一

pid = clone(fun,stack,flags,clone_arg);
(flags: CLONE_NEWPID | CLONE_NEWNS |
	CLONE_NEWUSER | CLONE_NEWNET |
	CLONE_NEWIPC | CLONE_NEWUTS
	...)

对于代码一,通过clone系统调用,并传入各个Namespace对应的clone flag,创建了一个新的子进程。该进程拥有自己的Namespace。根据以上代码可知,该进程拥有自己的pid,mount,user,net,ipc,ust namespace。

代码二

echo $pid > /sys/fs/cgroup/cpu/tasks
echo $pid > /sys/fs/cgroup/cpuset/tasks
echo $pid > /sys/fs/cgroup/blkio/tasks
echo $pid > /sys/fs/cgroup/memory/tasks
echo $pid > /sys/fs/cgroup/devices/tasks
echo $pid > /sys/fs/cgroup/freezer/tasks

对于代码二,将代码一中产生的进程pid写入各个Cgroup子系统中,这样该进程就可以受到相应Cgroup子系统的控制。

代码三

fun()
{
	...
	pivot_root("path_of_rootfs/",path);
	...
	exec("/bin/bash");
	...
}

对于代码三,该fun函数由上面生成的新进程执行,在fun函数中,通过调用pivot_root系统调用,使进程进入一个新的rootfs,之后通过exec系统调用,在新的Namespace,Cgroup,rootfs中执行“/bin/bash”程序

Namespace

namespace又称命名空间,它主要做访问隔离。其原理是针对一类资源进行抽象,并将其封装在一起提供给一个容器使用,对于这类资源,因为每个容器都有自己的抽象,而它们彼此之间是不可见的,所有就可以做到访问隔离。

namespace是将内核的全局资源做封装,使得每个namespace都有一份独立的资源,因此不同的进程在各自的namespace内对同一种资源的使用不会互相干扰。

举个例子,执行sethostname这个系统调用时,可以改变系统的主机名,这个主机名就是一个内核的全局资源。内核通过实现UTS Namespace,可以将不同的进程分隔在不同的UTS namespace中,在某个Namespace修改主机名时,另一个namespace的主机名还是保持不变。

目前Linux内核总共实现了6种namespace:

  • IPC:隔离System V IPC和POSIX消息队列

    IPC是Inter-Process Communication的简写,也就是进程间通信。Linux提供了很多种进程间通信的机制,IPC Namespace针对的是SystemV IPC和Posix消息队列。这些IPC机制都会用到标识符,例如用标识符来区别不同的消息队列,然后两个进程通过标识符找到对应的消息队列进行通信等。

    IPC Namespace能做到的事情是,是相同的标识符在两个Namespace中代表不同的消息队列,这样也就使得两个Namespace中的进程不能通过IPC进程通信了

  • Network:隔离网络资源

    这个Namespace会对网络相关的系统资源进行隔离,每个Network Namespace都有自己的网络设备、IP地址、路由表、/proc/net目录、端口号等。网络隔离的必要性是很明显的,举一个例子,在没有隔离的情况下,如果两个不同的容器都想要运行同一个Web应用,而这个应用有需要使用80端口,那就会有冲突了。

  • Mount:隔离文件系统挂载点

    Mount Namespace用来隔离文件系统挂载点,每个进程能看到的文件系统都记录在/proc/$$/mounts里。在创建了一个新的Mount Namespace后,进程系统对文件系统挂载/卸载的动作就不会影响到其他Namespace。

  • PID:隔离进程ID

    PID Namespace用于隔离进程PID号,这样一来,不同的Namespace里的进程PID号就可以是一样的了

  • UTS:隔离主机名和域名

    UST Namespace用于对主机名和域名进行隔离,为什么要使用UTS Namespace做隔离?这是因为主机名可以用来代替IP地址,因此,业绩可以使用主机名在网络上访问某台机器了,如果不做隔离,这个机制在容器里就会出问题

  • User:隔离用户ID和组ID

    User Namespace用来隔离用户和组ID,也就是说一个进程在Namespace里的用户和组ID与它在host里的ID可用不一样,User Namespace最有用的地方在于,host的普通用户进程在容器里可以是0号用户,也就是root用户。这样,进程在容器内可以做各种特权操作,但是它的特权被限定在容器内,离开了这个容器它就只有普通用户的权限了。

    容器内的这类root用户,实际上还是有很多特权不能执行,基本上如果这个特权操作会影响到其他容器或者host,就不会被允许。

Cgroup

cgroup是control group 的简称,又称控制组,它主要是做资源控制。其原理是将一组进程放在一个控制组里,通过给这个控制组分配指定的可用资源,达到控制这一组进程可用资源的目的。

cgroup属于linux内核提供的一个特性,用于限制和隔离一组进程对系统资源的使用,也就是做资源QoS,这些资源主要包括CPU,内存,block I/O和网络带宽。

Cgroup 是将任意进程进行分组化管理的 Linux 内核功能。Cgroup 本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为 Cgroup 子系统或控制器。Cgroup 子系统有控制内存的 Memory 控制器、控制进程调度的 CPU 控制器等。运行中的内核可以使用的 Cgroup 子系统由/proc/cgroup 来确认。

Cgroup 提供了一个 Cgroup 虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用 CGroup,必须挂载 Cgroup 文件系统。这时通过挂载选项指定使用哪个子系统。

我们可以通过mount -t cgroup 查询系统中已经挂载的cgroup的文件系统

在这里插入图片描述

cgroup子系统介绍

blkio – 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。

cpu – 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。

cpuacct – 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。

cpuset – 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。

devices – 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。

freezer – 这个子系统挂起或者恢复 cgroup 中的任务。

memory – 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。

net_cls – 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。

ns – 名称空间子系统。

发布了22 篇原创文章 · 获赞 2 · 访问量 588

猜你喜欢

转载自blog.csdn.net/weixin_42955452/article/details/105099607