#きょうの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

#きょうのsystemd: (翻訳) ブートアップを確かめる (systemd for Administrators, Part 1)

概要

以下は、レナートのブログの

0pointer.de

を翻訳したものである。

管理者のためのsystemd パート1

読者の多くが知っているようにsystemdFedoraの新しいinitシステムであり、Fedora 14から採用された。 また、同じような方法で他のディストリビューションの多く(例えば、OpenSUSE)でも採用されている。 管理者向けにsystemdは数多くの新しい新機能を提供しており、管理上のプロセスをかなり変化・強化している。 数カ月の間、だいたい毎週、シリーズとして記事を投稿しようとしている。 このブログストーリーはその最初のパートである。 それぞれの投稿でsystemdの新しい機能を1つ取り上げて説明しようと思う。 これらの機能の多くは小さくてシンプルなものなので、これらのストーリーが多くの読者の関心をひくものだろう。 ただ、折りに触れて、読者に提供されているsystemdの素晴らしい新しい機能について少しだけ掘り下げることもしようと思う。

ブートアップを確かめる

伝統的に、Linuxシステムがブートアップするとき、スクリーンを流れていく短いメッセージを目にするだろう。 ブートプロセスを速くしよう、パラレル化しようと我々が努めた結果、これらのメッセージを目にする時間はどんどん短くなり、そして、どんどん読みづらくなっている。 今日ではPlymouthのようなグラフィカルなブートスプラッシュテクノロジーを使って、できれば表示されないようになってすらいる。 それにもかかわらず、ブートスクリーンの情報はこれまでも、そして現在でも非常に適切なものである。 というのも、ブートアップの一部として起動されていたそれぞれのサービスがうまく起動したか、それとも失敗したか(緑か赤の[ OK ][ FAILED ]で示される)を示してくれるからである。 高速にブートアップし、パラレル化されたマシンの状況を改善するために、そして、この情報を実行時にもっとうまく使えるようにするために、我々はsystemdに新しい機能を追加した。 それは、それぞれのサービスについて、スタートアップは成功したかどうか、0出ない終了コードで終了したかどうか、タイムアウトしたかどうか、(セグフォやその他の)異常終了したかどうかを、スタートアップ時、実行時の両方で、追跡し、記憶するものである。 簡単にsystemctlとシェルでタイプすると、すべてのサービス、つまり、systemdネイティブなサービスとSysV/LSBサービスの両方の状態をクエリーすることができる。

[root@lambda] ~# systemctl
UNIT                                          LOAD   ACTIVE       SUB          JOB             DESCRIPTION
dev-hugepages.automount                       loaded active       running                      Huge Pages File System Automount Point
dev-mqueue.automount                          loaded active       running                      POSIX Message Queue File System Automount Point
proc-sys-fs-binfmt_misc.automount             loaded active       waiting                      Arbitrary Executable File Formats File System Automount Point
sys-kernel-debug.automount                    loaded active       waiting                      Debug File System Automount Point
sys-kernel-security.automount                 loaded active       waiting                      Security File System Automount Point
sys-devices-pc...0000:02:00.0-net-eth0.device loaded active       plugged                      82573L Gigabit Ethernet Controller
[...]
sys-devices-virtual-tty-tty9.device           loaded active       plugged                      /sys/devices/virtual/tty/tty9
-.mount                                       loaded active       mounted                      /
boot.mount                                    loaded active       mounted                      /boot
dev-hugepages.mount                           loaded active       mounted                      Huge Pages File System
dev-mqueue.mount                              loaded active       mounted                      POSIX Message Queue File System
home.mount                                    loaded active       mounted                      /home
proc-sys-fs-binfmt_misc.mount                 loaded active       mounted                      Arbitrary Executable File Formats File System
abrtd.service                                 loaded active       running                      ABRT Automated Bug Reporting Tool
accounts-daemon.service                       loaded active       running                      Accounts Service
acpid.service                                 loaded active       running                      ACPI Event Daemon
atd.service                                   loaded active       running                      Execution Queue Daemon
auditd.service                                loaded active       running                      Security Auditing Service
avahi-daemon.service                          loaded active       running                      Avahi mDNS/DNS-SD Stack
bluetooth.service                             loaded active       running                      Bluetooth Manager
console-kit-daemon.service                    loaded active       running                      Console Manager
cpuspeed.service                              loaded active       exited                       LSB: processor frequency scaling support
crond.service                                 loaded active       running                      Command Scheduler
cups.service                                  loaded active       running                      CUPS Printing Service
dbus.service                                  loaded active       running                      D-Bus System Message Bus
getty@tty2.service                            loaded active       running                      Getty on tty2
getty@tty3.service                            loaded active       running                      Getty on tty3
getty@tty4.service                            loaded active       running                      Getty on tty4
getty@tty5.service                            loaded active       running                      Getty on tty5
getty@tty6.service                            loaded active       running                      Getty on tty6
haldaemon.service                             loaded active       running                      Hardware Manager
hdapsd@sda.service                            loaded active       running                      sda shock protection daemon
irqbalance.service                            loaded active       running                      LSB: start and stop irqbalance daemon
iscsi.service                                 loaded active       exited                       LSB: Starts and stops login and scanning of iSCSI devices.
iscsid.service                                loaded active       exited                       LSB: Starts and stops login iSCSI daemon.
livesys-late.service                          loaded active       exited                       LSB: Late init script for live image.
livesys.service                               loaded active       exited                       LSB: Init script for live image.
lvm2-monitor.service                          loaded active       exited                       LSB: Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
mdmonitor.service                             loaded active       running                      LSB: Start and stop the MD software RAID monitor
modem-manager.service                         loaded active       running                      Modem Manager
netfs.service                                 loaded active       exited                       LSB: Mount and unmount network filesystems.
NetworkManager.service                        loaded active       running                      Network Manager
ntpd.service                                  loaded maintenance  maintenance                  Network Time Service
polkitd.service                               loaded active       running                      Policy Manager
prefdm.service                                loaded active       running                      Display Manager
rc-local.service                              loaded active       exited                       /etc/rc.local Compatibility
rpcbind.service                               loaded active       running                      RPC Portmapper Service
rsyslog.service                               loaded active       running                      System Logging Service
rtkit-daemon.service                          loaded active       running                      RealtimeKit Scheduling Policy Service
sendmail.service                              loaded active       running                      LSB: start and stop sendmail
sshd@172.31.0.53:22-172.31.0.4:36368.service  loaded active       running                      SSH Per-Connection Server
sysinit.service                               loaded active       running                      System Initialization
systemd-logger.service                        loaded active       running                      systemd Logging Daemon
udev-post.service                             loaded active       exited                       LSB: Moves the generated persistent udev rules to /etc/udev/rules.d
udisks.service                                loaded active       running                      Disk Manager
upowerd.service                               loaded active       running                      Power Manager
wpa_supplicant.service                        loaded active       running                      Wi-Fi Security Service
avahi-daemon.socket                           loaded active       listening                    Avahi mDNS/DNS-SD Stack Activation Socket
cups.socket                                   loaded active       listening                    CUPS Printing Service Sockets
dbus.socket                                   loaded active       running                      dbus.socket
rpcbind.socket                                loaded active       listening                    RPC Portmapper Socket
sshd.socket                                   loaded active       listening                    sshd.socket
systemd-initctl.socket                        loaded active       listening                    systemd /dev/initctl Compatibility Socket
systemd-logger.socket                         loaded active       running                      systemd Logging Socket
systemd-shutdownd.socket                      loaded active       listening                    systemd Delayed Shutdown Socket
dev-disk-by\x1...x1db22a\x1d870f1adf2732.swap loaded active       active                       /dev/disk/by-uuid/fd626ef7-34a4-4958-b22a-870f1adf2732
basic.target                                  loaded active       active                       Basic System
bluetooth.target                              loaded active       active                       Bluetooth
dbus.target                                   loaded active       active                       D-Bus
getty.target                                  loaded active       active                       Login Prompts
graphical.target                              loaded active       active                       Graphical Interface
local-fs.target                               loaded active       active                       Local File Systems
multi-user.target                             loaded active       active                       Multi-User
network.target                                loaded active       active                       Network
remote-fs.target                              loaded active       active                       Remote File Systems
sockets.target                                loaded active       active                       Sockets
swap.target                                   loaded active       active                       Swap
sysinit.target                                loaded active       active                       System Initialization

LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.
JOB    = Pending job for the unit.

221 units listed. Pass --all to see inactive units, too.
[root@lambda] ~#

(上の出力はちょっと削って、このブログ投稿に関係のない行は削除した。)

ACTIVE列を見てほしい。 これはサービスの高度な状態を示している(実際にはsystemdがメンテしているすべての種類のユニットの状態を示している。これらのユニットは単なるサービス以上のものでもありうる。ただ、これについては後々のブログ投稿で一瞥するつもりだ)。 示される状態はactive(つまり、実行中)、inactive(つまり、実行していない)ないしはその他の状態だ。 よくよく見ると、リストにあるアイテムの1つがmaintenanceとマークされ、赤でハイライトされているのに気づくだろう*1。 これで、あるサービスが起動に失敗したか、さもなくば、問題にぶち当たったことを知ることができる。 今回の場合、ntpdである。 さて、ntpdに実際に何が起きたかを調べることにしよう。 systemctl statusコマンドを使う

[root@lambda] ~# systemctl status ntpd.service
ntpd.service - Network Time Service
      Loaded: loaded (/etc/systemd/system/ntpd.service)
      Active: maintenance
        Main: 953 (code=exited, status=255)
      CGroup: name=systemd:/systemd-1/ntpd.service
[root@lambda] ~#

コマンドの結果は、NTPが実行時(PID953として動作していた)に終了したことを示している。 これは正確なエラーの状態を示している。 プロセスが終了コード255で終了したのだ。

のちのsystemdのバージョンでは、この機能強化リクエストがフィックスされたらすぐに、これをABRTまで引き上げることを計画をしている。 そうすれば、もしsystemctl statusがクラッシュしたサービスについての情報を示せば、ABRTにある適切なクラッシュダンプに利用者を直ちに案内するだろう*2

要約

systemctlsystemctl statusを使おう。これは、伝統的な、SysVサービスのブートアップ時の状態メッセージのモダンで、より完璧な代替だ。 systemctl statusはより詳細にエラーの状態を取得するだけではなく、スタートアップのエラーに加え、実行時のエラーも表示する。

今週はここまで。管理者のためのsystemdについての次の投稿を読みに、きっと来週には戻って来てほしい。

*1:仕様上、赤くできていない。あとでレナートが書いているが、該当するのはntpd.serviceだ。

*2:自分用メモ: 要追跡調査

#きょうのsystemd: (翻訳)Socket Activation (1)

今日のUbuntu Weekly Recipeはsystemdネタだった!

gihyo.jp

それで、というわけでもなく、ちょうどたまたま書き溜めてただけだけど、今日はsocketユニットについて。

またですが、レナートのブログ記事の翻訳。

原文: 0pointer.net

ソケットアクティベーション

systemdについてオリジナルなブログストーリーで、私はなぜ、ソケットアクティベーションがサービスをスポーンするための素晴らしい技術であるかについて説明しようとした。 ここでちょっと、その背景を繰り返しになるが説明しようと思う。

ソケットアクティベーションの基本的な考え方は新しいものではない。 inetdスーパーサーバーは、ずっとほとんどのLinuxUnixシステムで標準的なコンポーネントであった。 その挙動はこうだ。 起動時にすべてのローカルのインターネットサービスをスポーンする代わりに、スーパーサーバーがそれらのサービスの代わりにリスンする。 そして、接続が来たらいつでも対応するサービスのインスタンスがスポーンされるというものである。 これによりリソースの少ない比較的貧弱なサーバーでも多種多様なサービスを同時に提供することが可能になった。 しかし、この方法はすぐに、いくぶんか遅いという評判を得るようになってしまった。 接続が入ってくるたびに、デーモンがスポーンされるため、サービスのフォークと初期化--それぞれの接続につき1回だ。それらすべてについて1回ではない--にたくさんの時間が必要となる。

接続のたびに1つのインスタンスをスポーンするのはinetdの主たる使われ方であった。一方で、inetdは現に別のモードも理解した。 こちらの挙動はこうだ。 最初の接続が入ってきたら、poll()(ないしはselect())を通じて、このことを伝え、以降のすべての接続のために1つだけインスタンスをスポーンさせる(これはwait/nowaitオプションでコントロールできる)。 この方法だと最初の接続はセットアップに時間がかかるが、続く接続ではスタンドアローンのサービスと同じくらい早い。 このモードでは、inetdは真の意味でオンデマンドモードとして動作する。つまり、サービスは必要とされるときになって初めて利用可能になるのである。

inetdのフォーカスは明らかにAF_INET(つまりインターネット)ソケットにあった。 ときが進んで、Linux/Unixがサーバーという枠から離れ、デスクトップやモバイル、組み込みといった環境に徐々に関係するようになった結果、inetdは時間のトラブルに巻き込まれるようになる。 その評判は遅いというものであったし、インターネットサービスのみがLinuxの焦点でなくなったことで、inetd(ないしは、xinetdのようなその新しい実装の1つ)を動かしているLinuxマシンは一般的ではなく例外的となった。

