コントロールグループ

提供: ArchWiki
2022年9月22日 (木) 16:22時点におけるAshMyzk (トーク | 投稿記録)による版 (→‎systemd を使う: 同期)
ナビゲーションに移動 検索に移動

関連記事

コントロールグループ (一般的には cgroup と知られています) とは、プロセスのグループを管理、制限、監査するための、Linux カーネルによって提供されている機能です。cgroup はプロセスの集合 (およびその部分集合) に対して操作することができる (また、異なるシステムユーザを用いることもできる) ので 、nice(1) コマンドや /etc/security/limits.conf のような他のアプローチと比べて、cgroup はより柔軟です。

コントロールグループは様々なツールによってアクセスできます:

  • systemd ユニットファイルでディレクティブを使ってサービスやスライスに対する制限を指定する。
  • cgroup ファイルシステムに直接アクセスする。
  • cgcreatecgexeccgclassify (libcgroupAURlibcgroup-gitAUR の一部です) のようなツールを使う。
  • "Rules Engine Daemon" を使って、特定のユーザ/グループ/コマンドをグループに自動的に移動する (/etc/cgrules.confcgconfig.service) (libcgroupAURlibcgroup-gitAUR の一部です)。
  • Linux Containers (LXC) 仮想化などのような他のソフトウェアを使う。

Arch Linux の場合は、systemd がデフォルトのインストールの一部なので、cgroup を起動・管理する方法として systemd が最も簡単で推奨されます。

インストール

cgroup を自動的に管理するために、以下のパッケージのうち1つをインストールしていることを確認してください:

  • systemd - systemd サービスのリソースを管理するために必要です。
  • libcgroupAURlibcgroup-gitAUR - スタンドアロンツールのセット (cgcreatecgclassify、永続的な設定は cgconfig.conf)。

systemd を使う

階層

現在の cgroup 階層は systemctl status コマンドか systemd-cgls コマンドを使って見ることができます。

$ systemctl status
● myarchlinux
    State: running
     Jobs: 0 queued
   Failed: 0 units
    Since: Wed 2019-12-04 22:16:28 UTC; 1 day 4h ago
   CGroup: /
           ├─user.slice 
           │ └─user-1000.slice 
           │   ├─user@1000.service 
           │   │ ├─gnome-shell-wayland.service 
           │   │ │ ├─ 1129 /usr/bin/gnome-shell
           │   │ ├─gnome-terminal-server.service 
           │   │ │ ├─33519 /usr/lib/gnome-terminal-server
           │   │ │ ├─37298 fish
           │   │ │ └─39239 systemctl status
           │   │ ├─init.scope 
           │   │ │ ├─1066 /usr/lib/systemd/systemd --user
           │   │ │ └─1067 (sd-pam)
           │   └─session-2.scope 
           │     ├─1053 gdm-session-worker [pam/gdm-password]
           │     ├─1078 /usr/bin/gnome-keyring-daemon --daemonize --login
           │     ├─1082 /usr/lib/gdm-wayland-session /usr/bin/gnome-session
           │     ├─1086 /usr/lib/gnome-session-binary
           │     └─3514 /usr/bin/ssh-agent -D -a /run/user/1000/keyring/.ssh
           ├─init.scope 
           │ └─1 /sbin/init
           └─system.slice 
             ├─systemd-udevd.service 
             │ └─285 /usr/lib/systemd/systemd-udevd
             ├─systemd-journald.service 
             │ └─272 /usr/lib/systemd/systemd-journald
             ├─NetworkManager.service 
             │ └─656 /usr/bin/NetworkManager --no-daemon
             ├─gdm.service 
             │ └─668 /usr/bin/gdm
             └─systemd-logind.service 
               └─654 /usr/lib/systemd/systemd-logind

プロセスの cgroup を見つける

あるプロセスの cgroup 名は、/proc/PID/cgroup で見ることができます。

例えば、現在のシェルの cgroup は:

$ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-3.scope

