@legitwhiz

legit whiz 技術レポート

View on GitHub

cgroupsを利用したリソースコントロール(RHEL7)

cgroupsとは

cgroups(Control Groups)とは、「プロセスをグループ化して、 リソースの利用をコントロール」するカーネル機能で、 Linux 2.6.24からマージされています。

具体的には、グループ単位でリソースの割り当て、 優先度、管理、モニタリングなどが設定できます。

cgroupsそのものはプロセスを「コントロールグループ」 と呼ばれる単位にまとめるだけで、リソースコントロールを 行うにはコントロールグループに「サブシステム」 と呼ばれる抽象化されたリソース群をつなげる必要があります。

主なサブシステムには、次のようなものがあります。

・cpu CPUへのアクセス ・cpuacct CPUについての自動レポートを生成 ・cpuset マルチコアCPUのコア単位およびメモリノードを割り当て ・memory メモリに対する制限設定とメモリリソースについての自動レポートの生成 ・blkio ブロックデバイスの入出力アクセス ・devices デバイスへのアクセス ・net_cls ネットワークパケットへのタグ付け ・net_prio ネットワークトラフィックの優先度を動的に設定 ・freezer タスクを一時停止または再開

「コントロールグループ」自体は、あくまで名前だけなので 1つの「コントロールグループ」で複数のリソースをコントロールすることも可能です。

cgroupsのインストール

cgroupsのインストール

# yum install libcgroup libcgroup-tools

cgroupsの起動設定

# /usr/bin/systemctl enable cgconfig
# /usr/bin/systemctl enable cgred

コマンドによる設定方法

まずはグループ作成

対象をCPUとメモリとした[CPU75_Mem_Group]というコントロールグループを作成する。

# sudo cgcreate -g cpu,memory:/CPU75_Mem_Group

制限値を設定

作成したコントロールグループにリソースの制限値を設定する。 CPUを使用率75%に制限する場合は、1000000マイクロ秒(1秒)あたり 75000マイクロ秒(0.75秒)間だけ、単一のCPUを使用するよう に指定したいので以下のようになる。と思いがちですが 「単一のCPU」というのがネックで複数コアを搭載している場合は

制限したい使用率75%×コア数(4コアの場合) [cpu.cfs_quota_us]を計算すると [cpu.cfs_quota_us]= 0.75 x 4core x 100000(sをµsに変換) = 300000 となる。

以下のように設定したい。

# cgset -r cpu.cfs_quota_us=300000 CPU75_Mem_Group
# cgset -r cpu.cfs_period_us=1000000 CPU75_Mem_Group

メモリの割り当て制限する場合は、 memory.limit_in_bytes(ユーザーメモリの上限)とmemory.memsw.limit_in_bytes(ユーザーメモリ+スワップの上限)の値を設定する必要がある。

例えばユーザーメモリ(物理メモリ)を100MB、スワップを500MBに制限するとした場合は

# cgset -r memory.limit_in_bytes=100M CPU75_Mem_Group
# cgset -r memory.memsw.limit_in_bytes=500M CPU75_Mem_Group

その他のcgroupsに関連するコマンド

コマンド 機能 ・cgcreate コントロールグループの作成 ・cgdelete コントロールグループの削除 ・cgset サブシステムのパラメーター設定 ・cgget コントロールグループのパラメーター表示 ・cgclassify コントロールグループのタスク移動 ・cgexec コントロールグループ内のプロセス開始 ・cgsnapshot 既存サブシステムから設定ファイルを生成 ・cgconfigparser 設定ファイルを解析して階層をマウント ・cgclear コントロールグループのアンロード ・lscgroup コントロールグループの確認 ・lssubsys サブシステムのマウントポイント表示

設定ファイルによる設定方法

設定ファイルによる設定

cgconfigサービスが参照している[/etc/cgconfig.conf]を編集し設定する方法です。 コマンドと同じ条件で制限する場合は、以下のようにファイルに編集する必要がある。

[/etc/cgconfig.conf]

