LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第3回 Linuxカーネルのコンテナ機能[2] ─cgroupとは?(その1)

この記事を読むのに必要な時間:およそ 4 分

前回は,コンテナの仕組みとLinuxカーネルに実装されているコンテナ関連機能のひとつである名前空間について説明しました。今回は名前空間と並んでコンテナの実現に重要な役割を担っているcgroupについて説明していきます。

cgroupは2006年9月にGoogleのエンジニアによって最初のパッチが投稿され,2.6.24カーネルで最初のマージがなされた機能です。2010年にリリースされたRed Hat Enterprise Linux 6.0にこの機能が搭載され,専用のマニュアルが存在したことから,使ったことのある方や,名前をご存知の方も多いのではないでしょうか。

cgroupは"Control Group"の略です。プロセスをグループ化して,そのグループ内に存在するプロセスに対して共通の管理を行うために使います。たとえば,ホストOSが持つCPUやメモリなどのリソースに対して,グループごとに制限をかけることができます。

前回説明したように,コンテナはプロセスを隔離空間に入れることによって作成しますので,コンテナ内に入っているプロセスの集合に対してまとめてリソース制限をかける必要のある場面は多く,このような場面にcgroupが使えます。cgroupを使って,あるコンテナがホストOSの持つ有限なリソースを使いつくして,ホストOS上のプロセスや他のコンテナに影響を与えないようにできます。

名前空間機能と同様に,cgroupもコンテナ専用の機能ではなく,ホストOS上で実行されているプロセスであれば,任意にグループ化できます。また,異なるコンテナ内に存在するプロセスであっても,それをグループ化して制限をかけることもできます。

cgroupがどのようなリソースに対して制限や管理を行えるのかという説明をする前に,まずはcgroupがどのようにプロセスをグループ化し,どのようにグループに対する操作を行うのかについて説明しましょう。

cgroupファイルシステム

cgroup機能を使ってプロセスをグループ化したものをcgroupと呼びます。cgroupはcgroupファイルシステム(以降cgroupfs)という仮想的なファイルシステムを使って操作します。カーネルのパラメータの値を見たり,パラメータを変更したりするときに/proc以下のファイルを使う場合があると思います。この/proc以下のファイルと同じような感覚で操作できます。

では,cgroupfsの操作をいくつか例をあげてみていきましょう。今までの例はUbuntu 14.04 LTS上で行っていましたが,ここではUbuntu 12.04 LTSを最小インストールした環境を使って説明を行います。これは,最近のバージョンだと最小インストールしてもcgroupfsが最初からマウントされていることが多いためです。

 CentOS 6,Debian 7の最小インストールでも例と同じような操作は可能です。

cgroupファイルシステムのマウント

cgroupfsは以下のようにマウントします。ここで"-o"で指定しているオプションは次回説明します。cgroupfsはどのディレクトリにでもマウント可能ですが,現在では/sys/fs/cgroup以下にマウントするという風に決められています。

 $ sudo mount -t tmpfs cgroup /sys/fs/cgroup  (/sys/fs/cgroupをtmpfsでマウント)
 $ sudo mkdir /sys/fs/cgroup/cpu              (cgroupマウント用のディレクトリ作成)
 $ sudo mount -t cgroup -o cpu cgroup /sys/fs/cgroup/cpu   (cgroupfsのマウント)

この例では,まずtmpfsとして/sys/fs/cgroupをマウントします。これは必須の処理ではありませんが,このような処理を行ってからcgroupfsをマウントすることが多いので,ここでも合わせています。

その後,マウントポイントとして使用するディレクトリ/sys/fs/cgroup/cpuを作成しています。その後,mountコマンドの-tオプションにcgroupを指定してcgroupfsをマウントしています。

マウントしたcgroupfsの中は普通のディレクトリのように見ることができます。lsコマンドで見てみましょう。

 $ ls /sys/fs/cgroup/cpu
 cgroup.clone_children  cpu.cfs_period_us  cpu.rt_runtime_us  notify_on_release
 cgroup.event_control   cpu.cfs_quota_us   cpu.shares         release_agent
 cgroup.procs           cpu.rt_period_us   cpu.stat           tasks

マウントしただけでいろいろなファイルが自動的に作られていますね。この自動的に作られたファイルがcgroupの使用状況を見たり,cgroupをコントロールするのに使うファイルです。

cgroupの作成

