CGroups

CGroups

2006年,Google的工程师发明了Linux控制组,缩写为CGroups。这是Linux内核的一项功能,可隔离和控制用户进程的资源使用情况。这些进程可以放入命名空间,实质上是共享相同资源限制的进程集合。计算机可以有多个命名空间,每个命名空间都具有内核强制执行的资源属性。

我们可以管理每个命名空间的资源分配,以便限制一组进程可以使用的总CPURAM等的数量。例如,后台日志聚合应用程序可能需要限制其资源,以免意外地压倒它正在记录的实际服务器。虽然不是原始功能,但Linux中的CGroups最终被重新设计为包含命名空间隔离的功能。命名空间隔离本身并不新鲜,Linux已经有多种命名空间隔离。一个常见的例子是进程隔离,它将每个单独的进程分开并防止诸如共享内存之类的事情。

CGroups隔离是一种更高级别的隔离,可确保CGroups命名空间中的进程独立于其他命名空间中的进程。

  • PID(进程标识符)命名空间:这可确保一个命名空间内的进程不知道其他命名空间中的进程。
  • 网络命名空间:隔离网络接口控制器,iptables,路由表和其他低级网络工具。
  • 挂载命名空间:已挂载文件系统,因此命名空间的文件系统范围仅限于已挂载的目录。
  • 用户名空间:将命名空间内的用户限制为仅限该命名空间,并避免跨命名空间的用户ID冲突。

cgroup-bin

通过工具cgroup-bin (sudo apt-get install cgroup-bin)可以创建CGroup并进入该CGroup执行命令。

root@host:/home/vagrant# cgcreate -a vagrant -g cpu:cg1
root@host:/home/vagrant# ls /sys/fs/cgroup/cpu/cg1/
cgroup.clone_children  cpu.cfs_period_us  cpu.shares  cpuacct.stat   cpuacct.usage_all     cpuacct.usage_percpu_sys   cpuacct.usage_sys   notify_on_release
cgroup.procs           cpu.cfs_quota_us   cpu.stat    cpuacct.usage  cpuacct.usage_percpu  cpuacct.usage_percpu_user  cpuacct.usage_user  tasks

cpu.cfs_period_uscpu.cfs_quota_us,它们分别用来限制该组中的所有进程在单位时间里可以使用的cpu时间,这里的cfs(Completely Fair Scheduler)是完全公平调度器的意思。cpu.cfs_period_us是时间周期,默认为100000,即100毫秒。而cpu.cfs_quota_us是在时间周期内可以使用的时间,默认为-1即无限制。cpu.shares用于限制cpu使用的,它用于控制各个组之间的配额。比如组cg1cpu.shares1024,组cg2cpu.shares也是1024,如果都有进程在运行则它们都可以使用最多50%的限额。如果cg2组内进程比较空闲,那么cg1组可以将使用几乎整个cputasks存储的是该组里面的进程ID

我们先在默认的分组里面运行一个死循环程序loop.py,因为默认分组/sys/fs/cgroup/cpu/cpu.cfs_period_uscfs_quota_us是默认值,所以是没有限制cpu使用的。可以发现1cpu us立马接近100%了。

# loop.py
while True: pass

设置cg1组的cfs_quota_us50000,即表示该组内进程最多使用50%cpu时间,运行cgexec命令进入cg1cpu组,然后运行loop.py,可以发现cpu us50%以内了,此时也可以在tasks文件中看到我们刚刚cgexec创建的进程ID

root@host:/home/vagrant# echo 50000 > /sys/fs/cgroup/cpu/cg1/cpu.cfs_quota_us
root@host:/home/vagrant# cgexec -g cpu:cg1 /bin/bash

Docker里面要限制内存和CPU使用,可以在启动时指定相关参数即可。比如限制cpu使用率,加cpu-periodcpu-quota参数,限制执行的cpu核,加–cpuset-cpus参数。限制内存使用,加–memory参数。当然,我们可以看到在/sys/fs/cgroup/cpu/docker/目录下有个以containerid为名的分组,该分组下面的cpu.cfs_period_uscpu.cfs_quota_us的值就是我们在启动容器时指定的值。

root@host:/home/vagrant# docker run -i -t --cpu-period=100000 --cpu-quota=50000 --memory=512000000 alpine /bin/ash

Capabilities

我们在启动容器时会时常看到这样的参数 --cap-add=NET_ADMIN,这是用到了Linuxcapability特性。capability是为了实现更精细化的权限控制而加入的。我们以前熟知通过设置文件的SUID位,这样非root用户的可执行文件运行后的euid会成为文件的拥有者ID,比如passwd命令运行起来后有root权限,有SUID权限的可执行文件如果存在漏洞会有安全风险。(查看文件的capability的命令为filecap -a,而查看进程capability的命令为pscap -apscapfilecap工具需要安装libcap-ng-utils这个包)

对于capability,可以看一个简单的例子便于理解。如Debian系统中自带的ping工具,它是有设置SUID位的。这里拷贝ping重命名为anotherpinganotherpingSUID位没有设置,运行会提示权限错误。这里,我们只要将其加上cap_net_raw权限即可,不需要设置SUID位那么大的权限。

vagrant@host:~$ ls -ls /bin/ping
60 -rwsr-xr-x 1 root root 61240 Nov 10  2016 /bin/ping
vagrant@host:~$ cp /bin/ping anotherping
vagrant@host:~$ ls -ls anotherping
60 -rwxr-xr-x 1 vagrant vagrant 61240 May 19 10:18 anotherping
vagrant@host:~$ ./anotherping -c1 yue.uu.163.com
ping: socket: Operation not permitted
vagrant@host:~$ sudo setcap cap_net_raw+ep ./anotherping
vagrant@host:~$ ./anotherping -c1 yue.uu.163.com
PING yue.uu.163.com (59.111.137.252) 56(84) bytes of data.
64 bytes from 59.111.137.252 (59.111.137.252): icmp_seq=1 ttl=63 time=53.9 ms

--- yue.uu.163.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 53.919/53.919/53.919/0.000 ms
上一页
下一页