きょうのsystemd: 3つのレベルの「オフ」

概要

これは

0pointer.de

の翻訳。

3つのレベルの「オフ」

systemdではサービス(ないしは、その他のユニット)を3つのレベルでオフにできる。 これについて見てみよう。

1.サービスを停止することができる。これは単純にサービスのインスタンスを停止させることであり、それ以外はほとんど何もない。 アクティベーションのいくつかの方法(例えば、手動によるアクティベーション、ソケットアクティベーション、バスアクティベーション、システム起動時のアクティベーション、あるいは、ハードウェアの抜き差しによるアクティベーション)によって、停止させた後に、サービスが再び要求されれば、そのサービスは起動する。 サービスを止めるということは非常に単純で、一時的なもので、表層的な操作なのである。 これはNTPサーバーを停止させる方法の例である:

$ systemctl stop ntpd.service

これはざっくり言うと、SysVで動いている多くのシステムで利用可能な、次の伝統的なコマンドと同等だ:

$ service ntpd stop

実際、Fedora15では、後者のコマンドを実行すれば、透過的に前者へと変換されるようになっている。

2.サービスを無効にすることができる。 これはサービスがそのアクティベーションのトリガーによりフックされないようにする。 これは、サービスによっては、起動時にアクティベーションされたり、ソケットやバス、ハードウェアの抜き差し(その他、そのサービスに適用されているトリガー)でサービスがもはやアクティベートされないことを意味する。 しかし、必要であれば、手動で起動させることはまだできる。 もしすでに起動済みのインスタンスがある場合、サービスを無効にしてもそれを止めるような効果はない。 これはサービスを無効にする方法の例である:

$ systemctl disable ntpd.service

伝統的なFedoraのシステムでは、これは、ざっくり言うと次のコマンドに等しい:

$ chkconfig ntpd off

ここでも、Fedora15では後者のコマンドは、必要に応じて、透過的に前者へと変換される。

しばしば、サービスの停止と無効化を一緒におこないたい場合がある。というのも、現在のインスタンスを取り除いて、(手動でトリガーする場合をのぞいて)確実にそれが再び起動しないようにするためである。

$ systemctl disable ntpd.service
$ systemctl stop ntpd.service

これらのコマンドは例えば、Fedora上のsystemdサービスのパッケージを取り除く(訳注: deinstall)ときに使われる。

サービスの無効化は永続的な変更である。 元の状態に戻すまで、再起動を繰り返しても、その状態は保たれる。

3.サービスをマスクすることができる。 これはサービスの無効化と似ているが、極端である。 これは、サービスが自動的にもはや起動しなくなるようにするだけでなく、手動でさえもサービスを起動できなくさせるようにする。 これはsystemdのちょっとした隠し機能である。というのも、一般に使うものでもないし、ユーザーを混乱させてしまうかもしれないためだ。 しかし、やり方はこうだ:

$ ln -s /dev/null /etc/systemd/system/ntpd.service
$ systemctl daemon-reload

サービスファイルを/dev/nullへのシンボリックリンクにすることで、systemdに件のサービスを決して起動させず、完全に実行をブロックするように伝えることができる。 /etc/systemd/systemにあるユニットファイルは/lib/systemd/systemにある同じ名前のファイルを上書きする。 前者のディレクトリは管理者の領域であり、後者はパッケージマネージャーの領域である。 /etc/systemd/system/ntpd.serviceシンボリックリンクを差し込むことで、systemdがアップストリームが同梱しているサービスファイル/lib/systemd/system/ntpd.serviceを決して使わないようにすることになる。

systemdは/dev/nullシンボリックリンクの張られたユニットを認識することができ、これをmaskedと表示する。 手動で(例えばsystemctl start経由で)サービスを起動させようとしても、エラーで失敗するだろう。

SysVのシステムでは(公式には)似たようなトリックは存在しない。 しかし、いくつかの非公式のハックは存在していて、例えば、initスクリプトを修正して、頭の方にexit 0を置いたり、実行ビットを取り除いたりする方法がある。 しかし、これらの解決方法は様々な欠点がある。例えば、これらの方法はパッケージマネージャーからの妨害を受けてしまう。

サービスのマスクは永続的な変化で、サービスの無効化とかなり似ている。

さて、3つのレベルでサービスをオフにする方法を学んだが、ただ1点、問題が残されている。つまり、どうやってオンに戻すのか? まぁ、これはかなり対称的なものだ。 systemctl startを使ってsystemctl stopを元に戻す。 systemctl enableを使って、systemctl disableを元に戻す。 rmを使ってlnを元に戻す。

今日はここまで。読んでくれてありがとう!

#きょうのsystemd : サービスを停止させる (systemd for Administrators, Part 4)

概要

これは

0pointer.de

の翻訳。

サービスを停止させる

システムデーモンを停止させるのは簡単、本当?

確かに、デーモンが単一のプロセスで構成されている限り、これは確かにある程度は正しいだろう。 killall rsyslogdとタイプすれば、syslogデーモンはどこかへ行く。 しかし、このようにプロセスを止めるのはちょっと汚いやりかただ。 というのも、このように呼び出されることになったプロセスをこれはすべて停止させ、そこには偶然そのような名前を付けてしまった不幸なユーザーのプロセスも含まれるかもしれないからだ。 ほんの少し正しいバージョンは.pidファイルを読み込む方法で、例えば

kill `cat /var/run/syslogd.pid`

というものだ。 この時点ですでにかなり進むことができたが、それでもまだ、これが本当に我々のやりたいことなのだろうか?

多くの場合で、実際にはそうではない。 Apacheやcrondあるいはatdといったサービスを考えよう。これらはたいていの操作の一部として、子プロセスをスポーンさせる。 任意の、ユーザー設定可能な子サービスであり、例えば、cronやatのジョブ、CGIスクリプト、完全なアプリケーションサーバーといった場合もある。 apache、crond、atdのメインプロセスを停止させると、子プロセスも一緒に落ちるかもしれなければ、そうでないこともあり、残り続けるか停止するかはそれらのプロセス次第なのである。 基本的に、Apacheをターミネートさせることは、そのCGIスクリプトを残留させ、initの子として再割り当てをし、追跡を困難なものにするのである。

systemdはこれをレスキューする。 systemctl killであるサービスのすべてのプロセスに対し、簡単にシグナルを送ることができる。 例:

# systemctl kill crond.service

これはSIGTERMがcrondサービスのメインプロセスのみならず、すべてのプロセスに届けられることを保証する。 もちろん、望むなら違うシグナルを送ることも可能である。 例えば、あなたのお行儀が悪いなら、SIGKILLをすぐに送ってしまおうと思うだろう。

# systemctl kill -s SIGKILL crond.service

これで望み通りに、サービスが何回フォークしようと、ダブルフォークやフォークボミングで監視下から逃れようと、サービスはその全体が虐殺される。

ときどき、サービスのメインプロセスに特定のシグナルを送ることだけが必要となることがある。例えば、SIGHUP経由でリロードをトリガーしたいといった理由によるものだ。 PIDファイル経由でおこなう代わりに、もっと簡単な方法がある。

