Linux之namespace 和 cgroups
先放结论, namespace 是用来做资源隔离, cgroup 是用来做资源限制。
Namespace
先说Namespace,虚拟技术基本要求就是资源隔离,简单的说就是我独占当前所有的资源。比如我在 8080 端口起 web 服务器,不用担心其他进程端口占用。Linux 自带 namespace 就能达到这个目的。 namespace 从2002 开始开发到现在已经快20年的历史了,到现在一共有6种 namespace:
- mnt, 文件系统
- pid, 进程
- net, 网络
- ipc, 系统进程通信
- uts, hostname
- user, 用户
可以通过三个系统调用的方式
- clone, 创建新的进程和新的namespace,新创建的进程 attach 到新创建的 namespace
- unshare,不创建新的进程,创建新的 namespace 并把当前进程 attach 上
- setns, attach 进程到已有的 namespace 上
shell 也提供了一个和系统调用同名的 unshare 命令可以非常简单的创建 namespace。
1 2 3 | [postgres@lhrpolardb ~]$ sudo unshare --fork --pid --mount-proc bash [root@lhrpolardb postgres]# [root@lhrpolardb postgres]# |
这样创建了一个新的 PID namespace 并在里面运行了 bash。我们看看当前 namespace 的进程:
1 2 3 4 | [root@lhrpolardb postgres]# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.7 0.0 16348 3196 pts/0 S 14:05 0:00 bash root 25 0.0 0.0 55016 1868 pts/0 R+ 14:05 0:00 ps aux |
在这个 namespace 里,就只有两个进程了。
Cgroups
cgroups 是 control groups 控制组的意思, 可以通过文件系统来访问这些信息。一般cgroups 挂载在 /sys/fs/cgroup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | [root@lhrpolardb ~]# cd /sys/fs/cgroup [root@lhrpolardb cgroup]# ll total 0 drwxr-xr-x 5 root root 0 Mar 26 20:00 blkio lrwxrwxrwx 1 root root 11 Mar 26 20:00 cpu -> cpu,cpuacct lrwxrwxrwx 1 root root 11 Mar 26 20:00 cpuacct -> cpu,cpuacct drwxr-xr-x 5 root root 0 Mar 26 20:00 cpu,cpuacct drwxr-xr-x 3 root root 0 Mar 26 20:00 cpuset drwxr-xr-x 5 root root 0 Mar 26 20:00 devices drwxr-xr-x 3 root root 0 Mar 26 20:00 freezer drwxr-xr-x 3 root root 0 Mar 26 20:00 hugetlb drwxr-xr-x 5 root root 0 Mar 26 20:00 memory lrwxrwxrwx 1 root root 16 Mar 26 20:00 net_cls -> net_cls,net_prio drwxr-xr-x 3 root root 0 Mar 26 20:00 net_cls,net_prio lrwxrwxrwx 1 root root 16 Mar 26 20:00 net_prio -> net_cls,net_prio drwxr-xr-x 3 root root 0 Mar 26 20:00 perf_event drwxr-xr-x 5 root root 0 Mar 26 20:00 pids drwxr-xr-x 5 root root 0 Mar 26 20:00 systemd |
内核会读取这些信息来调度资源分配给每个进程。比如我要限制进程占用CPU的时间。我用 Go 写了一个模拟高 CPU 的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | func IsPrime(value int) bool { for i := 2; i <= int(math.Floor(float64(value)/2)); i++ { if value%2 == 0 { return false } } return true } func main() { for i := 0; i < 999999999; i++ { fmt.Printf("%v is prime: %v\n", i, IsPrime(i)) } } |
我创建两个 CPU 的 cgroups
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | yum install -y libcgroup-tools sudo cgcreate -g cpu:/cpulimited sudo cgcreate -g cpu:/lesscpulimited [root@lhrpolardb cgroup]# ll total 0 drwxr-xr-x 5 root root 0 Mar 26 20:00 blkio lrwxrwxrwx 1 root root 11 Mar 26 20:00 cpu -> cpu,cpuacct lrwxrwxrwx 1 root root 11 Mar 26 20:00 cpuacct -> cpu,cpuacct drwxr-xr-x 7 root root 0 Mar 26 20:00 cpu,cpuacct drwxr-xr-x 3 root root 0 Mar 26 20:00 cpuset drwxr-xr-x 5 root root 0 Mar 26 20:00 devices drwxr-xr-x 3 root root 0 Mar 26 20:00 freezer drwxr-xr-x 3 root root 0 Mar 26 20:00 hugetlb drwxr-xr-x 5 root root 0 Mar 26 20:00 memory lrwxrwxrwx 1 root root 16 Mar 26 20:00 net_cls -> net_cls,net_prio drwxr-xr-x 3 root root 0 Mar 26 20:00 net_cls,net_prio lrwxrwxrwx 1 root root 16 Mar 26 20:00 net_prio -> net_cls,net_prio drwxr-xr-x 3 root root 0 Mar 26 20:00 perf_event drwxr-xr-x 5 root root 0 Mar 26 20:00 pids drwxr-xr-x 5 root root 0 Mar 26 20:00 systemd [root@lhrpolardb cgroup]# cd cpu [root@lhrpolardb cpu]# ll total 0 -rw-r--r-- 1 root root 0 Mar 26 20:00 cgroup.clone_children --w--w--w- 1 root root 0 Mar 26 20:00 cgroup.event_control -rw-r--r-- 1 root root 0 Mar 26 20:00 cgroup.procs -r--r--r-- 1 root root 0 Mar 26 20:00 cgroup.sane_behavior -r--r--r-- 1 root root 0 Mar 26 20:00 cpuacct.stat -rw-r--r-- 1 root root 0 Mar 26 20:00 cpuacct.usage -r--r--r-- 1 root root 0 Mar 26 20:00 cpuacct.usage_percpu -rw-r--r-- 1 root root 0 Mar 26 20:00 cpu.cfs_period_us -rw-r--r-- 1 root root 0 Mar 26 20:00 cpu.cfs_quota_us drwxr-xr-x 2 root root 0 Apr 21 14:14 cpulimited -rw-r--r-- 1 root root 0 Mar 26 20:00 cpu.rt_period_us -rw-r--r-- 1 root root 0 Mar 26 20:00 cpu.rt_runtime_us -rw-r--r-- 1 root root 0 Mar 26 20:00 cpu.shares -r--r--r-- 1 root root 0 Mar 26 20:00 cpu.stat drwxr-xr-x 6 root root 0 Apr 15 15:21 docker drwxr-xr-x 2 root root 0 Apr 21 14:14 lesscpulimited -rw-r--r-- 1 root root 0 Mar 26 20:00 notify_on_release -rw-r--r-- 1 root root 0 Mar 26 20:00 release_agent drwxr-xr-x 108 root root 0 Apr 21 09:35 system.slice -rw-r--r-- 1 root root 0 Apr 7 17:56 tasks drwxr-xr-x 2 root root 0 Mar 26 20:00 user.slice [root@lhrpolardb cpu]# tree cpulimited cpulimited ├── cgroup.clone_children ├── cgroup.event_control ├── cgroup.procs ├── cpuacct.stat ├── cpuacct.usage ├── cpuacct.usage_percpu ├── cpu.cfs_period_us ├── cpu.cfs_quota_us ├── cpu.rt_period_us ├── cpu.rt_runtime_us ├── cpu.shares ├── cpu.stat ├── notify_on_release └── tasks 0 directories, 14 files [root@lhrpolardb cpu]# tree lesscpulimited/ lesscpulimited/ ├── cgroup.clone_children ├── cgroup.event_control ├── cgroup.procs ├── cpuacct.stat ├── cpuacct.usage ├── cpuacct.usage_percpu ├── cpu.cfs_period_us ├── cpu.cfs_quota_us ├── cpu.rt_period_us ├── cpu.rt_runtime_us ├── cpu.shares ├── cpu.stat ├── notify_on_release └── tasks 0 directories, 14 files |
cpu.shares 是给内核为每个进程决定 CPU 计算资源,默认值是1024。给 cpulimited 设置为 512,lesscpulimited 保留默认值,那么在这两个组的进程会以1 :2
的比例占用CPU。
1 2 3 4 | sudo cgset -r cpu.shares=512 cpulimited [root@lhrpolardb cpu]# cat ./cpulimited/cpu.shares 512 |
我们来验证一下。
在 cpulimited 起一个进程
1 | sudo cgexec -g cpu:cpulimited ./main > /dev/null & |
两个 cpulimited 进程的 CPU 之和 与 一个 lesscpulimited 进程的 CPU 差不多就是 1:2的关系。
可以看到独占了 100% 的 CPU,在 cpulimited 再起一个进程
两个进程都在 cpulimited,各占50%的 CPU。在 lesscpulimited 起一个进程
1 | sudo cgexec -g cpu:lesscpulimited ./main > /dev/null & |
两个 cpulimited 进程的 CPU 之和 与 一个 lesscpulimited 进程的 CPU 差不多就是 1:2的关系。
参考链接
https://zhuanlan.zhihu.com/p/55099839
https://link.zhihu.com/?target=https%3A//jvns.ca/blog/2016/10/10/what-even-is-a-container/