group CPU75_Mem_Group  {
    cpu {
        cpu.cfs_quota_us = 300000;
        cpu.cfs_period_us = 1000000;
    }
    memory {
        memory.limit_in_bytes = 100M
        memory.memsw.limit_in_bytes = 500M
    }
}

特定ユーザのみ制限をかける方法

cgredサービスが参照している[/etc/cgrules.conf]に下記の設定を行い、 [/etc/cgconfig.conf]で設定したコントロールグループを特定ユーザに関連づける。

[/etc/cgrules.conf]

hoge_user cpu CPU75_Mem_Group

設定を反映するには、サービスの再起動が必要となる。

特定プロセスを制限をかけて起動

cgroup経由でSofteterを起動 [/etc/cgconfig.conf]で設定したコントロールグループ

# cgexec -g cpu:CPU75_Mem_Group <プロセス起動コマンド>

特定ユーザの特定プログラムだけに制限をかける方法

[/etc/cgrules.conf]に下記の設定を行い、[/etc/cgconfig.conf]で設定した コントロールグループを特定ユーザ、特定プログラムに関連づける。

[/etc/cgrules.conf]

hoge_user:<プログラム名> cpu CPU75_Mem_Group

制限をかけた特定プログラムを他コントロールグループに移す方法

ユーザ単位で制限したときのタスク(プロセス)は、CPU利用率が制限され 実行時点では、コントロールグループの管理下となる。 そのため、psコマンドを実行すると管理下にあるタスク(プロセス)の PIDが出力される。

例えばpsコマンド実行時、[hoge_user]のPID[3333]だったとしよう [/sys/fs/cgroup/cpu/CPU75_Mem_Group/tasks]にPIDが格納されているので [CPU75_Mem_Group]から[CPU25_Mem_Group]へコントロールグループを 移すのは、以下のように実施する。

# sh -c "echo 3333 >> /sys/fs/cgroup/cpu/CPU25_Mem_Group/tasks"

ディスクI/O帯域制限する方法

以下のパラメータを変更するとディスクI/O制限制限することが可能となる。

・特定のデバイスに対するアクセス速度の上限をByte/Sec単位で指定。 0を指定すると制限を解除。

指定方法は”Major:Minor 設定値”(例”8:0 1048576”)

・blkio.throttle.read_bps_device ・blkio.throttle.write_bps_device

・特定のデバイスに対するアクセス速度の上限をIO/Sec単位で指定。 0を指定すると制限を解除。

指定方法は”Major:Minor 設定値”(例”8:0 2048”)

・blkio.throttle.read_iops_device ・blkio.throttle.write_iops_device

※Minor番号の指定がありますが、実際にはディスクパーティション単位での 指定はできない場合があるようです。 パーティション/dev/sda1, /dev/sda2・・などを使用している場合は、 /dev/sdaに対して指定すること。

まずは、対象となるブロックデバイスのメジャー番号の確認 以下の場合、[/dev/sda]は[8:0]となる。

# lsblk -p
NAME                         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
/dev/fd0                      2:0    1    4K  0 disk
/dev/sda                      8:0    0   40G  0 disk
├/dev/sda1                   8:1    0    1G  0 part /boot
└/dev/sda2                   8:2    0   39G  0 part
  ├/dev/mapper/centos-root 253:0    0   37G  0 lvm  /
  └/dev/mapper/centos-swap 253:1    0    2G  0 lvm  [SWAP]
/dev/sr0                     11:0    1 1024M  0 rom

書き込みの上限を1MB/Secに指定した場合、[/etc/cgconfig.conf]に 以下のように設定する。

[/etc/cgconfig.conf]

group DiskIO_Group  {
    blkio {
        blkio.throttle.read_bps_device = "8:0 1048576";
        blkio.throttle.write_bps_device = "8:0 1048576";
    }
}

同様にIOPSの上限を999IOPSに制限する場合は、[/etc/cgconfig.conf]に 以下のように設定する。

[/etc/cgconfig.conf]

group DiskIO_Group  {
    blkio {
        blkio.throttle.read_iops_device = "8:0 999";
        blkio.throttle.write_iops_device = "8:0 999";
    }
}

