使用cgroups限制MongoDB的內存使用

jopen 9年前發布 | 26K 次閱讀 cgroups MongoDB NoSQL數據庫

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>

    cgroups相關概念

    1. 任務( task )。在cgroups中,任務就是系統的一個進程。
    2. 控制組群( control group )。控制組群就是一組按照某種標準劃分的進程。cgroups中的資源控制都是以控制組群為單位實現。一個進程可以加入到某個控制組群,也從一個進程組遷移到另一個控制組群。一個進程組的進程可以使用cgroups以控制組群為單位分配的資源,同時受到cgroups以控制組群為單位設定的限制。
    3. 層級( hierarchy )。控制組群可以組織成hierarchical的形式,既一顆控制組群樹。控制組群樹上的子節點控制組群是父節點控制組群的孩子,繼承父控制組群的特定的屬性。
    4. 子系統( subsystem )。一個子系統就是一個資源控制器,比如cpu子系統就是控制cpu時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起作用,一個子系統附加到某個層級以后,這個層級上的所有控制組群都受到這個子系統的控制。
    5. </ol>

      當前的cgroup有一下規則:

      1.每次在系統中創建新層級時,該系統中的所有任務都是那個層級的默認 cgroup(我們稱之為 root cgroup ,此cgroup在創建層級時自動創建,后面在該層級中創建的cgroup都是此cgroup的后代)的初始成員。

      2.一個子系統最多只能附加到一個層級。 (一個層級不會附加兩個同樣的子系統)

      3.一個層級可以附加多個子系統

      4.一個任務可以是多個cgroup的成員,但是這些cgroup必須在不同的層級。

      5.系統中的進程(任務)創建子進程(任務)時,該子任務自動成為其父進程所在 cgroup 的成員。然后可根據需要將該子任務移動到不同的 cgroup 中,但開始時它總是繼承其父任務的cgroup。

      </div>

      cgroup子系統

      cgroups為每種可以控制的資源定義了一個子系統。典型的子系統介紹如下:

      1. cpu 子系統,主要限制進程的 cpu 使用率。
      2. cpuacct 子系統,可以統計 cgroups 中的進程的 cpu 使用報告。
      3. cpuset 子系統,可以為 cgroups 中的進程分配單獨的 cpu 節點或者內存節點。
      4. memory 子系統,可以限制進程的 memory 使用量。
      5. blkio 子系統,可以限制進程的塊設備 io。
      6. devices 子系統,可以控制進程能夠訪問某些設備。
      7. net_cls 子系統,可以標記 cgroups 中進程的網絡數據包,然后可以使用 tc 模塊(traffic control)對數據包進行控制。
      8. freezer 子系統,可以掛起或者恢復 cgroups 中的進程。
      9. ns 子系統,可以使不同 cgroups 下面的進程使用不同的 namespace。
      10. </ol>

        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 就介紹了他的實際方法。

        配置有幾個步驟:

        1. 創建一個控制組群:cgcreate -g memory:DBLimitedGroup
        2. 指定可用的最大內存16G:echo 16G > /sys/fs/cgroup/memory/DBLimitedGroup/memory.limit_in_bytes
        3. 將緩存頁丟掉 (flush and drop):sync; echo 3 > /proc/sys/vm/drop_caches
        4. 將mongodb的進程加入控制組:cgclassify -g memory:DBLimitedGrouppid of mongod``
        5. </ol>

          基本上就完成了任務,這樣此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' &gt; /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 &quot;1000&quot; &gt; /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>

          參考文檔

          </div> </div> 原文 http://colobu.com/2015/07/23/Using-Cgroups-to-Limit-MongoDB-memory-usage/

           本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
           轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
           本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!