AppleのエンジニアがMacOSのブート時間の最適化に取り組んでいたところ、彼らはソケットアクティベーションを使う新しい方法を発見した。つまり、彼らはAF_INETソケットからAF_UNIXソケットに焦点をシフトさせたのである。 そして、彼らは、オンデマンドのソケットアクティベーションは物語の一部に過ぎなかったことに気づいた。つまり、何があっても起動時にスタートさせなければならないモノを含め、すべてのローカルサービスにそれを使えば、ソケットアクティベーションがもっともっと強力になると気づいたのである。 彼らはこのアイディアをlaunchdで実装した。launchdはモダンなMacOS Xシステムの礎石となるものであり、おそらく、MacOSがすぐに起動してくる一番の理由である。

しかし、次へ行く前にここで、非オンデマンド、非インターネットサービスへのソケットアクティベーションを使うことの利益が何であるかを詳細に見ておこう。 Syslog、D-Bus、Avahi、Bluetoothデーモンの4つのサービスを考える。 伝統的なLinuxシステムではD-BusはSyslogにログを書き出すため、Syslogより後に起動しなければならない。 同様に、AvahiははSyslogとD-Busを必要とするためこの2つのあとに起動しなければならない。 最後に、BluetoothはAvahiと似ており、SyslogとD-Busを必要とするが、Avahiとは接続されていない。 伝統的なSysVベースのシステムでは同時に1つのサービスだけしか、起動中のプロセスにできないため、スタートアップのシリアル化は次のようになる。Syslog→D-Bus→Avahi→Bluetoothである(もちろん、AvahiとBluetoothは逆の順序で起動させることもできる。しかし、ここでは1つだけを取り上げたいので、単純にアルファベット順にする)。 これを図式化すると、これがシステムのスタートアップで始まるスタートアップの順番を示しているプロットになる(トップに来ている)。

f:id:popo1897:20171116223724p:plain

特定のディストリビューションではこの厳格なシリアルのスタートアップを改善しようとしている。AvahiとBluetoothとは互いに独立しているため、これらは同時に開始することができる。 パラレル化が進み、全体のスタートアップ時間は少し小さくなっている(これはプロットの真ん中で図示されている)*1

ソケットアクティベーションは4つすべてのサービスを、いかなる順序化もなしに、完全に同時に開始させることができる。 リスンするソケットの作成をデーモン自身の外に移すことにより、これらを同時に実行することができ、これらは互いのソケットにすぐに接続することができるのである。 つまり、単一のステップで/dev/logソケットと/run/dbus/system_bus_socketソケットを作成し、次のステップで4つすべてのサービスを同時にスポーンさせるのである。 D-Busがsyslogにログを書き出したいと思ったときは、D-Bus/dev/logにメッセージを書くだけである。 ソケットバッファーがいっぱいにならない限りは、D-Busはその他、初期化のためにしたいと思うことをすぐに続けることができるのである。 syslogサービスがD-Busに追いつけば、syslogはキューに入っているメッセージの処理を始める。 もし、ソケットバッファーがいっぱいになった場合には、クライエントのロギングはソケットが再び書き込み可能になるまで一時的にブロックされ、ログメッセージが書き込み可能になるまでそれが続く。 これは、サービスのスケジューリングが全体的にカーネルにより行われることを意味する。ユーザースペースの観点からすると、すべてのサービスは同時に実行され、あるサービスがそれを必要とする他のサービスに追いつかない場合は、前者は後者の要求を一時的にブロックすることになるが、リクエストが発行されるとすぐに続く。 したがって、以前は厳格にシリアル化が必要であると考えられていたサービスを同時に起動することができようになったことで、ソケットアクティベーションによりスタートアップのパラレル化は劇的に進むことになる。 多くのLinuxサービスはコミュニケーションチャネルとしてソケットを利用している。ソケットアクティベーションでこれらのチャネルのクライアントとサーバーの起動を同時におこなうことができるのである。

しかし、話はパラレル化だけにとどまらない。ソケットアクティベーションは他にも多くの利点がある。

  • 我々はもはや依存関係を明示的に設定する必要がなくなる。ソケットはすべてのサービスの前に初期化されるため、簡単に利用可能になる。また、ユーザースペースでのサービスのスタートアップを順序化する必要ももうなくなる。したがって、ソケットアクティベーションはサービスの設定と開発を劇的に単純化する。
  • サービスが死んだとしても、ソケットはそのままで残り、メッセージをロストすることがなくなる。クラッシュしたサービスが再起動したら、サービスが残してしまったものを引き継ぐことができる。
  • サービスがアップグレードされると、ソケットを残したままサービスを再起動させることができ、これで、サービスが継続的に反応できるようになる。アップグレード中にコネクションが1つも失われることはない。
  • クライアントに見えないような方法で実行中のサービスを入れ替えることだって可能になる。例えば、systemdを動かしているすべてのシステムが小さいsyslogデーモンを起動時に動かしている。これはすべてのログメッセージをカーネルメッセージバッファーに繋がっている/dev/logに書き出す。この方法で信頼の置けるユーザースペースロギングを起動時の最初の瞬間から提供することができる。そして、実際のrsyslogデーモンが起動すると、ミニ版のデーモンを停止させ、それを本物のデーモンに置き換えられるようになる。オリジナルのロギングソケットを維持し、これを2つのデーモンで共有することで、メッセージを1つも失わなくて済む。rsyslogが起動後にカーネルログバッファーをディスクに書き出すため、カーネルからの、ブートの初期段階からの、実行時からの、すべてのメッセージを完全にディスク上に置くことができる。

この考えについての他の説明はsystemdのオリジナルのブログストーリーを参照してほしい。

2へ続く

というか、2いるかな?

*1:UbuntuについてはRethinking PID 1Upstartをこき下ろしているが、SUSEの件は軽くスルーされている。SUSE(自分が触ったことのあるSLES 11)の場合、startpar(manページ)というのを利用し、サービス起動のパラレル化を図っている。これは、/etc/init.d以下のLSBヘッダーを読み取り、依存関係(=起動順)に指定あるものだけをピックアップし、理解する。これを

  • /etc/init.d/.depend.boot
  • /etc/init.d/.depend.start
  • /etc/init.d/.depend.stop

というパスでMakefileの書式で保持する。.depend.*に書かかれたサービスはその内容にしたがって起動・停止され、何も書かれていないサービスはパラレルに起動・停止される。

管見の限り(ググった限り)では、日本語で読めるのは次のサイト:

#きょうのsystemd : Ubuntuで新しいめのsystemdを使おうとしてコケた話

はじめに

きょうは何だか翻訳の気分ではないし、自分の文章を書きたいので、寄り道をする。

翻訳が増えることを期待してくれた方には申し訳ない。

新しいめのsystemdを試したい

systemd 235では、以前に紹介したような機能が搭載された。

popopopoon.hatenadiary.jp

例えば、「Ubuntuでちょっとこれを使ってみたいな」と思ったとしよう、というのが本稿のスタート地点である。

ところで、先月リリースされたばかりのUbuntu 17.10のsystemdのバージョンは234である。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 17.10
Release:        17.10
Codename:       artful
$ systemctl --version
systemd 234
+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN2 +IDN default-hierarchy=hybrid

というわけで、systemd 235のパッケージをビルドして、インストールしようというわけだ。

比較的正しい手順

@MurabitoL さんが「比較的正しい手順」を教えてくれた。

こちらに従うのが良いだろう。

おことわり

本稿の作業を最後までおこなうと、ディストリビューション標準のパッケージ、しかも、システムの根幹に関わるsystemdのパッケージを上書きインストールすることになる。

壊れてもいい環境か、本番環境を壊してもいいという覚悟かのいずれかをもって臨んでほしい。

パッケージビルド

流れは以下の記事の「すでにソースパッケージがある場合」に沿っている。

gihyo.jp

Ubuntuの条件

systemd 235のビルドではmesonというビルドシステムが使われるようなのだが、Ubuntu 17.04でインストールできるmesonはsystemd 235で指定されているバージョンに届かないようだ。 Ubuntu 16.04でインストールできるmesonもバージョンが足りないことは想像に難くない*1

一方、Ubuntu 17.10でインストールできるmesonはバージョンの要件を満たしているようである。

したがって、要件を満たすmesonを自前でビルド・インストールするか、Ubuntu 17.10を使うかのどちらかの選択となる。

おそらく、仮想マシンなり、任意のシステムコンテナなりで作業しているであろうから、素直に後者ということで話を進める。

ビルドに必要なパッケージをインストールする

さて、systemdのビルドに必要なパッケージをインストールする。

デフォルトではソースパッケージをレポジトリから取ってくるようになっていないはずなので、これを有効にする。

$ sudo sed -i /etc/apt/sources.list -e 's/# deb-src/deb-src/'

そして、パッケージリストを更新する。ついでに、アップグレードもしておこう。

$ sudo apt update
$ sudo apt upgrade

systemdのパッケージビルドに必要なパッケージをインストールする

$ sudo apt -y install fakeroot
$ sudo apt -y build-dep systemd

どこからソースコードを取ってくるのが良いか?

Ubuntu 17.10で提供されているsystemd 234のパッケージ・ビルドに必要なものが整ったところで、ディストロ提供のsystemdのソースをダウンロードしてみるフリをする。

$ apt source --dry-run systemd
パッケージリストを読み込んでいます... 完了                                      
注意: 'systemd' パッケージは以下の場所の 'Git' バージョン制御システムで保守されています:
https://anonscm.debian.org/git/pkg-systemd/systemd.git
パッケージの最新の (まだリリースされていないかもしれない) 更新を取得するには、
git clone https://anonscm.debian.org/git/pkg-systemd/systemd.git
を使用してください。
4,948 kB のソースアーカイブを取得する必要があります。                                                 
ソース systemd を取得

我々がビルドしたいのは234ではなく235なので、注意に従うことにする*2

ソースコードを取得する

インストールされていなければ、gitをインストールする

$ sudo apt install git

任意の場所で指示通り、git cloneする

$ git clone https://anonscm.debian.org/git/pkg-systemd/systemd.git

一旦、クローンしたURLをブラウザで開いてみる。

pkg-systemd/systemd - systemd packaging

'Tag'の欄に'debian/235-2'というのが見える。今回はこれをビルド・インストールしてみる。

ここで、クローンしたgitのローカルレポジトリに移動する。

$ cd systemd

さっき確認したタグでチェックアウトする。メッセージが表示されるが、ビルドだけなら特に気にしなくて良い。

$ git checkout refs/tags/debian/235-2

次のコマンドでdebian/235-2と見えていればOKだ。

$ git branch

ビルドする

ここからは簡単なはずであった。先に挙げた記事にあるとおり、ビルドのコマンドを叩く。

$ dpkg-buildpackage -r -uc -b

うーん、エラーでコケる。

$ ./configure
[...]
Meson encountered an error in file meson.build, line 2387, column 8:
File src/test/test-helper.c does not exist.

これがヒントになる。gitで調べると

$ git status src/test/test-helper.c
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    src/test/test-helper.c

no changes added to commit (use "git add" and/or "git commit -a")

なぜか消えている。復活させる*3

$ git checkout src/test/test-helper.c

再度、ビルドを走らせる。

$ dpkg-buildpackage -r -uc -b

今度はビルドが走る。お茶を飲みながら待つ。

インストールする

ビルドが終わると、systemdディレクトリの1つ上の階層に*.debができているはずである。

$ cd ..
$ *.deb
-rw-r--r-- 1 popo popo  105316 11月  8 21:59 libnss-myhostname_235-2_amd64.deb
-rw-r--r-- 1 popo popo  174338 11月  8 21:59 libnss-mymachines_235-2_amd64.deb
-rw-r--r-- 1 popo popo  173768 11月  8 21:58 libnss-resolve_235-2_amd64.deb
-rw-r--r-- 1 popo popo  171718 11月  8 21:58 libnss-systemd_235-2_amd64.deb
-rw-r--r-- 1 popo popo  174736 11月  8 21:59 libpam-systemd_235-2_amd64.deb
-rw-r--r-- 1 popo popo  249148 11月  8 21:58 libsystemd-dev_235-2_amd64.deb
-rw-r--r-- 1 popo popo  267560 11月  8 21:58 libsystemd0_235-2_amd64.deb
-rw-r--r-- 1 popo popo   93654 11月  8 21:58 libudev-dev_235-2_amd64.deb
-rw-r--r-- 1 popo popo  125620 11月  8 21:58 libudev1_235-2_amd64.deb
-rw-r--r-- 1 popo popo  297980 11月  8 21:58 systemd-container_235-2_amd64.deb
-rw-r--r-- 1 popo popo  116260 11月  8 21:58 systemd-coredump_235-2_amd64.deb
-rw-r--r-- 1 popo popo  129242 11月  8 21:58 systemd-journal-remote_235-2_amd64.deb
-rw-r--r-- 1 popo popo   83696 11月  8 21:58 systemd-sysv_235-2_amd64.deb
-rw-r--r-- 1 popo popo 3111888 11月  8 21:58 systemd-tests_235-2_amd64.deb
-rw-r--r-- 1 popo popo 2933620 11月  8 21:58 systemd_235-2_amd64.deb
-rw-r--r-- 1 popo popo 1156966 11月  8 21:58 udev_235-2_amd64.deb

これらを全部インストールしようとする。

$ sudo dpkg -i *.deb
処理中にエラーが発生しました:
 systemd-coredump_235-2_amd64.deb

と出るが、普通*4に使う分には特に問題がないことは確認しているので、気にしない。

再起動する*5

$ sudo reboot

バージョンを確認する。

