いますぐ実践! Linuxシステム管理

いますぐ実践! Linux システム管理 / Vol.229 / 読者数:2215名

こんばんは、うすだです。

お仕事で、ずっと Eclipse とにらめっこをしています。

Eclipse (統合開発環境) - Wikipedia
http://ja.wikipedia.org/wiki/Eclipse_...

もともと、IDE(Integrated Development Environment - 統合開発環境) とほぼ無縁の生活を営んできました。ですので、40歳を過ぎて初体験なこの状況に、 毎日、戸惑いまくっております。
(大昔に Windows CE の開発をしていた際も、 コマンドラインでのビルドとデバッグを貫き通しました。 (ま、前任者のおかげなんですが…。))

ですが、こんなご時世でもありますし、 ドッグイヤーを通り越してマウスイヤーとまで言われる分野ですから、 過去の経験だけで食べていくのは、まず不可能と言っても過言ではありません。

…いやいやいやいや、そういう後ろ向きな考えではいけませんね。

自分の人生をこの分野にかけることにしたのですから、今やっていることが、 周囲の人のため、世の中のためになるのであれば、全力を注いで打ち込むべきですよね。 自分の人生のためにも。ミッション、使命です。

というわけで、上記本業と、当メルマガと、 某雑誌の真っ白な原稿(…といいますか、 サイズがほぼ 0 の空っぽなテキストファイルですね)を前にして、 決意も新たにこの3連休をがんばろうと思っている次第です。

結局〆切に追われているだけですが、今回もはりきってまいりましょう!

今回のお題 - cgroup でメモリと走行時間に制限を加える

前回は、cgroup のさわりをご紹介しました。

Vol.228 - cgroup について理解する
http://www.usupi.org/sysad/228.html

大雑把な概要と、freezer を例にざっくりした使い方をご紹介しました。

ですので今回は、cgroupをもう少し具体的に使ってみようということで、 メモリと走行時間に制限をかけてみたいと思います。

…とその前に、cgroupを使うには、mountされているディレクトリを起点として、 あれこれ操作します。
ですが、そのディレクトリが、 ディストリビューションによってまちまちだったりします。 ざっくりと調べた限りでは、以下のようでした。

  /sys/fs/cgroup    Ubuntu,Fedora
  /mnt/cgroups      Debian
  /cgroup           CentOS

以降では、これらを「$CGROUP」で記述したいと思います。
たとえば Debian な方は、 「$CGROUP」の箇所を「/mnt/cgroups」に脳内置換しながら読んでいただけますと、 まことに幸いです。

やっぱりかけたい制限はメモリですよね

最近、メモリを 8GB に増設しましたので、 個人的にはあまり節約指向的ではなくなったのですが、それでもやっぱり、 メモリは大切です。

基本的には、要求された分を忌憚なく律儀に割り当ててしまいますので、 どかっとメモリを確保して潤沢に使おうとするアプリがいらっしゃると、 制限したくなるのが人情というものではないでしょうか。

というわけで、「memory」サブシステムです。
メモリやスワップの使用量を制限したり、状況を確認したりできます。

前回同様、サブシステムの下にディレクトリを作成して、 対象のプロセスを tasks に登録する、という手順からはじめましょう。
以下では、foo というディレクトリ…いえ、以降ではcgroupと呼びます…を作成して、 実行中の firefox を対象としています。

  # mkdir $CGROUP/memory/foo
  # ps -C firefox -o pid,comm
    PID COMMAND
  12093 firefox
  # echo 12093 > $CGROUP/memory/foo/tasks
  # cat $CGROUP/memory/foo/tasks
  12093

上記では、firefoxのプロセスIDが 12093 であることを確認して、 それをtasksに登録するところまで行っています。

メモリの使用量を制限するには、「memory.limit_in_bytes」にその値を設定します。 たとえば、100MB を上限に設定したい場合は、以下のように実行します。

  # echo 100M > $CGROUP/memory/foo/memory.limit_in_bytes
  # cat $CGROUP/memory/foo/memory.limit_in_bytes
  104857600

cat コマンドなどで読み出せば、現在設定されている値を確認することができます。
(104857600/1024/1024 = 100.0 MB です。)

この状態で、firefox を酷使してみると、徐々にもたついたり、 ひどいと強制終了してしまったりします。

現在のメモリ使用量を確認するには、「memory.usage_in_bytes」を参照します。

  # cat $CGROUP/memory/foo/memory.usage_in_bytes 
  101597184