CPU、メモリ同様にプロセス毎、ユーザ毎にディスクも制限可能だ。

ネットワーク帯域制限する方法

プロセスごとのネットワーク帯域制限は、[cgroups]の[net_cls]と[tc]もしくは[netfilter]を使うことで実現可能です。

net_cls サブシステムは、Linux トラフィックコントローラー (tc) が 特定の cgroup から発信されるパケットを識別できるようにし、 クラス識別子 (classid) を使用して、ネットワークパケットをタグ付けします。 トラフィックコントローラーは、異なる cgroup からのパケットに 異なる優先順位を割り当てるように設定できます。

送出するネットワークパケットに『帯域クラスラベル』を付与します。帯域クラスは、別途 tc コマンドで設定しておく必要があります。tc コマンドは、送出パケットに対するネットワーク帯域を制御する機能を提供します。

なお、tc コマンドは受信パケットに対する帯域制御はできません。

まずは、ネットワーク制限するグループを作成し

[/etc/cgconfig.conf]

group Network_Group1 {
    net_cls {
        net_cls.classid = 0x100001;
    }
}
group Network_Group2 {
    net_cls {
        net_cls.classid = 0x100002;
    }
}

tcコマンドで制限値を設定し、cgroupのclassidと紐づける

# <Network I/F>を除外
tc qdisc del dev <Network I/F①> root
tc qdisc del dev <Network I/F②> root

# <Network I/F>を追加
tc qdisc add dev <Network I/F①> root handle 1: htb default 1
tc qdisc add dev <Network I/F②> root handle 1: htb default 1

# <Network I/F①>を10MBに制限
tc class add dev <Network I/F①> parent 1: classid 10:1  htb rate 1000Mbit ceil 1000Mbit burst 10MB cburst 10MB

# <Network I/F①>を5MBに制限
tc class add dev <Network I/F②> parent 1: classid 10:2 htb rate  500Mbit ceil  500Mbit burst  5MB cburst  5MB

# tc filterに<Network I/F①>を追加
tc filter add dev <Network I/F①> parent 1: protocol ip prio 1 handle 1 cgroup
tc filter add dev <Network I/F②> parent 1: protocol ip prio 1 handle 1 cgroup

# プロセスに帯域制限を付与して実行
cgexec --sticky -g net_cls:Network_Group1 ./scp_test.sh

ネットワーク帯域制限の設定確認するには

tc qdisc show dev <Network I/F①>
qdisc tbf 1: root refcnt 2 rate 1000Mbit burst 10MB lat 36.9ms 
qdisc netem 10: parent 1:1 limit 1000 delay 50.0ms  10.0ms

ネットワーク帯域制限がかかっている状態か確認するには

tc -s qdisc show dev <Network I/F①>
qdisc tbf 1: root refcnt 2 rate 1000Mbit burst 10MB lat 36.9ms 
qdisc netem 10: parent 1:1 limit 1000 delay 50.0ms  10.0ms

また、tcコマンドでは、帯域幅だけでなく、以下オプションによりデータキュー、パケットのサイズに制限をかけることも可能です。

limit 500Kb … データキューのサイズを 500KByte に設定。 buffer 100Kb … パケットのサイズを 100KByte に設定。 rate 500Kbps … 帯域幅を 200KBps に設定。 注意点として、[tc]での[b]表記は「ビット」ではなく「バイト」であること。

tcの詳しい説明は以下を参考にするといいだろう。 http://labs.gree.jp/blog/2014/10/11266/ https://qiita.com/hana_shin/items/d9ba818b49aca87b2314

cgroupsの用途

・KVM仮想マシンのIO帯域を制限 ・Dockerのコンテナ毎にCPU,メモリ制限 ・アンチウィルスソフトウェアへのCPU制限 ・業務に影響を与えないためにscp等のファイル転送時に帯域制限 ・AWS等のクラウド環境で課金対象を低減させるための、リソース制限等々

参考

https://htaira.fedorapeople.org/hbstudy19/hbstudy19-cgroups.pdf