$ systemctl --version
systemd 235
+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN2 +IDN default-hierarchy=hybrid

IPアカウンティングで遊んでみる。

$ unset LANG
$ sudo systemd-run -p IPAccounting=yes --wait ping -c 5 localhost
Running as unit: run-u14.service
Finished with result: success
Main processes terminated with: code=exited/status=0
Service runtime: 4.096s
IP traffic received: 520B
IP traffic sent: 520B
$ journalctl -u run-u14.service 
-- Logs begin at Wed 2017-11-08 22:06:05 JST, end at Wed 2017-11-08 22:10:14 JST. --
Nov 08 22:10:10 systemd systemd[1]: Started /bin/ping -c 5 localhost.
Nov 08 22:10:10 systemd ping[931]: PING localhost(localhost (::1)) 56 data bytes
Nov 08 22:10:10 systemd ping[931]: 64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.035 ms
Nov 08 22:10:11 systemd ping[931]: 64 bytes from localhost (::1): icmp_seq=2 ttl=64 time=0.055 ms
Nov 08 22:10:12 systemd ping[931]: 64 bytes from localhost (::1): icmp_seq=3 ttl=64 time=0.068 ms
Nov 08 22:10:13 systemd ping[931]: 64 bytes from localhost (::1): icmp_seq=4 ttl=64 time=0.056 ms
Nov 08 22:10:14 systemd ping[931]: 64 bytes from localhost (::1): icmp_seq=5 ttl=64 time=0.057 ms
Nov 08 22:10:14 systemd ping[931]: --- localhost ping statistics ---
Nov 08 22:10:14 systemd ping[931]: 5 packets transmitted, 5 received, 0% packet loss, time 4092ms
Nov 08 22:10:14 systemd ping[931]: rtt min/avg/max/mdev = 0.035/0.054/0.068/0.011 ms
Nov 08 22:10:14 systemd systemd[1]: run-u14.service: Received 520B IP traffic, sent 520B IP traffic

ヒャッハー

追記(コケる件について)

エラーについて報告する必要があるかもしれないので、メモ。

1回目のdpkg-buildpackage -r -uc -bは最後に以下のようなダイイングメッセージを残して死ぬ。