# systemctl kill -s HUP --kill-who=main crond.service

そして再び、systemdでサービスを停止させることについて何がそんなに新しくてファンシーなのか? Linuxで初めて、我々がそれを実際、適切におこなうことができている。 以前のソリューションは常にデーモン頼みであり、デーモンが自分自身がターミネートするときに自分がスポーンしたすべてのものを停止させるよう実際に協力してくれるかに頼っていた。 しかし、普通、SIGTERMやSIGKILLを使いたい場合に、そのようにすることになる。それはデーモンが実際に適切に協力してくれないからなのだ。

これがsystemctl stopとどのように関係してくるのか? killはグループにあるすべてのプロセスに直行し、シグナルを送る。 しかし、stopはサービスをシャットダウンする公式の設定を通過する。例えば、サービスファイルのExecStop=で設定された停止コマンドを実行する。 通常はstopで十分なはずである。 killはより荒っぽいバージョンであり、実行するサービスの公式のシャットダウンコマンドを使いたくなかったり、サービスを他の方法できれいにしたりハングさせたりする場合に使われる。

(ところで、人に寄ってはSIGプレフィックスを-sスイッチに付けたり付けなかったりするが、どちらでも動く。)

Linuxでサービスを適切に停止させる方法なしでここまでやってきたのにはちょっと驚く。 systemdはこれを適切におこなうことを、初めて可能にするものなのである。

#きょうのsystemd: Surface Proを持ち歩いている人はサーバーを持ち歩いていると言おう

完全にネタ。

hostnamed needs a quirk database for chassis type · Issue #7390 · systemd/systemd · GitHub

レナートがタイトルを変えちゃってるけど、もともとは "hostnamectl reports chassis-type as server for a Surface Pro tablet(hostnamectlがSurface Proタブレットの筐体をサーバーと報告する)"

#きょうのsystemd : (翻訳) どうやったらSysV initのスクリプトをsystemdのサービスファイルに換えられるのか? (systemd for Administrators, Part 3)

概要

これは

systemd for Administrators, Part III

の翻訳。

どうやったらSysV initのスクリプトをsystemdのサービスファイルに換えられるのか?

伝統的にUnixLinuxのサービス(デーモン)はSysV init スクリプト経由で起動されている。 これらはBourne Shellスクリプトであり、通常は/etc/rc.d/init.dといったディレクトリに置かれている。 これらのスクリプトstartstoprestartといった数個の標準化された引数(動詞)を与えられて呼び出されたときに、件のサービスの制御、すなわち、起動、停止、再起動をおこなう。 起動の場合、スクリプトは通常、デーモンのバイナリーの呼び出しに関わり、バックグラウンドのプロセスをフォークする(より正確にはデーモン化させる)。 これらは非常に柔軟性がある(なんといったって、これらは単なるコードである)が、いくつかのことはシェルスクリプトでは適切におこなうことが非常に難しい。 具体的には、実行をパラレル化するように並べる、適切にプロセスを監督する、あるいは、非常に細かいところまで実行のコンテクストを設定するといったことである。 systemdはこれらのシェルスクリプトとの互換性を提供しているが、ここで指摘した短所ゆえに、インストールされているすべてのデーモンがネイティブなsystemdサービスファイルをインストールすることが推奨される。 また、ディストリビューションに合うよう調整しなければいけないSysV initスクリプトと比べると、systemdのサービスファイルはsystemdを動かしているディストリビューション(最近どんどん増えている)ならどの種類でも互換性がある。 となると、次に続くのは、どのようにSysV initスクリプトを取り上げ、ネイティブなsystemdサービスファイルに翻訳するかの完結なガイドということになる。 理想的には、上流のプロジェクトがそれらのtarballにsystemdサービスファイルを同梱しインストールすることだ。 もし、SysVスクリプトガイドラインに従ってうまく変換しているならば、そのファイルを上流にパッチとして送ってしまうのはいい考えだ。 このようなパッチを準備する方法は、後の投稿で論じるつもりだが、現時点で十分に言えることは、systemdに同梱されているdaemon(7)のマニュアルページには、これに関する有益な情報が多く含まれているということだ。

閑話休題。 一例として、ここではABRTデーモンのinitスクリプトをsystemdサービスファイルに変換することにしよう。 ABRTは各Fedoraのインストールの標準的なコンポーネントであり、Automatic Bug Reporting Toolの略だ。 何をするかはその名のとおりで、クラッシュダンプを集めるサービスである。 これのSysVスクリプトはもうここに上げてある。

こういったスクリプトを変換するときの第一歩はこれを読むこと(驚け、驚け!)で、たいていは長いこのスクリプトから有益な情報を抽出することだ。 ほとんどすべてのケースで、スクリプトはほとんど決まり文句のコードで構成されている。 このコードはすべてのinitスクリプトで一致ないしは少なくともかなり似ており、通常はあっちからこっちへのコピペである。 さて、上でリンクを張ったスクリプトから興味深い情報を抽出してみよう。

  • サービスの説明文は「クラッシュしたアプリを検出するデーモン」である。 見たらわかるとおり、ヘッダーのコメントには余分な数の説明の文字列が含まれている。 これらのいくつかは実際のサービスを説明しているというよりかは、それをスタートさせるinitスクリプトについての説明をしている。 systemdサービスも説明文は含んではいるが、それが説明すべきなのはサービスのことであり、サービスファイルのことではない。

  • LSBヘッダー*1には依存関係の情報が含まれている。 systemdはそのソケットベースのアクティベーションの設計のため、通常は手動で依存関係を設定する必要はまったく(ないしはほんの少しだけしか)ない(ソケットアクティベーションの詳細はオリジナルのアナウンスのブログ投稿を見てほしい)。 今回の場合は$syslogに依存(これはabrtdがsyslogデーモンを必要としているということがエンコードされている)することが唯一、価値のある情報である。 ヘッダーがその他の依存関係($local_fs)を挙げているが、これはsystemdには余分な情報である。 というのも、通常のシステムサービスは常にすべてのローカルファイルシステムが利用可能な状態で起動されるためだ。

  • LSBヘッダーはこのサービスがランレベル3(マルチユーザー)と5(グラフィカル)で起動するべきことを示唆している。

  • デーモンのバイナリーは/usr/sbin/abrtdである。

準備はととのった。 この115行のシェルスクリプトの残りの行は全部、単なる決まり文句か余分なコードだ。 このコードで、スタートアップの同期やシリアル化(つまり、ロックファイルを検討するコード)を取り扱ったり、状態メッセージを出力(つまり、echoを呼び出すコード)したり、単に動詞をパース(つまり大きなcaseブロック)したりしている。

上で抽出した情報から、もう、systemdサービスファイルを書くことができる。

[Unit]
Description=Daemon to detect crashing apps
After=syslog.target

[Service]
ExecStart=/usr/sbin/abrtd
Type=forking

[Install]
WantedBy=multi-user.target

