实时警报通知:微信告警通知的重要性解析
724
2023-02-11
使用cgroups限制MongoDB的内存使用
这个项目最早是由Google的工程师在2006年发起(主要是Paul Menage和Rohit Seth),最早的名称为进程容器(process containers)。在2007年时,因为在Linux内核中,容器(container)这个名词有许多不同的意义,为避免混乱,被重命名为cgroup,并且被合并到2.6.24版的内核中去。自那以后,又添加了很多功能。
使用 cgroup,系统管理员可更具体地控制对系统资源的分配、优先顺序、拒绝、管理和监控。可更好地根据任务和用户分配硬件资源,提高总体效率。在实践中,系统管理员一般会利用cgroup做下面这些事:
cgroups相关概念
任务(task)。在cgroups中,任务就是系统的一个进程。控制组群(control group)。控制组群就是一组按照某种标准划分的进程。cgroups中的资源控制都是以控制组群为单位实现。一个进程可以加入到某个控制组群,也从一个进程组迁移到另一个控制组群。一个进程组的进程可以使用cgroups以控制组群为单位分配的资源,同时受到cgroups以控制组群为单位设定的限制。层级(hierarchy)。控制组群可以组织成hierarchical的形式,既一颗控制组群树。控制组群树上的子节点控制组群是父节点控制组群的孩子,继承父控制组群的特定的属性。子系统(subsystem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制组群都受到这个子系统的控制。
当前的cgroup有一下规则:1.每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。2.一个子系统最多只能附加到一个层级。 (一个层级不会附加两个同样的子系统)3.一个层级可以附加多个子系统4.一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级。5.系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的cgroup。
cgroup子系统
cgroups为每种可以控制的资源定义了一个子系统。典型的子系统介绍如下:
cpu 子系统,主要限制进程的 cpu 使用率。cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。memory 子系统,可以限制进程的 memory 使用量。blkio 子系统,可以限制进程的块设备 io。devices 子系统,可以控制进程能够访问某些设备。net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。freezer 子系统,可以挂起或者恢复 cgroups 中的进程。ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。
cgroups安装
如果系统还没有安装cgroups,可以通过下面的命令进行安装
1 | yum install libcgroup |
启动和查看服务状态:
1 2 | service cgconfig start service cgconfig status |
Linux把cgroups实现成一个文件系统,各个子系统的挂载点配置在/etc/cgconfig.conf文件中:
1 2 3 4 5 6 7 8 9 10 | mount{ cpuset=/cgroup/cpuset; cpu=/cgroup/cpu; cpuacct=/cgroup/cpuacct; memory=/cgroup/memory; devices=/cgroup/devices; freezer=/cgroup/freezer; net_cls=/cgroup/net_cls; blkio=/cgroup/blkio; } |
或者也可以通过命令lssubsys -m或者mount -t cgroup挂载。
1 2 3 4 5 6 7 8 9 | # lssubsys -m cpuset/cgroup/cpuset cpu/cgroup/cpu cpuacct/cgroup/cpuacct memory/cgroup/memory devices/cgroup/devices freezer/cgroup/freezer net_cls/cgroup/net_cls blkio/cgroup/blkio |
或者你单独挂载某几个子系统:
1 | mount-tcgroup-oremount,cpu,cpuset,memory cpu_and_mem/cgroup/cpu_and_mem |
cgroups使用
挂载某一个 cgroups 子系统到挂载点之后,就可以通过在挂载点下面建立文件夹或者使用cgcreate命令的方法创建 cgroups 层级结构中的节点。比如通过命令cgcreate -g cpu:test就可以在 cpu 子系统下建立一个名为 test 的节点。结果如下所示:
1 2 3 4 5 | # cgcreate -g cpu:test # ls /cgroup/cpu cgroup.event_control cpu.cfs_quota_us cpu.shares release_agent cgroup.procs cpu.rt_period_us cpu.stat tasks cpu.cfs_period_us cpu.rt_runtime_us notify_on_release test |
然后可以通过写入需要的值到 test 下面的不同文件,来配置需要限制的资源。每个子系统下面都可以进行多种不同的配置,需要配置的参数各不相同,详细的参数设置需要参考 cgroups 手册。使用 cgset 命令也可以设置 cgroups 子系统的参数,格式为 cgset -r parameter=value path_to_cgroup。比如:cgset -r cfs_quota_us=50000 test限制进程组 test 使用50%的CPU。或者直接写文件:
1 | echo50000>/cgroup/cpu/test/cpu.cfs_quota_us |
当需要删除某一个 cgroups 节点的时候,可以使用 cgdelete 命令,比如要删除上述的 test 节点,可以使用 cgdelete -r cpu:test命令进行删除。
也可以在/etc/cgconfig.conf文件中定义group,格式如下:
1 2 3 4 5 6 7 8 | group<name>{ [<permissions>] <controller>{ <param name>=<param value>; … } … } |
比如:
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 | mount{ cpuset=/cgroup/cpuset; cpu=/cgroup/cpu; cpuacct=/cgroup/cpuacct; memory=/cgroup/memory; devices=/cgroup/devices; freezer=/cgroup/freezer; net_cls=/cgroup/net_cls; blkio=/cgroup/blkio; } groupmysql_g1{ cpu{ cpu.cfs_quota_us=50000; cpu.cfs_period_us=100000; } cpuset{ cpuset.cpus="3"; cpuset.mems="0"; } cpuacct{ } memory{ memory.limit_in_bytes=104857600; memory.swappiness=0; # memory.max_usage_in_bytes=104857600; # memory.oom_control=0; } blkio{ blkio.throttle.read_bps_device="8:0 524288"; blkio.throttle.write_bps_device="8:0 524288"; } } |
Redhat的文档详细的介绍了cgroups的配置和使用方法,是很好的一个参考资料。
MongoDB是个吃内存的大户,它会尽可能的使用服务器的内存。在数据量巨大的时候,内存很快会被吃光,导致服务器上其它进程无法分配内存。我们可以使用cgroups来限制MongoDB的内存使用。实际上,在参考文档2中 Vadim Tkachenko 就介绍了他的实际方法。
配置有几个步骤:
创建一个控制组群:cgcreate -g memory:DBLimitedGroup指定可用的最大内存16G: echo 16G > /sys/fs/cgroup/memory/DBLimitedGroup/memory.limit_in_bytes将缓存页丢掉 (flush and drop): sync; echo 3 > /proc/sys/vm/drop_caches将mongodb的进程加入控制组:cgclassify -g memory:DBLimitedGrouppid of mongod
基本上就完成了任务,这样此MongoDB最多可以使用16G的内存。为了处理机器重启还得手工添加的问题,你可以按照上面的文档将Mongo服务加入到控制组中。
除此之外,作者还提到了 dirty cache flush的问题, 注意两个参数:/proc/sys/vm/dirty_background_ratio和/proc/sys/vm/dirty_ratio。
这里有一篇关于调整磁盘缓冲参数的介绍:1) /proc/sys/vm/dirty_ratio 这个参数控制文件系统的文件系统写缓冲区的大小,单位是百分比,表示系统内存的百分比,表示当写缓冲使用到系统内存多少的时候,开始向磁盘写出数据。增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒定的写入场合时,应该降低其数值,:echo '1' > /proc/sys/vm/dirty_ratio
2) /proc/sys/vm/dirty_background_ratio 这个参数控制文件系统的pdflush进程,在何时刷新磁盘。单位是百分比,表示系统内存的百分比,意思是当写缓冲使用到系统内存多少的时候,pdflush开始向磁盘写出数据。增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒定的写入场合时,应该降低其数值,:
echo '1' > /proc/sys/vm/dirty_background_ratio
3) /proc/sys/vm/dirty_writeback_centisecs 这个参数控制内核的脏数据刷新进程pdflush的运行间隔。单位是 1/100 秒。缺省数值是500,也就是 5 秒。如果你的系统是持续地写入动作,那么实际上还是降低这个数值比较好,这样可以把尖峰的写操作削平成多次写操作。设置方法如下:
echo "100" > /proc/sys/vm/dirty_writeback_centisecs如果你的系统是短期地尖峰式的写操作,并且写入数据不大(几十M/次)且内存有比较多富裕,那么应该增大此数值:
echo "1000" > /proc/sys/vm/dirty_writeback_centisecs
echo "100" > /proc/sys/vm/dirty_expire_centisecs当然,如果你的系统内存比较大,并且写入模式是间歇式的,并且每次写入的数据不大(比如几十M),那么这个值还是大些的好。
5) /proc/sys/vm/vfs_cache_pressure 该文件表示内核回收用于directory和inode cache内存的倾向;缺省值100表示内核将根据pagecache和swapcache,把directory和inode cache保持在一个合理的百分比;降低该值低于100,将导致内核倾向于保留directory和inode cache;增加该值超过100,将导致内核倾向于回收directory和inode cache
缺省设置:100
6) /proc/sys/vm/min_free_kbytes 该文件表示强制Linux VM最低保留多少空闲内存(Kbytes)。缺省设置:724(512M物理内存)
7) /proc/sys/vm/nr_pdflush_threads 该文件表示当前正在运行的pdflush进程数量,在I/O负载高的情况下,内核会自动增加更多的pdflush进程。缺省设置:2(只读)
缺省设置:0
10) /proc/sys/vm/page-cluster 该文件表示在写一次到swap区的时候写入的页面数量,0表示1页,1表示2页,2表示4页。缺省设置:3(2的3次方,8页)
11) /proc/sys/vm/swapiness 该文件表示系统进行交换行为的程度,数值(0-100)越高,越可能发生磁盘交换。
参考文档
发表评论
评论列表