また、いままで最大でどれだけ使ったか、何回制限に達したかを確認するには、 それぞれ「memory.max_usage_in_bytes」と「memory.failcnt」を参照します。

  # cat $CGROUP/memory/foo/memory.max_usage_in_bytes 
  106668032
  # cat $CGROUP/memory/foo/memory.failcnt 
  1245

これらを参照しながら、上限値を調整していけばよいと思います。
(memory.failcnt があまりにも多くて、 アプリケーションが頻繁に死んでしまったり停滞するなら緩く、 memory.max_usage_in_bytes が上限とは程遠い値ならもっと厳しく、とかでしょうか。)

firefox を終了して、tasks に誰もいないことを確認したら、 cgroup を消しておきましょう。

  # cat $CGROUP/memory/foo/tasks
  # rmdir $CGROUP/memory/foo

次にやりたいのは走行時間でしょうか

次に、走行時間にやんわりと制限をかけてみましょう。

リソースで大事なもののベスト3と言えば、メモリ、ディスク、 そしてCPUではないでしょうか。(注: 筆者の主観的な感覚によります。)

いくらディスクやメモリが潤沢でも、延々と走行するプロセスが多数存在すると、 CPUパワーをまるっと取られてしまいます。

ですので、そんなに急いで処理する必要のないプロセスには、 CPUを少し少なく割り当てるようにしてみましょう。

「cpu」サブシステムでは、CPUの割り当てる割合を設定できます。
具体的には、「cpu.shares」というパラメータです。 デフォルトでは1024という値が入っています。

  $ cat $CGROUP/cpu/cpu.shares
  1024

これは重みですので、新たにcgroupを作成して、この値よりも大きい値を設定すると、 そのcgroupに属するプロセスに対して、 その分多く CPU を割り当ててくれるようになります。

それではさっそく試してみましょう。
まず、以下のようなスクリプトを用意します。 ファイル名は loop_sub.sh とでもしておきましょう。

#!/bin/sh
CGROUP="ご使用の環境に合わせて記述してください"
mkdir $CGROUP/cpu/$$
echo $$ > $CGROUP/cpu/$$/tasks
echo $1 > $CGROUP/cpu/$$/cpu.shares
n=0
while [ 1 ]; do n=$((n + 1)); done

自身のプロセスIDと同じ名前のcgroupを作成して、 第1引数を cpu.shares に設定します。そして、あとは延々と無駄な処理をしつづけます。

そして、これを呼び出す以下のようなスクリプトを、さらに用意します。
ファイル名は、loop.sh とでもしておきます。

#!/bin/sh
CGROUP="ご使用の環境に合わせて記述してください"
./loop_sub.sh 512 &
pid1=$!
./loop_sub.sh 1024 &
pid2=$!
cmd=""
while [ "$cmd" != "exit" ]; do
    ps -C loop_sub.sh -o pid,%cpu,comm
    read cmd
done
for pid in $pid1 $pid2; do
    if [ -d $CGROUP/cpu/$pid ]; then
        kill $pid
        sleep 1
        rmdir $CGROUP/cpu/$pid
    fi
done

まず、先ほどのスクリプトを、 片方は重み 512、もう片方は重み 1024 で実行します。
その後、exit と入力されるまで、なにか入力があるとpsコマンドを実行し続けます。 ここで、先ほどのスクリプトのCPUの使用率をみて、 重みと同じ割合であることを確認しようという魂胆です。
exit と入力されたら、その無限ループから脱し、 スクリプトをkillして作成したcgroupも消して、 立つ鳥後を濁さないで終了します。(えらい!)

それでは、いつものように実行権を付加し、 後者のスクリプトを実行してみてください。
実行すると、リターンキーを押す度に、前者のスクリプト2つの実行状況を、 psコマンドで確認できます。

  # chmod +x loop.sh loop_sub.sh
  # ./loop.sh
    PID %CPU COMMAND
  16844  0.0 loop_sub.sh
  16845  0.0 loop_sub.sh

  (ここでリターンキーなどを押すと、↓ psコマンドを再実行)

    PID %CPU COMMAND
  16844 29.0 loop_sub.sh
  16845 58.3 loop_sub.sh
  ...後略...

CPUの使用率をみると、ちょうど倍になっていることがわかります。
もういいかなと思ったら、exit と入力してください。
(最後に、cgroupがなくなっているか、念のためlsで確認しています。)

  ...前略...
  exit
  ./loop.sh: line 9: 16844 終了しました      ./loop_sub.sh 512
  ./loop.sh: line 9: 16845 終了しました      ./loop_sub.sh 1024
  # ls $CGROUP/cpu/1684[45]
  ls: cannot access $CGROUP/cpu/1684[45]: そのようなファイルや\
  ディレクトリはありません