cgroup のリソース使用量

systemd-cgtop コマンドを使用して、cgroup のリソース使用量を見ることができます:

$ systemd-cgtop
Control Group                            Tasks   %CPU   Memory  Input/s Output/s
user.slice                                 540  152,8     3.3G        -        -
user.slice/user-1000.slice                 540  152,8     3.3G        -        -
user.slice/u…000.slice/session-1.scope     425  149,5     3.1G        -        -
system.slice                                37      -   215.6M        -        -

カスタムの cgroup

systemd.slice(5) systemd ユニットファイルを使うことで、カスタムの cgroup 設定を定義できます。このユニットファイルは、systemd のディレクトリ内 (/etc/systemd/system/ など) に配置しなければなりません。リソースの割り当て可能な管理オプションは、systemd.resource-control(5) にてドキュメント化されています。

以下は、1つの CPU の 30% のみの使用を許可する slice ユニットの例です:

/etc/systemd/system/my.slice
[Slice]
CPUQuota=30%

サービスとして

サービスユニットファイル

リソースは、サービスの定義で、あるいはドロップインファイル として指定可能です:

[Service]
MemoryMax=1G 

この例では、サービスのメモリ使用量を 1 GB に制限します。

スライスでユニットをグループ化

サービスは、どのスライス内で実行するかを指定できます:

[Service]
Slice=my.slice

root として

systemd-run を使うことで、指定されたスライス内でコマンドを実行することができます。

# systemd-run --slice=my.slice command

--uid=username オプションを使うことで、指定されたユーザとしてコマンドをスポーンできます。

# systemd-run --uid=username --slice=my.slice command

--shell オプションを使うことで、スライスの中でコマンドシェルをスポーンできます。

非特権ユーザとして

非特権ユーザは、いくつかの条件を満たせば、そのユーザに与えられたリソースを新しい cgroup に配分することができます。

非 root ユーザが cgroup リソースを管理できるようにするには、cgroup v2 を使用しなければなりません。

コントローラのタイプ

すべてのリソースがユーザによって制御できるわけではありません。

コントローラ ユーザによって制御可能か オプション
cpu 委譲する必要あり CPUAccounting, CPUWeight, CPUQuota, AllowedCPUs, AllowedMemoryNodes
io 委譲する必要あり IOWeight, IOReadBandwidthMax, IOWriteBandwidthMax, IODeviceLatencyTargetSec
memory Yes MemoryLow, MemoryHigh, MemoryMax, MemorySwapMax
pids Yes TasksMax
rdma No ?
eBPF No IPAddressDeny, DeviceAllow, DevicePolicy
ノート: 技術的には eBPF はコントローラではありませんが、上記の systemd オプションは eBPF を使って実装されており、root のみがそれらのオプションの設定を許可されています。

ユーザ委譲

ユーザが cpu や io リソースを制御するには、リソースを委譲する必要があります。これはドロップインファイルを使うことで可能です。

例えば、あなたのユーザ ID が 1000 の場合:

/etc/systemd/system/user@1000.service.d/delegate.conf
[Service]
Delegate=cpu cpuset io

再起動し、あなたのセッションのスライスが cpu と io コントローラを所有していることを確認してください:

$ cat /sys/fs/cgroup/user.slice/user-1000.slice/cgroup.controllers
cpuset cpu io memory pids

ユーザ定義のスライス

ユーザのスライスファイルは ~/.config/systemd/user/ に配置できます。

特定のスライス下でコマンドを実行するには:

$ systemd-run --user --slice=my.slice command

また、ログインシェルをスライス内で実行することも可能です:

$ systemd-run --user --slice=my.slice --shell

実行時の調整

cgroup のリソースは、systemctl set-property コマンドを使うことで実行時に調整可能です。オプションについては systemd.resource-control(5) と同じです。