このファイルの中身についてちょっと説明しよう。 [Unit]セクションにはサービスに関する汎用的な情報が含まれている。 systemdはシステムサービスを管理するだけでなく、デバイスやマウントポイント、タイマー、あるいはその他のシステムのコンポーネントも管理している。 systemdでぇあこれらのオブジェクトすべてに汎用的な用語を与えており、それはユニットという。 そして[Unit]セクションにはユニットに関して、サービスだけでなく、systemdが維持しているその他のユニットタイプでも適用可能かもしれない情報をまとめている。 今回の場合、次のようなユニットの設定をおこなっている。 説明の文字列を設定し、デーモンがSyslog*2の後に開始されるように設定した。これはオリジナルのinitスクリプトのLSBヘッダーに書かれているのと同じようなことだ。 このsyslog依存関係のために、After=という種類の依存関係をsystemdのユニットであるsyslog.targetに対し作った。 後者はsystemdでは特別なターゲットユニットであり、これはsyslog実装を制御するための標準化された名前である。 これら標準化された名前についての詳細はsystemd.special(7)を見てほしい。 After=という種類の依存関係は示唆された順番をエンコードしているにすぎず、実際にabrtdが起動しするときにsyslogを起動させようとするものではないことに注意してほしい。 そしてこれが実際に我々が求めていることあることにも注意してほしい。abrtdは実際にsyslogが動いていなかったとしてもうまく動くためだ。 とはいえ、2つとも起動していれば(そして、普通はそうだ)、この2つを含む順序はこの依存関係によって制御される。

次のセクションは[Service]であり、これにはサービス自身の情報が含まれている。 これにはサービスにだけ適用される、すべての情報が含まれており、systemdが維持している他の種類のユニット(マウントポイント、デバイス、タイマー、……)にはない。 ここでは2つの設定が使われている。 ExecStart=はサービスが起動したときに実行されるべきバイナリのパスが書かれている。 Type=では、サービスがどのようにしてinitシステムに自身が起動を終えたことを伝えるかが設定されている。 伝統的なUnixデーモンではバックグラウンドデーモンのフォークと初期化が終わってから、親プロセスに戻ることでこれを実現しているため、ここではforkingに設定している。 これはsystemdに対し、スタートアップバイナリーが戻ってくるまで待ち、その後、デーモンプロセスを動かしているプロセスを考慮するように伝えるのである。

最後のセクションは[Install]である。 これは示唆されるインストレーションがどのように見えるべきかについての情報を含んでいる。 つまり、どの環境とどのトリガーに寄ってサービスがスタートすべきかである。 今回の場合、簡単に、このサービスはmulti-user.targetユニットがアクティブになったら開始されるべきと言っている。 これは特別なユニット(既述)であり、基本的には、古典的なSysVのランレベル3*3の役割を引き受ける。 WantedBy=という設定は実行時のデーモンにはほとんど効果がない。 これはsystemctl enableコマンドによってのみ読み込まれる。 このコマンドはsystemdでサービスを有効にするのに推奨されている方法だ。 このコマンドは我々の小さいサービスが、すべての通常起動*4でオンになるmulti-user.targetが要求されればすぐに自動的にアクティブになるようにするものである。

中身の説明はここまでだ。 さて、もう機能する最小限のsystemdサービスファイルを手に入れた。 テストするため、これを/etc/systemd/system/abrtd.serviceにコピーし、systemctl daemon-reloadを実行しよう。 これでsystemdにこのサービスファイルの存在を気づかせ、systemctl start abrtd.serviceを使ってサービスを起動させられるようになる。 systemctl status abrtd.serviceを使って状態を確かめることができる。 systemctl stop abrtd.serviceでサービスを再び停止させられる。 最後に、systemctl enable abrtd.serviceこれを有効にし、次回以降の起動時にデフォルトでこのサービスがアクティブになるようにすることができる。

上のサービスファイルは、十分かつ、基本的な1対1のSysV initスクリプトの翻訳(機能その他)なのだが、まだ改善の余地がある。 ここで、ちょっとだけアップデートしてみる。

[Unit]
Description=ABRT Automated Bug Reporting Tool
After=syslog.target

[Service]
Type=dbus
BusName=com.redhat.abrt
ExecStart=/usr/sbin/abrtd -d -s

[Install]
WantedBy=multi-user.target