ちなみに、ここではどちらにも1つのプロセスしか登録していませんが、 グループですので、複数のプロセスを登録できます。
たとえば、cpu.shares が 512 のグループに、 プロセスIDが 16909,16910 の2つのプロセスを登録し、 1024 のグループにはプロセスIDが 16911 のプロセスだけを登録すると、 以下のようになりました。

    PID %CPU COMMAND
  16909 16.4 loop_sub.sh
  16910 16.4 loop_sub.sh
  16911 65.4 loop_sub.sh

512のグループに対して、さらに2つのプロセスが取り合いますので、 1024のプロセスよりもCPUの割合が 1/4 程度になっています。

以上のことはコマンドでできます

さて、いままで、ディレクトリを掘ったりファイルに読み書きしたりと、 ベタな作業を淡々とご説明しました。

ですが、実は、これらのことは、コマンドでも実現できてしまいます。
ディストリビューションによってパスが違っても、 コマンドの使い方さえわかっていれば、路頭に迷うこともありません。
(じゃあなんで今までベタにやってきたのかと言いますと、 裏でやってることを知っていただきたかったからです。コマンドの存在に気づいて、 急遽この節を書いたんじゃないんですよ!(汗))


…下手な言い訳はこのくらいにして、淡々とご紹介していきます。

ディレクトリ…もとい、新たなcgroupを作成したい場合は、 「cgcreate」コマンドを使います。たとえば、memory の直下に foo を作成するには、 「-g」オプションを使って以下のように実行します。

  # cgcreate -g memory:foo

-g オプションは複数指定できます。つまりいっぱい作れます。
また、いままで root 権限であれこれやってきましたが、一般人でも操作できるよう、 ファイルやディレクトリの所有者を「-a」オプションで指定できます。 たとえば、usuユーザ、usersグループのモノにするには、以下のように実行します。

  # cgcreate -a usu:users -g memory:foo
  # ls -l $CGROUP/memory/foo/
  合計 0
  -rw-r--r-- 1 usu  users 0 10月  7 10:38 cgroup.clone_children
  --w--w--w- 1 usu  users 0 10月  7 10:38 cgroup.event_control
  -rw-r--r-- 1 usu  users 0 10月  7 10:38 cgroup.procs
  ...後略...

あるいは、tasksファイルだけ権限を変更するには、「-t」オプションを使用します。

  # cgcreate -t usu:users -g memory:bar
  # ls -l $CGROUP/memory/bar/
  ...前略...
  -rw-r--r-- 1 root  root  0 10月  7 10:38 notify_on_release
  -rw-r--r-- 1 root  root  0 10月  7 10:38 release_agent
  -rw-r--r-- 1 usu   users 0 10月  7 10:38 tasks

また、グループで管理できるよう、「-d」オプションや「-f」オプションで、 ディレクトリとファイルのパーミッションを変えられます。

  # cgcreate -a usu:users -d 775 -f 775 -g memory:buz
  # ls -l $CGROUP/memory/buz/
  合計 0
  -rwxrwxr-x 1 usu  users 0 10月  7 10:43 cgroup.clone_children
  -rwxrwxr-x 1 usu  users 0 10月  7 10:43 cgroup.event_control
  -rwwrwxr-x 1 usu  users 0 10月  7 10:43 cgroup.procs
  ...後略...

みんな一律に変わっちゃうのかよ…という気もしますが…。

さて、作って使って使い終わったら、ちゃんと消しておきましょう。 消すには「cgdelete」コマンドを使います。オプションなしで、 消したいモノを引数に列挙します。(もちろん、1つでも構いません)

  # cgdelete memory:foo memory:bar memory:buz

階層的に作っていると叱られますが、そんなときは、 「-r」オプションで根こそぎ消せます。

  # cgcreate -g cpu:foo/bar/buz
  # cgdelete cpu:foo
  cgdelete: cannot remove group 'foo': Device or resource busy
  # cgdelete -r cpu:foo
  # 

パラメータの設定には「cgset」コマンドを使います。 「-r」オプションでパラメータと値を指定して、 さらに対象のcgroup名を指定します。
たとえば、foo グループに対して、 memoryの limit_in_bytes を 100M に設定したい場合は、以下のように実行します。

  # cgset -r memory.limit_in_bytes=100M foo

値を確認するには、「cgget」コマンドを使います。引数などは cgset と同様です。 以下では、fooグループのmemory.limit_in_bytesとcpu:sharesの値を出力しています。

  # cgset -r memory.limit_in_bytes -r cpu.shares foo
  foo:
  memory.limit_in_bytes: 104857600
  cpu.shares: 2048

