Movatterモバイル変換


[0]ホーム

URL:


めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Systemd入門(1) - Unitの概念を理解する

Linuxの起動処理は、これまでinit/upstartと呼ばれる仕組みで行われていました。Red Hat Enterprise Linux 7 (RHEL7)では、これが、systemdと呼ばれるまったく新しい仕組みに置き換わります。Fedoraでは、すでに先行してsystemdが採用されていますが、この連載(?)では、Fedora 17での実装をベースとして、systemdの考え方や仕組み、利用方法を説明していきます。今回は、systemdの動作の基礎となる「Unit」の概念を理解します。

systemdを採用したFedoraでLinuxの基礎を学びなそう!という方には、「「独習Linux専科」サーバ構築/運用/管理――あなたに伝えたい技と知恵と鉄則」がお勧めです。(^^/

systemdの考え方

参考資料
Rethinking PID 1:systemdの開発者であるLennartによるイントロダクション。
systemd System and Service Manager:systemdのプロジェクトページ

従来のinit/upstratでは、「サービス」の単位で処理が管理されており、大雑把には次の流れでシステムが起動します。

・/etc/rc.d/rc.sysinitでルートファイルシステムのマウントなど、サービス起動の下準備を行う。
・/etc/init.d/以下のシェルスクリプトで順次サービスを起動する。

rc.sysinitを含めて、個々のサービスの起動処理はすべてシェルスクリプトで実装されており、スクリプトの書き方次第でさまざまな処理が実施可能ですが、その一方でいくつかの欠点もあります。

・お作法に従わないシェルスクリプトがあるとシステムの動作が予測不能になる。
・シェルスクリプトを順番に実行していくので、システムの起動に時間がかかる。
・システム起動後にオンデマンドでサービスを起動・停止する仕組みがない。
・サービスごとにcgroupsでリソース配分を調整するなどの仕組みがない。

最後のcgroupsは、サービスの起動スクリプトを書き換えて対応することもできなくはないですが、すべてのサービスが自動的にcgroupsの管理下に入るようなシステム標準の仕組みがあった方が便利ですよね。systemdは、シェルスクリプトに依存した起動処理を完全に排除してこれらの問題を解決します。

Unitによる管理

systemdでは、「Unit」という単位で処理を管理します。これまで、rc.sysinitやサービス起動スクリプトが実施していた処理の内容は、すべて、Unitとして定義されます。Unitは、「target」「mount」「service」「device」など、役割によってタイプがわかれています。それぞれのUnitは、依存関係が定義されており、PID=1の最初のプロセスとしてsystemdが起動すると、「default.target」というUnitを頂点とする、依存関係のツリーを構築した後、依存するUnitを起動していきます。

この時、それぞれのUnitは、依存関係(AはBが必要)の情報とは別に、起動順序(AはBの後に起動)についての設定が与えられます。起動順序の指定がない場合、依存関係のあるUnitについても並列に起動処理が行われます。systemdは、順序関係の情報をもとにして、複数のUnitをできるかぎり並列に起動していきます。

Unit間の「依存関係」と「順序関係」を分けて定義できるところが、systemdの特徴と言えるかも知れません。Upstartでは、イベントの連鎖によって依存する処理が順次実行されていきますが、この場合は、「依存関係=順序関係」となるため、「依存はしているが並列に起動してもかまわない」というような処理を並列化することができませんでした。

さらに、Unitの中には、「特定のデバイスが接続された」「D-Bus経由でサービスが要求された」など、システム稼働中に発生するイベントをトリガーとして、オンデマンドに起動するものもあります。つまり、必ずしもシステム起動時に立ち上げる必要の無いサービスを起動処理から除外することが可能になります。

このように、systemdを利用すると、

・シェルスクリプトを使わずにsystemdが直接Unit(サービス)を起動する。
・Unitの起動処理を可能な限り並列化する。
・Unitの起動をオンデマンド化する。

という工夫にによって、システムの起動時間が圧倒的に短縮されます。

ちなみに、LennartのBlogによると、systemdが標準提供するUnitで実施可能な処理には、次のようなものがあります。

  • Checking and mounting of all file systems
  • Updating and enabling quota on all file systems
  • Setting the host name
  • Configuring the loopback network device
  • Loading the SELinux policy and relabelling /run and /dev as necessary on boot
  • Registering additional binary formats in the kernel, such as Java, Mono and WINE binaries
  • Setting the system locale
  • Setting up the console font and keyboard map
  • Creating, removing and cleaning up of temporary and volatile files and directories
  • Applying mount options from /etc/fstab to pre-mounted API VFS
  • Applying sysctl kernel settings
  • Collecting and replaying readahead information
  • Updating utmp boot and shutdown records
  • Loading and saving the random seed
  • Statically loading specific kernel modules
  • Setting up encrypted hard disks and partitions
  • Spawning automatic gettys on serial kernel consoles
  • Maintenance of Plymouth
  • Machine ID maintenance
  • Setting of the UTC distance for the system clock

ファイルシステムのfsckとマウントなど、これまで、rc.sysinitの中で実行されていた処理も個別のUnitとして提供されていることがわかります。

Unitの依存関係

まず、Unitの定義ファイルは以下のディレクトリ配下にあります。/usr/lib/systemd/system/以下の設定ファイルを変更する際は、一旦、/etc/systemd/system/以下にコピーしてから変更するようにします。

ディレクトリ説明
/usr/lib/systemd/systemシステムデフォルトの設定。RPMパッケージが提供するデフォルト設定を配置する。
/etc/systemd/systemユーザ設定。ここに同名のファイルを配置するとこちらのファイルの内容が優先される。

Unitには、「target」「service」などのタイプがあると説明しましたが、設定ファイル名の末尾「.target」「.service」で判別ができます。また、ある設定ファイルに対して、「設定ファイル名.wants」というディレクトリが用意されている場合、このディレクトリ内に前提となるUnitを記載する(前提Unitの設定ファイルへのシンボリックリンクを作成する)ことで、依存関係が定義されます。

依存関係の定義方法には、.wantsディレクトリを使う他に、設定ファイル内の[Unit]セクションにおいて、「Wants=」オプション、もしくは「Requires=」オプションで指定する方法があります。一般に、システム的に必須の依存関係は「Wnats/Requires=」オプションに記載しておき、システム管理者が環境に応じて設定する依存関係は、.wantsディレクトリを使用します。特に、後述のsystemctlコマンドで、サーバ起動時に自動起動するサービスを設定した場合は、.wantsディレクトリにシンボリックリンクを作成することで、自動起動が設定されます。

「Wants=」と「Requires=」には微妙な違いがあります。Requiresの場合は、前提のUnitが起動に失敗すると、このUnitの起動処理を中断します。一方、Wantsの場合は、前提のUnitが起動に失敗した場合でも、このUnitの起動処理は継続します。

話が抽象的になってきたので、少し具体例を示しておきましょう。まず、主なUnitのタイプをまとめておきます。

Unitのタイプ説明
mount指定のファイルシステムをマウントする
automount  オートマウント処理を実施する(automountdの代替的な機能)
service指定のバイナリを実行する(主にはデーモンの起動に使用する)
socketsystemdがSocketをListenして、接続があるとプロセスに受け渡す(xinetdの代替的な機能)
path指定のファイルが作成されると、指定されたサービスを起動する
deviceudevから通知されたデバイスを表す
target複数のUnitをまとめるために使用する

上記のようにデーモンプロセスの起動など、従来の「サービス」に相当するUnitは「service」です。一方、「target」は、複数のUnitを束ねるために使用するもので、それ自体は特定の処理を行うわけではありません。たとえば、従来rc.sysinitの内部で行っていた処理に対応するservice群を「sysinit.target」の前提Unitとしてまとめておけば、「sysinit.taget」を起動することで、これらをまとめて実行することができます。

sysinit.targetに対して、依存関係が設定されているUnitは、「system.target.wants」ディレクトリにあるはずですが、実際に確認すると次のようになっています。

# tree /usr/lib/systemd/system/sysinit.target.wants/usr/lib/systemd/system/sysinit.target.wants|-- cryptsetup.target -> ../cryptsetup.target|-- dev-hugepages.mount -> ../dev-hugepages.mount|-- dev-mqueue.mount -> ../dev-mqueue.mount|-- plymouth-read-write.service -> ../plymouth-read-write.service|-- plymouth-start.service -> ../plymouth-start.service|-- proc-sys-fs-binfmt_misc.automount -> ../proc-sys-fs-binfmt_misc.automount|-- sys-fs-fuse-connections.mount -> ../sys-fs-fuse-connections.mount|-- sys-kernel-config.mount -> ../sys-kernel-config.mount|-- sys-kernel-debug.mount -> ../sys-kernel-debug.mount|-- systemd-ask-password-console.path -> ../systemd-ask-password-console.path|-- systemd-binfmt.service -> ../systemd-binfmt.service|-- systemd-journald.service -> ../systemd-journald.service|-- systemd-modules-load.service -> ../systemd-modules-load.service|-- systemd-random-seed-load.service -> ../systemd-random-seed-load.service|-- systemd-sysctl.service -> ../systemd-sysctl.service|-- systemd-tmpfiles-setup.service -> ../systemd-tmpfiles-setup.service`-- systemd-vconsole-setup.service -> ../systemd-vconsole-setup.service

前述のように、.wantsディレクトリ配下に前提Unitの設定ファイルへのシンボリックリンクが作成されています。それぞれのUnit名から、特殊ファイルシステムのマウントやカーネルパラメータの設定(sysctl)などの処理が想像されます。それから、設定ファイル内に直接に記載された前提Unitもあります。次のように、「local-fs.target」と「swap.target」が前提として設定されています。このように、targetの前提として、さらに他のtargetをネストすることもできるわけです。

# cat /usr/lib/systemd/system/sysinit.target | grep -E '(Wants|Requires)'Wants=local-fs.target swap.target

それでは、「sysinit.target」自体はどのようにして起動されるのでしょうか? システム上でsystemdが起動すると、まずはじめに「default.target」を起動するようになっています。したがって、default.targetの前提としてsysinit.targetを設定することで、これが行われます。

default.targetからの依存関係ツリー

それでは改めて、default.targetからどのような依存関係が定義されているかを見てみましょう。default.targetはシステム管理者が変更できるように、/etc/systemd/system/以下に用意されており、他のtargetへのシンボリックリンクになっています。このシンボリックリンクが指す先を変えることは、従来の「デフォルトrunlevelを変更する」という処理に相当します。テキストログイン環境のFedora 17では、次のようになっています。

# ls -l default.targetlrwxrwxrwx. 1 root root 36  9月 14 19:57 default.target -> /lib/systemd/system/runlevel3.target# ls -l /lib/systemd/system/runlevel3.targetlrwxrwxrwx. 1 root root 17  9月 14 19:33 /lib/systemd/system/runlevel3.target -> multi-user.target

ちょっとまわりくどいですが、シンボリックリンクを2回たどって、「/lib/systemd/system/multi-user.target」を差しています。最初からmulti-user.targetを指してもいいのですが、旧来のrunlevelとの対応が分かるように、runlevel3.targetというシンボリックリンクが用意されているようですね。その他には、次のようなシンボリックリンクが用意されています。

# ls -l /lib/systemd/system/runlevel*.targetlrwxrwxrwx. 1 root root 15  9月 14 19:33 /lib/systemd/system/runlevel0.target -> poweroff.targetlrwxrwxrwx. 1 root root 13  9月 14 19:33 /lib/systemd/system/runlevel1.target -> rescue.targetlrwxrwxrwx. 1 root root 17  9月 14 19:33 /lib/systemd/system/runlevel2.target -> multi-user.targetlrwxrwxrwx. 1 root root 17  9月 14 19:33 /lib/systemd/system/runlevel3.target -> multi-user.targetlrwxrwxrwx. 1 root root 17  9月 14 19:33 /lib/systemd/system/runlevel4.target -> multi-user.targetlrwxrwxrwx. 1 root root 16  9月 14 19:33 /lib/systemd/system/runlevel5.target -> graphical.targetlrwxrwxrwx. 1 root root 13  9月 14 19:33 /lib/systemd/system/runlevel6.target -> reboot.target

したがって、systemdは、「multi-user.target」の前提となるUnitを探して、これを起動します。これもまた、システム管理者が変更できるように、/etc/systemd/system以下に.wantsディレクトリが用意されています。

# tree /etc/systemd/system/multi-user.target.wants/etc/systemd/system/multi-user.target.wants|-- NetworkManager.service -> /usr/lib/systemd/system/NetworkManager.service|-- arp-ethers.service -> /usr/lib/systemd/system/arp-ethers.service|-- auditd.service -> /usr/lib/systemd/system/auditd.service|-- crond.service -> /usr/lib/systemd/system/crond.service|-- remote-fs.target -> /usr/lib/systemd/system/remote-fs.target|-- rsyslog.service -> /usr/lib/systemd/system/rsyslog.service|-- sendmail.service -> /usr/lib/systemd/system/sendmail.service|-- sm-client.service -> /usr/lib/systemd/system/sm-client.service`-- sshd.service -> /usr/lib/systemd/system/sshd.service

それっぽいserviceが起動されることがわかります。設定ファイルに内に直接に記載された前提は次のとおりです。

# cat /usr/lib/systemd/system/multi-user.target | grep -E '(Wants|Requires)'Requires=basic.target# cat /usr/lib/systemd/system/basic.target | grep -E '(Wants|Requires)'Requires=sysinit.target sockets.target

これもちょっとまわりくどいですが、「multi-user.target -> basic.target -> sysinit.target」という順番で、最終的にsysinit.targetがmulti-user.targetの前提になっていることがわかります。おそらく、複数の"runlevel"で必要なサービスをbasic.targetにまとめているのでしょう。

この例から分かるように、設定ファイルだけを追って、依存関係の全体像を把握するのはかなり面倒です。Fedora 19では、「systemctl list-dependencies」コマンドにより、systemdが実際に認識している依存関係を表示することができるようになっています。

systemctlコマンドによるUnitの確認

「systemctl list-units」コマンドを利用すると、現在の設定で稼働しているUnitを確認することができます。次は、稼働しているserviceの一覧です。

# systemctl list-units --type=serviceUNIT                             LOAD   ACTIVE SUB     JOB DESCRIPTIONauditd.service                   loaded active running     Security Auditing Servicecrond.service                    loaded active running     Command Schedulerdbus.service                     loaded active running     D-Bus System Message Busfedora-readonly.service          loaded active exited      Configure read-only root supportfedora-storage-init-late.service loaded active exited      Initialize storage subsystems (RAID, LVM, etc.)fedora-storage-init.service      loaded active exited      Initialize storage subsystems (RAID, LVM, etc.)fedora-wait-storage.service      loaded active exited      Wait for storage scangetty@tty1.service               loaded active running     Getty on tty1ip6tables.service                loaded active exited      IPv6 firewall with ip6tablesiptables.service                 loaded active exited      IPv4 firewall with iptableslvm2-monitor.service             loaded active exited      Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or pNetworkManager.service           loaded active running     Network Managerrsyslog.service                  loaded active running     System Logging Servicesendmail.service                 loaded active running     Sendmail Mail Transport Agentsm-client.service                loaded active running     Sendmail Mail Transport Clientsshd.service                     loaded active running     OpenSSH server daemonsystemd-journald.service         loaded active running     Journal Servicesystemd-logind.service           loaded active running     Login Servicesystemd-remount-fs.service       loaded active exited      Remount Root and Kernel File Systemssystemd-sysctl.service           loaded active exited      Apply Kernel Variablessystemd-tmpfiles-setup.service   loaded active exited      Recreate Volatile Files and Directoriessystemd-user-sessions.service    loaded active exited      Permit User Sessionssystemd-vconsole-setup.service   loaded active exited      Setup Virtual Consoleudev-settle.service              loaded active exited      udev Wait for Complete Device Initializationudev-trigger.service             loaded active exited      udev Coldplug all Devicesudev.service                     loaded active running     udev Kernel Device Manager

serviceの中には、デーモンプロセスとして起動したままになるものと、必要な処理を実行して終了するものがあります。「SUB」が「running」のものは、デーモンとして起動中のもので、「exited」は必要な処理が終了した状態のものです。その他のタイプも同様に確認できます。mountタイプはファイルシステムをマウントすることが仕事でしたが、次のように有効化されてマウント処理が終わっていることがわかります。

# systemctl list-units --type=mountUNIT                    LOAD   ACTIVE SUB     JOB DESCRIPTION-.mount                 loaded active mounted     /boot.mount              loaded active mounted     /bootdev-hugepages.mount     loaded active mounted     Huge Pages File Systemdev-mqueue.mount        loaded active mounted     POSIX Message Queue File Systemmedia.mount             loaded active mounted     Media Directorysys-kernel-config.mount loaded active mounted     Configuration File Systemsys-kernel-debug.mount  loaded active mounted     Debug File System

一方、現在の状況とは関係なく、すべてのUnitを表示するには次のようにします。次はserviceタイプの例です。

# systemctl list-unit-files --type=serviceUNIT FILE                                   STATE   arp-ethers.service                          enabled auditd.service                              enabled autovt@.service                             disabledconsole-getty.service                       disabledconsole-shell.service                       disabledcrond.service                               enabled dbus-org.freedesktop.hostname1.service      static  dbus-org.freedesktop.locale1.service        static  dbus-org.freedesktop.login1.service         static  dbus-org.freedesktop.NetworkManager.service enabled dbus-org.freedesktop.timedate1.service      static  dbus.service                                static  debug-shell.service                         disableddisplay-manager.service                     static  dm-event.service                            disableddnsmasq.service                             disableddracut-shutdown.service                     static  emergency.service                           static  fedora-autorelabel-mark.service             static  fedora-autorelabel.service                  static  fedora-configure.service                    static  fedora-import-state.service                 static  fedora-loadmodules.service                  static  fedora-readonly.service                     static  fedora-storage-init-late.service            static  fedora-storage-init.service                 static  fedora-wait-storage.service                 static  fsck-root.service                           static  fsck@.service                               static  getty@.service                              enabled halt-local.service                          static  halt.service                                static  hibernate.service                           static  ip6tables.service                           enabled iptables.service                            enabled kexec.service                               static  lvm2-monitor.service                        enabled messagebus.service                          static  NetworkManager-wait-online.service          disabledNetworkManager.service                      enabled plymouth-halt.service                       static  plymouth-kexec.service                      static  plymouth-poweroff.service                   static  plymouth-quit-wait.service                  static  plymouth-quit.service                       static  plymouth-read-write.service                 static  plymouth-reboot.service                     static  plymouth-start.service                      static  poweroff.service                            static  prefdm.service                              static  quotacheck.service                          static  quotaon.service                             static  rc-local.service                            static  rdisc.service                               disabledreboot.service                              static  rescue.service                              static  rsyslog.service                             enabled saslauthd.service                           disabledsendmail.service                            enabled serial-getty@.service                       static  single.service                              static  sm-client.service                           enabled sshd.service                                enabled suspend.service                             static  syslog.service                              enabled ...(以下省略)...

ここで、「enabled」「disabled」「static」という表記が出てきました。これは、以前のchkconfigによるon/offに対応するものです。まず、「enabled/disabled」が表示されているものは、次のようにシステム起動時の自動起動(default.targetの前提とするかどうか)をon/offすることができます。

# systemctl disable sshd.servicerm '/etc/systemd/system/multi-user.target.wants/sshd.service'# systemctl enable sshd.serviceln -s '/usr/lib/systemd/system/sshd.service' '/etc/systemd/system/multi-user.target.wants/sshd.service'

これは、sshd.serviceの自動起動を無効化/有効化している例ですが、systemctlコマンドの実行直後に「rm/ln」コマンドが表示されています。有効化した場合は、multi-user.target.wantsディレクトリにシンボリックリンクをおいて、multi-user.targetの前提としています。無効化した場合は、シンボリックリンクを削除して依存関係をなくしています。先に説明した仕組みと動きが合致していますね。

一方、[Install]セクションを持たないserviceは、有効化/無効化の対象にはなりません。先の出力で「static」と表示されたものがこれにあたります。一般には、他のUnitからの固定的な依存関係で起動することになります。たとえば、plymouth-poweroff.serviceは「static」になっていますが、これは、システムをシャットダウンする際に起動するpoweroff.targetの前提として設定されています。

なお、serviceを有効化した際に、どのtargetの前提として設定するかは、そのserviceの設定ファイルの[Install]セクションにおいて、「WantedBy=」オプションで指定されています。

# cat /usr/lib/systemd/system/sshd.service ...(中略)...[Install]WantedBy=multi-user.target

WantedByには、次にように、複数のtargetを指定することも可能です。

[Install]WantedBy=multi-user.target rescue.target

ただし、runlevel 5に相当する「graphical.target」に対しては、「multi-user.target」が前提として定義されていますので、multi-user.targetで有効化されたserviceは、自動的にgraphical.targetでも有効化されます。multi-user.targetとgraphical.targetの両方を指定する必要はありません。

# cat /usr/lib/systemd/system/graphical.target | grep -E '(Wanted|Requires)'Requires=multi-user.target

まとめると、

・runlevel 3と5で有効化したい → WantedByにmulti-user.targetを指定
・runlevel 5だけで有効化したい → WantedByにgraphical.targetを指定

ということになります。

最後に、systemdが特定のUnitに対して認識している設定の詳細は、次のコマンドで表示します。該当Unitに対して、デフォルト値として自動設定されているオプションを確認することもできます。

# systemctl show sshd.serviceId=sshd.serviceNames=sshd.serviceRequires=systemd-journald.socket basic.targetWantedBy=multi-user.targetConflicts=shutdown.targetBefore=shutdown.target multi-user.targetAfter=syslog.target network.target auditd.service systemd-journald.socket basic.targetDescription=OpenSSH server daemon...(以下省略)...
検索
最近のコメント

    引用をストックしました

    引用するにはまずログインしてください

    引用をストックできませんでした。再度お試しください

    限定公開記事のため引用できません。

    読者です読者をやめる読者になる読者になる

    [8]ページ先頭

    ©2009-2025 Movatter.jp