今年も年末を迎えて、
昨年のこの時期に書いた第16回は、
今年同じことをやっても新鮮味にはかけると思いますが、
Linux Advent Calendarということで、
cgroup
この連載では、
その際に説明したように、
カーネルのバージョン2.
その後、
新しい機能のリリースはなくても、
それが4.
cgroupによるプロセス数の制限
コンテナごとに起動できるプロセスの数を制限したいという要求は、
cgroupでも、
筆者が主催しているコンテナ型仮想化の情報交換会の第2回では、
私は、
pidsサブシステムを有効にしたカーネルの作成
それでは早速pidsサブシステムを使ってみましょう。今回の記事の実行例はPlamo 6.
pidsサブシステムを使うには、CONFIG_
を有効にする必要があります。make menuconfig
などで設定する場所は他のcgroupサブシステムと同じ場所です。
General setup ---> Control Group support ---> [*] PIDs cgroup subsystem
新しく作ったカーネルで起動すると、/proc/
にpids
という行が現れています。
$ cat /proc/cgroups | grep pids pids 12 5 1
マウントとグループの作成
pidsサブシステムを使う際にマウントを行う方法は、-o
オプションでpids
を指定します。
たとえば以下のように行います。
$ sudo mkdir -p /sys/fs/cgroup/pids $ sudo mount -t cgroup -o pids none /sys/fs/cgroup/pids
このマウントした/
$ ls /sys/fs/cgroup/pids/ cgroup.clone_children cgroup.sane_behavior notify_on_release release_agent cgroup.procs pids.current tasks
ここにtest01
というグループを作成してみましょう。
$ sudo mkdir /sys/fs/cgroup/pids/test01 $ ls /sys/fs/cgroup/pids/test01 cgroup.clone_children notify_on_release pids.max cgroup.procs pids.current tasks
pids.
とpids.
が、
pidsサブシステム用ファイル
pidsサブシステム独自で使用するファイルは以上の 2 つです。ファイル名ですぐに役割が分かったのではないでしょうか。
ファイル名 | ファイルの役割 |
---|---|
pids. |
グループ内の現在のプロセス数 |
pids. |
グループ内で許可するプロセス数 |
それぞれのファイルが、test01
グループで見てみます。
$ cat /sys/fs/cgroup/pids/test01/pids.current 0 $ cat /sys/fs/cgroup/pids/test01/pids.max max
グループを作成した直後ですから、pids.
は当然0になりますね。制限なしとするにはpids.
にmax
と書きます。初期値は制限なしでmax
です。
制限を追加する
制限値を設定する方法は通常のcgroupの使い方と同じです。制限値として2を設定してみましょう。
$ echo 2 | sudo tee /sys/fs/cgroup/pids/test01/pids.max 2 $ cat /sys/fs/cgroup/pids/test01/pids.max 2
ファイルの内容は、
現在のシェルをグループに追加してみましょう。
$ echo $$ | sudo tee /sys/fs/cgroup/pids/test01/tasks 5600 $ cat /sys/fs/cgroup/pids/test01/pids.current 2
pids.
の内容は、cat
で2になっています。
ここでパイプでつないでコマンドを実行してみましょう。
$ ( /bin/echo "Gihyo" | cat ) bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: Resource temporarily unavailable Terminated
シェルを登録してひとつ消費した状態で2つ実行しようとしたので、
階層構造
第3回でcgroupの特徴として階層構造が取れることを紹介しました。
それでは多層の場合に、
先ほどのtest01
グループの下層にtest02
グループを作成します。test01
は2を設定しておき、test02
は作成したままで特に制限をかけないままにしておきます。
$ sudo mkdir /sys/fs/cgroup/pids/test01/test02 $ cat /sys/fs/cgroup/pids/test01/pids.max 2 $ cat /sys/fs/cgroup/pids/test01/test02/pids.max max
test02
にプロセスを追加して、test01
とtest02
のpids.
を確認してみます。
$ echo $$ | sudo tee /sys/fs/cgroup/pids/test01/test02/tasks 26500 $ cat /sys/fs/cgroup/pids/test01/tasks $ cat /sys/fs/cgroup/pids/test01/test02/tasks 26500 26582 $ cat /sys/fs/cgroup/pids/test01/pids.current 2 $ cat /sys/fs/cgroup/pids/test01/test02/pids.current 2
test01
のtasks
は空ですので、test01
グループにはプロセスは登録されていない状態です。test02
のtasks
には追加したシェルのPIDとcat
のPIDが表示されています。つまりcat
を実行した瞬間は、test02
には2つプロセスが存在する状態ですね。
pids.
を見てみると、test01
、test02
ともに2となっています。これは、
ここで先ほどと同様にpids.
を超えるプロセスを生成してみます。
$ ( /bin/echo "Gihyo" | cat ) bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: Resource temporarily unavailable Terminated
プロセスは生成できませんね。つまり階層構造を持つグループの場合、
pidsサブシステムのカーネルの実装
今回は、
昨年のLinux Advent Calendar 2014の25日目のfork()
を探してみるとkernel/
1792 SYSCALL_DEFINE0(fork)
1793 {
:(略)
1795 return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0);
:(略)
1800 }
以上のfork()
の定義行の少し下にはvfork()
とclone()
の定義もあります。いずれも_do_
という関数を呼んでいますので、_do_
関数を見れば良いことがわかります。
_do_
関数も同じくkernel/
1697 long _do_fork(unsigned long clone_flags,
1698 unsigned long stack_start,
1699 unsigned long stack_size,
1700 int __user *parent_tidptr,
1701 int __user *child_tidptr,
1702 unsigned long tls)
1703 {
:(略)
1726 p = copy_process(clone_flags, stack_start, stack_size,
1727 child_tidptr, NULL, trace, tls);
:(略)
_do_
関数は指定されたcloneフラグにしたがって各種資源のコピーを作成します。このコピーを行うのが、上記のcopy_
関数です。この関数の中では、copy_
で始まる名前の関数が多数呼ばれて、
このcopy_
関数をきちっと追っていけば、
pidsサブシステムは比較的シンプルに実装されていて、
1つ目のパッチを見てみると、copy_
関数内の処理にパッチが当たっていることがわかります。パッチが当たっている部分を見てみると、cgroup_
関数が呼ばれています。
1526 /*
1527 * Ensure that the cgroup subsystem policies allow the new process to be
1528 * forked. It should be noted the the new process's css_set can be changed
1529 * between here and cgroup_post_fork() if an organisation operation is in
1530 * progress.
1531 */
1532 retval = cgroup_can_fork(p, cgrp_ss_priv);
1533 if (retval)
1534 goto bad_fork_free_pid;
ここでエラーになると、
2つ目のパッチはpidsサブシステム自体の処理を実装してあり、
もう少し詳しく見ると正確に処理がわかるとは思います。しかしパッチをさらっと見るだけでも、fork()
、vfork()
、clone()
システムコールの実体である関数内にチェックが追加され、
制限以上のプロセスをグループに追加する
pids.
を設定した上で、
$ sudo mkdir /sys/fs/cgroup/pids/test01 $ echo 2 | sudo tee /sys/fs/cgroup/pids/test01/pids.max 2
制限を2に設定したので、test01
グループにどんどんプロセスを追加してみましょう。
$ cat /sys/fs/cgroup/pids/test01/tasks (登録されているプロセスはない) $ echo 7189 | sudo tee /sys/fs/cgroup/pids/test01/tasks 7189 (1つ目のプロセス追加) $ echo 7855 | sudo tee /sys/fs/cgroup/pids/test01/tasks 7855 (2つ目のプロセス追加) $ echo 8885 | sudo tee /sys/fs/cgroup/pids/test01/tasks 8885 (3つ目のプロセス追加。エラーにならない) $ echo 8992 | sudo tee /sys/fs/cgroup/pids/test01/tasks 8992 (4つ目のプロセス追加。エラーにならない) $ cat /sys/fs/cgroup/pids/test01/pids.current 4 (pids.max以上のプロセスが登録されている)
pids.
を2に設定したのに、pids.
がそれ以上になってもエラーにはなりません。
この動きは、tasks
にプロセスを追加していっても、
もちろん、
$ echo $$ | sudo tee /sys/fs/cgroup/pids/test01/tasks 10648 (さらにプロセスをtest01に追加) $ cat /sys/fs/cgroup/pids/test01/tasks bash: fork: retry: No child processes (新たにプロセスは起動しない) :(略)
プロセスが登録された状態でグループの制限値を変更する
さらに、pids.
となるようなケースを試してみましょう。
$ cat /sys/fs/cgroup/pids/test01/pids.max 4 $ cat /sys/fs/cgroup/pids/test01/pids.current 4
以上のようにすでに制限値に達しているグループがあります。この状態でpids.
を減らしてみましょう。
$ echo 2 | sudo tee /sys/fs/cgroup/pids/test01/pids.max 2 (制限値を2に減らす) $ cat /sys/fs/cgroup/pids/test01/pids.current 4 (グループ内のプロセス数は4のまま)
プロセスが登録された状態で、
この動きも、
まとめ
今回は少し予定を変更して、
- pidsサブシステムを使うと、
グループ内で起動するプロセス数が制限できる - 制限はこれから起動しようとするプロセスに対する制限で、
既存のプロセスには影響を与えない
サブシステムの動きだけを見ると、
カーネルのコードはもう少し詳細に追っかけないと、
次回は、