でもって、実際にコマンドを実行するには、「cgexec」コマンドを使用します。 「-g」オプションで対象のcgroupを指定して実行します。
たとえば、 memory:foo および cpu:foo の範疇で firefox -search kuri を実行したい場合は、 以下のようにします。

  # cgexec -g memory,cpu:foo firefox -search kuri

ちなみにこれらは、cgcreate で一般ユーザの所有者にしていれば、 一般ユーザが直接実行できます。
(パーミッションも変えて、 グループに属するみんなが使えるようにすることもできますね。)

  $ sudo cgcreate -a usu:users -g memory:foo
  $ cgset -r memory.limit_in_bytes=100M foo
  $ cgexec -g memory:foo firefox -search marron

前回もご紹介しましたが、お使いのLinuxで、 どんなサブシステムが利用できるか確認するには、「lssubsys」コマンドを使います。

  $ lssubsys
  cpu
  cpuacct
  devices
  memory
  ...後略...

また、cgroupを確認するには、「lscgroup」コマンドを使います。

  $ lscgroup
  cpu:/
  cpu:/hoge
  cpu:/sysdefault
  ...後略...

他にもコマンドがありますが、主なものはこんなところでしょうか。

おわりに

以上、cgroup を使って、メモリと走行時間に制限を加える方法をご紹介しました。 また、それらを行うためのコマンドについても、 高速でさらりと書かせていただきました。

ベタにやるとややこしいですが、コマンドを使うとシンプルになります。 あとは、これらをどううまく使っていくかですが、まず、 使い方に慣れるところから始めてみましょう。とにかく実践あるのみです!

あ、もし万が一、これらのコマンドが見当たらないなら、 前回ご紹介したパッケージを入れてみてください。(われながら大雑把だ…)

宿題の答え

前回の宿題は、

  freezerサブシステムに新たなグループを作り、なにか効果的な使い方
  をしてみましょう。

でした。

シグナルで一時停止と再開(kill -STOP で止めて kill -CONT で再開) をさせればよいのでは、と思ってしまいますが、 カーネルソースに含まれるドキュメントによると、 これらではうまくいかないときがあるようです。
(ちなみに、Documentation/cgroups/freezer-subsystem.txt です。)

たとえば、bash を起動しておいて、

  $ bash
  $ echo $$
  12345

別の端末から kill -STOP と kill -CONT を実行すると、 上記の bash がおかしなことになります。

  $ kill -STOP 12345
  $ kill -CONT 12345

また、pkillコマンドでごっそり SIGSTOP シグナルを送る場合、 一つずつkillシステムコールを発行することになるため、 厳密には一斉に停止することにはならないように思われます。
(freezerサブシステムだと、一応、 カーネル内でまとめて止めてくださるように見えます。 (kernel/cgroup_freezer.c の try_to_freeze_cgroup 関数あたりです))

ですので、複数のプロセスで何かバッチ的な処理をしていて、 なんらかのリソースが不足しそうなときに FROZEN して一斉に止めて、 めでたく対処し終えたら THAWED する、みたいなのが効果的ではないかと思います。

…ああ、不完全燃焼的ですが、このくらいで許してください…。

今回の宿題

今回の宿題は、

  メモリの使用量を制限して実行するスクリプトを書いてみましょう。

です。

メモリの上限とコマンドを指定すると、 その上限内でコマンドを実行してくれる…ああ、とっても有用で楽そうではないですか。

そんな夢のようなスクリプトを、ぜひ、作ってみてください。

また、立つ鳥後を濁さないようにもしていただけますと、大変幸いです。

あとがき

21世紀になるちょっと前から、 世間の流れに負けて(?) Linux をサーバとして使うようになって、 Linux とは10年以上のお付き合いとなります。

その間、様々なディストリビューションが生まれては消えて行きました。
ですが、わたしがちゃんと使ったことのあるものは、 おそらく1桁にしか過ぎないように思います。
(Turbo, RedHat系, Kondara, Vine, Debian, openSUSE, Ubuntu くらい?)

いまや、星の数ほど存在し、どれにすればいいのか迷ってしまいますが、 そんな迷える子羊たちの希望の光的な記事がありましたので、 ここでネタに…もとい、ご紹介したいと思います。

どのディストロを、どんな順番で使ってきた? スラッシュドットジャパン Linux
http://linux.slashdot.jp/story/12/09/29/200225/%E3%81%A9%E3...

100個以上あるコメントが、予想通り昔話中心になっていて、 年寄りには非常に楽しい内容となっております。
(ちなみに、わたしの事実上のファーストインパクトは SunOS4 でした。)