それではマウントしたcgroupfsを使ってプロセスをグループ化するためのcgroupを作ってみましょう。cgroupはcgroupfs上のディレクトリで表されます。作成は通常のファイルシステムと同じようにmkdirコマンドで可能です。それではtest01というcgroupを作ってみましょう。

 $ cd /sys/fs/cgroup/cpu
 $ sudo mkdir test01
 $ ls -F
 cgroup.clone_children  cpu.cfs_quota_us   cpu.stat           test01/
 cgroup.event_control   cpu.rt_period_us   notify_on_release
 cgroup.procs           cpu.rt_runtime_us  release_agent
 cpu.cfs_period_us      cpu.shares         tasks

test01というディレクトリが作られています。

 $ ls test01
 cgroup.clone_children  cpu.cfs_period_us  cpu.rt_runtime_us  notify_on_release
 cgroup.event_control   cpu.cfs_quota_us   cpu.shares         tasks
 cgroup.procs           cpu.rt_period_us   cpu.stat

このディレクトリの中をのぞいてみると,先にマウントした際に/sys/fs/cgroup以下に出来たのと同じようなファイルが自動的に作られています。

プロセスの登録

さて,次は作成したcgroupにプロセスを登録してみましょう。この時使用するファイルがcgroupの各ディレクトリにあるtasksファイルです。

先ほど作成したtest01 cgroupを使って説明していきます。プロセスを登録する前に,test01にあるtasksファイルの中身を見てみましょう。

 $ cat /sys/fs/cgroup/cpu/test01/tasks
 $

このとおり,ファイルの中身は空です。

プロセスを登録するには,このtasksファイルにPIDを登録するだけです。登録方法は簡単で通常のファイルに書き込むように登録できます。ここでは現在のシェルを登録してみましょう。

 $ echo $$ | sudo tee -a /sys/fs/cgroup/cpu/test01/tasks
 17955

プロセスがcgroupに登録されたかどうかを確認するのは,先ほど登録前に試したようにtasksファイルの中身を確認して,PIDが登録されているかどうかを見るだけです。では確認してみましょう。

 $ cat /sys/fs/cgroup/cpu/test01/tasks
 17955
 18092

先ほど登録したシェルのPIDが登録されているのがわかります。シェルのPIDの他にもう1つPIDが登録されていますね。これは確認に使ったcatのPIDです。このようにcgroupに登録したプロセスの子プロセスやその子孫も自動的に同じcgroupに登録されます

プロセスの移動

次にcgroupに登録したプロセスを別のcgroupに移動させてみましょう。test02というcgroupを作成します。そして,先ほど登録したシェルのPIDをtest02に登録して,どのような動きになるか見てみます。

 $ sudo mkdir /sys/fs/cgroup/cpu/test02   (test02グループの作成)
 $ echo $$ | sudo tee -a /sys/fs/cgroup/cpu/test02/tasks  (プロセスをtest02に登録)
 17955
 $ cat /sys/fs/cgroup/cpu/test02/tasks    (test02グループに属するプロセスの確認)
 17955                                    (確かに登録されている)
 18728
 $ cat /sys/fs/cgroup/cpu/test01/tasks    (test01グループに属するプロセスの確認)
 $                                        (登録されているプロセスはない!)

このように新しく作成したtest02グループに先ほどtest01に登録したシェルのPIDを登録すると,test02グループに登録されているのがわかります。その後test01ディレクトリを確認してみると,tasksファイルの中身が空になっています。つまり,あるcgroupに登録されているプロセスを別のcgroupに移動させたいときは,単に移動させたいcgroupにPIDを登録するだけで良いことがわかります。

cgroupの削除

この時点でtest01に登録されたプロセスはなくなりました。tasksファイルの中が空になったcgroupは削除できます。削除も通常のディレクトリ操作と同じようにrmdirコマンドで削除できます。

 $ sudo rmdir /sys/fs/cgroup/cpu/test01
 $ ls /sys/fs/cgroup/cpu/test01
 ls: cannot access /sys/fs/cgroup/cpu/test01: No such file or directory

これでcgroupが削除できました。

ここまでの例で,cgroupの操作は非常に簡単であることがおわかりいただけたのではないかと思います。

著者プロフィール

加藤泰文(かとうやすふみ)

2009年頃にLinuxカーネルのcgroup機能に興味を持って以来,Linuxのコンテナ関連の最新情報を追っかけたり,コンテナの勉強会を開いたりして勉強しています。英語力のない自分用にLXCのmanページを日本語訳していたところ,あっさり本家にマージされてしまい,それ以来日本語訳のパッチを送り続けています。

Plamo Linuxメンテナ。ファーストサーバ株式会社所属。

Twitter:@ten_forward
技術系のブログ:http://d.hatena.ne.jp/defiant/

コメント

コメントの記入