通过命名空间可以实现进程间在文件系统、网络以及宿主进程之间的相互隔离,但是对于宿主机的物理资源比如 CPU、内存、块设备等,如果同一机器上运行了多个相互独立的容器,这些容器在执行高负载任务时必定会因此产生严重的资源争用问题。比如,当其中的某个容器正在执行 CPU 密集型的任务,此时如果没有某种限制,这个容器必定会影响在同一主机上运行的其他容器中任务,进而导致这些容器的性能受到极大的影响,这在生产上是必须要避免的。
解决进程间虚拟资源隔离问题的办法就是 CGroups(Control Groups),它是 Linux 内核的一种机制,用于对系统资源(如 CPU、内存、磁盘 I/O、网络等)进行分组管理和限制。CGroups 允许系统管理员将进程分组,并为每个组分配特定的资源限制,从而实现资源的隔离、控制和监控。
CGroups 的核心功能包括:
- 限制组内进程的资源使用量(如 CPU 使用率、内存使用量等)。防止某个进程或组占用过多资源,影响其他进程或系统的稳定性。
- 为不同的组分配不同的资源优先级。为高优先级任务分配更多的 CPU 时间。
- 监控和记录组内进程的资源使用情况(如 CPU 时间、内存使用量等)。
- 对组内进程进行挂起、恢复、终止等操作。
- CGroups 支持层次化结构,可以将组嵌套在其他组中,形成树状结构。
CGroups 通过子系统(subsystem)来管理不同类型的资源。常见的子系统包括:
root@docker-host:~# ls /sys/fs/cgroup/ blkio cpu,cpuacct freezer net_cls perf_event systemd cpu cpuset hugetlb net_cls,net_prio pids unified cpuacct devices memory net_prio rdma root@docker-host:~#其中,cpu:限制 CPU 使用率,支持按比例分配 CPU 时间。memory:限制内存使用量,支持设置内存和交换空间的上限。blkio:限制块设备如磁盘的 I/O 操作。devices:控制组内进程对设备的访问权限。freezer:挂起或恢复组内的进程。net_cls:标记网络数据包,用于流量控制。pids:限制组内进程的数量。cpuacct:统计组内进程的 CPU 使用情况。
我们将在两个 shell 会话中同时运行这个程序,不出意外的话,主机内存很快就会耗尽。为防止这种情况的出现,可以创建一个 CGroups 来限制这两个 shell 进程的内存使用,当内存达到限制上限时终止程序,从而保证主机内存资源的安全使用。
创建 CGroups 很简单,只需在控制组文件系统创建一个目录即可。在本例中,我们将创建一个名为 demo 的内存组,之后内核会自动将可用的子系统配置文件填充到这个目录:
root@docker-host:~# mkdir /sys/fs/cgroup/memory/demo root@docker-host:~# ls /sys/fs/cgroup/memory/demo/ 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 root@docker-host:~#将 demo 内存控制组的最大内存使用量限制在 100M,为演示效果考虑,禁止使用交换分区:
root@docker-host:~# echo "100000000" > /sys/fs/cgroup/memory/demo/memory.limit_in_bytes root@docker-host:~# echo "0" > /sys/fs/cgroup/memory/demo/memory.swappiness root@docker-host:~#启动的两个 bash 进程 id 显示如下:
root@docker-host:~# ps -ef | grep bash root 1585 1378 0 12:55 pts/0 00:00:00 -bash root 1695 1371 0 12:55 pts/1 00:00:00 -bash root 1770 1695 0 12:56 pts/1 00:00:00 grep --color=auto bash root@docker-host:~#将这两个进程加入到 demo 内存控制组的 tasks 文件中:
root@docker-host:~# ps PID TTY TIME CMD 1585 pts/0 00:00:00 bash 1850 pts/0 00:00:00 ps root@docker-host:~# echo $$ >> /sys/fs/cgroup/memory/demo/tasks root@docker-host:~# root@docker-host:~# ps PID TTY TIME CMD 1695 pts/1 00:00:00 bash 1845 pts/1 00:00:00 ps可以看到,当两个应用的内存使用达到 100M 时,demo 内存控制组会终止程序的运行,如果要取消控制组的限制,只要删除控制目录即可.
注意:只有 tasks 文件中的所有进程都已退出或已重新分配给另一个组才能删除。
CGroup 的前身是出自于 2006 年 Google 工程师 Paul Menage 和 Rohit Seth 启动的一个名字叫做 process containers 的项目,后来因为与 container 在内核中的名字有冲突,因此在 2007 年改名为 control groups,合并到 2008 年发布的 2.6.24 内核版本中。
最初 CGroups 的版本被称为 v1,后续的开发工作由 Tejun Heo 接管,他重新设计并重写了 CGroups,新版本被称为 v2,并在 kernel 4.5 版本中引入。随着 v2 版本的普及,容器资源的管理变得更加高效和灵活。
在容器环境中,CGroup 扮演着至关重要的角色,它是容器资源管理和隔离的核心技术之一。在 Docker 和 Kubernetes 中,CGroup 被广泛用于限制容器的 CPU、内存、磁盘 I/O 等资源。