…じゃなくて、コメント欄に書かれている、みなさんの経験と経緯を観察していると、 どれがいいか見えてくるのではないでしょうか。


ちなみに、恥ずかしながら、わたしはここで初めて「Arch Linux」の存在を知り、 先日、Virtual Box に放り込んでみました。

Arch Linux(日本語)
https://wiki.archlinux.org/index.php/Arch_Linux_(%E6%97%A5%E6%9C%AC%E8%AA%9E)

ミニマルさと、パッケージの豊富さによる柔軟性を兼ね備えた、古き良き匂いのする、 あまり初心者向けでないディストリビューションという印象を受けました。

インストール手順も、自分でコマンドを打ち込んで仕立て上げるという、 良く言えばしょっぱなから勉強になる、 悪く言えばユーザフレンドリーさのかけらもない、とても好感の持てるものでした。
(注: 感じ方や効果には個人差があります。)

そんな硬派な匂いのする Arch Linux ですが、気になる貴兄は、 ぜひ挑戦して翻弄されていただきたいと思います。


おっと、そういえば、11月8日発売予定の、某日経Linux12月号の特集が、 「世界のLinuxディストリビューション100」となっています。

日経Linux - 本誌次号予告 - 2012年12月号
http://itpro.nikkeibp.co.jp/article/MAG/20121005/427929/

100個もあるのかーと驚いてしまいましたが、下記をみる限りでは、 そのくらいは余裕でありそうです。(ランキングが100位までありますので。)

DistroWatch.com: Put the fun back into computing. Use Linux, BSD.
http://distrowatch.com/

とはいえ、100個も説明するのは大変ですよね…。
発売は1ヶ月先ですが、どんな特集になるのか、11月が待ち遠しいです。

 

うまく宣伝につなげたところで、今回はここまでにしたいと思います…。

 

今回も、ここまで読んでいただき、たいへんありがとうございました。
次回は、10月21日(日) の未明にお会いしましょう!

 

「いますぐ実践! Linux システム管理」の解除は、以下からできます。
http://www.usupi.org/sysad/ (まぐまぐ ID:149633)

バックナンバーは、こちらにほぼ全部そろっています。
http://www.usupi.org/sysad/backno.html

「栗日記」- もう10月…栗でがんばらないと!
http://www.usupi.org/kuri/ (まぐまぐ ID:126454)
http://usupi.seesaa.net/ (栗日記ブログ)
http://usupi.org/k/ (モバイル栗日記)
http://twitter.com/kuriking/ (栗つぶやき)
http://facebook.com/kuriking3 (栗顔本)


[バックナンバーのトップへ] [Linux システム管理のトップへ]

トップ

バックナンバー
    [日付順] [目的別]

プロフィール

▼ リンク

独学Linux
Linuxデスクトップ環境に関する情報が満載です。 メルマガもありますよ。
Server World
CentOS 6をサーバとしたときの設定例が、これでもかというくらいたくさん載っています。 CentOS以外のディストリビューション(Fedora, Ubuntu)も充実しています。
LINUXで自宅サーバーを構築・導入(Fedora9)
Fedora9のインストールの仕方から管理方法まで、詳しく載っています。 SearchManには情報がもりだくさんです。
マロンくん.NET
〜サーバ管理者への道〜
Linuxをサーバとして使用するための、いろいろな設定方法が載っています。 マロンくんもかわいいです。 なんといっても、マロンくんという名前がいいですね!!
日経Linux
今や数少なくなってしまったLinuxの雑誌。ニュースやガイドもあります。
Linux Square − @IT
@ITが提供する、Linux の情報が満載。 載っていない設定方法はないんじゃないでしょうか。
gihyo.jp…技術評論社
Linuxに限らず様々な技術情報が満載のサイト。 SoftwareDesign誌も、 ソフトウェア技術者は必見です。
SourceForge.JP Magazine
Linux に限らず、オープンソース関連の記事が網羅されています。
ITmediaエンタープライズ:Linux Tips 一覧
Tips というより FAQ 集でしょうか。わからないことがあれば覗きましょう。
IBM developerWorks : Linux
開発者向けですが、勉強になりますよ。
Yahoo!ニュース - Linux
Yahoo!のLinuxに関するニュース一覧です。
栗日記
システム管理とかと全然関係ありませんが、毎日栗の絵を描いています。
システム管理につかれちゃったとき、癒されたいときに、ご覧ください。:-)
WEB RANKING - PC関連
ランキングに参加してみました。押してやってください。

▼ 作ってみました

Add to Google

▼ せんでん




▼ 最近読んだ本

▼ 気に入ってる本