最近由于工作原因接触了docker,也就研究一下它的原理,主要看了阿里出的<<自己动手写docker>>,感觉蛮有意思,姑且记录一下。docker可以算是当前非常火热的技术了。我们知道docker基于Namespce和Cgroups,其中
- Namespace主要用于隔离资源
- Cgroups用来提供对一组进程以及将来子进程的资源限制
Cgrougps的三个组件
cgrpups包含三个组件
控制组 一个cgroups包含一组进程,并可以在这个cgroups上增加Linux subsystem的各种参数配置,将一组进程和一组subsystem关联起来。
subsystem 子系统 是一组资源控制模块,可以通过lssubsys -a
命令查看当前内核支持哪些subsystem。
1 2 3 4 5 6 7 8 9 10 11
| cpuset cpu,cpuacct blkio memory devices freezer net_cls,net_prio perf_event hugetlb pids rdma
|
subsystem作用于hierarchy的cgroup节点,并控制节点中进程的资源占用。
hierarchy 层级树 主要功能是把cgroups串成一个树型结构,使cgruops可以做到继承。也就是说将cgroup通过树状结构串起来,通过虚拟文件系统的方式暴露给用户。
三个组件之前的关系
cgroup中的组件是相互关联的
系统创建新的hierarchy之后,系统中所有的进程都会加入这个hierarchy的cgroup的根节点,这个cgroup根节点是hierarchy默认创建的,在这个hierarchy中创建的所有cgroup都是这个cgroup根节点的子节点。
一个subsystem只能附加到一个hierarchy上
一个hierarchy可以附加多个subsystem
一个进程可以作为多个cgroup的成员,但是这些cgroup必须在不同的hierarchy下
一个进程fork出子进程时,子进程和父进程是在同一个cgroup中的,根据需要也可以移动到其他的cgroup中
使用Cgroup
创建挂载点
我们知道kernel是通过一个虚拟树状文件系统来配置Cgroups的。我们首先需要创建并挂载一个hierarchy(cgroup树)。即先mkdir后mount。
1 2 3 4 5 6
| readlnh@readlnh-Inspiron-3542:~$ sudo mount -t cgroup -o none,name=cgroup-test cgroup-test cgroup-test/ readlnh@readlnh-Inspiron-3542:~$ ls ./c cgroup-test/ clone_test.c readlnh@readlnh-Inspiron-3542:~$ ls ./cgroup-test/ cgroup.clone_children cgroup.sane_behavior release_agent cgroup.procs notify_on_release tasks
|
可以看到挂载后系统在目录下生成了一系列文件,这些文件其实就是根节点文件的配置项。
创建子cgroup
创建cgroup-1,我们可以看到在cgroup文件夹下再创建文件夹,系统会将这个文件夹也标记为cgroup,并且它是上一个cgroup的子cgroup,它会继承父cgroup的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| readlnh@readlnh-Inspiron-3542:~/cgroup-test$ sudo mkdir cgroup-1 readlnh@readlnh-Inspiron-3542:~/cgroup-test$ tree . ├── cgroup-1 │ ├── cgroup.clone_children │ ├── cgroup.procs │ ├── notify_on_release │ └── tasks ├── cgroup.clone_children ├── cgroup.procs ├── cgroup.sane_behavior ├── notify_on_release ├── release_agent └── tasks
1 directory, 10 files
|
移动进程
将终端进程移动到cgroup-1(只要将进程ID写到相应的cgroup的tasks文件)
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
| readlnh@readlnh-Inspiron-3542:~/cgroup-test/cgroup-1$ echo $$ 10644 readlnh@readlnh-Inspiron-3542:~/cgroup-test/cgroup-1$ cat /proc/10644/cgroup 13:name=cgroup-test:/ 12:perf_event:/ 11:memory:/user.slice 10:hugetlb:/ 9:cpuset:/ 8:pids:/user.slice/user-1000.slice/user@1000.service 7:rdma:/ 6:devices:/user.slice 5:freezer:/ 4:cpu,cpuacct:/user.slice 3:net_cls,net_prio:/ 2:blkio:/user.slice 1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service 0::/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service readlnh@readlnh-Inspiron-3542:~/cgroup-test/cgroup-1$ sudo sh -c "echo $$ >> tasks" readlnh@readlnh-Inspiron-3542:~/cgroup-test/cgroup-1$ cat /proc/10644/cgroup 13:name=cgroup-test:/cgroup-1 12:perf_event:/ 11:memory:/user.slice 10:hugetlb:/ 9:cpuset:/ 8:pids:/user.slice/user-1000.slice/user@1000.service 7:rdma:/ 6:devices:/user.slice 5:freezer:/ 4:cpu,cpuacct:/user.slice 3:net_cls,net_prio:/ 2:blkio:/user.slice 1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service 0::/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service
|
可以看到现在终端进程已经在cgroup-1了
通过subsystem限制cgroups中的进程资源
之前我们创建的cgroup是没有和任何subsystem相关联的,所以没法通过hierarchy中的cgroup节点限制资源。实际系统中已经默认为每个subsystem创建了一个默认的hierarchy,我们这里就直接在这个hierarchy下创建cgroup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| readlnh@readlnh-Inspiron-3542:~/cgroup-test/test-limit-memory$ mount | grep memory cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) readlnh@readlnh-Inspiron-3542:~/cgroup-test/test-limit-memory$ cd /sys/fs/cgroup/memory/ readlnh@readlnh-Inspiron-3542:/sys/fs/cgroup/memory$ sudo mkdir test-limit-memory readlnh@readlnh-Inspiron-3542:/sys/fs/cgroup/memory$ cd test-limit-memory/ readlnh@readlnh-Inspiron-3542:/sys/fs/cgroup/memory/test-limit-memory$ ls cgroup.clone_children memory.limit_in_bytes cgroup.event_control memory.max_usage_in_bytes cgroup.procs memory.move_charge_at_immigrate memory.failcnt memory.numa_stat memory.force_empty memory.oom_control memory.kmem.failcnt memory.pressure_level memory.kmem.limit_in_bytes memory.soft_limit_in_bytes memory.kmem.max_usage_in_bytes memory.stat memory.kmem.slabinfo memory.swappiness memory.kmem.tcp.failcnt memory.usage_in_bytes memory.kmem.tcp.limit_in_bytes memory.use_hierarchy memory.kmem.tcp.max_usage_in_bytes notify_on_release memory.kmem.tcp.usage_in_bytes tasks memory.kmem.usage_in_bytes readlnh@readlnh-Inspiron-3542:/sys/fs/cgroup/memory/test-limit-memory$ stress --vm-bytes 200m --vm-keep -m 1 stress: info: [15357] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
|
用top查看,发现内存为100m,实际上这里限制内存就是一个向memory_limit_in_bytes文件中写入100m这个操作,可以说相简单了
1
| 15358 readlnh 20 0 213044 100916 212 D 39.5 1.3 7:54.10 stress
|
从这里我们就可以猜测,docker实际上就是通过go语言挂载,创建cgroups再向相应文件写入相应条件来限制容器资源的。cgroup确实是一个很有意思也很方便的功能。