警告: --runtime オプションを渡さなかった場合、加えた調整は 永続化 されます。加えた調整は、システム全体のオプションは /etc/systemd/system.control/ に保存され、ユーザのオプションは .config/systemd/user.control/ に保存されますs。
ノート: すべてのリソースの変更が即座に反映されるわけではありません。例えば、TaskMax を変更した場合、新しいプロセスを作成したときに変更が適用されます。

例えば、すべてのユーザセッションに対してインターネットアクセスを切断するには:

$ systemctl set-property user.slice IPAddressDeny=any

シンプルな使い方

手動で使用する

次のセクションで説明している cgm を使う方法の代わりに、このセクションでは手動でメモリ使用量を制限する方法を説明します。

groupname という名前の新しい cgroup を作成:

# mkdir /sys/fs/cgroup/memory/groupname

最大メモリ使用制限を 100MB に設定:

# echo 100000000 > /sys/fs/cgroup/memory/groupname/memory.limit_in_bytes

プロセスを cgroup に移動 (一度に指定できる PID はひとつだけで、複数指定することはできません):

# echo pid > /sys/fs/cgroup/memory/groupname/cgroup.procs

一時的なグループ

cgroups を使って即座に"一時的な"グループを作成することができます。実際に、通常ユーザーにカスタムグループを作成する権限を与えることも可能です。次のコマンドを root で実行してください (user はあなたのユーザー名に、groupname は cgroup に付けたい名前に置き換えてください):

# cgcreate -a user -t user -g memory,cpu:groupname

もしくは cgmanager を使用 (ユーザー ID `1000` とシステムグループ root (GID 0) は適当な値に置き換えてください):

# cgm create memory groupname
# cgm chown memory groupname 1000 0
# cgm create cpu groupname
# cgm chown cpu groupname 1000 0
# chown user /sys/fs/cgroup/{memory,cpu}/groupname/*

ユーザーによってグループ groupname の全ての設定を書き換えることができます:

$ ls -l /sys/fs/cgroup/memory/groupname
total 0
-rwxrwxr-x 1 user root 0 Sep 25 00:39 cgroup.event_control
-rwxrwxr-x 1 user root 0 Sep 25 00:39 cgroup.procs
-rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.rt_period_us
-rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.rt_runtime_us
-rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.shares
-rwxrwxr-x 1 user root 0 Sep 25 00:39 notify_on_release
-rwxrwxr-x 1 user root 0 Sep 25 00:39 tasks

Cgroups は階層式なので、好きなだけサブグループを作成することができます。例えば、通常ユーザーとして、bash シェルを新しいサブグループ 'foo' 下で動作させたい場合:

$ cgcreate -g memory,cpu:groupname/foo
$ cgexec -g memory,cpu:groupname/foo bash

同じことを cgmanager で設定するには:

$ cgm create memory groupname/foo
$ cgm create cpu groupname/foo
$ bash
$ cgm movepid memory groupname/foo $pid_of_bash
$ cgm movepid cpu groupname/foo $pid_of_bash

さあ、確認してみましょう:

$ cat /proc/self/cgroup
11:memory:/groupname/foo
6:cpu:/groupname/foo

このグループの新しいサブディレクトリが作成されています。このグループ内の全てのプロセスのメモリ使用量を 10 MB に制限するには、次を実行します:

$ echo 10000000 > /sys/fs/cgroup/memory/groupname/foo/memory.limit_in_bytes

cgmanager を使用する場合:

$ cgm setvalue memory groupname/foo memory.limit_in_bytes 10000000

メモリ制限は RAM の使用にしか適用されないので注意してください -- タスクが制限に達すると、スワップを始めます。しかしながら他のプロセスのパフォーマンスにはあまり影響を与えません。

同じようにこのグループの CPU 優先度 ("shares") を変更することもできます。デフォルトでは全てのグループは 1024 shares になっています。100 shares のグループは CPU 時間の ~10% だけを使うようになります:

$ echo 100 > /sys/fs/cgroup/cpu/groupname/foo/cpu.shares

cgroup ディレクトリを表示することで他の設定や統計を見ることができます。

