前言
当人们聊到容器技术,就会躲不开这几个关键字 namespace, cgroup, ufs,但说完这几个关键字之后貌似就不能往下谈了。在这里将通过通俗易懂的方式直接揭开这几个关键字的含义,及进程是如何使用这些技术达到容器化。
- 文章涉及一点点golang 代码,但都做了详细解释,不影响理解;
1、Linux Namespace
Namespace 用于对资源的隔离,包括进程隔离,挂载点隔离,网络隔离,用户隔离,程序组进程间通信隔离;
当一个进程被创建的时候,可以使用clone() 创建这些NS,当然也可使用setns() 来加入已存在的NS,最后还可以使用unshare() 进程移出某个NS
Namespace 类型 | 系统调用参数 | 内核版本 |
---|---|---|
Mount NS | CLONE_NEWNS | 2.4.19 |
UTS NS | CLONE_NEWUTS | 2.6.19 |
IPC NS | CLONE_NEWIPC | 2.6.19 |
PID NS | CLONE_NEWPID | 2.6.24 |
Network NS | CLONE_NEWNET | 2.6.29 |
User NS | CLONE_NEWUSER | 3.8 |
1.1、环境准备
后面通过代码的实现来了解以上的namespace , 前提需要准备一下环境:
1、centos 7 系统, 内核4.4 以上,因为4.4系统调用方式改了;(需支持aufs的内核版本,后面实现需要)
2、golang 1.12以上;
2、 代码实现NS
2.1、UTS Namespace
1 | golang复制代码package main |
- 使用以下命令校验
1 | bash复制代码# 执行当前脚本, 进入容器 |
2.2、IPC Namespace
1 | golang复制代码package main |
Term1
1 | bash复制代码# 执行当前脚本 |
Term2
1 | bash复制代码$ ipcs -q |
2.3、PID Namespace
1 | golang复制代码package main |
Term1
1 | bash复制代码# 进入容器 |
2.4、Mount Namespace
1 | go复制代码package main |
下面可以看出,挂载/proc到自己的namespace 后,我们只会看到我们自己进程信息;
1 | bash复制代码$ go run mount.go |
2.5、User Namespace
1 | go复制代码package main |
可以看出当前用户不一致
Term1
1 | bash复制代码# 进入容器 |
Term2 物理机
1 | bash复制代码$ id |
2.6、Network Namespace
1 | go复制代码package main |
- 进入容器后,看不到有网络配置
1 | bash复制代码$ go run net.go |
3、Linux Cgroup
什么是Cgroups? Cgroup 提供了对一组进程的资源限制、控制和统计的能力,这些资源包括CPU,内存,存储,网络等。通过Cgroups,可以方便的限制程序占用的资源,并且可以实时的监控进程和统计信息。
Cgroups 中的3个组件
cgroup
是对进程分组管理的一种机制,一个cgroup包含一组进程,并且可以在这个cgroup上增加 linux subsystem的各种参数配置,将一组进程和一组subsystem的系统参数关联起来。subsystem
是一组资源控制的模块,一般包含如下配置:- blkio 谁知对块设备的输入输出访问控制;
- cpu 设置cgroup中进程的cpu 被调度的策略;
- cpuacct 可以统计cgroup中进程的cpu占用;
- cpuset 在多核机器上设置ccgroup中进程可以使用的cpu
- devices 控制cgroup中进程对设备的访问;
- freeze 用去刮起和恢复cgroup 中的进程;
- memory 用于控制cgroup中的进程内存;
- net_cls 用于将cgroup中进程产生的网络包分类,以便linux的tc(traffic controller) 可以根据分类区分出来自某个cgroup中的包做限流或监控;
- net_prio 设置cgroup中的进程产生的网络流量优先级;
- ns 它的作用是使cgroup中的进程在新的namespace中fork新进程(newns)时,创建一个新的cgroup,这个cgroup包含新的namespaace中的进程;
1 | bash复制代码# // 查询系统中支持的subsystems |
hierarchy
的功能是把一组cgroup串成一个树状结构,便于继承,相当于默认有一个cgroup根结点,其他cgroup都是该cgroup的子节点;
三个组件的关系
- 系统创建新的hierarchy之后,系统中所有的进程都会加入到这个hierarchy的cgroup根节点中;
- 一个subsystem只能附加到一个hierarchy;
- 一个hierarchy 可以附加多个 subsystem;
- 一个进程可以作为多个cgroup的成员,但是这些cgroup必须在不同的hierarchy中;
- 一个进程fork 出子进程时,子进程是和父进程在同一个cgroup中,也可以根据需要移动到其他的cgroup中;
3.1、呈现Cgroup
1、创建并挂载一个hierarcy;
1 | bash复制代码$ mkdir cgroup-test |
这些文件是hierachey的根cgroup节点配置
- cgroup.clone_children, cpuset 的subsystem会读取这个配置文件,如这个值是1(default 0),子cgroup才会继承父cgroup的cpuset的配置
- cgroup.procs 是树中当前节点cgroup中的进程ID,现在的位置是在根节点,这个文件中会有现有系统的所有进程组ID;
- notify_on_rellease 和 release_agent 会一起使用。notify_on_releaase 标示当这个cgroup最后一个进程退出的时候是否执行了release_agent; release_agent 则是一个路径,通常用作进程退出之后自动清理掉不再使用的cgroup;
- task 标识该cgroup下面的进程ID, 如果把一个进程ID写到tasks文件中,便会将相应的进程加入到这个cgroup中;
2、扩展两个子的cgroup
1 | bash复制代码$ mkdir cgroup-1 cgroup-2 |
3、在cgroup中添加和移动进程
1 | bash复制代码$ pwd |
4、通过subsystem限制cgroup中的进程资源
- 由于系统早就默认为所有的subsystem创建了一个默认的hierachy,而一个susbsysten只能属于一个hierachy,所以我们只能使用默认的hierachy 来做限制实验;
1 | bash复制代码# // 默认的memory hierachy |
3.2、Docker 中使用cgroups
1 | shell复制代码$ docker run -itd -m 128m ubuntu |
4、Union File System
4.1、什么是 Union File System
UFS 是一种把其他文件系统联合到一个联合挂载带你的文件系统服务,它使用branch 把不同文件系统的文件和目录“透明地”覆盖,形成一个单一一致的文件系统。这些branch 或者是read-only 的或这的read-write的,所以当这个虚拟后的联合文件系统进行写操作的时候,系统是真整写到一个新的文件中。看起来这个整个系统是可以对任何文件进行操作,但是并没有改变原来这个文件。因为unionfs 用到一个叫写时复制的技术(COW)。
Copy-on-write, 也叫隐式共享;如果一个资源是重复的,在没有任何修改的前提下,是不需要建立一个新的资源,这个资源可以同时被新旧实例共享;当第一次写操作发生时,会对该资源完整的复制并进行修改;
4.2、AUFS
AUFS 是 UFS 的一种实现,Docker 选用的第一种存储驱动并沿用至今,还有其他同类型驱动,overlay, overlay2, overlyafs 等;AUFS 需要内核支持
查看是否支持aufs
1 | bash复制代码$ cat /proc/filesystems |grep aufs |
- 内核切换,推荐一个我当时参考: www.cnblogs.com/xzkzzz/p/96…
4.3、Docker 如何使用AUFS
Docker aufs存储目录
- /var/lib/docker/aufs/diff
- docker host filesystem 存储在该目录下
- /var/lib/docker/aufs/layers/
- docker 的镜像主要存储位置
- /var/lib/docker/aufs/mnt;
- 运行时修改的文件内容
4.4、手写aufs
以下是全过程
1 | bash复制代码$ pwd |
结语
- 如果文章对您有帮助请
点赞
,收藏
;
参考
- 推荐书籍: <<自己动手写docker>>
本文转载自: 掘金