さて、何が変わっただろうか? 答えは2つだ。説明の文字列をちょっと改善した。 しかしもっと重要なのは、サービスの種類をdbusに変えて、サービスのD-Busバス名を設定したことだ。 なぜこうしたのか? すでに述べたとおり、伝統的なSysVサービスはスタートアップ後にデーモン化する。 これは通常ダブルフォークとすべてのターミナルからのデタッチが起こる。 これはデーモンがスクリプト経由で起動されるときには便利であり、必須なのだが、systemdのような適切なプロセスのベビーシッターが使われている場合、逆効果なのに加え、不必要(で遅い)のだ。 その理由はフォークされたデーモンプロセスは通常、systemdによって起動されたオリジナルのプロセスとほとんど関係を持たないからであり、それ故、systemdが、フォークが終わった後に、サービスに属しているプロセスのどれがメインプロセスでどれが単なる補助のサービスなのかの見分けるのが難しいためだ。 だが、この情報は高度なベビーシッター、つまり、プロセスの監督、異常終了時の自動的な再スポーン、クラッシュおよび終了コード情報の収集、などを実装するには重要なことだ。 systemdがデーモンのメインプロセスを見つけるのが簡単になるようにサービスタイプをdbusへ変更した。 このサービスタイプのセマンティクスは、サービスの初期化の最終段階でD-Busシステムバス上に名前を取るすべてのサービスに適切なものである*5。 ABRTはその1つである。 この設定によりsystemdはABRTプロセスをスポーンするが、(訳注: 今度の)そのプロセスはもはやフォークしない(これはデーモンに与えられた-dsスイッチで設定されている)し、systemdはバス上にcom.redhat.abrt`が現れたら、すぐにサービスが完全に起動したとみなすようになる。 この方法でsystemdがスポーンしたプロセスはデーモンのメインプロセスであり、systemdはデーモンが完全にスタートアップした時を知る信頼できる方法を手に入れ、systemdはそれを簡単に監督することができるのだ。

それだけのことだ。 オリジナルのSysV initスクリプトでは115行でエンコードされていた以上の情報を10行でエンコードした、簡単なsystemdのサービスファイルを手に入れた。 そして、実はまだsystemdが提供するより多くの機能を使って、さらに改善する余地がたくさんある。 例えば、Restart=restart-alwaysを設定し、systemdにサービスが死んでしまったときにこれを自動的に再起動するように伝えることができる。 あるいはOMMScoreAdjust=0500を使うことで、カーネルにお願いだから、このプロセスをOOM Killerが大損害を与えるときにこのプロセスだけは見逃してくれるよう頼むことができる。 あるいはCPUSchedulingPolicy=idleを使うことで、abrtdプロセスのクラッシュダンプをバックグラウンドでのみ取得し、カーネルに何であれ他に動いていて、CPU時間を必要としているものにパフォーマンスを与えることができる。

ここで言及した設定オプションについての詳細情報は対応するmanページ、systemd.unit(5)systemd.service(5)systemd.exec(5)を見てほしい。 あるいはsystemdのmanページすべてをブラウズするのでも良い。

もちろん、すべてのSysVスクリプトがこれみたいに簡単に変換できるわけではない。 だが、嬉しいことに、実際には大半が簡単に変換できることがわかっている。

今日はここまで。次回の投稿ですぐまた会おう。

*1:原文注: initスクリプトのLSBヘッダーはSysV initスクリプトのトップにあるコメントブロックでサービスについてのメタデータを含ませる慣習であり、Linux Standard Baseで定義されている。これはディストリビューション間でinitスクリプトを標準化することを意図したものである。ほとんどのディストリビューションはこのスキームを採用しているが、ヘッダーの取り扱い方はディストリビューション間でかなり違っており、実際、各ディストリビューションごとにinitスクリプトを調整する必要がある。このようにLSBの仕様は自分がした約束を決して守っていないのである。

*2:原文注: 厳密なことをいうと、この依存関係は実はここで書く必要はなかった。というのも、Syslogデーモンがソケットアクティベーション可能なシステムなら、これは余分なのだ。モダンなsyslogシステム(例えばv5)はソケットアクティベーション可能なよう上流でパッチが当てられている。もしinitシステムがAfter=syslog.targetの設定を使っているなら、依存関係は余分であり、暗黙のものとなる。だが、アップデートされていないsyslogサービスとの互換性維持のために、ここではこの依存関係を含ませた

*3:原文注: 少なくともFedoraではそのように定義されていたものだ。

*4:原文注: systemdではグラフィカルなブートアップ(graphical.targetつまり、SysVランレベル5の役目を引き受ける)はコンソールのみのブートアップ(multi-user.targetつまりランレベル3のようなもの)の暗黙的なスーパセットである。これは後者でサービスを起動させれば、前者でもそのサービスが上がってくることを意味する。

*5:実際、デフォルトのFedoraインストールのサービスの大多数はいまやスタートアップ後にバス上に名前を取る

いろいろ新調した

昨日、今日とで色々新調した。

昨日はユニクロに服を買いに行った。最低限の冬の準備はできたと思う。

今日は友人に会いに秋葉原へ。 ついでに、つくもへSSDを見に行った。

これまで使っていたSSDIntelの330で60GBと120GBで、前者を/、後者を/homeとしてマウントしていた。 とても調子よく動いているが、買ったのが5年以上前のため、そろそろ不安になってきた。 あと、データドライブで120GBは手狭になってきているので、60GBを外して、120GBを/に、新しいSSD/homeにとスライドさせることにした。

新しいSSDはこれ。

TLCなのが気になるけど、まぁ、ええかということで。

rsync -av/homeをまるごと移したけど、SSD同士なので超早かった。

現役からは引退させる60GBも何らかの形で使いたいので、これをポチった。

透明でかっこいい。

あと、IntelNICも買った。

インテル Gigabit CT Desktop Adapter EXPI9301CT

インテル Gigabit CT Desktop Adapter EXPI9301CT

実は、デスクトップ機にもかかわらず、ずっと無線LANPCI-Eカードを挿してネットに繋いでいた。 実家にいた頃はそのほうが便利だったから仕方がない。

ただ、

  • 実家から出たので、無線である必要はなくなった
  • systemd-nspawnで遊ぶのに、MACVLANをすると、無線ではちょっと困ることがわかった

ということで、以前より有線にしたいなとは思っていた。

ところが、IvyBridgeのCPU + H77のマザボなので、オンボのNICが蟹さん。 「なので」というのは、オンボNICIntelはHaswell世代のマザボは多くがそうだったり、Ivy世代でもチップセットが最上位のZ77だとそうだったり、という印象・記憶。 色々惜しい。

ネットで見て記憶していた値段より安かったので、エイヤということで買った。若干早い気がする。気だけかも。

#きょうのsystemd: (翻訳) どのサービスがどのプロセスを動かしてる? (systemd for Administrators, Part 2)

概要

systemd for Administrators, Part II

の翻訳。

どのサービスがどのプロセスを動かしている?

ほとんどのLinuxシステムで、実質的にはデフォルトでかなりの数のプロセスが動かされている。 どのプロセスが動いていて、それが何の、どこに属しているのかを知ることはどんどん難しくなっている。 サービスのいくつかは1組のワーカープロセスを維持していることすらあり、これらは、たいてい簡単には見分けのつかない数多くの追加のプロセスでpsの出力を乱雑にしている。 デーモンが任意のサードパーティーのプロセスを、ApacheCGIプロセスでやっていたり、cronがユーザージョブでやっているように、動かそうものなら、状況はさらに複雑になる。

この状況へのほんのわずかな対処になるのは、たいてい、プロセスの継承ツリーで、これはps xafによって表示されるようなものだ。 だが、これは普通、信頼できるものではない。 というのも、親プロセスが死んだプロセスはPID 1を親に設定し直すためであり、したがって、継承に関するすべての情報が失われてしまう。 プロセスが「ダブルフォーク」している場合、このプロセスはそれを開始したプロセスとの関係も失ってしまうこととなる。(これは実際には特徴であり、伝統的なUnixのデーモン化のロジックに依存している。) さらに、プロセスはPR_SETNAMEargv[0]をパッチすることにより、自由に名前を変えることができ、これもまた、プロセスを識別するのを難しくしている。 事実、プロセスはこの方法で管理者とかくれんぼを楽しくやっているのだ。

systemdではサービスから名前を取ったコントロールグループにスポーンされたすべてのプロセスを置いている。 コントロールグループ(あるいはcgroup)は基本の「き」として、簡単にプロセスのグループなのである。そして、プロセスのグループは階層的に、別個にラベル付けされて、整理されるのである。 プロセスが他のプロセスをスポーンしたら、その子プロセスは自動的に親プロセスのcgroupのメンバーとなる。 したがって、cgroupsはプロセスに対し、それが属しているサービスに沿ってラベルをつける効果的な方法の一つとして使うことができ、どれだけフォークしても名前を変えても、そのラベルからサービスを逃さないようにすることができるのである。 さらに、これは安全にサービスとそれが作り出したすべてのプロセスを安全に停止させるのに使うことができる。ここでも、プロセスは逃れることができない。

さて、今日の投稿では、2つのコマンドを紹介しようと思う。 これらはシステムのサービスとプロセスに関係して、使うことができるものだ。 最初はよく知られたpsコマンドなのだが、これはcgroupの情報をプロセスの他の詳細情報と合わせて表示することができるようにアップデートされてきてる。 こんな感じだ

$ ps xawf -eo pid,user,cgroup,args
  PID USER     CGROUP                              COMMAND
    2 root     -                                   [kthreadd]
    3 root     -                                    \_ [ksoftirqd/0]
[...]
 4281 root     -                                    \_ [flush-8:0]
    1 root     name=systemd:/systemd-1             /sbin/init
  455 root     name=systemd:/systemd-1/sysinit.service /sbin/udevd -d
28188 root     name=systemd:/systemd-1/sysinit.service  \_ /sbin/udevd -d
28191 root     name=systemd:/systemd-1/sysinit.service  \_ /sbin/udevd -d
 1096 dbus     name=systemd:/systemd-1/dbus.service /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
 1131 root     name=systemd:/systemd-1/auditd.service auditd
 1133 root     name=systemd:/systemd-1/auditd.service  \_ /sbin/audispd
 1135 root     name=systemd:/systemd-1/auditd.service      \_ /usr/sbin/sedispatch
 1171 root     name=systemd:/systemd-1/NetworkManager.service /usr/sbin/NetworkManager --no-daemon
 4028 root     name=systemd:/systemd-1/NetworkManager.service  \_ /sbin/dhclient -d -4 -sf /usr/libexec/nm-dhcp-client.action -pf /var/run/dhclient-wlan0.pid -lf /var/lib/dhclient/dhclient-7d32a784-ede9-4cf6-9ee3-60edc0bce5ff-wlan0.lease -
 1175 avahi    name=systemd:/systemd-1/avahi-daemon.service avahi-daemon: running [epsilon.local]
 1194 avahi    name=systemd:/systemd-1/avahi-daemon.service  \_ avahi-daemon: chroot helper
 1193 root     name=systemd:/systemd-1/rsyslog.service /sbin/rsyslogd -c 4
 1195 root     name=systemd:/systemd-1/cups.service cupsd -C /etc/cups/cupsd.conf
 1207 root     name=systemd:/systemd-1/mdmonitor.service mdadm --monitor --scan -f --pid-file=/var/run/mdadm/mdadm.pid
 1210 root     name=systemd:/systemd-1/irqbalance.service irqbalance
 1216 root     name=systemd:/systemd-1/dbus.service /usr/sbin/modem-manager
 1219 root     name=systemd:/systemd-1/dbus.service /usr/libexec/polkit-1/polkitd
 1242 root     name=systemd:/systemd-1/dbus.service /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplicant.pid
 1249 68       name=systemd:/systemd-1/haldaemon.service hald
 1250 root     name=systemd:/systemd-1/haldaemon.service  \_ hald-runner
 1273 root     name=systemd:/systemd-1/haldaemon.service      \_ hald-addon-input: Listening on /dev/input/event3 /dev/input/event9 /dev/input/event1 /dev/input/event7 /dev/input/event2 /dev/input/event0 /dev/input/event8
 1275 root     name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-rfkill-killswitch
 1284 root     name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-leds
 1285 root     name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-generic-backlight
 1287 68       name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-acpi
 1317 root     name=systemd:/systemd-1/abrtd.service /usr/sbin/abrtd -d -s
 1332 root     name=systemd:/systemd-1/getty@.service/tty2 /sbin/mingetty tty2
 1339 root     name=systemd:/systemd-1/getty@.service/tty3 /sbin/mingetty tty3
 1342 root     name=systemd:/systemd-1/getty@.service/tty5 /sbin/mingetty tty5
 1343 root     name=systemd:/systemd-1/getty@.service/tty4 /sbin/mingetty tty4
 1344 root     name=systemd:/systemd-1/crond.service crond
 1346 root     name=systemd:/systemd-1/getty@.service/tty6 /sbin/mingetty tty6
 1362 root     name=systemd:/systemd-1/sshd.service /usr/sbin/sshd
 1376 root     name=systemd:/systemd-1/prefdm.service /usr/sbin/gdm-binary -nodaemon
 1391 root     name=systemd:/systemd-1/prefdm.service  \_ /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 --force-active-vt
 1394 root     name=systemd:/systemd-1/prefdm.service      \_ /usr/bin/Xorg :0 -nr -verbose -auth /var/run/gdm/auth-for-gdm-f2KUOh/database -nolisten tcp vt1
 1495 root     name=systemd:/user/lennart/1             \_ pam: gdm-password
 1521 lennart  name=systemd:/user/lennart/1                 \_ gnome-session
 1621 lennart  name=systemd:/user/lennart/1                     \_ metacity
 1635 lennart  name=systemd:/user/lennart/1                     \_ gnome-panel
 1638 lennart  name=systemd:/user/lennart/1                     \_ nautilus
 1640 lennart  name=systemd:/user/lennart/1                     \_ /usr/libexec/polkit-gnome-authentication-agent-1
 1641 lennart  name=systemd:/user/lennart/1                     \_ /usr/bin/seapplet
 1644 lennart  name=systemd:/user/lennart/1                     \_ gnome-volume-control-applet
 1646 lennart  name=systemd:/user/lennart/1                     \_ /usr/sbin/restorecond -u
 1652 lennart  name=systemd:/user/lennart/1                     \_ /usr/bin/devilspie
 1662 lennart  name=systemd:/user/lennart/1                     \_ nm-applet --sm-disable
 1664 lennart  name=systemd:/user/lennart/1                     \_ gnome-power-manager
 1665 lennart  name=systemd:/user/lennart/1                     \_ /usr/libexec/gdu-notification-daemon
 1670 lennart  name=systemd:/user/lennart/1                     \_ /usr/libexec/evolution/2.32/evolution-alarm-notify
 1672 lennart  name=systemd:/user/lennart/1                     \_ /usr/bin/python /usr/share/system-config-printer/applet.py
 1674 lennart  name=systemd:/user/lennart/1                     \_ /usr/lib64/deja-dup/deja-dup-monitor
 1675 lennart  name=systemd:/user/lennart/1                     \_ abrt-applet
 1677 lennart  name=systemd:/user/lennart/1                     \_ bluetooth-applet
 1678 lennart  name=systemd:/user/lennart/1                     \_ gpk-update-icon
 1408 root     name=systemd:/systemd-1/console-kit-daemon.service /usr/sbin/console-kit-daemon --no-daemon
 1419 gdm      name=systemd:/systemd-1/prefdm.service /usr/bin/dbus-launch --exit-with-session
 1453 root     name=systemd:/systemd-1/dbus.service /usr/libexec/upowerd
 1473 rtkit    name=systemd:/systemd-1/rtkit-daemon.service /usr/libexec/rtkit-daemon
 1496 root     name=systemd:/systemd-1/accounts-daemon.service /usr/libexec/accounts-daemon
 1499 root     name=systemd:/systemd-1/systemd-logger.service /lib/systemd/systemd-logger
 1511 lennart  name=systemd:/systemd-1/prefdm.service /usr/bin/gnome-keyring-daemon --daemonize --login
 1534 lennart  name=systemd:/user/lennart/1        dbus-launch --sh-syntax --exit-with-session
 1535 lennart  name=systemd:/user/lennart/1        /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
 1603 lennart  name=systemd:/user/lennart/1        /usr/libexec/gconfd-2
 1612 lennart  name=systemd:/user/lennart/1        /usr/libexec/gnome-settings-daemon
 1615 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfsd
 1626 lennart  name=systemd:/user/lennart/1        /usr/libexec//gvfs-fuse-daemon /home/lennart/.gvfs
 1634 lennart  name=systemd:/user/lennart/1        /usr/bin/pulseaudio --start --log-target=syslog
 1649 lennart  name=systemd:/user/lennart/1         \_ /usr/libexec/pulse/gconf-helper
 1645 lennart  name=systemd:/user/lennart/1        /usr/libexec/bonobo-activation-server --ac-activate --ior-output-fd=24
 1668 lennart  name=systemd:/user/lennart/1        /usr/libexec/im-settings-daemon
 1701 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfs-gdu-volume-monitor
 1707 lennart  name=systemd:/user/lennart/1        /usr/bin/gnote --panel-applet --oaf-activate-iid=OAFIID:GnoteApplet_Factory --oaf-ior-fd=22
 1725 lennart  name=systemd:/user/lennart/1        /usr/libexec/clock-applet
 1727 lennart  name=systemd:/user/lennart/1        /usr/libexec/wnck-applet
 1729 lennart  name=systemd:/user/lennart/1        /usr/libexec/notification-area-applet
 1733 root     name=systemd:/systemd-1/dbus.service /usr/libexec/udisks-daemon
 1747 root     name=systemd:/systemd-1/dbus.service  \_ udisks-daemon: polling /dev/sr0
 1759 lennart  name=systemd:/user/lennart/1        gnome-screensaver
 1780 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfsd-trash --spawner :1.9 /org/gtk/gvfs/exec_spaw/0
 1864 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfs-afc-volume-monitor
 1874 lennart  name=systemd:/user/lennart/1        /usr/libexec/gconf-im-settings-daemon
 1903 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfsd-burn --spawner :1.9 /org/gtk/gvfs/exec_spaw/1
 1909 lennart  name=systemd:/user/lennart/1        gnome-terminal
 1913 lennart  name=systemd:/user/lennart/1         \_ gnome-pty-helper
 1914 lennart  name=systemd:/user/lennart/1         \_ bash
29231 lennart  name=systemd:/user/lennart/1         |   \_ ssh tango
 2221 lennart  name=systemd:/user/lennart/1         \_ bash
 4193 lennart  name=systemd:/user/lennart/1         |   \_ ssh tango
 2461 lennart  name=systemd:/user/lennart/1         \_ bash
29219 lennart  name=systemd:/user/lennart/1         |   \_ emacs systemd-for-admins-1.txt
15113 lennart  name=systemd:/user/lennart/1         \_ bash
27251 lennart  name=systemd:/user/lennart/1             \_ empathy
29504 lennart  name=systemd:/user/lennart/1             \_ ps xawf -eo pid,user,cgroup,args
 1968 lennart  name=systemd:/user/lennart/1        ssh-agent
 1994 lennart  name=systemd:/user/lennart/1        gpg-agent --daemon --write-env-file
18679 lennart  name=systemd:/user/lennart/1        /bin/sh /usr/lib64/firefox-3.6/run-mozilla.sh /usr/lib64/firefox-3.6/firefox
18741 lennart  name=systemd:/user/lennart/1         \_ /usr/lib64/firefox-3.6/firefox
28900 lennart  name=systemd:/user/lennart/1             \_ /usr/lib64/nspluginwrapper/npviewer.bin --plugin /usr/lib64/mozilla/plugins/libflashplayer.so --connection /org/wrapper/NSPlugins/libflashplayer.so/18741-6
 4016 root     name=systemd:/systemd-1/sysinit.service /usr/sbin/bluetoothd --udev
 4094 smmsp    name=systemd:/systemd-1/sendmail.service sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
 4096 root     name=systemd:/systemd-1/sendmail.service sendmail: accepting connections
 4112 ntp      name=systemd:/systemd-1/ntpd.service /usr/sbin/ntpd -n -u ntp:ntp -g
27262 lennart  name=systemd:/user/lennart/1        /usr/libexec/mission-control-5
27265 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-haze
27268 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-logger
27270 lennart  name=systemd:/user/lennart/1        /usr/libexec/dconf-service
27280 lennart  name=systemd:/user/lennart/1        /usr/libexec/notification-daemon
27284 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-gabble
27285 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-salut
27297 lennart  name=systemd:/user/lennart/1        /usr/libexec/geoclue-yahoo

(この出力は短くなっていることに注意。このブログストーリーと関係がないため、カーネルスレッドのほとんどはここでは削除している。)

3つ目の列で、それぞれのプロセスをsystemdがどのcgroupに割り当てているかを確認することができる。 udevプロセス群がname=systemd:/systemd-1/sysinit.servicecgroupにあることがわかるだろう。 systemdがブートの初期段階をカバーするsysinit.serviceサービスで起動されたすべてのプロセスをここに配置している。

個人的におすすめなのが、シェルのエイリアスpscを上で示したようなpsコマンドラインに設定することだ。

alias psc='ps xawf -eo pid,user,cgroup,args'

これで、たった4回キーを押すだけで、サービスの情報が見られる!

同じ情報を表示する別の方法はsystemd-cglsで、これはsystemdに同梱されている。 出力はこんな感じだ

$ systemd-cgls
+    2 [kthreadd]
[...]
+ 4281 [flush-8:0]
+ user
| \ lennart
|   \ 1
|     +  1495 pam: gdm-password
|     +  1521 gnome-session
|     +  1534 dbus-launch --sh-syntax --exit-with-session
|     +  1535 /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
|     +  1603 /usr/libexec/gconfd-2
|     +  1612 /usr/libexec/gnome-settings-daemon
|     +  1615 /ushr/libexec/gvfsd
|     +  1621 metacity
|     +  1626 /usr/libexec//gvfs-fuse-daemon /home/lennart/.gvfs
|     +  1634 /usr/bin/pulseaudio --start --log-target=syslog
|     +  1635 gnome-panel
|     +  1638 nautilus
|     +  1640 /usr/libexec/polkit-gnome-authentication-agent-1
|     +  1641 /usr/bin/seapplet
|     +  1644 gnome-volume-control-applet
|     +  1645 /usr/libexec/bonobo-activation-server --ac-activate --ior-output-fd=24
|     +  1646 /usr/sbin/restorecond -u
|     +  1649 /usr/libexec/pulse/gconf-helper
|     +  1652 /usr/bin/devilspie
|     +  1662 nm-applet --sm-disable
|     +  1664 gnome-power-manager
|     +  1665 /usr/libexec/gdu-notification-daemon
|     +  1668 /usr/libexec/im-settings-daemon
|     +  1670 /usr/libexec/evolution/2.32/evolution-alarm-notify
|     +  1672 /usr/bin/python /usr/share/system-config-printer/applet.py
|     +  1674 /usr/lib64/deja-dup/deja-dup-monitor
|     +  1675 abrt-applet
|     +  1677 bluetooth-applet
|     +  1678 gpk-update-icon
|     +  1701 /usr/libexec/gvfs-gdu-volume-monitor
|     +  1707 /usr/bin/gnote --panel-applet --oaf-activate-iid=OAFIID:GnoteApplet_Factory --oaf-ior-fd=22
|     +  1725 /usr/libexec/clock-applet
|     +  1727 /usr/libexec/wnck-applet
|     +  1729 /usr/libexec/notification-area-applet
|     +  1759 gnome-screensaver
|     +  1780 /usr/libexec/gvfsd-trash --spawner :1.9 /org/gtk/gvfs/exec_spaw/0
|     +  1864 /usr/libexec/gvfs-afc-volume-monitor
|     +  1874 /usr/libexec/gconf-im-settings-daemon
|     +  1882 /usr/libexec/gvfs-gphoto2-volume-monitor
|     +  1903 /usr/libexec/gvfsd-burn --spawner :1.9 /org/gtk/gvfs/exec_spaw/1
|     +  1909 gnome-terminal
|     +  1913 gnome-pty-helper
|     +  1914 bash
|     +  1968 ssh-agent
|     +  1994 gpg-agent --daemon --write-env-file
|     +  2221 bash
|     +  2461 bash
|     +  4193 ssh tango
|     + 15113 bash
|     + 18679 /bin/sh /usr/lib64/firefox-3.6/run-mozilla.sh /usr/lib64/firefox-3.6/firefox
|     + 18741 /usr/lib64/firefox-3.6/firefox
|     + 27251 empathy
|     + 27262 /usr/libexec/mission-control-5
|     + 27265 /usr/libexec/telepathy-haze
|     + 27268 /usr/libexec/telepathy-logger
|     + 27270 /usr/libexec/dconf-service
|     + 27280 /usr/libexec/notification-daemon
|     + 27284 /usr/libexec/telepathy-gabble
|     + 27285 /usr/libexec/telepathy-salut
|     + 27297 /usr/libexec/geoclue-yahoo
|     + 28900 /usr/lib64/nspluginwrapper/npviewer.bin --plugin /usr/lib64/mozilla/plugins/libflashplayer.so --connection /org/wrapper/NSPlugins/libflashplayer.so/18741-6
|     + 29219 emacs systemd-for-admins-1.txt
|     + 29231 ssh tango
|     \ 29519 systemd-cgls
\ systemd-1
  + 1 /sbin/init
  + ntpd.service
  | \ 4112 /usr/sbin/ntpd -n -u ntp:ntp -g
  + systemd-logger.service
  | \ 1499 /lib/systemd/systemd-logger
  + accounts-daemon.service
  | \ 1496 /usr/libexec/accounts-daemon
  + rtkit-daemon.service
  | \ 1473 /usr/libexec/rtkit-daemon
  + console-kit-daemon.service
  | \ 1408 /usr/sbin/console-kit-daemon --no-daemon
  + prefdm.service
  | + 1376 /usr/sbin/gdm-binary -nodaemon
  | + 1391 /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 --force-active-vt
  | + 1394 /usr/bin/Xorg :0 -nr -verbose -auth /var/run/gdm/auth-for-gdm-f2KUOh/database -nolisten tcp vt1
  | + 1419 /usr/bin/dbus-launch --exit-with-session
  | \ 1511 /usr/bin/gnome-keyring-daemon --daemonize --login
  + getty@.service
  | + tty6
  | | \ 1346 /sbin/mingetty tty6
  | + tty4
  | | \ 1343 /sbin/mingetty tty4
  | + tty5
  | | \ 1342 /sbin/mingetty tty5
  | + tty3
  | | \ 1339 /sbin/mingetty tty3
  | \ tty2
  |   \ 1332 /sbin/mingetty tty2
  + abrtd.service
  | \ 1317 /usr/sbin/abrtd -d -s
  + crond.service
  | \ 1344 crond
  + sshd.service
  | \ 1362 /usr/sbin/sshd
  + sendmail.service
  | + 4094 sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
  | \ 4096 sendmail: accepting connections
  + haldaemon.service
  | + 1249 hald
  | + 1250 hald-runner
  | + 1273 hald-addon-input: Listening on /dev/input/event3 /dev/input/event9 /dev/input/event1 /dev/input/event7 /dev/input/event2 /dev/input/event0 /dev/input/event8
  | + 1275 /usr/libexec/hald-addon-rfkill-killswitch
  | + 1284 /usr/libexec/hald-addon-leds
  | + 1285 /usr/libexec/hald-addon-generic-backlight
  | \ 1287 /usr/libexec/hald-addon-acpi
  + irqbalance.service
  | \ 1210 irqbalance
  + avahi-daemon.service
  | + 1175 avahi-daemon: running [epsilon.local]
  + NetworkManager.service
  | + 1171 /usr/sbin/NetworkManager --no-daemon
  | \ 4028 /sbin/dhclient -d -4 -sf /usr/libexec/nm-dhcp-client.action -pf /var/run/dhclient-wlan0.pid -lf /var/lib/dhclient/dhclient-7d32a784-ede9-4cf6-9ee3-60edc0bce5ff-wlan0.lease -cf /var/run/nm-dhclient-wlan0.conf wlan0
  + rsyslog.service
  | \ 1193 /sbin/rsyslogd -c 4
  + mdmonitor.service
  | \ 1207 mdadm --monitor --scan -f --pid-file=/var/run/mdadm/mdadm.pid
  + cups.service
  | \ 1195 cupsd -C /etc/cups/cupsd.conf
  + auditd.service
  | + 1131 auditd
  | + 1133 /sbin/audispd
  | \ 1135 /usr/sbin/sedispatch
  + dbus.service
  | +  1096 /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
  | +  1216 /usr/sbin/modem-manager
  | +  1219 /usr/libexec/polkit-1/polkitd
  | +  1242 /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplicant.pid
  | +  1453 /usr/libexec/upowerd
  | +  1733 /usr/libexec/udisks-daemon
  | +  1747 udisks-daemon: polling /dev/sr0
  | \ 29509 /usr/libexec/packagekitd
  + dev-mqueue.mount
  + dev-hugepages.mount
  \ sysinit.service
    +   455 /sbin/udevd -d
    +  4016 /usr/sbin/bluetoothd --udev
    + 28188 /sbin/udevd -d
    \ 28191 /sbin/udevd -d

(これも短くしている。同じ方法だ。)

見てわかったとおり、このコマンドはcgroupつまりサービスごとにプロセスを表示しており、systemdがサービスに従ってラベルを付けている。 例えば、簡単にわかるとおり、auditサービスのauditd.serviceは3つの別々のサービス、auditdaudispsedispatchをスポーンしている。

詳しく見ると気づくと思うが、多くのプロセスが/user/1というcgroupに割り当てられている。 ここでは簡単に、systemdはサービスをcgroupsで管理しているだけでなく、ユーザーセッションのサービスも同じようにcgroupsで管理している、ということにしておいてほしい。 のちのちの投稿でこの件についてはもっと詳しく論じることとする。

ここでおしまい。次の投稿を楽しみに、すぐ戻ってきてね!

#きょうのsystemd: ハイブリッドcgroupのセットアップは動くのか?

ちょっと前から気になっていたsystemd-develでのやり取りについて。

そもそも、「はいぶりっどこんとろーるぐーぷ?」なのだが、ちょっとまとめてみる。

ことの発端はUmut Tezduyar Lindskog*1の以下のメール。

[systemd-devel] How does hybrid cgroup setup work?

ハロー、

(訳注: cgroupの)CPUコントローラーをv1で、メモリコントローラーをunifiedにしようとしているんだけど、思ったとおり動かないんだ。 CONFIG_MEMCGを有効にすると、cgroupが/proc/cgroupsにポップアップしてきて、mount_cgroup_controllers()がかなり早くそれをマウントしている。 いったんこれがv1でマウントされると、v2では使えなくなってしまう。

ハイブリッドがどのように動くのか、自分が誤解しているんだろうか? Umut

v1だの、unifiedだの、v2だの、すでによくわからない。

ぐぐると、 id:defiant さんのブログがヒット記事が出た。systemdをやっている限りは足向けて眠れない気しかしない。

http://tenforward.hatenablog.com/entry/20160414/1460560309

http://gihyo.jp/admin/serial/01/linux_containers/0037 http://gihyo.jp/admin/serial/01/linux_containers/0038

v1 の問題点のひとつに、コントローラがばらばらに実装されているため、コントローラ間の連携ができないという問題がありました。

というわけで、「どうしてこうなった」という疑問は深堀しないことにして、これを解決するために、cgroupの構造を単一構成構造にしたのが、cgroup v2ととりあえず理解しておく。

なので、unifiedとv2は同じ、「ハイブリッド」はv1とv2のハイブリッドと理解してよさそうだ。そもそも、cgroup v1もろくに理解している気がしないのだが。

スレッドを読み進めよう。レナートのメール。

[systemd-devel] How does hybrid cgroup setup work?

ハイブリッドが意味するところは、v2がサービスのトラッキングにのみ使われているということ(これはいいことだ。なんせ、cgroupが存在するときに安全な通知をしてくれるからだ)で、どのコントローラーとしても使われていないということだ。 これは、ハイブリッドモードはほとんどピュアなv1と互換性があることを意味し、例外は、もう1つの階層構造(v2のそれだ)があって、systemdがそれをsystemdの目的のために使っているということだ。

レナート

端的に言うと、systemdはcgroup v2をフルには使っていません、というわけだ。

Umutが質問を続ける。

[systemd-devel] How does hybrid cgroup setup work?

v1からいくつかのコントローラーを、v2からいくつかのコントローラーを、みたいな幅広いハイブリッドにできない技術的な理由があるのか?

自分たちは、CPUに関してはv2が承認されるまではv1を使い続けて、一方で、メモリーに関してはv2のメリットを享受したいんだ。

UMUT

いいたいことはなんとなくわかる。

レナートの解説が入る。

[systemd-devel] How does hybrid cgroup setup work?

さて、現時点では、3つのタイプのセットアップがある。

1) レガシー

/sys/fs/cgroup/ → tmpfsインスタンス /sys/fs/cgroup/memory/ → メモリーコントローラーの階層構造 /sys/fs/cgroup/cpu/ → CPUコントローラーの階層構造 … /sys/fs/cgroup/systemd/ → namedの階層構造でsystemdがその管轄するものを管理する(そして、ほとんどsystemdの内部で利用される)もの

2) unified:

/sys/fs/cgroup/ → 単一階層構造(つまり、これは1つディレクトリを上がったところ、tmpfsのあったところだということに注意)

3) ハイブリッド:

/sys/fs/cgroup/ → tmpfs /sys/fs/cgroup/memory/ → メモリーコントローラーの階層構造 /sys/fs/cgroup/cpu/ → CPUコントローラーの階層構造 … /sys/fs/cgroup/systemd/ → namedの階層構造で互換性のため /sys/fs/cgroup/unified/ → 単一階層構造で、コントローラーはなく、systemdがその管轄するものを管理する(そして、ほとんどsystemdの内部で利用される)もの

さて、#1と#2をサポートするのは、なるほど納得である。1つは古いセットアップで、もう1つは将来のセットアップだ。 #3はたいていの方法で#1と互換性がある。唯一変わっていることは階層構造がもう1つ増えたということだけだ。 だが、古いコントローラーの階層構造は同じ場所に残っている。 別の言い方をすれば、systemdのプライベートな階層構造は変わっているが、その他の管轄するも*2のはまったく同じ場所に残っているということだ。

さて、君らが目指しているのは1と3とのどちらにも似ていない(3は単一階層構造からみて、動いてしまっているからだ)。 あるいは#2にも近くない(というのも、単一のディレクトリは/sys/fs/cgroupにはならない。なぜならレガシーなコントローラーをマウントさせる場所がないからだ)。

なので、君らの目指していることを加えようとすると、間に合わせにしかならないんだ。 つまり、加えたその瞬間に、すでに、終わりが見えている。 僕は、そんなに長くは生きないだろうとしているもののサポートを今から加えようとするのには消極的だな……。

そして、大きな問題がある。 cgroupのコードは難しすぎて、3つの異なるセットアップをサポートはできないんだ。 これにさらに付け加えるなんてことがないなら、本当にそれがいいと思う。 事実、僕は本当に、単一構造のcgroupを除いて、すべてのcgroupのサポートを、自分たちの(訳注: コード)ツリーからドロップできる日が待ち遠しいよ。 そのときには、めっちゃコードを消せるね! ハッカーがコードを書くよりも唯一好きなことは、コードを消すことさ…… ;-)

レナート

続くメールでUmutは「先生、先生の見込みではそれいつですか?」と聞いている。

[systemd-devel] How does hybrid cgroup setup work?

私も気になる。

レナート先生の回答*3

[systemd-devel] How does hybrid cgroup setup work?

自分がTejun*4の話を咀嚼すると、主要なコントローラーはすべて(訳注: v2に)動かすつもりなんだけど、いくつかは全体的にドロップするみたいだ。例えば、"devices"(というのも、これのやっていることは正確にはリソースの管理じゃなくて、アクセスの管理で、seccompと/devをどう一緒にするかを注意深く選び取れば、こいつはほとんど余分だからだ。)

モリーコントローラーはもう完全に移っただろう。 systemdのMemoryMax=はすでに正しくことを運んでいる。

*1:ぐぐると名前がそこそこ出る人であった。経歴は彼のLinkedinページを参照。systemd.conf 2015でもどうやら喋っているようだ。発表のタイトルを見る限り、組み込み系の人だろうか。

*2:原語は'stuff'なのだが、訳語はいつもどうしたらいいのか悩む……。

*3:ちょっと誤字・脱字が多いみたいなので補正している

*4:cgroupsのメンテナーsystemdのコミッターWikipedia - cgroupsLinkedin