docker基础之资源隔离
/ / 点击Linux Namespace
概念
Linux Namespace是Linux内核提供的一个功能,它可以隔离一系列的系统资源,比如:PID,UserID,NetWork等。
当前Linux内核一共实现了6种不同类型的Namespace:
| Namespace类型 | 系统调用参数 | 内核版本 |
|---|---|---|
| Mount Namespace | CLONE_NEWNS | 2.4.19 |
| UTS Namespace | CLONE_NEWUTS | 2.6.19 |
| IPC Namespace | CLONE_NEWIPC | 2.6.19 |
| PID Namespace | CLONE_NEWPID | 2.6.24 |
| Network Namespace | CLONE_NEWNET | 2.6.29 |
| User Namespace | CLONE_NEWUSER | 3.8 |
Namespace 的API主要使用如下三个系统调用:
- clone() 创建新进程。根据系统调用参数来判断哪些Namespace被创建,而且它们的紫禁城也会被包含到这些Namespace中
- unshare() 将进程移除某个Namespace
- setns() 将进程加入到Namespace中
UTS Namespace
UTS Namespace主要用来隔离 nodename和domainname两个系统标识,在UTS Namespace中,每个Namespace允许有自己的hostname。
golang测试代码:
1 | // file : uts.go |
在代码目录下执行命令:
go run uts.go,然后进入shell交互页面。然后执行命令
echo $$,打印出当前shell的进程id,然后再执行命令readlink /proc/$pid/ns/uts,打印出的结果与直接在terminal中执行该命令结果是不一样的,说明已实现了uts隔离。在该shell中修改hostname:
hostname -b test,然后同时在该shell中和直接在terminal中打印hostname,发现两者不一致,说明在uts隔离的shell中对uts修改不会影响宿主机。
IPC Namespace
IPC Namespace 用来隔离 信号量、消息队列和共享内存,每一个IPC Namespace的这些资源都是隔离的。
在上一版本的代码中略作修改:
1 | //file: ipc.go |
IPC隔离测试:
首先在宿主机上打开一个shell
1 | 查看现有 ipc message queues |
由以上实验可以发现,在新建的Namespace里,看不到宿主机上创建的queue,说明IPC Namespace创建成功,IPC已经被隔离。
PID Namespace
PID Namespace是用来隔离进程ID的,同一个进程在不同的PID Namespace里可以有不同的PID,在docker容器里面,使用
ps -ef就会发现,容器内,前台运行的进程PID是1,但在容器外使用ps -ef却发现同一进程有不同的PID,这就是PID Namespace的功劳。
测试代码,在上一节代码中增加一个CloneFlag:
1 | // file: pid.go |
同时打开两个shell,其中一个运行:
go run pid.go在宿主机shell中执行命令:
ps -ef|grep pid.go可以查到另一个shell中运行的go程序的pid,然后在另一个运行go程序的shell中执行命令:echo $$可以得到当前sh的pid,可以发现,在运行go程序的shell中其PID为1,而在宿主机看到该程序的PID不为1。说明PID已隔离。
Mount Namespace
Mount Namespace 用来隔离各个进程看到的挂载点视图,在不同的Namespace中,看到的文件层次是不一样的,在Mount Namespace中调用mount() umount()仅影响当前Namespace的文件系统(注:Mount有传播模式,如果是share模式还是会影响到的,测试时设置private模式可避免影响)
测试代码,在上一节基础上再增加一个CloneFlag:
1 | // file: ns.go |
运行程序: go run ns.go
在程序shell中执行命令mount --make-private /proc,先修改挂载模式为private
修改 mount 之前,查看 /proc (ls /proc)目录发现里边文件有很多,然后执行命令:mount -t proc proc /proc,在查看 /proc目录,发现里边文件少了很多。此时使用 ps -ef查看系统进程,可以看到只有 sh进程和 ps -ef进程。说明Mount隔离成功。(注:/proc目录下有很多数字的目录,这些每个目录都代表一个进程,数字为进程的PID,里边存储着跟进程相关数据,Mount隔离和PID隔离配合,使得ps -ef命令查看到当前Namespace下的进程,且PID是从1开始编号的)
User Namespace
User Namespace主要是隔离用户的用户组ID,可以在宿主机上以一个非root用户运行创建一个User Namespace,并且在这个Namespace下创建root用户,且在namespace下有root权限。
测试代码:
1 | // file: user.go |
以普通用户执行:
go run user.go,在shell中输入命令id,打印出:uid=0(root) gid=0(root) 组=0(root),65534(nogroup),但当尝试列出/root目录时,会提示权限不够。
Network Namespace
Network Namespace是用来隔离网络设备,IP地址端口等网络栈的Namespace。
测试代码:在上一节代码基础上增加CloneFlag:
1 | // file: network.go |
在宿主机上查看网络设备,
ifconfig,可以看到很多个网络设备。执行命令:
go run network.go后,在查看网络设备,发现只有 lo 一个网络设备了,说明Network Namespace隔离成功。
Linux Cgroup
Linux Cgroup提供了对一组进程及将来子进程的资源限制、控制和统计的能力。
Cgroup的三个组件:
- cgroup是对进程分组管理的一种机制,一个cgroup包含一组进程,并可以在这个cgroup上增加subsystem的各种参数配置,将一组进程和一组subsystem的系统参数关联起来
- subsystem是一组资源控制的模块,一般包含以下几项:
- blkio:设置对块设备输入输出的访问控制
- cpu:设置cgroup中进程的cpu被调度的策略
- cpuacct:可以统计cgroup中进程的cpu占用
- cpuset:在多核机器上设置cgroup中进程可以使用的cpu和内存
- devices:控制cgroup中进程对设备的访问
- freezer:用于挂起和恢复cgroup中的进程
- memory:用于控制cgroup中进程的内存占用
- net_cls:用于将cgroup中进程产生的网络包分类,以便Linux的tc(traffic controller)可以根据分类区分出来自某个cgroup的包并做限流或监控
- net_prio:设置cgroup中进程产生的网络流量的优先级
- ns:使cgroup中的进程在新的Namespace中fork新进程时,创建一个新的cgroup,这个cgroup包含新的Namespace中的进程
- hierarchy的功能是把一组cgroup串成一个树状结构,通过树状结构Cgroup可以做到集成
三个组件相互关系
- 系统在创建了新的hierarchy后,系统中所有进程都会加入这个hierarchy的cgroup根节点,这个cgroup根节点是hierarchy默认创建的
- 一个subsystem只能附加到一个hierarchy上
- 一个hierarchy能附加多个subsystem
- 一个进程可以作为多个cgroup成员,但这些cgroup必须在不同的hierarchy中
- 一个进程fork出子进程时,子进程和父进程是在同一个cgroup中,也可移到其他cgroup中