Running command: /usr/bin/git --git-dir=/home/popo/systemd/.git ls-files :/*.[ch]
Died at /usr/share/perl5/Debian/Debhelper/Buildsystem/meson.pm line 61.
debian/rules:153: recipe for target 'override_dh_auto_configure' failed
make[1]: *** [override_dh_auto_configure] Error 2
make[1]: Leaving directory '/home/popo/systemd'
debian/rules:293: recipe for target 'binary' failed
make: *** [binary] Error 2
dpkg-buildpackage: error: fakeroot debian/rules binary gave error exit status 2

その時のgit statusは次の通り。

On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   man/journald.conf.xml
        modified:   man/rules/meson.build
        modified:   man/systemd-system.conf.xml
        modified:   meson.build
        modified:   po/POTFILES.in
        modified:   rules/50-udev-default.rules.in
        modified:   src/basic/cgroup-util.c
        modified:   src/basic/time-util.c
        modified:   src/core/cgroup.c
        modified:   src/core/dynamic-user.c
        modified:   src/core/dynamic-user.h
        modified:   src/core/execute.c
        modified:   src/core/locale-setup.c
        modified:   src/core/main.c
        modified:   src/core/mount-setup.c
        modified:   src/core/namespace.c
        modified:   src/core/system.conf
        modified:   src/core/unit.c
        modified:   src/fstab-generator/fstab-generator.c
        modified:   src/hostname/org.freedesktop.hostname1.service
        modified:   src/journal/journald-audit.c
        modified:   src/journal/journald-server.c
        modified:   src/journal/journald.conf
        modified:   src/libsystemd/sd-id128/sd-id128.c
        modified:   src/libsystemd/sd-login/sd-login.c
        modified:   src/locale/keymap-util.c
        modified:   src/locale/org.freedesktop.locale1.service
        modified:   src/login/logind-core.c
        modified:   src/login/org.freedesktop.login1.service
        modified:   src/login/pam_systemd.c
        modified:   src/shared/sleep-config.c
        modified:   src/systemctl/systemctl.c
        modified:   src/test/meson.build
        modified:   src/test/test-bpf.c
        modified:   src/test/test-cgroup-mask.c
        modified:   src/test/test-engine.c
        modified:   src/test/test-execute.c
        modified:   src/test/test-fs-util.c
        deleted:    src/test/test-helper.c
        modified:   src/test/test-helper.h
        modified:   src/test/test-path.c
        modified:   src/test/test-sched-prio.c
        modified:   src/test/test-unit-file.c
        modified:   src/test/test-unit-name.c
        modified:   src/timedate/org.freedesktop.timedate1.service
        modified:   src/timedate/timedated.c
        modified:   src/timesync/meson.build
        modified:   src/udev/udev-event.c
        modified:   src/udev/udev.conf
        modified:   sysctl.d/50-coredump.conf.in
        modified:   tmpfiles.d/legacy.conf
        modified:   tmpfiles.d/tmp.conf
        modified:   units/meson.build
        modified:   units/systemd-fsck-root.service.in
        modified:   units/systemd-fsck@.service.in
        modified:   units/systemd-logind.service.in
        modified:   units/user/graphical-session-pre.target

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        .pc/
        man/systemd-fsckd.service.xml
        src/fsckd/
        units/systemd-fsckd.service.in
        units/systemd-fsckd.socket

no changes added to commit (use "git add" and/or "git commit -a")

modifiedなのもよくわからないが、とりあえず、切り分けたところ、原因はdeletedになっているsrc/test/test-helper.cである。

本文の再掲となるが

$ ./configure
[...]
Meson encountered an error in file meson.build, line 2387, column 8:
File src/test/test-helper.c does not exist.

がヒントだった。

git checkout src/test/test-helper.cで復活させて、2回めのdpkg-buildpackage -r -uc -b後のgit statusは以下の通り

On branch master
Your branch is up-to-date with 'origin/master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        debian/.debhelper/
        debian/debhelper-build-stamp
        debian/files
        debian/install/
        debian/libnss-myhostname.substvars
        debian/libnss-myhostname/
        debian/libnss-mymachines.substvars
        debian/libnss-mymachines/
        debian/libnss-resolve.substvars
        debian/libnss-resolve/
        debian/libnss-systemd.substvars
        debian/libnss-systemd/
        debian/libpam-systemd.substvars
        debian/libpam-systemd/
        debian/libsystemd-dev.substvars
        debian/libsystemd-dev/
        debian/libsystemd0.substvars
        debian/libsystemd0/
        debian/libudev-dev.postinst.debhelper
        debian/libudev-dev.postrm.debhelper
        debian/libudev-dev.preinst.debhelper
        debian/libudev-dev.prerm.debhelper
        debian/libudev-dev.substvars
        debian/libudev-dev/
        debian/libudev1-udeb.substvars
        debian/libudev1-udeb/
        debian/libudev1.substvars
        debian/libudev1/
        debian/shlibs.local
        debian/systemd-container.postinst.debhelper
        debian/systemd-container.postrm.debhelper
        debian/systemd-container.preinst.debhelper
        debian/systemd-container.prerm.debhelper
        debian/systemd-container.substvars
        debian/systemd-container/
        debian/systemd-coredump.substvars
        debian/systemd-coredump/
        debian/systemd-journal-remote.substvars
        debian/systemd-journal-remote/
        debian/systemd-sysv.substvars
        debian/systemd-sysv/
        debian/systemd-tests.substvars
        debian/systemd-tests/
        debian/systemd.postinst.debhelper
        debian/systemd.postrm.debhelper
        debian/systemd.preinst.debhelper
        debian/systemd.prerm.debhelper
        debian/systemd.substvars
        debian/systemd/
        debian/udev-udeb.substvars
        debian/udev-udeb/
        debian/udev.postinst.debhelper
        debian/udev.postrm.debhelper
        debian/udev.preinst.debhelper
        debian/udev.prerm.debhelper
        debian/udev.substvars
        debian/udev/

nothing added to commit but untracked files present (use "git add" to track)

modifiedはきれいに消えてる。というか、戻しているというのが正確か。

察しの良い方はお気づきだろうが、origin/masterでも再現する。

*1:確認してはいない

*2:当初はアップストリームのsystemdをgit cloneしてビルドするつもりだった。systemd-234のdebianディレクトリをこれに差し込んで、ちょっとファイルいじったりして、なんとかビルドできないか試した。しかし、うまくいかなかった。アップストリームのものではないという意味で「新しいめのsystemd」である。

*3:コケる原因はわかったし、ビルドもできるが、バリバリのワークアラウンドで納得はいかない。パッケージングのことはほとんどわからない。debian/controlあたりに問題があるんだろうか。そして、どこに報告すれば良いんだろう。というか、unstableなものを使ってる自信はあるので、報告すべきことなのだろうか。

*4:普通って何

*5:virt-managerkvmとの組み合わせでVMを立ててやったら、落ちきらなかったので、強制的に再起動をかけた。

#きょうのsystemd : 一覧

きょうのsystemd

リンクなしは予定*1

レナートのブログ記事翻訳

ちゃんと訳したやつ

一応、レナート本人の許可は取ってから翻訳している。( #きょうのsystemd : レナートにブログ翻訳の許可を取り付けた - ぽぽの備忘録 )

The systemd for Administratorsシリーズ

  • 1: ブートアップを確かめる(Verifying Bootup)
  • 2: Which Service Owns Which Processes?
  • 3: How Do I Convert A SysV Init Script Into A systemd Service File?
  • 4: Killing Services
  • 5: The Three Levels of "Off"
  • 6: Changing Roots
  • 7: The Blame Game
  • 8: The New Configuration Files
  • 9: On /etc/sysconfig and /etc/default
  • 10: Instantiated Services
  • 11: Converting inetd Services
  • 12: Securing Your Services
  • 13: Log and Service Status
  • 14: The Self-Explanatory Boot
  • 15: Watchdogs
  • 16: Gettys on Serial Consoles (and Elsewhere)
  • 17: Using the Journal
  • 18: Managing Resources
  • 19: Detecting Virtualization
  • 20: Socket Activated Internet Services and OS Containers
  • 21: Container Integration

MLとかレナートのブログで集めたネタ

つまみぐいで訳してる感じのやつ

*1:予定は予定であって未定である

#きょうのsystemd: (翻訳)PID 1 を考え直す

レナートがsystemdの構想について語ったブログエントリーの翻訳。

原文はこちら:

Rethinking PID 1

投稿は2010年4月30日とかなり古いものではあるが、この時点で現在に至るまでのsystemdの設計思想がしっかり示されており、いまもなお色あせていないと思う。

レナートからブログエントリーの翻訳の許可を取り付けたので、せっかくだから一番最初に訳すのはこの記事にしたい。

  • 一気に訳すだけの気力と時間はないので、何回かに分けて訳す。
  • 記事を分割すると検索性が落ちるので、この記事に加筆修正していく。
  • 最初からきちんと訳せるという気はしておらず、誤字・脱字・誤訳がたぶんいっぱいある。
  • なので、見つけた方はご指摘ください。徐々にいいものにしていきたい。 *1

PID 1 を考え直す

十分な関わりがあったり、行間を読むのが得意であったりすれば、このブログ投稿が何に関することかはもう察しがついているであろう。 だが、そうであったとしても、このストーリーは面白いと思うかもしれない。 なので、1杯のコーヒーを手に取り、座って、何が来るのか読んでほしい。

このブログストーリーは長い。私としては長くてもこのストーリーを読むことしかおすすめできないのだが、1文でまとめるとこうである。 我々は新しいinitを試しており、それはとても楽しい。

コードはここ *2にある。ストーリーはここにある。

プロセス識別子 1

すべてのUnixシステムで、特別なプロセス識別子である1を与えられる1つのプロセスが存在する。 これは他のすべてのプロセスよりも前にカーネルによって起動され、他に親となるプロセスを持たない他のプロセスすべての親となるものである。 そのため、このプロセスは他のプロセスにはできないようなたくさんのことをすることができる。 そして、ブート時にユーザースペースを立ち上げ、維持するといった、他のプロセスが責任を負えないようなことに対しても責任を負うのである。

歴史的に、LinuxではPID 1として振る舞うソフトウェアは由緒あるsysvinitパッケージであった。一方で、sysvinitはかなりの時間、衰えを感じさせていた。 多くの代替案が提案されてきたが、そのうちのたった一つだけが実際に成功した。それはUpstartであり、これは今のところすべてのメジャーなディストリビューションにまで到達することができた。

すでに述べたとおり、initシステムの主たる責任はユーザースペースの立ち上げにある。 そして、優れたinitシステムはそれを速くおこなうものである。 不幸なことに、伝統的なSysV initシステムは特に速いというわけではない。

素早く、そして、効果的なブートアップには2つのことが重要になる

  • 小さくスタート
  • よりパラレルにスタート

これは何を意味するのか?小さくスタートは、より少ないサービスを起動させる、あるいは、サービスが実際に必要とされるまではその起動を遅らせることを意味する。 サービスの中には、すでに知られているように、早かれ遅かれ必要とされるものもある(syslog, D-Busシステムなどである)が、その他の多くはこのケースには当てはまらない。 例えば、bluetoothdはBluetoothドングルが実際にプラグインされるか、アプリケーションがD-Busインターフェイストークしたいと思わない限りは、実行されている必要はない。 同じことはプリンティングシステムにも言える。つまり、マシンに物理的にプリンターが接続されているか、アプリケーションが何かを印刷したいと思うまでは、CUPSといったプリンティングデーモンが起動している必要はないのである。 Avahiもそうである。もしマシンがネットワークに繋がれていない場合、アプリケーションがAPIを使いたいと思わない限りは、Avahiが起動している必要がない。 SSHにだって同じことが言える。誰もマシンに接続しようとしなければ、SSHが起動している必要はなく、最初に接続が来たときに起動させればよい。(そして、sshdがリスンしているかもしれないマシンの殆どは誰かが数カ月にたった1度といった感じで接続していると認めよう。)

よりパラレルにスタートは何かを起動させなければならないなら、スタートアップを(sysvinitがするように)シリアルでおこなうべきではなく、すべてを同時に実行すべきであるということを意味する。こうすることで、使用可能なCPUやディスクIOの帯域を織り交ぜて、全体としてのスタートアップ時間を最小化できる。

ハードウェアとソフトウェアが動的に変化する

モダンなシステム(特に汎用OS)は設定と利用においてかなり動的である。それらはモバイルであり、様々なアプリケーションが起動・停止し、様々なハードウェアが脱着される。 initシステムはサービスの維持に責任があり、ハードウェア・ソフトウェアの変化に注意を向ける必要がある。 initシステムはプログラムを実行したり、ハードウェアを有効にしたりするのに必要なサービスを動的に起動(そして、ときには停止)させる必要がある。

ブートアップをパラレル化しようとしているほとんどの流通しているシステムは、まだ、関連する様々なデーモンの起動を同期させている。 つまり、AvahiがD-Busを必要とするため、D-Busを最初に起動させ、D-Busが準備ができたとシグナルを送らないと、Avahiはスタートしない。 類似のことは他のサービスにも言える。 libvirtdとX11はHAL(ここではFedora13を念頭に置いているので、HALが推奨されないことは無視してほしい)を必要とする。なので、libvirtdとX11が起動する前に、HALが最初に起動する。 そして、libvirtdもまたAvahiを必要とするため、libvirtdはAvahiも待つ。 そして、これらすべてがsyslogを必要とするため、全部が全部syslogが完全にスタートアップし、初期化されるのを待つのである。他もこんな感じだ。

ソケットサービスをパラレル化する

この類のスタートアップの同期で、ブートプロセスの重要な部分がシリアル化してしまう。 もし、同期とシリアル化とのコストを取り除くことができたら、素晴らしくないか? あぁ、実際はできるんだよ。 そのためには、デーモン同士が正確に何を要求しあっているか、スタートアップが遅れるのはなぜかについて理解する必要がある。 伝統的なUnixデーモンにとって、これに対する一つの答えがある。それは、他のデーモンが提供するサービスのソケットが接続の準備ができるまで、デーモンが待っているからである。 ふつう、それはファイルシステムにあるAF_UNIXソケットだが、AF_INET[6]というのもありうる。 例えばD-Busのクライアントは/var/run/dbus/system_bus_socketに接続できるようになるまで待つ。 syslogのクライアントは/dev/logを待つ。 CUPSのクライアントは/var/run/cups/cups.sockを待つ。 NFSマウントは/var/run/rcpbind.sockとportmapper IPポートを待つ、など。 こう考えると、実はデーモンが待っている唯一のものはこれなのである。

さて、もしデーモンが待っているものがこれだけなのであれば、我々が、もっと早く接続できるようにこれらのソケットを管理し、デーモンの完全な起動ではなく、実際にはソケットの起動を待つようにすれば、ブート全体をスピードアップさせ、より多くのプロセスをパラレルに起動させることができる。 さて、これはどうやったら可能なのだろうか? 実際はUnixライクなシステムではかなり簡単に可能である。 つまり、我々はリスンするソケットを、デーモンが実際に起動するに作ること、そして、ソケットをexec()している中でデーモンに渡すだけということが可能なのである。 この方法で、我々はすべてのデーモンのためのすべてのソケットをinitシステムの1ステップで作成することができ、2つ目のステップですべてのデーモンを一度に起動させることができる。 もし、あるサービスがその他のサービスを必要としており、それが完全にスタートアップしていなかったとしても、それはまったく問題はない。 というのも、それで起こることは、接続は提供側のサービスにキューイングされ、クライアント側はその一つのリクエストで潜在的にそれ以上進めなくなるということなのである。 しかし、一つのクライアントがそれ以上進めなくなるだけであり、それは、一つのリクエストでしか起こらないのである。 また、サービス間の依存関係も、もはや、より適切にパラレル化されたスタートアップをするために設定される必要はなくなるのである。 つまり、一度にすべてのソケットを起動し、あるサービスが他のサービスを必要とするのであれば、そのサービスがそのソケットに接続できることを確実なものにすることができるからである。

これはこの後に続くことの核心となるものなので、違う言葉で例を使ってもう一度これを説明させてほしい。 syslogと様々なsyslogサービスを同時に起動させると、上で説明したようなスキームの場合、何が起こるかといえば、クライアントのメッセージは/dev/logソケットバッファーに追加されるということが起こるのである。 バッファーが満杯にならない限りは、クライアントは待つ必要はまったくないし、自身のスタートアップをすぐに続けることが可能なのである。 syslog自身がスタートアップを完了したらすぐに、syslogはすべてのメッセージをデキューし、それらを処理する。 もし、同期的なバスのリクエストが送られ、リプライが期待される場合、何が起こるかといえば、そのクライアントはそれ以上先に進めないということである。 ただ、それは、1つのクライアントでのみ起こることであり、D-Busがそれをキャッチアップし、処理するように管理されるまでのことである*3

基本的に、カーネルソケットバッファーを使えば、パラレル化を最大化することができる。そして、序列化と同期はカーネルによっておこなわれるので、ユーザースペースからの管理は不要なのである! そして、デーモンが実際に起動する前にソケットがすべて利用可能になるのであれば、依存関係の管理も不要なものに(あるいは、少なくとも二の次)となるのである。 つまり、もし、あるデーモンが他のデーモンを必要とするのであれば、そのデーモンはただ単に必要とするデーモンに接続すれば良いのである。 もし、必要としているデーモンがすでに起動しているのであれば、これはすぐに成功するであろう。 もしそうでなく、起動中であれば、そのデーモンが同期的なリクエストを発行しない限りは、必要とされているデーモンを待つ必要はないのである。 そして、必要とされているデーモンがまったく起動していなかったとしても、自動的にスポーンさせることができるのである。 他のデーモンを必要とするデーモンからみると、違いは存在せず、依存関係の管理はほとんど不必要か、すくなくとも二の次となる。そして、このすべては最適なパラレル化の状態であり、任意にオンデマンドでロードする状態にある。 その上、もっと堅牢でもある。というのも、実際のデーモンが一時的に(おそらくクラッシュしているために)利用不可能であったとしても、ソケットは利用可能な状態に保つからである。 実際、これを使って我々は簡単に、起動し、終了(ないしはクラッシュ)し、また起動させて、また終了する(以下続く)ようなデーモンを作ることができ、この起動終了のすべてをクライアントが気づかなかったり、リクエストを1つも取りこぼさないようにすることができるのである。

さて、そろそろ休憩の時間だろう。席を立って、コーヒーをカップに補充しよう。安心してくれ。面白いことはまだ続く。

だが、まず、いくつかの点についてクリアにしておこう。この類のロジックは新しいものなのか?いや、そうではない。 このように動作するもっとも有名なシステムはAppleのlaunchdシステムである。MacOSではソケットのリスンはすべてのデーモンから切り離され、launchdにより実行されている。 したがって、サービス自体はパラレルに起動することができ、それらの依存関係は設定する必要がない。 そしてこれは本当に巧妙なデザインで、MacOSの夢のような起動時間を実現することができる主な理由なのである。 私はlaunchd関係者が、自分たちが何をやっているかを説明しているこの動画を非常におすすめする。 残念ながら、このアイディアはApple陣営の外では決して人気を得ることはなかった。

このアイディア自体はlaunchdよりもさらに古い。 launchd以前の、由緒あるinetdはかなりこのように動作していた。ソケットは実際のサービスデーモンを起動し、ソケットのファイル記述子をexec()の中で渡すデーモンを中心につくられたのである。 しかし、inetdのフォーカスははっきりとローカルサービスにはなく、インターネットサービスにあった(後の再実装ではAF_UNIXソケットもサポートされたが)。 inetdはブートアップをパラレル化するツールでもなかったし、潜在的な依存関係を正しく把握するのに役立つものでもなかった。

TCPソケットに対し、inetdが主に使われていた方法は、入ってくる接続それぞれに対し、1つの新しいデーモンインスタンスをスポーンさせるというものであった。 これは、それぞれの接続に対し、1つの新しいプロセスがスポーンされ、初期化されるというものであり、ハイパフォーマンスのサーバーのためのレシピではなかった。 しかし、当初より利口なことに、inetdは別のモードもサポートしていた。このモードでは、最初の接続で1つのデーモンがスポーンし、その後、このインスタンスが残り続けて後続の接続の受付もするというものである(これはinetd.confにあるwaitおよびnowaitオプションで設定されるが、残念なことにまったく適切に説明されていないオプションである)。 接続ごとにデーモンがスタートすることで、おそらく、inetdは遅いという悪評を得てしまった。だが、これはまったくフェアではない。

バスサービスをパラレル化する

Linuxのモダンなデーモンは素のAF_UNIXソケットではなくD-Busを通じてサービスを提供する傾向がある。さて、ここで問題がある。これらのサービスに、伝統的なソケットサービスの場合と同一の、ブートロジックをパラレル化は適用可能なのだろうか? 答えはイエス、できる。D-Busはこのための正しいフックをすべて、すでに持っている。バスアクティベーションを使うことで、サービスは最初にアクセスされたときに起動させることができる。 ただ、D-Busサービスの提供側と利用側とを同時に起動させるのに我々はリクエストごとの同期が必要なのだが、このアクティベーションは最小限しか提供しない。 つまり、AvahiとCUPS(注: CUPSはmDNS/DNS-SDプリンターをブラウズするためにAvahiを利用する)を同時に起動させようとすると、我々は単純にこの2つを同時に起動させれば良い。 もし、バスアクティベーションロジックを通じて、CUPSがAvahiより速く起動すれば、Avahiが自身のサービス名を確立するまでD-Busに(訳注: CUPSの)リクエストをキューさせておけば良い。

なので、要点はこうである。 ソケットベースのサービスアクティベーションとバスベースのサービスアクティベーションは、ともに、さらなる同期なしに、すべてのデーモンをパラレルに起動させることを可能にする。 アクティベーションはサービスの遅延読み込みも可能にする。 もし、あるサービスがめったに使われないのであれば、ブート時にそれを起動させる代わりに、誰かがソケットやバス名に初めてアクセスしてきたときにそれを読み込めばよい。

これが素晴らしくなければ、一体何が素晴らしいというのだろう!

ファイルシステムのジョブをパラレル化する

現在のディストリビューションのブートプロセスをシリアル化したグラフ*4をみれば、デーモンのスタートアップよりも多くの同期ポイントがある。 もっとも目立つのがファイルシステムに関連するジョブである。つまり、マウント、fsck、クォーターである。 今まさに、ブート時には多くの時間を消費して、/etc/fstabに記述されているすべてのデバイスがデバイスツリーに現れ、fsckされ、マウントされ、(有効であれば)クォーターがチェックされるのをアイドリングして待つことになる。 これが完全に終わってからようやく、後続作業にとりかかり、実際のサービスをブートさせるのである。

これは改善できるのであろうか?できることがわかっている。Herald Hoyerは由緒あるautofsシステムをこれに使うというアイディアに至った。

connect()コールがサービスが他のサービスに関心があることを示すのとちょうど同じように、open()(ないしは類似のコール)で、サービスが特定のファイルやファイルシステムに関心があることを示す。 なので、どれだけパラレル化の度合いを改善するために、これらのアプリが探しているファイルシステムがマウントされていなかったり、すぐに利用できなかったりする場合は、これらのアプリに待たせるようにすることができる。 つまり、autofsのマウントポイントをセットアップし、その後、通常のブートアップで実行されることになっているファイルシステムfsckやクォーターを終えたら、本物のマウントとautofsのマウントとを置き換えるのである。 ファイルシステムがまだ準備できていなければ、アクセスはカーネルによりキューに入れられ、アクセスしてきたプロセスは待たされる。しかし、その1つのデーモンと1つのアクセスに限る。 この方法で、ファイルシステムが完全に利用可能になる前にでも、デーモンを起動させることが可能になる。デーモンは1つもファイルを失わないし、パラレル化も最大化される。

ファイルシステムジョブとサービスジョブのパラレル化は/には当てはまらない。サービスのバイナリーが通常はそこに格納されているからである。 しかし、/homeのようなファイルシステムについて言えば、通常は、/よりも大きかったり、ひょっとすると暗号化されていたり、リモートであるかもしれなかったり、通常のブートでデーモンがめったにアクセスしなかったりする。これ(訳注: パラレル化)によりブート時間をかなり改善できる。 言うまでもないかもしれないが、procfsやsysfsといった仮想のファイルシステムはautofsでマウントすべきではない。

initシステムにautofsを組み込むことを、少し危ういと思ったり、異様だと思ったり、もしかしたら、「狂った*5」ものの見方だと思ったりする読者がいても驚きはしない。 しかし、これをいじくり回してみて、広く言えることとしては、これは確実に、かなり正しいと感じるということだ。 autofsをここで使うということは、単純に、マウントポイントをそれが紐づくファイルシステムを提供する必要なしに、マウントポイントを作成できるということを意味する。 実際にはアクセスを遅らせているだけに過ぎない。 アプリケーションがautofsのファイルシステムにアクセスしようとしており、それを本物のファイルシステムに置き換えるのに時間がかかってしまえば、そのアプリケーションは割り込み可能なスリープ状態でハングすることになる。これは、例えばC-cなどで安全にキャンセルすることができることを意味している。 また、留意してほしいのは、任意のポイントで、マウントポイントが最終的にマウントすべきでない場合(たぶん、fsckが失敗したからだ)は、autofsに(ENOENTのような)クリーンなエラーコードを返すように伝えればいいということだ。 なので、私が言いたいと思っていることは、initシステムにautofsを組み込むことが、一見、冒険的に見えたとしても、我々の書いたコードはこのアイディアが--正しい理由で正しい方法で行われていれば--実際には驚くほどうまく動くことを示しているということだ。

さらに、これらはダイレクトなautofsマウントであるべきだということにも留意してほしい。これはアプリケーションから見ると、古典的なマウントポイントとautofsベースのマウントポイントには実質的な差異はほとんどないことを意味する。

最初のユーザーPIDを小さく保つ

MacOSのブートアップロジックから学んだもう1つのことはシェルスクリプトは有害であるということである。 シェルは速くもあり、遅くもある。 つまり、ハックするのは早いが、実行は遅いのである。 シェルが/bin/bashであろうが、(シェルスクリプトをより速く実行できるように書かれた)その他のシェルであれ、結局、このアプローチは遅くなる運命にある。 私のシステムで/etc/init.dにあるスクリプトgrepを少なくとも77回呼び出している。 awkは92回、cutは23回、sedは74回である。 これらのコマンドやその他のコマンドが呼び出されるたびにプロセスがスポーンし、ライブラリは検索され、i18nのようなものがいくつかセットアップされ、その他もろもろがおこなわれる。 そして、一部の例外を除き、ちょっとした文字列を操作する程度のことを行った後、プロセスは再び終了する。 もちろん、これは間違いなく、驚くほど遅い。 ほかでもないシェルがこのように物事を実行するのである。 これにくわえて、シェルスクリプトは非常に壊れやすくもあり、環境変数やその種のもの、予測と統制のしづらい要素によって、劇的にその挙動を変化させるものなのである。

なので、ブートプロセスからシェルスクリプトを取り除こう! それをする前に、我々はシェルスクリプトが現時点で正確には何のために使われているかを把握する必要がある。 あぁ、全体像としては、ほとんどの時間、シェルスクリプトがやっていることは実際、かなりつまらないことであろう。 スクリプトでやっていることのほとんどは取るに足らないセットアップであったり、サービスの取り壊しであったりで、Cで書き直すべきであり、別の実行ファイルにあるべきだったり、デーモン自身の中に移すべきだったり、あるいは、シンプルにinitシステムがやるべきだったりするのだ。

システムのブートアップからシェルスクリプトをすっかり取り除くというのは、近いうちにできるようなことではない。 これらをCで書き直すのには時間がかかるし、いくつかのケースではまったく意味がなかったりするだろうし、場合によってはシェルスクリプトがこれなしに済ませるには便利すぎるということもあるだろう。 だが、少なくとももっと目立たせないようにすることはできる。

ブートプロセスへのシェルスクリプトの蔓延具合を図る良きメトリックはシステムが完全にブートアップしたあとに、開始される最初のプロセスのPIDである。 ブートアップ後、ログインし、ターミナルを開き、echo $$をタイプしよう。 これを手元のLinuxシステムで試してみて、MacOSでやってみた結果と比べてみよう!(ここでヒント。こんな感じだ。我々が持っているテストシステムでやってみたところ、LinuxのPIDは1823なのに対しMacOSのPIDは514だ。)

プロセスを追跡し続ける

サービスを起動し、維持するシステムの中心的部分は、プロセスのベビーシッターを務めること、つまり、サービスを監視するということである。 サービスがシャットダウンすれば再起動させる。 サービスがクラッシュすれば、関連する情報を集め、管理者の手元に置き、その情報とABRTのようなクラッシュダンプシステムから利用可能なものとつなぎ合わせ、syslogやauditシステムのようなロギングシステムに入れておく。

このシステムはサービスを完全にシャットダウンさせる能力が必要である。 これは簡単に聞こえるが、考えているよりは難しいものである。 Unixの伝統ではプロセスはダブルフォークしており、その親の監督下を逃れることができていしまう。 古い親は、自身が確かに起動させたサービスと新しく生まれたプロセスとの関係を知ることはないのである。 例を挙げよう。 現在のところ、ダブルフォークされ、誤った挙動をしているCGIスクリプトApacheをシャットダウンしても停止されることはない。 さらに言えば、名前と目的を知らない限りはそのプロセスとApacheの関係を知ることさえできないのである。

さて、プロセスを追跡し続け、数え切れないほどフォークしているような場合でも、それらがベビーシッターから逃れることなく、1つのユニットとしてコントロールするにはどうしたらいいのだろうか?

様々な人々がこれに対する様々な解法を考えついた。 ここで詳細を触れることはしないが、少なくとも次のことは言わせてほしい。 ptraceないしはnetlinkコネクター(カーネルインターフェイスで、任意のプロセスがfork()exit()するたびにnetlinkメッセージを得られるようになるもの)は、いくらかの人々が調査し、生み出したものだが、これらをベースにしたアプローチは、美しくなく、そこまでスケーラブルではないとして批判されてきた。

では、どうすればよいか? あぁ、ちょっと前から、カーネルコントロールグループ("cgroups"としても知られている)を知っている*6。 基本的にcgroupではプロセスのグループの階層を作り出すことが可能になる。 階層が仮想ファイルシステムとして直接見えるため、簡単にアクセスすることができる。 グループの名前は基本的にそのファイルシステムディレクトリ名である。 特定のcgroupに属している、あるプロセスがfork()をすると、その子は同じグループのメンバーとなる。 その子が特権を与えられ、cgroupファイルシステムにアクセスできない限りは、そのグループから逃れることはできない。 もともと、cgroupがカーネルに導入されたのはコンテナーのためであった。 あるカーネルのサブシステムはあるグループのリソースに対し制限をかけることができるのだ。例えば、CPUやメモリーの使用量だ。 伝統的なリソース制限(setrlimit()で実装されたようなもの)は(ほとんど)プロセスごとである。 一方、cgroupはプロセスのグループ全体に対し制限をかけることができる。 また、cgroupはコンテナーというユースケース直接でなくても、制限をかけるのに便利である。 例えば、Apacheとその子すべてが使ってもよいメモリーやCPUの合計量に制限をかけることができる。 そして、誤った挙動をするCGIスクリプトは、簡単にフォークすることで、setrlimit()のリソースコントロールからもはや逃れることはできないのである。

コンテナーやリソース制限の強化に加え、cgroupはデーモンを追跡し続けるのに大変便利である。 cgroupのメンバーであることは確実に子に継承され、逃れることはできないのだ。 ある通知システムが利用できるのだが、これによってcgroupが空になったら監督者のプロセスは通知を受けることが可能になる。 あるプロセスのcgroupは/etc/$PIDを読むことで調べることができる。 したがって、cgroupはベビーシッターの目的でプロセスを追跡し続けるのにとても良い選択肢なのである*7

プロセスの実行環境をコントロールする

良きベビーシッターはデーモンの起動・終了・クラッシュを監督・コントロールするだけのものではない。 デーモンに優れた、最小限で、安全な実行環境をセットアップすることもある。

これはsetrlimit()によるリソース制限やユーザーIDやグループID、環境ブロックといった、プロセスの明らかなパラメータを設定することを意味するが、それだけでは終わらない。 Linuxカーネルによりユーザーと管理者にプロセスに対する多大なコントロール(いくつかは現時点ではめったに使われない)を享受する。 それぞれのプロセスに対し、CPUとIOスケジューラーのコントロール、ケーパビリティ―バウンディングセット*8、CPUアフィニティやもちろん、追加の制限をcgroup環境に設定することなどができる。

例えば、ioprio_set()IOPRIO_CLASS_IDLEを組み合わせるとlocateupdatedbによるシステムの対話への影響を最小限にする優れた方法である*9

加えて、ある高度なコントロールも非常に便利である。読み込み専用のbindマウントをベースにしたリードオンリーのファイルシステムのオーバーレイを設定するようなことである。 この方法だと、あるデーモンをすべて(あるいは一部)のファイルシステムをデーモンに対し、読み込み専用で見せることができ、それによりすべての書き込み要求に対しEROFSを返すことになる。 このような感じで、これはデーモンができることを制限するのに使うことができる。 これはやり方としては手軽なSELinuxポリシーシステムと似ている(ただ、SELinuxを確実に置き換えるものではない。そういう悪い考えは持たないでくれ、頼むから。)

最後に、ログを取ることはサービスを実行する上で重要な部分である。 理想的にはサービスが生み出す、僅かな出力のすべてをログに取るべきである。 そのため、initシステムは自分がスポーンさせたデーモンを起動直後からログを取る機能を提供し、stdout(訳注: 標準出力)やstderr(訳注: 標準エラー出力)をsyslogにつなぐ。 あるいは、いくつかのケースでは/dev/kmsgに繋ぐ。 ちなみに、/dev/kmsgは多くのケースで便利なsyslogの代替となるものだ(組み込みな人たちよ、よく聞くがいい!)。 特に、カーネルログバッファーが非常に巨大なすぐ使えるものとして設定されている場合は特にそうだ *10

Upstartの場合

最初に、強調しておきたいのは、私はUpstart*11のコードが本当に好きで、十分にコメントがついていて、中身を追いやすい。他のプロジェクトもここから学ぶべきものがある(自分も含めて。)

それはそうだが、私はUpstartの全体的なアプローチには同意できない。 だが、最初に少し、このプロジェクトについて説明しておこう。

Upstartはsysvinitとはコードを共有していないが、機能面では上位互換であり、よく知られたSysV initスクリプトとある程度は互換性がある。 Upstartの主な特徴はイベントベースのアプローチである。 プロセスの起動と停止はシステムに起こる「イベント」に紐付いており、「イベント」は多種多様なものがなりえる。例えば、ネットワークインターフェイスが有効になったり、他のソフトウェアが起動したりだ。

Upstartはこれらのイベントによりサービスをシリアル化している。 syslog-startイベントがトリガーされると、これはD-Busを開始することの指示として使われる。というのも、D-BusがSyslogを使えるようになったからだ。 そして、dbus-startedがトリガーされると、NetworkManagerが起動する。これはNetworkManagerが今やD-Busを使えるようになったからだ、という具合だ。

この方法で、存在し、管理者や開発者が理解する実際の論理的な依存関係のツリーがイベントやアクションのルールに翻訳、エンコードされるという人がいる。 すべての「AがBを必要とする」というルールは管理者や開発者によって「Bが起動したらAが起動する」プラス「Bが停止したらAも停止する」ものとして、理解されるのである。 ある点ではこれは確かに簡素化ではある。Upstart自身のコードにとっては特にそうだ。 しかし、この簡素化は実際には有害であると私は主張したい。 何と言っても最初に、論理的な依存関係の体系はどこへも行っておらず、Upstartファイルを書く人間がいまや依存関係を手動でこれらのイベントやアクションルール(実際には、それぞれの依存関係のための2つのルール)に翻訳しなければならないのである。 なので、コンピューターに依存関係をベースに何をすべきかを示させる代わりに、ユーザーが手動で依存関係を簡単なイベントやアクションルールに翻訳しなければならないのである。 また、依存関係の情報は決してエンコードされないため、実行時に利用することはできず、これは事実上、なぜ、これが起きたのか、例えば、なぜ、Bが起動したら、Aが起動したのか、を調べようとする管理者には何もわからないということを意味する。

さらに、イベントロジックはすべての依存関係を、つま先から頭の先までぐるっと一回りすることになる。仕事の量を最小化する(これはこのブログストーリーの冒頭で指摘したとおり、良きinitシステムがフォーカスすべきものである)代わりに、実際には作業中にすべき仕事の量を最大化することになる。 他の言い方をすれば、明確なゴールを持ち、そのゴールに到達するために本当に必要なことだけをする代わりに、Upstartは一歩進んでしまうと、その後、それに続くすべてのステップを歩んでしまうのである*12

より簡単に説明してみよう。 ユーザーがD-Busを起動させただけという事実は、決してNetworkManagerも起動すべきという指示にはならない(しかし、これがUpstartのやっていることである)。 逆に考えてみよう。ユーザーがNetworkManagerを必要としたときに、これが明確な指示となって、D-Busも起動すべきだ(これがほとんどのユーザーが本当に期待するものではないかい?)。

良きinitシステムは必要なものだけを起動すべきで、オンデマンドであるべきだ。遅延かパラレル化と先行だ。 しかし、initシステムは必要以上のものを起動させるべきではない。とくに、そのサービスを使えるようなもので、インストールされているすべてを起動させてはいけない。

最後に、私はイベントロジックの実際の利便性を見出すことができなかった。 Upstartで見せられるほとんどのイベントが実際には本来は即座のものではなく、持続性があるこように私には思えた。 サービスが起動し、実行中になり、停止する。 サービスがプラグインされ、利用可能になり、またプラグアウトされる。 マウントポイントがマウントされるプロセスにあり、完全にマウントされ、アンマウントの途上にある。 電源プラグが差し込まれ、AC電源でシステムが稼働し、電源プラグが抜かれる。 initシステムやプロセスの監督者が取り扱うべきイベントのほんの少数だけが、即座のものであり、それらのほとんどは、起動、実行状態、停止のタプルである。 Upstartに戻ると、こういった情報はUpstartでは利用できないのだ。なぜなら、Upstartは単一のイベントにフォーカスしており、持続性のある依存関係は無視してしまうからである。

さて、気づいてしまったのだが、上で私が指摘したような問題のいくつかは、最近のUpstartの変化でいくらか和らげられている。 とくに、Upstartルールファイルの状況ベースのシンタックス、例えば、start on local-filesystems and net-device-up IFACE=loといったものだ。 だが、私からすれば、これはほとんど、コアのデザインに弱点を抱えるシステムを直そうとする試みのように見えてしまう。

いくつかの選択に疑問があるかもしれない(上記参照)としても、そして、実際に多くの機会を逸している(これも上記参照)としても、その他は、Upstartにデーモンの子守としてOKを出せる。

sysvinitを除けば、他にinitシステムとして挙げられるのはUpstartとlaunchdだ。 initシステムの多くはUpstartやsysvinit以上のものを打ち出せていない。 この他、最も興味深い競争相手はSolaris SMFである。これはサービス間の適切な依存関係をサポートする。 しかし、多くの点でSolaris SMFはとても複雑であり、言わせてもらえば、XMLを過度に使っていて、知られていることへの新しい術語がちょっとアカデミックである。 これはcontractシステムといったSolarisに固有な機能に密接に結びついてもいる。

すべてをうまくやる: systemd

おっと、もう1回、ちょっと小休止を挟むのにいい時間になったようだ。というのも、ここまでで、私が考える良きPID 1が何をすべきで、現時点で最も使われているinitシステムが何をしているかを説明できたことだし、そろそろメインディシュに移るころだからだ。 さて、席を立って、もう一度、コーヒーカップをいっぱいにしよう。 それだけの価値はあるはずだ。

読者はこう思っているかもしれない。 ここまでで理想的なinitシステムの必要条件と機能として提案してきたようなものが、実際には、systemdと呼ばれる(まだ実験段階の)initシステムにはもうあるのだろと。これこそが、私がここでアナウンスしたいものだ。 再度になるが、コードはここにある*13。 そして、ここではその機能について簡単な紹介とその背後にある理由付けだ。

systemdはシステム全体の起動と監督をおこなう(だから、この名前なんだ)。 systemdはここまでで指摘してきたような機能プラスアルファを実装している。 systemdはユニットという概念を軸につくられている。 ユニットは名前とタイプを持つ。 ユニットの設定は通常はシステムより直接読み込まれるため、ユニット名は実際にはファイル名である。 例えばこうだ。 avahi.serviceというユニットは同名の設定ファイルから読み込まれ、もちろんAvahiデーモンをカプセル化したユニットになる。 ユニットにはいくつかの種類がある。

  1. service: ユニットの中でも明確な種類のユニットである。 デーモンは起動、停止、再起動、リロードすることができる。 SysVとの互換性のため、我々は自分たちのサービスの設定ファイルだけでなく、古典的なSysv initのスクリプトも読み込むことができる。 とりわけ、存在すれば、LSBヘッダーをパースする。 したがって、/etc/init.dは別の設定ファイルの置き場所以上のものである。

  2. socket: このユニットはファイルシステム上やインターネット上のソケットをカプセル化する。 現時点ではAF_INETAF_INET6AF_UNIXソケットをサポートし、種類としてはストリーム、データグラム、シーケンシャルパケットをサポートしている。 また、古典的なFIFOも転送としてサポートしている。 それぞれのsocketユニットは対応するserviceユニットを持っており、ソケットやFIFOに最初の接続が来ると、対応するserviceユニットが起動される。 例えば、nscd.socketは、接続があるとnscd.serviceを起動させる。

  3. device: このユニットはLinuxのデバイスツリーにあるデバイスカプセル化する。 デバイスがudevのruleを通じて、このユニットにマークされると、systemdではdeviceユニットとして見ることができるようになる。 udevに寄って設定されたプロパティーはデバイスユニットの依存関係を設定するための設定情報として利用することができる。

  4. mount: このユニットはファイルシステム階層のマウントポイントをカプセル化する。 systemdはすべてのマウントポイントを監視し、マウントポイントが行ったり来たりする様子を把握する。 また、マウントポイントをマウント・アンマウントするのにも使える。 /etc/fstabはここではこれあのマウントポイントに関する追加の設定情報として使われる。 これはSysVのinitスクリプトserviceユニットの追加の設定情報として使われるのと似ている。

5.automount: この種類のユニットはファイルシステム階層のautomountポイントをカプセル化する。 それぞれのautomountユニットは対応するmountユニットを持ち、mountユニットはautomountディレクトリにアクセスがあるとすぐに起動(=マウント)される。

  1. target: このユニットタイプはユニットの論理的なグループとして使われる。 それ自身では一切何もしない代わりに、targetユニットは単純に他のユニットを参照し、参照されたユニットを一緒にコントロールすることができる。 例を上げるとこうだ。 multi-user.targetは基本的に、古典的なSysVシステムではランレベル5の役割を果たすユニットである。 bluetooth.targetBluetoothドングルが利用可能になるとすぐに要求されるターゲットで、Bluetooth関係のサービスを制御する。 これによって制御されるサービスはこういうときでなければ起動されないようなサービスで、bluetoothdobexdといったものだ。

  2. snapshot: targetユニットと同様に、snapshotユニットも自分自身では何もせず、他のユニットを参照することだけを目的とするユニットである。 スナップショットはinitシステムのすべてのサービスやユニットの状態を保存し、ロールバックするのに使える。 主として2つのユースケースが考えられている。 1つは、「エマージェンシーシェル」のような特殊な状態に一時的にユーザーが入ることを許可する場合だ。 現在のサービスを停止し、元の状態に戻るための簡単な方法を用意し、一時的に引き下げたすべてのサービスを再び引き上げるのである。 もう1つはシステムのサスペンドである。 未だに多くのサービスが正しくシステムのサスペンドを取り扱えないが、サスペンドする前にこれらをシャットダウンし、後から復帰させてしまうのはたいていベターな考えだ。

これらすべてのユニットには互いに依存関係がある(依存関係はポジティブなのもネガティブなのもある。例えば、「必要とする(Requires)」と「対立する(Conflicts)」だ)。 デバイス(訳注: ユニット)にはサービスに対する依存関係をもたせることができる。これは、デバイスが利用可能になれば、すぐにあるサービスが起動することを意味する。 マウント(訳注: ユニット)には、自身がそこからマウントされているデバイスに対する明示的な依存関係を得ることになる。 マウント(訳注: ユニット)はまた、それらのプレフィックスであるマウント(訳注: ユニット)に対し明示的な依存関係を持つことになる(つまり、/home/lennartというマウントは追加で/homeのマウントに明示的に依存関係を得るのだ)。

その他の機能を短いリストに記す

  1. スポーンされたプロセスに対して、それぞれ管理することができる。 環境、リソース、制限、ワーキングディレクトリ・ルートディレクトリ、umask、OOMキラー調整、niceレベル、IOのクラスと優先度、CPUのポリシと優先度、CPUアフィニティ、timer slack*14、ユーザーID、グループID、補助グループID、読み込み可能・書き込み可能・アクセス不可のそれぞれのディレクトリ、共有・プライベート・スレーブそれぞれのマウントフラグ、ケーパビリティ・バウンディングセット*15、セキュアビット、CPUスケジューラーのフォークの再設定、プライベートな/tmp名前空間、様々なサブシステムへのcgroup。 また、簡単にサービスの標準入力、標準出力、標準エラー出力をsyslogや/dev/kmsgや任意のTTYに接続することができる・ 入力にTTYを接続すると、systemdは必要に応じてそれを待ったり、強制したりし、プロセスが排他的なアクセスを得られるようにする。

  2. 実行されるプロセスはすべて自分自身のcgroupを獲得し(現時点のデフォルトではdebugサブシステムにおいて。というのも、このサブシステムは他では使用されておらず、プロセスの最も基本的なグループ化以上でもなければ以下でもないから*16である)、systemdを設定し、外部ですでに設定されたcgroupにサービスを置くことができる。例えば、libcgroupユーティリティー経由で(外部でcgroupを設定する)といった具合である。

  3. ネイティブな設定ファイルの構文はよく知られている.desktopファイルのそれに厳格に従っている。 これは簡単な構文で、すでに多くのソフトウェアフレームワークでこれのパーサーが存在する。 また、これのおかげで、サービスの説明やその他類似のもので、国際化のための既存のツールを利用することができるようになる。 管理者や開発者は新しい構文を学ぶ必要はないのである。

  4. すでに述べたとおり、我々はSysV initスクリプトに対するご完成を提供する。 我々はLSBおよびRed Hat chkconfigヘッダーが利用可能であれば、それを駆使する。 もし、これらが利用できなければ、/etc/rc.dの起動の優先順といったその他の利用可能な情報で最善を尽くすようにする。 これらのinitスクリプトは単純に設定の異なるソースとして考えられており、そのため、systemdサービスへの簡単なアップグレードパスを提供することができる。 必要に応じて、古典的なPIDファイルをサービスがデーモンのメインプロセスのIDを特定するのに読み込むことも可能である。 我々はLSB initスクリプトヘッダーからの依存関係の情報を利用し、これらをネイティブなsytemdの依存関係に翻訳することに注意してほしい。 さらに、Upstartはこれらの情報を取得し、利用できないことに注意してほしい。 したがって、素のUpstartシステムと大半のLSB SysV initスクリプトとの組み合わせでは、パラレル化はできない一方、systemdを動かしている似たようなシステムではそれができるのである。 事実、Upstartにとって、SysVスクリプトはまとまって、1つのジョブとなり、実行されるため、これらを別個に扱うことができない。 再び、systemdと比べると、systemdではSysV initスクリプトはただ別の設定元であり、これらはすべて、その他のネイティブなsystemdのサービスと同じように別個に扱われ、制御される。

  5. 同様に、我々は既存の/etc/fstab設定ファイルも読み込むことができ、これを、ただの別の設定元として考慮する。 commnet=というfstabオプションを使うことで、/etc/fstabエントリーをsystemd制御下のautomountのポイントとしてマークすることさえできる。

  6. 複数の設定素で同じユニットが設定されている場合(例えば、/etc/systemd/system/avahi.service/etc/init.d/avahiとが存在する場合)、ネイティブな設定が常に上位となり、レガシーなフォーマットは無視される。 これにより、簡単なアップグレードパスを用意でき、パッケージはSysV initスクリプトとsystemdのサービスファイルとの双方をしばらくの間、持つことができる。

  7. 我々は簡単なテンプレートおよびインスタンスのメカニズムをサポートする。 例えば、6つのgettyのための6つの設定ファイルを用意する代わりに、我々は1つのgetty@.serviceファイルを用意するだけで良い。 これにより、getty@tty2.serviceといった具合に、インスタンス化することができる。 インターフェイス部分さえも依存関係の表現により継承することができる。 例えば、eth0という文字列はワイルドカードで扱いながら、dhcpd@eth0.serviceというサービスはavahi-autopid@eth0.serviceを制御するようにエンコードすることが簡単にできる。

  8. ソケットアクティベーションにより我々は伝統的なinetdモードとの完全な互換性をサポートする。これは、launchdのソケットアクティベーションを真似しようとするもので、新しいサービスには推奨されている非常に簡単なモードに加えてサポートされるものである。 inetdモードはソケットを起動済みのデーモンに渡すことだけができるが、ネイティブモードでは任意の数のファイル記述子を渡すことができる。 また、我々は接続ごとに1つのインスタンスをサポートする。これは、すべての接続に対し1つのインスタンスというモードに加えてサポートされる。 前者では、接続パラメーターに従って、デーモンがcgroupを名付け、これのために、既述のテンプレートロジックを利用する。 例えば、sshd.socketsshd@192.168.0.1-4711-192.168.0.2-22.servicesshd@.service/192.168.0.1-4711-192.168.0.2-22というcgroupでスポーンさせるかもしれない(つまり、IPアドレスとポート番号がインスタンス名に使われるのである。AF_UNIXソケットに対してはPIDと接続してきたクライアントのユーザーIDを利用する)。 これにより、管理者はデーモンの様々なインスタンスを識別子、その実行を別個に制御するための素晴らしい方法を手に入れる。 ネイティブなソケットを渡すモードはアプリケーションで非常に簡単に実装できる。 $LISTEN_FDSが設定されていると、これには渡されたソケットの番号が含まれており、デーモンは.serviceファイルに一覧でソートされているとおりに、ソケットを見つけてくる。 このリストはファイル記述子3から始まる(うまく書かれたデーモンは自身が受け取るソケットが1つ以上の場合に、fstat()getsockname()も、ソケットを識別するのに使うことができる)。 加えて、$LISTEN_PIDをファイル記述子を受け取るデーモンのPIDに設定することもできる。というのも、環境変数は通常、サブプロセスにより継承されるものであり、(訳注: プロセスの親子関係の)鎖を下るほどプロセスを混乱させるものとなるためである。 このソケットを渡すロジックをデーモンで実装するのが非常に簡単であったとしても、ソケット渡しをどうすべきかを示しているBSDライセンスのリファレンス実装をサポートする。 我々はすでに既存のデーモンのいくつかを新しいスキームへポートしている。

  9. 我々は/dev/initctlとの互換性をある程度は提供する。 この互換性は実際にはFIFOによりアクティブ化されるサービスとして実装される。 これはレガシーな要求をD-Busの要求へと単純に変換するものである。 事実上、これは、Upstartsysvinitの古いshutdownpoweroff、あるいは類似のコマンドをsystemdでも動かし続けられるということを意味する。

  10. utmpwtmpとの互換性も提供する。 utmpwtmpのお粗末さを考えると、妥当な程度以上のものになるかもしれない。

  11. systemdはユニット間のいくつかの依存関係をサポートする AfterBeforeを使って、ユニットがどのようにアクティブになるかを決めることができる。 これは完全にRequiresWantsと直行している。この2つは必須ないしは任意のポジティブな要件の依存関係を表現するものである。 そして、Conflictsはネガティブな要件の依存関係を表現する。 最後には、これらはあまり使われないが、さらに3つの依存関係がある。

  12. systemdは最小限のトランザクションシステムを持っている。 意味するところはこうだ。 あるユニットが起動ないしはシャットダウンを要求された場合、一時的なトランザクションにそれとそのすべての依存関係が追加される。 そして、そのトランザクションが一貫性のあるものか(つまり、すべてのユニットのAfterBeforeを順番付けることが循環しないかどうか)を検証する。 そうでなければ、systemdはこれを修復しようとし、ループを除くかもしれない不必要なジョブをトランザクションから除く。 また、systemdはトランザクションの中の不必要なジョブで実行中のサービスを止めるようなジョブを抑制しようとする。 不必要なジョブとは、オリジナルのリクエストには直接含まれていないが、Wantsタイプの依存関係により制御されているようなジョブのことである。 最後に、トランザクションのジョブがすでにキューに入れられているジョブと矛盾しないかどうか、そのときに任意にトランザクションを中止できるかをチェックする。 すべてがうまくいき、トランザクションには一貫性があって、影響が最小限に抑えられれば、そのトランザクションはすでに進行中のジョブとマージされ、実行キューに追加される。 事実上、これが意味するところは、我々は、要求された操作が実行する前に、これが妥当なものであるかを検証し、可能であれば修復し、本当に実行できない場合にのみ、失敗させるようにすることである。

  13. スポーンし、監督するすべてのプロセスのPIDと終了ステータスに加え、開始・停止時間も記録する。 このデータは、abrtdやauditd、syslogのデータを持つデーモンとの橋渡しに、使うことができる。 こう考えてほしい。クラッシュしたデーモンをハイライトして見せてくれるUIであり、syslogやabrtsic.)、auditdそれぞれのUIへ簡単にナビゲートしてくれるUIへだ。 syslogやabrt[sic.]やauditdは特定の進行の際に、このデーモンからデータを生成したり、このデーモンのためにデータを生成するだろう。

  14. initプロセス自身を任意のタイミングで再実行することをサポートする。 デーモンの状態は再実行前にシリアル化され、事後にシリアル化を解除される。 この方法で、我々はinitrdデーモンから最終のデーモンまでをハンドオーバーし、initシステムのアップグレードを容易にする簡単な方法を提供する。 開かれたソケットとautofsマウントは適切にシリアル化される。 これは、クライアントがinitシステム自身の再実行に気づきすらしない方法で、ソケットやマウントをいつでも接続可能な状態に保ち続けるためである。 また、システムサービスの大部分はともかくcgroupの仮想ファイルシステムエンコードされていることで、シリアル化のデータにアクセスすることなく再開することさえ可能である。 再実行のコードパスはinitシステムの設定をリロードするコードパスと実際にはほとんど同じであり、このコードパスでは、再実行(これはおそらく、(訳注: リロードに比べれば)トリガーされることは少ないだろう)がリロード(こちらのほうがおそらくより一般的)として同様のテストを受けられることを保証するものである。

  15. ブートプロセスからシェルスクリプトを取り除く作業をはじめたことで、基本的なシステムのセットアップの部分をCで再コーディングし、これを直接systemdへと移している。 この中には、APIファイルシステムのマウント(つまり、/proc/sys/devといった仮想ファイルシステム)やホスト名の設定がある。

  16. サーバーの状態はD-Busを通じて、検証可能*17・制御可能である。これはまだ完璧ではないが、かなりの広がりを持つ。

  17. ソケットベースやバス名ベースのアクティベーションを我々は強調したく、それゆえ、ソケットとサービスの間の依存関係をサポートしている一方、伝統的なサービス間の依存関係もサポートしている。 我々はこういったサービスが自身の準備が整ったことを知らせる方法をいくつもサポートしている。 設定したサービス名が現れるまでバスを監視することに加え、フォークし、起動プロセスを終了させること(つまり、伝統的なdaemonize()の振る舞い)で(訳注: サービスの準備が整ったことを知らせることができる)。

  18. systemdによりプロセスがスポーンされるたびに確認を求めるインタラクティブモードがある。 これは、カーネルコマンドラインsystemd.confirm_spawn=1を渡すことで有効にすることができる。

  19. systemd.default=というカーネルコマンドラインパラメーターにより起動時にどのユニットをsystemdが開始するかを指定することができる。 通常、ここでは、multi-user.targetのようなものを指定するが、ターゲットの代わりに単一のサービスを指定することも可能である。 例えば、提供されたままの状態で、我々はemergency.serviceというサービスを同梱している。 これは、使い勝手という点ではinit=/bin/bashと似たものであるが、実際にはinitシステムを動かしているという利点があり、それゆえ、緊急のシェルから完全なシステムを起動させるというオプションも提供している。

  20. サービスを起動、停止、検証することを可能にする最小限のUIがある。 完成にはほどとおいが、デバッグツールとしては便利なものである。 これはValaで書かれており(イェイ!)、systemadmという名前でいく。

systemdはLinux固有の機能を多く使っており、POSIXに閉じこもっていない点は注意すべきでだろう。 他のオペレーティングシステムへの移植性のためにデザインされているシステムでは提供できないような機能の多くを解き放っている。

状態

上で挙げたすべての機能はすでに実装されている。 今からでも、すでにsystemdはUpstartやsysvinitを気軽に入れ替えるものとして使うことができる(少なくとも、多すぎるほどのネイティブなUpstartのサービスがまだない限りは。幸いなことに、多くのディストリビューションで、ネイティブなUpstartサービスは多すぎるというほどには多くない)。

しかし、テストは最小限であり、バージョン番号は現時点では象徴的な0である。 現在の状態で実行すれば、壊れることは予期してほしい。 とは言うものの、かなり安定しているし、我々の中にはすでに通常の開発環境をsystemdで動かしているものもいる(VMだけと比べて)。 特に、もし、これを我々開発者が使っていないディストリビューションで使えば、どうなるかはわからない。

どこへ向かうのか?

上で説明したような機能は確かにすでに包括的である。 だが、我々は取り掛かったばかりである。 私はビッグマウスは好まないのだが、これは、我々が推し進める方向の短い概要に過ぎない。

我々は少なくともユニットの種類をあと2つは追加したいと思っている。 swapスワップバイスを、すでにマウントを制御しているのと同じ方法で制御するのに使えるものだ。 つまり、デバイスツリーのデバイスの自動的な依存関係により、それらがアクティブになるのと同じような感じだ。 timerではcronと似たような機能を提供したい。 つまり、時間イベントをベースにサービスを開始させる。 ここで対象となっているのは単調な時計(monotonic clock)と経過時間・カレンダーイベントとの双方である(つまり、前者が「最後に実行されてから5時間後に開始」で、後者が「毎週月曜午前5時に開始」といったものだ)。

しかし、より重要なのことに、我々の計画では、systemdで実験するのは、起動時間の最適化だけではなく、systemdを理想のセッションマネージャーとすること、つまり、gnome-sessionkdeinit、その他類似のデーモンを置き換える(ないしは可能であれば修正するだけ)ことも目的の1つである。 セッションマネージャーとinitシステムの抱える問題は非常に似通っている。 迅速な起動が必須であり、プロセスのベビーシッターを務めることだ。 したがって、それぞれの使用目的のために同じコードを使うということが自ずと考えとして出てくる。 Appleはこれをわかっていて、launchdでこれをやった。 我々もそうすべきだ。 ソケットとバスのアクティベーションとパラレル化は、セッションサービスとシステムサービスとが平等に恩恵を受けることができるものである。

これらの機能のうち3つすべてがすでに部分的に現在のコードベースで利用できること、しかし、完璧ではないことはおそらく書いておくべきだろう。 例えば、すでに、一般ユーザーとしてsystemdは十分に動き、systemdはそのように動いていることを検出でき、最初から、このモードをサポートし、核心である*18。(これは例外的にデバッグにも便利なのである!これはシステムをsystemdでブートするようにしなくてもうまく動くのである。)

しかし、この仕事を終わらせる前に、カーネルや他の場所で修正すべきことがいくつかあるのである。 我々はスワップのステータス変更の通知をカーネルから受け取るようにする必要がある。これは、すでに我々がマウントの変更を見ることができるようになっているのと同じようなものだ。 CLOCK_REALTIMECLOCK_MONOTONICに対してジャンプしたときに通知を受け取りたい。 通常のプロセスがinit-likeなパワーの一部を得られるようにしたい。 ユーザーのソケットを置ける、十分に定義された場所が必要だ。 これらの問題点で本当にsystemdにとって必要なものはないが、きっと物事は良くなるだろう。

動いているこれが見たいかい?

現時点では、tarballによるリリースはないが、我々のレポジトリからコードをチェックアウトするのが簡単だろう。 さらに、使い始めようと思えば、ここにあるユニット設定ファイルのtarball*19を使えば、これ以外に変更を加えていないFedora 13のシステムでsystemdを動かすことができる。 今のところ、提供できるようなRPMはない。

より簡単な方法はこのFedora 13のqemuイメージ*20を使う方法である。これはsystemdを動かすように準備ができている。 grubのメニューでシステムをUpstartでブートさせるか、systemdでブートさせるかを選ぶことができる。 このシステムは最小限の修正しかされていないことに注意してほしい。 サーバーの情報は既存のSysV initスクリプトからしか読み出していない。 したがって、ここまでに書いたような、完全なソケットないしはバスによるパラレル化の恩恵は受けていない。 しかし、LSBヘッダーからパラレル化のヒントを解釈し、Upstartのシステムより速く起動させることができる。Upstartは現時点のFedoraではいかなるパラレル化もしていない。 このイメージでは、カーネルログバッファー(これはdmesgでアクセスできる)に加えて、デバッグ情報はシリアルコンソールに書き出されるように設定されている。 仮想シリアル端末で設定されたqemuを実行したくなるかもしれない。 すべてのパスワードはsystemdに設定されている。

qemuイメージをダウンロードしてブートさせるよりもさらに簡単なのは小奇麗なスクリーンショットを見ることだ。 initシステムは通常、ユーザーインターフェイスの下に十分に隠されているため、systemadmpsのショットを数枚見せる。 f:id:popo1897:20171114212714p:plain

これはsystemadmが読み込まれたすべてのユニットを表示しているところで、gettyインスタンスの1つの詳細な情報を下に表示している。

f:id:popo1897:20171114212822p:plain

これはps xaf -eo pid,user,args,cgroupの出力の抜粋で、プロセスがそれらのサービスのcgroupにどれだけきちんとソートされているかを示している。 (4つ目の列はcgroupでdebug: という接頭辞が表示されているのは、先に述べたとおrい、我々がsystemd用にデバッグのcgroupコントローラーを使っているからである。これは一時的なものに過ぎない。)

この2枚のスクリーンショットはどちらも、Fedora 13のLive CDインストールを最小限に修正したものであることに注意してほしい。 ここでは、サービスは既存のinitスクリプトからのみロードされている。 したがって、既存のサービスのためにソケットやバスのアクティベーションは使われていない。

残念だが、現時点では、起動時間に関するブートチャートやハードデータはない。 デフォルトのFedoraインストールのすべてのサービスを完全にパラレル化したらすぐに公開するつもりだ。 なので、我々は読者がsystemdのアプローチをベンチマークすることを歓迎するし、我々自身のベンチマークデータを提供することもする。

さて、おそらく、みんな、これが気になってしつこく聞くだろうから、2つの数字を伝えておこう。 だが、これらは完全に非科学的なものだ。 というのも、VM(シングルCPU)で測ったものや、私の時計のストップウォッチで測ったものだからだ。 Feodra 13はUpstartではブートに27秒かかったが、systemdは24秒だった(これは、grubからgdmまでの時間で、同じシステム、同じ設定、2回の起動のうち短い方の値で、このうち1つはもう1つのすぐ後に続いた((Upstartとsystemdとで2回ずつブートさせたが、それぞれの2回はそんなに差がなかったと言いたいのだろう)))。

しかし、これは、initスクリプトからパースされたLSB依存関係情報をパラレル化に利用しただけで到達できたスピードアップ効果以外のことは示していないことに注意してほしい。 ソケットないしはバスによるアクティベーションはこれには利用されていない。 したがって、これらの数字は上で述べたようなアイディアを評価するには不適切なのだ。 また、systemdはデバッグ用の詳細出力をシリアルコンソールに出すように設定されている。 だから、再度となるが、このベンチマークデータはほとんど価値がない。

デーモンを書く

systemdで使われる理想的なデーモンは伝統的に行われてきたことと、数個の違いしかない。 あとで、もっと長いガイドを出して、systemdで使われるデーモンをどう書くべきかを説明、提案するつもりではある。 基本的にはデーモン開発者にとって物事は簡単になる。

  • デーモンの作成者はプロセスをフォークやダブルフォークをするのではなく、systemdが起動させる最初のプロセスからイベントのループを始めること。 また、setsid()は呼び出さないこと。

  • デーモン自身でユーザー特権をドロップさせず、これをsystemdに任せ、systemdのサービス設定ファイルの中で設定すること。 (例外はある。例えば、いくつかのデーモンにとって、特権の昇格を必要とする初期化フェーズのあとで、デーモンコードの中で特権をドロップさせるべき正しい理由があるかもしれない。)

  • PIDファイルは書き出さないこと。

  • バス名をつかむこと

  • ロギングはsystemdに任せることができる。標準エラー出力にログを出す必要があるものは何でもログに出すのは歓迎だ。

  • systemdにソケットをつくらせ、監視させること。これでソケットアクティベーションが動作する。 それゆえに、上で述べたように$LISTEN_FDS$LISTEN_PIDが解釈される。

  • デーモンをシャットダウンするように要求するにはSIGTERMを使うこと

この上のリストはAppleがlaunchdと互換性のあるデーモンにすすめているものと非常によく似ている*21。 すでにlaunchdアクティベーションをサポートしているデーモンを拡張して、systemdアクティベーションもサポートするようにするのは簡単なはずだ。

systemdはこのスタイルで書かれていないデーモンもすでに完全にサポートする。というのも、互換性の理由からだ(launchdはこれを部分的にしかサポート指定いない)。 すでに述べたとおり、これは、使用可能で、systemdによるソケットアクティベーションに対応させるための修正を加えていない、inetd対応の既存のデーモンにも広げることさえできる。

なので、そうだ、systemdが我々の実験の中で自らを示し、ディストリビューションで採用されれれば、これらのサービスがソケットやバスベースのアクティベーションをデフォルトに起動するようにポートすることも腑に落ちるだろう。 すでに、概念実証のためのパッチを書いており*22、チューンアウトされたポーティングは非常に簡単だ。 また、すでにlaunchdでおこなわれた業績をある程度、利用することもできる。 さらに、ソケットによるアクティベーションのサポートを追加することで、サービスが非systemdのシステムと非互換になるというわけでもないのである。

FAQs

バックに控えているのは何?

えー、現時点でのコードベースはほとんど私、Lennart Poettering (Red Hat)の成果だ。 だが、その詳細のすべてのデザインに関してはKay Sievers (Novell)*23と私の密接な強力の結果である。 他に関わっている人物としては、Harald Hoyer (Red Hat)やDhaval Giani (前IBM)*24、そして、その他、IntelSUSENokiaといった様々な会社からの数人だ。

これはRed Hatのプロジェクトなのか?

いいえ。これは私の個人的なサブのプロジェクトだ。これも強調させてくれ: ここに書かれている意見は私自身の意見だ。これらは私の雇用者やRonald McDonaldやその他の人の見解ではない

これはFedoraになるのか?

もし実験でこのアプローチがうまく動くとわかり、Fedoraのコミュニティーでの議論がこれに賛意を示すなら、イエスになるだろう。 実際、これをFedoraに入れようとしている。

OpenSUSEには来るのか?

KayはFedoraがやるのと同じような方法でそうしようとしている。

これはDebian/Gentoo/Mandriva/MeeGo/Ubuntu/(ここにお好みのディストロを入れる)に来るのか?

彼ら次第だ。関心を示してくれるのは実際、歓迎するし、統合の手伝いだってするつもりだ。

どうしてこれをUpstartに追加しなかったのか?なぜ新しいものを発明したのか?

Upstartについて述べた部分で指摘したのは、Upstartの核のデザインには欠点がある、というのが我々の意見だということだ。 もし既存のソリューションが核に欠点を抱えているなら、完全にスクラッチで始めることがそれを示唆することになる。 しかし、我々はUpstartのコードベースその他で多大なインスピレーションを受けたことは注意しなければならない。

そんなにAppleのlaunchdが好きなら、なぜそれを使わなかった?

launchdは偉大なイノベーションだが、それがLinuxに合うとも、Linuxのような、数多くの目的と用途とに対しかなりのスケーラビリティーと柔軟性とを持つシステムに適合的とは思えないからだ。

これはNIHのプロジェクトなのか?*25

えっと、ここまでの文章で、我々がなぜ新しい物に至り、Upstartやlaunchdの上に作ろうとしなかったかを説明できていればいいなと思う。 systemdに落ち着いたのは技術的な理由で政治的理由ではない。

NIHと呼ばれるライブラリ(glibの再実装の一種)を含んでいるのはsystemdではなく、Upstartであることは忘れないでほしいな!*26

これは(お好みの非Linux OSをここに入れる)で動くのか?

多分動かない。 すでに書いたとおり、systemdは多くのLinux固有のAPI(epoll、signalfd、libudev、cgroup、その他もろもろ)を使っており、これを他のオペレーティングシステムにポートするのはそこまで妥当とは思えない。 また、我々、関わった人間は他のプラットフォームにもしかしたらありうるかもしれないポートをマージすることに関心はないし、制限のもとでこれを導入することに従事するつもりもない。 そう入っても、本当にこれをポートしたいと考える人がいれば、gitがブランチやリベースでかなり助けてくれるだろう。

実際、ポータビリティーの限界は他のOSに移す以前の問題である。 我々はかなり最近のLinuxカーネルglibc、libcgroupおよびlibudevを要求している。 あまり最近ではないLinuxシステムのサポートはない。すまない。

他のオペレーティングシステムに似たようなものを実装したい人たちがいれば、協力の好ましい状態はおそらく、そうしたいシステムとどのインターフェイスが共有できて、systemdとsystemdに対応するものとの双方をサポートするデーモンを書く人の生活をより楽にさせる事で我々が手伝いをする、というものになるだろう。 おそらく、焦点はインターフェイスの共有であって、コードの共有ではないはずだ。

私は(次のうち1つを選んで埋める: Gentooのブートシステムやinitng、Solaris SMF、runit、uxlaunch、…)が素晴らしいinitシステムで、パラレルな起動もおこなうと聞いたんだが、なぜ、それを使わないのか?

さて、これを始める前に、実際、我々は様々なシステムを非常に細かく調べてきたが、どれ一つとしてsystemdとして心に描いたものではなかった(もちろんlaunchdは例外だ)。 わからないようだったら、もう一度、上で書いたことを読み直してほしい。

貢献

パッチやヘルプに大変関心がある。 すべてのフリーソフトウェアプロジェクトは幅広い可能性ある外部の貢献からのみ恩恵を受けられることは広く共有されている感覚である。 これは特にinitシステムのようなOSのコア部分では特に真実である。 我々は皆さんの貢献を評価しており、したがって、著作権の割り当てを要求したりはしない(Canonical/Upstartとは大違いだ!) そして、gitも使っている。みんな大好きなVCSだ。イェイ!

我々は特に、systemdをFedoraOpenSUSEに加え、他のディストリビューションで動かすための助けを欲している(へい、DebianGentooMandriva、MeeGoを使っている人で何かやることを探している人はいないか?((Ubuntuが入ってないのが面白い。)))。 しかし、それを抜きにしても、各レベルで貢献者を集めることに必死である。C開発者の人、パッケージャー、ドキュメントを書くのに関心がある人、ロゴを作ってくれる人、歓迎する。

コミュニティ

現時点では、我々にはソースコードレポジトリIRCチャンネル(#systemd on Freenode)しかない。 メーリングリストも、ウェブサイトも、バグトラッキングシステムもない。 freedesktop.orgに間もなく何かをセットアップするかもしれない。 何か質問や我々と連絡を取りたい、その他あれば、招待するからIRCに参加してくれ!

*1:編集履歴 17.11.16-01: 誤字を修正 thanks to Kazuhiro NISHIYAMAさんいくやさん 17.11.14-01: 最後まで翻訳 17.11.13-01: 'Putting it All Together: systemd'の残りを訳出 / 'Status'と'Where is This Going?'を訳出 17.11.12-02: 手元の環境がおかしく、UpstartUbuntu 17.10でインストール可能と書いていたのを修正 thanks to MurabitoLさん 17.11.12-01: 'A short list of other features' in 'Putting it All Together: systemd'の8までを訳出 17.11.09-01: 'Putting it All Together: systemd'の前半を訳出 17.11.07-02: 'On Upstart' を訳出 17.11.07-01: 'Controlling the Process Execution Environment' を訳出 17.11.06-01: 'Keeping Track of Processes'を訳出 17.11.05-03: 誤字を修正 原文へのリンクを追加 thanks to nekomatuさん 17.11.05-02: 誤字を修正 thanks to いくやさん 17.11.05-01: 'Keeping the First User PID Small'まで訳出

*2:原文のリンクは http://git.0pointer.net/?p=systemd.git どうやら、レナートの個人サイトにgitレポジトリがあったようだ。訳文ではgithubのレポジトリにリンクを張り直した

*3:原文の記述が複雑で、訳文としては適切かはちょっと怪しい。ただ、ここで言いたいことは、1. 他のサービスのソケットにメッセージを送るだけで、返信を必要としないサービスであれば、ソケットのバッファーに貯めておけば、そのサービスは起動を続けられる。2. 他のサービスとの同期(メッセージの送受信)が必要なサービスであれば、メッセージを受け取るだけ受け取っておいて、待たせておけばいい、ということである。

*4:画像があったはずだが、picasa終了に伴いリンク切れ

*5:原文は'crakish'で、「麻薬中毒者」を形容する口語のようである。日本語で相当するのは「ラリった」なのかもしれないが、訳文として書くのが憚られたため、このように訳した。

*6:原文のリンクは切れていたため適切なものに付け替えた。cgroupについて日本語で読めるものに Redhatのドキュメントがあるほか、幸いにして、 LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術:連載|gihyo.jp … 技術評論社 もある。

*7:原文では案内がないが、このへんで一旦コーヒーを入れに行くのが良い。

*8: https://linuxjm.osdn.jp/html/LDP_man-pages/man7/capabilities.7.html

*9:原文は'a great away'とあるが、'a great way'の間違いか。

*10:この一文は自信がない。また、どこにかかるのかも自信がない。当該部分の原文は'especially in times where the kernel log buffer is configured ridiculously large out-of-the-box.'である。ここでは、これが前にある'a very useful replacement for syslog'にかかるものと解釈して訳した。

*11:Ubuntuを後援するCanonicalの社員であったScott James Remnantらが開発した、従来のSysV initの代替実装。2006年にリリースされた。Upstartの特徴(とその限界)についてはレナートが本文で語っているため、省略するが、「Upstartは[...]特に動的なシステムに向けて設計されて」おり、「イベントを発行することで非同期性を取り扱う」「革新的な」ものとして当時、登場した。Ubuntuが6.10で採用したのみならず、REHL6でもinitとして採用されていた。だが、アップストリームにあたるDebianが標準のinitをsystemdに移行したことで、Ubuntuは15.10でinitをsystemdに移行した。なお、Ubuntu 17.10でUpstartはレポジトリから削除された(参考)。UpstartのマニュアルはUpstart Cookbookを参照。このマニュアルを日本語化するプロジェクトの残骸はこちらを参照。

*12:ドミノ倒しをイメージするとよいか。

*13:再度になるが、リンクは張り直している。

*14:https://linuxjm.osdn.jp/html/LDP_man-pages/man7/time.7.html

*15:https://linuxjm.osdn.jp/html/LDP_man-pages/man7/capabilities.7.html

*16:毒にも薬にもならないので、実験段階では好都合位の意で解釈した

*17:原文では'introspectable': introspect「内省する」 + able「〜可能な」ということから、文脈を意識して、このように訳した。

*18:自信がない。原文は'already, you can run systemd just fine as a normal user, and it will detect that is run that way and support for this mode has been available since the very beginning, and is in the very core.'

*19:このリンクはまだ生きているようだ

*20:一応リンクは張ったが、torrentファイルなのでダウンロードは無理だった

*21:原文では、リンクが貼られているがリンク切れ。相当するページはここか。

*22:リンクがあったが切れている

*23:今はRed Hatのようだ。 kaysievers (Kay Sievers) · GitHub

*24:今はOracleのようだ。 Dhaval Giani / Linux Plumbers Conference: Developing the Kernel, Libraries and Utilities

*25:UpstartCanonical社主導なのに対し、LennartがRed Hatの人間であることを当てこすって

*26:Upstart側の人間から何か言われたのかなと想像させるくらいには当てこすりである

#きょうのsystemd : レナートにブログ翻訳の許可を取り付けた

レナートのブログは面白くて情報の宝庫なので、ちょくちょくチェックしている。

だけど、有名なあの投稿を含め、誰も翻訳をしていないようなので、レナートに「翻訳していいか?」と許可を取ってみた。

"Go Ahead!"と返事が来た。もちろん他にもちょっとだけ書いてあったけど。

とりあえず、例の投稿をやりたいかなぁ。あと、"systemd for Administrators"シリーズを訳して、公式wikiにリンクを貼ってもらおう。