使用cgroups限制MongoDB的內存使用
cgroups ,其名稱源自控制組群(control groups)的簡寫,是Linux內核的一個功能,用來限制,控制與分離一個進程組群的資源(如CPU、內存、磁盤輸入輸出等)。
這個項目最早是由Google的工程師在2006年發起(主要是Paul Menage和Rohit Seth),最早的名稱為進程容器(process containers)。在2007年時,因為在Linux內核中,容器(container)這個名詞有許多不同的意義,為避免混亂,被重命名為 cgroup,并且被合并到2.6.24版的內核中去。自那以后,又添加了很多功能。
使 用 cgroup,系 統 管 理 員 可 更 具 體 地 控 制 對 系 統 資 源 的 分 配 、 優 先 順 序 、 拒 絕 、 管 理 和 監 控 。 可 更 好 地 根 據 任 務 和 用 戶 分 配 硬 件 資 源 ,提 高 總 體 效 率 。在實踐中,系統管理員一般會利用cgroup做下面這些事:
- 隔離一個進程組(比如:nginx的所有進程),并限制他們所消費的資源,比如綁定CPU的核。
- 為這組進程 分配其足夠使用的內存
- 為這組進程分配相應的網絡帶寬和磁盤存儲限制
- 限制訪問某些設備(通過設置設備的白名單) </ul>
- 任務( task )。在cgroups中,任務就是系統的一個進程。
- 控制組群( control group )。控制組群就是一組按照某種標準劃分的進程。cgroups中的資源控制都是以控制組群為單位實現。一個進程可以加入到某個控制組群,也從一個進程組遷移到另一個控制組群。一個進程組的進程可以使用cgroups以控制組群為單位分配的資源,同時受到cgroups以控制組群為單位設定的限制。
- 層級( hierarchy )。控制組群可以組織成hierarchical的形式,既一顆控制組群樹。控制組群樹上的子節點控制組群是父節點控制組群的孩子,繼承父控制組群的特定的屬性。
- 子系統( subsystem )。一個子系統就是一個資源控制器,比如cpu子系統就是控制cpu時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起作用,一個子系統附加到某個層級以后,這個層級上的所有控制組群都受到這個子系統的控制。 </ol>
- cpu 子系統,主要限制進程的 cpu 使用率。
- cpuacct 子系統,可以統計 cgroups 中的進程的 cpu 使用報告。
- cpuset 子系統,可以為 cgroups 中的進程分配單獨的 cpu 節點或者內存節點。
- memory 子系統,可以限制進程的 memory 使用量。
- blkio 子系統,可以限制進程的塊設備 io。
- devices 子系統,可以控制進程能夠訪問某些設備。
- net_cls 子系統,可以標記 cgroups 中進程的網絡數據包,然后可以使用 tc 模塊(traffic control)對數據包進行控制。
- freezer 子系統,可以掛起或者恢復 cgroups 中的進程。
- ns 子系統,可以使不同 cgroups 下面的進程使用不同的 namespace。 </ol>
- 創建一個控制組群: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`` </ol>
cgroups相關概念
當前的cgroup有一下規則:
1.每次在系統中創建新層級時,該系統中的所有任務都是那個層級的默認 cgroup(我們稱之為 root cgroup ,此cgroup在創建層級時自動創建,后面在該層級中創建的cgroup都是此cgroup的后代)的初始成員。
2.一個子系統最多只能附加到一個層級。 (一個層級不會附加兩個同樣的子系統)
3.一個層級可以附加多個子系統
4.一個任務可以是多個cgroup的成員,但是這些cgroup必須在不同的層級。
5.系統中的進程(任務)創建子進程(任務)時,該子任務自動成為其父進程所在 cgroup 的成員。然后可根據需要將該子任務移動到不同的 cgroup 中,但開始時它總是繼承其父任務的cgroup。
</div>cgroup子系統
cgroups為每種可以控制的資源定義了一個子系統。典型的子系統介紹如下:
cgroups安裝
如果系統還沒有安裝cgroups,可以通過下面的命令進行安裝
yum install libcgroup
</div>啟動和查看服務狀態:
service cgconfig start
service cgconfig status
</div>Linux把cgroups實現成一個文件系統,各個子系統的掛載點配置在/etc/cgconfig.conf文件中:
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;
}
</div>或者也可以通過命令lssubsys -m或者mount -t cgroup掛載。
# 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
</div>或者你單獨掛載某幾個子系統:
mount -t cgroup -o remount,cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem
</div>cgroups使用
掛載某一個 cgroups 子系統到掛載點之后,就可以通過在掛載點下面建立文件夾或者使用cgcreate命令的方法創建 cgroups 層級結構中的節點。比如通過命令cgcreate -g cpu:test就可以在 cpu 子系統下建立一個名為 test 的節點。結果如下所示:
# 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
</div>然后可以通過寫入需要的值到 test 下面的不同文件,來配置需要限制的資源。每個子系統下面都可以進行多種不同的配置,需要配置的參數各不相同,詳細的參數設置需要參考 cgroups 手冊。使用cgset命令也可以設置 cgroups 子系統的參數,格式為cgset -r parameter=value path_to_cgroup。
比如:cgset -r cfs_quota_us=50000 test限制進程組 test 使用50%的CPU。
或者直接寫文件:
</div>echo 50000 > /cgroup/cpu/test/cpu.cfs_quota_us
</div>命令可以參考redhat的文檔: Setting Parameters
當需要刪除某一個 cgroups 節點的時候,可以使用cgdelete命令,比如要刪除上述的 test 節點,可以使用cgdelete -r cpu:test命令進行刪除。
把進程加入到 cgroups 子節點也有多種方法,可以直接把 pid 寫入到子節點下面的 task 文件中。也可以通過cgclassify添加進程,格式為cgclassify -g subsystems:path_to_cgroup pidlist,也可以直接使用cgexec在某一個 cgroups 下啟動進程,格式為cgexec -g subsystems:path_to_cgroup command arguments.
也可以在/etc/cgconfig.conf文件中定義group,格式如下:
group < name > {
[ < permissions > ]
< controller > {
< param name > = < param value > ;
…
}
…
}
</div>比如:
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;
}
group mysql_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" ;
}
}
</div>還可以讓一個服務Service啟動的時候加入進程組,具體文檔請參考: Starting_a_Service
Redhat的文檔詳細的介紹了cgroups的配置和使用方法,是很好的一個參考資料。
實踐,限制MongoDB的內存使用
MongoDB是個吃內存的大戶,它會盡可能的使用服務器的內存。在數據量巨大的時候,內存很快會被吃光,導致服務器上其它進程無法分配內存。我們可以使用cgroups來限制MongoDB的內存使用。實際上,在參考文檔2中 Vadim Tkachenko 就介紹了他的實際方法。
配置有幾個步驟:
基本上就完成了任務,這樣此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 </div>2) /proc/sys/vm/dirty_background_ratio
這個參數控制文件系統的pdflush進程,在何時刷新磁盤。單位是百分比,表示系統內存的百分比,意思是當寫緩沖使用到系統內存多少的時候,pdflush開始向磁盤寫出數據。增大之會使用更多系統內存用于磁盤寫緩沖,也可以極大提高系統的寫性能。但是,當你需要持續、恒定的寫入場合時,應該降低其數值,:
</div>echo '1' > /proc/sys/vm/dirty_background_ratio
3) /proc/sys/vm/dirty_writeback_centisecs
這個參數控制內核的臟數據刷新進程pdflush的運行間隔。單位是 1/100 秒。缺省數值是500,也就是 5 秒。如果你的系統是持續地寫入動作,那么實際上還是降低這個數值比較好,這樣可以把尖峰的寫操作削平成多次寫操作。設置方法如下:
</div>echo "100" > /proc/sys/vm/dirty_writeback_centisecs
如果你的系統是短期地尖峰式的寫操作,并且寫入數據不大(幾十M/次)且內存有比較多富裕,那么應該增大此數值:
echo "1000" > /proc/sys/vm/dirty_writeback_centisecs
4) /proc/sys/vm/dirty_expire_centisecs
這個參數聲明Linux內核寫緩沖區里面的數據多“舊”了之后,pdflush進程就開始考慮寫到磁盤中去。單位是 1/100秒。缺省是 30000,也就是 30 秒的數據就算舊了,將會刷新磁盤。對于特別重載的寫操作來說,這個值適當縮小也是好的,但也不能縮小太多,因為縮小太多也會導致IO提高太快。
</div>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
</div>缺省設置:100
6) /proc/sys/vm/min_free_kbytes
該文件表示強制Linux VM最低保留多少空閑內存(Kbytes)。
缺省設置:724(512M物理內存)
</div>7) /proc/sys/vm/nr_pdflush_threads
該文件表示當前正在運行的pdflush進程數量,在I/O負載高的情況下,內核會自動增加更多的pdflush進程。
缺省設置:2(只讀)
</div>8) /proc/sys/vm/overcommit_memory
該文件指定了內核針對內存分配的策略,其值可以是0、1、2。
0, 表示內核將檢查是否有足夠的可用內存供應用進程使用;如果有足夠的可用內存,內存申請允許;否則,內存申請失敗,并把錯誤返回給應用進程。
1, 表示內核允許分配所有的物理內存,而不管當前的內存狀態如何。
2, 表示內核允許分配超過所有物理內存和交換空間總和的內存(參照overcommit_ratio)。
</div>缺省設置:0
9) /proc/sys/vm/overcommit_ratio
該文件表示,如果overcommit_memory=2,可以過載內存的百分比,通過以下公式來計算系統整體可用內存。
系統可分配內存=交換空間+物理內存*overcommit_ratio/100
</div>10) /proc/sys/vm/page-cluster
該文件表示在寫一次到swap區的時候寫入的頁面數量,0表示1頁,1表示2頁,2表示4頁。
缺省設置:3(2的3次方,8頁)
</div>11) /proc/sys/vm/swapiness
該文件表示系統進行交換行為的程度,數值(0-100)越高,越可能發生磁盤交換。
</div>