また、既に実行中のプロセスの cgroup を変更することも可能です。全ての 'bash' コマンドをこのグループに移動するには:

$ pidof bash
13244 13266
$ cgclassify -g memory,cpu:groupname/foo `pidof bash`
$ cat /proc/13244/cgroup
11:memory:/groupname/foo
6:cpu:/groupname/foo

永続的なグループ設定

ノート: systemd 205 以上を使って cgroups を管理する場合、このファイルは完全に無視することができます。

起動時に cgroup が作成されるようにしたい場合、代わりに /etc/cgconfig.conf でグループを定義することができます。例えば、"groupname" に制限を管理しタスクを追加する $USER のパーミッションとグループ $GROUPusers を定義します。サブグループの "groupname/foo" グループの定義は以下のようになります。

/etc/cgconfig.conf 
group groupname {
  perm {
# who can manage limits
    admin {
      uid = $USER;
      gid = $GROUP;
    }
# who can add tasks to this group
    task {
      uid = $USER;
      gid = $GROUP;
    }
  }
# create this group in cpu and memory controllers
  cpu { }
  memory { }
}

group groupname/foo {
  cpu {
    cpu.shares = 100;
  }
  memory {
    memory.limit_in_bytes = 10000000;
  }
}
ノート:
  • コメントは行の初めに置いて下さい。そうしないと、cgconfigparser が解析するときに問題が発生しますが、cgconfigSystemd で起動しない限り、エラーとしては cgroup change of group failed しか報告されません。
  • permissions セクションは任意です。
  • /sys/fs/cgroup/ ディレクトリ階層には controllers サブディレクトリが全て含まれ、起動時に仮想ファイルシステムとして作成・マウントされます。$CONTROLLER-NAME { } コマンドを使って新しいグループエントリを作成することが可能です。何らかの理由で他の場所に階層を作成・マウントしたいときは、/etc/cgconfig.conf に以下のようにふたつ目のエントリを書く必要があります:
mount {    
   cpuset = /your/path/groupname;
}

これは以下のシェルコマンドと同等です:

# mkdir /your/path/groupname
# mount -t /your/path -o cpuset groupname /your/path/groupname

活用例

Matlab

Matlab にはマシンのメモリや CPU が全て使われるのに対するプロテクトがありません。膨大な計算を始めてしまうとシステムが使えなくなる可能性があります。以下はそれを防止するために /etc/cgconfig.conf に配置するファイルです ($USER はあなたのユーザー名に置き換えてください):

/etc/cgconfig.conf 
# Prevent Matlab from taking all memory
group matlab {
    perm {
        admin {
            uid = $USER;
        }
        task {
            uid = $USER;
        }
    }

    cpuset {
        cpuset.mems="0";
        cpuset.cpus="0-5";
    }
    memory {
# 5 GiB limit
        memory.limit_in_bytes = 5368709120;
    }
}
ノート: $USER を実際に Matlab を動かすユーザーの名前に変えるのを忘れないで下さい。

この cgroup は Matlab が使えるコアをコア0から5までに (Matlab には6つのコアだけが認識されます) そしてメモリの使用量を 5 GiB に制限します。"cpu" リソースの強制を使って CPU 使用量の制限も定義することが可能ですが、"cpuset" で十分だと思われます。

matlab を次のようにして起動します:

$ cgexec -g memory,cpuset:matlab /opt/MATLAB/2012b/bin/matlab -desktop

正しい実行可能ファイルのパスを使って下さい。

ドキュメント

  • コントローラーや特定のスイッチ・オプションに関する情報はカーネルのドキュメント (v1v2) を参照してください (または linux-docs をインストールして /usr/src/linux/Documentation/cgroup を見て下さい)。
  • 詳細かつ完全なリソース管理ガイドが fedora プロジェクトのドキュメント にあります。

コマンドや設定ファイルについては、man cgcreateman cgrules.conf などの該当する man ページを見て下さい。