Cgroups
Linux Cgroups 的全称是 Linux Control Group,它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。Cgroups 还能够对进程进行优先级设置、审计,以及将进程挂起和恢复等操作。
在 Linux 中,Cgroups 给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下。
root@ubuntu-xenial:~# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
这些都是这台机器当前可以被 Cgroups 进行限制的资源种类。
而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。比如,对 CPU 子系统来说,我们就可以看到如下几个配置文件,这个指令是:
[root@node1 ~]# ls /sys/fs/cgroup/cpu
cgroup.clone_children cpuacct.stat cpuacct.usage_percpu cpuacct.usage_sys cpu.cfs_quota_us cpu.shares release_agent
cgroup.procs cpuacct.usage cpuacct.usage_percpu_sys cpuacct.usage_user cpu.rt_period_us cpu.stat tasks
cgroup.sane_behavior cpuacct.usage_all cpuacct.usage_percpu_user cpu.cfs_period_us cpu.rt_runtime_us notify_on_release
在对应的子系统下面创建一个目录,比如,我们现在进入 /sys/fs/cgroup/cpu 目录下:
[root@node1 cpu]# mkdir container
[root@node1 cpu]# cd container
[root@node1 container]# ls
cgroup.clone_children cpu.cfs_quota_us cpu.shares cpuacct.usage cpuacct.usage_percpu_sys cpuacct.usage_user
cgroup.procs cpu.rt_period_us cpu.stat cpuacct.usage_all cpuacct.usage_percpu_user notify_on_release
cpu.cfs_period_us cpu.rt_runtime_us cpuacct.stat cpuacct.usage_percpu cpuacct.usage_sys tasks
这个目录就称为一个“控制组”。操作系统会在新创建的 container 目录下,自动生成该子系统对应的资源限制文件。
cfs_quota_us实现对CPU的限制
后台执行这样一条脚本:
$ while : ; do : ; done &[1]
226
它执行了一个死循环,可以把计算机的一个CPU核心 吃到 100%,根据它的输出,我们可以看到这个脚本在后台运行的进程号(PID)是 226。
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
-1
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_period_us
100000
向 container 组里的 cfs_quota 文件写入 20 ms(20000 us)
echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
把被限制的进程的 PID 写入 container 组里的 tasks 文件,上面的设置就会对该进程生效了。
$ echo 226 > /sys/fs/cgroup/cpu/container/tasks
可写入多个PID rmdir container 可以执行成功,rm -rf container 会提示
Operation not permitted
除 CPU 子系统外,Cgroups 的每一个子系统都有其独有的资源限制能力,比如:
- blkio,为块设备设定I/O 限制,一般用于磁盘等设备;
- cpuset,为进程分配单独的 CPU 核和对应的内存节点;
- memory,为进程设定内存使用的限制。
docker下手动自定义cgroup参数
对于 Docker 等 Linux 容器项目来说,它们只需要在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录),然后在启动容器进程之后,把这个进程的 PID 填写到对应控制组的 tasks 文件中就可以了。
而至于在这些控制组下面的资源文件里填上什么值,就靠用户执行 docker run 时的参数指定了,比如:
$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bas
启动这个容器后,可以通过查看 Cgroups 文件系统下,CPU 子系统中,“docker”这个控制组里的资源限制文件的内容来确认:
$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_period_us
100000
$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_quota_us
20000
/proc问题
Linux 下的 /proc 目录存储的是记录当前内核运行状态的一系列特殊文件,用户可以通过访问这些文件,查看系统以及当前正在运行的进程的信息,这些文件也是 top 指令查看系统信息的主要数据来源。但是,如果在容器里执行 top 指令,、它显示的信息居然是宿主机的 CPU 和内存数据,而不是当前容器的数据。
造成这个问题的原因就是,/proc 文件系统并不知道用户通过 Cgroups 给这个容器做了什么样的资源限制,即:/proc 文件系统不了解 Cgroups 限制的存在。
在生产环境中,这个问题必须进行修正,否则应用程序在容器里读取到的 CPU 核数、可用内存等信息都是宿主机上的数据。
lxcfs
补充
- windows docker技术的原理又是什么样的
—> 从官网上看,也是依赖hyper-v的虚拟机化技术
- 如何实现不同容器不同操作系统?
- 容器磁盘无法限制(IOPS与大小)?
- 容器的文件在宿主机的文件目录?
docker info | grep -w “Root Dir”
- 为什么k8s 在创建pod 时候 只有对cpu 和内存的限制 没有网络和磁盘io限制的选项?
网络和IO限制比较难实施
- 容器里调用 settimeofday 修改时间,会造成宿主机修改时间。 前提是容器要有相关的权限—privileged 或者 —cap-add SYS_TIME
- systemd 或者 supervisord 这样的软件来代替应用本身作为容器的启动进程。
—> 更好的是在前台运行。
- 容器内部还能再做namespace和cgroup实现docker的嵌套。
- 容器和宿主机共用内核。
因为容器和宿主机共用内核,那么内核调用的方法应该是统一的;所以这里的lib应该是指内核调用方法。文件系统中看到的lib是这些调用方法的实现,不同操作系统不能共用。我是这么理解的,不知道是否正确。
- 容器是一个“单进程”模型。这个特点 是K8s用Pod管理容器运行时的主要原因
- 容器和应用能够同生命周期