systemdエッセンシャル

RHEL8 で systemd の出力やログを読んで理解できないときに、どこを調べたらいいか見当がつくようになることを目標として、systemdの基本的な考え方や、調べるときに中心になりそうなトピックを紹介する資料。RHEL 8を想定しています。

systemd エッセンシャル レッドハット株式会社 ソリューションアーキテクト 森若和雄 2020-03-22 1この資料の位置づけ 2対象 : systemd をとりあえず使えているけど中で何をやってい るかブラックボックスで気持ち悪い or 納得感がない人 目的 : RHEL8 で systemd の出力やログを読んで理解できない ときに、どこを調べたらいいか見当がつくようになること 前提 : fork, exec システムコール、シグナル、ソケットくらい の言葉がなんとなくわかる程度の UNIX 系 OS についての知識この資料で扱わないこと RHEL8 より前の systemd から何が変わったかの差分unit 作成方法や コマンドの how to usedracut を含む起動処理の詳細Optional な機能の一部3ディレクティブの網羅的な一覧や解説 (systemd.directives から systemd の man page を探す のがおすすめです )–systemd-networkd, systemd-resolved, systemd-timesyncd–systemd-nspawn, machinectl–systemd-boot, Portable Service–デスクトップ環境とのインテグレーション全般個別のトラブルシュート (Red Hat の Knowledge base を検索するのがおすすめです ) agenda systemd は何をやるもの ?systemd の unit target unit: 同期ポイントを提供–service unit: サービス管理––4––socket, path, timer unit: イベントに よる Activation device, mount unit: ファイルシステ ムの mount slice, service, scope unit: cgroup 管理ユーザセッション用 systemd アドホックなコマンド実行の管理 systemd-runjournald によるロギングsysvinit からの移行systemd の動作を眺める周辺ツールなど参考資料概要図unit 作成unit unit unit unit 管理socket socket socket 待受けsystemdデバイス管理5サービスやユーザセッション cgroup などの環境 cgroup などの環境 cgroup などの環境path path timer timer timerunit path file環境セットアッププロセスログ出力udevjournalddevice device devicejournal プロセス child processystemd は何をやるもの ?6systemd って何? 7システムとサービスの管理をおこなうたくさんのサービス、ユー ティリティ、ライブラリ群。 PID 1 の init 実装を含む。 「 sysvinit の置き換え」とよく言われますが単純な置き換えでは なく linux の機能を活用する基盤として作り込まれています 目標は? –起動の高速化–ディストリビューション独自実装の統廃合–ベストプラクティスの統合 systemd の普及 2010 年 systemd の最初のリリース2011 年 Fedora に systemd を統合2013 年 Debian に統合 ( 複数ある init 実装の一つとして )2014 年 RHEL, SLES が systemd を統合2015 年 Debian のデフォルト init 実装に選出世にでてから約 10 年、一般的なディストリビューションで 標準的に利用されるようになってから約 5 年 8systemd はどのあたりをカバーする ? 9起動・終了・再起動に必要な処理全般 –デバイスの検出・命名・初期化–fs の mount, autmount, 暗号化 block device 対応–サービス起動・管理–ロギング–起動失敗時のレスキュー処理–パスワード確認–システムの locale, TimeZone, キーボード , 仮想コンソール–電源管理 ( サスペンド・ハイバネート・レジューム )サービス管理で典型的に必要な属性全般の管理 –control groups, namespace, ulimit, 通知 , /tmp の掃除–ユーザセッション管理 それぞれをカバーする systemd 関連プログラム 10起動・終了・再起動に必要な処理全般 –デバイスの検出・命名・初期化← udev–fs の mount, autmount, 暗号化 block device 対応 ← systemd-fstab-generator–サービス起動・管理–ロギング–起動失敗時のレスキュー処理–パスワード確認 ← systemd-ask-password-*–システムの locale, TimeZone, キーボード , 仮想コンソール ← localectl, timedatectl–電源管理 ( サスペンド・ハイバネート・レジューム )← systemd 本体← journald ← rescue.service← systemd-sleep, udev, systemd-inhibitサービス管理で典型的に必要な属性全般の管理 –control groups, namespace, ulimit, 環境変数 , /tmp の掃除 ← systemd 本体 , systemd-tmpfiles*–ユーザセッション管理 ← systemd-logind 宣言的なサービスの定義 sysvinit では shell を順次実行することで起動処理を行う – –編集すると rpm パッケージ等の更新時にトラブルが発生しがち ulimit 等リソース割り当てや設定方法が標準化されていないため Ansible 等での自動的なカ スタマイズが困難systemd では宣言的にサービスを定義し、実行は shell ではなく systemd 自体が行う [Unit] Description=Vsftpd ftp daemon After=network.target [Service] Type=forking ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf11[Install] WantedBy=multi-user.target従来からの互換性の維持 中身は大きく変わっていますが、操作の互換性を維持する工夫を実施sysvinit, LSB の init scripts から unit ファイルを自動生成インタフェースの互換性維持 – – – 互換コマンドの提供 – –12「ランレベル」への対応 : runlevelX.target, カーネルオプション等の維持 ソケット : /dev/initctl, /dev/log 互換ソケット dbus: ConsoleKit 等の dbus インタフェースを維持 halt, init, poweroff, reboot, runlevel, shutdown, telinit など service コマンドを実行すると systemctl を呼びだす# service cups start Redirecting to /bin/systemctl startcups.servicesystemd はプロセス実行環境を用意する systemd の主要な機能のひとつは実行環境の用意 –13UID, GID, 環境変数 , cwd, chroot, ulimit, capability, nice, cgroup, namespace, seccomp, taskset などを設定した上でプロ セスを実行systemd は linux kernel が提供する多様な機能を統一的なイン タフェースで提供systemd がプロセス実行環境を用意する方法 1. systemd は fork() して、子プロセスを cgroup に入れる systemd cgroup, 環境変数 , uid etc. systemd → プロセス2. 子プロセスの systemd が 環境を設定してから exec() でプロセス実行 プロセス child proces143. プロセスがデーモン化や子プロセスの 生成などを行っても cgroup で追跡する 使い捨て unit の作成 systemd-run systemd が用意する環境を試すには、使い捨ての unit を作る systemd-run コマンドが便利systemd-run [ オプション ] < コマンド > ––例 1: # systemd-run -t env サービス用に用意された環境変数を表示-t をつけないと端末ではなくログへ出力される例 2: # systemd-run -t --uid=apache id –例 3: # systemd-run -S -p ProtectHome=yes 15uid が変わっている service 用環境で shell を起動する。この例では /home 以下にアクセスできない ( 何もなくなっ ているように見える ) systemd で制限をかけた環境でプログラムをテストするときに便利。 unit systemd の管理するモノ16unit とは ? システム管理に登場するいろいろなモノを抽象化し た、 systemd で使われる概念 例 : 以下の「」内にあるものは全て unit 「ブロックデバイス sda2 」を「 /var に mount 」して 「パス /var/cache/cups/org.cups.cupsd が存在」すれば 「サービス cupsd 」を起動して「ソケット /run/cups/ cups.sock 」で待ちうける unit17systemd は unit 群を管理する systemd はおおむね以下のような動作を無限に繰り返します : システムの起動、ハードウェアの挿抜、ユーザコマンド、 タイムアウト、 unit 状態変化などのイベントを取得する イベントに対応する unit があれば、あらかじめ定義された制限に従って有 効化 (Activation) や無効化などの job を行う。 unit の内容や依存関係から 複数の job になることもある。 systemd は job queue を持っていて複数の job を適切な順序で実施する event18systemdactivationunitunit がとる状態 基本的な 2 状態 : Active, Inactive中間的な状態 : Activating, Deactivating, Reloading例外 : Failed, Maintenance ActivatingFailed InactiveMaintenance 19ActiveDeactivating Reloadingunit 間の依存関係 unit は他の unit と依存関係をもてる 20Wants: B を active にするなら A も active にした いが A が無い場合や失敗しても OKARequires: B を active にするなら A も active にし する。 A が成功しないと B は失敗AAfter: B を activating にするのは A を activating にしたあとAConflicts: A と B は同時に Active になれない AWantsRequiresAfterConflictsBBBBsystemd の unit 初期化イメージ chronyd After chrony-wait Before time-sync .service Require .service Wants .targetsystemd は 多数の unit 間の After 依存関係を見て 何を active に network するか、順序を .target どうするか決めるWants Wants multi-user .targetBefore Wantsnetwork-pre .target 21Aftercrond .serviceNetworkManager After .serviceWants 起動設定 : これを active に するぞsystemd の unit 初期化イメージ ( 続 ) networkpreNetworkManagernetwork依存関係を満たしつつ 起動処理を並行して行うchronyd 起動 chrony-wait22timesynccrond 起動 multiuser実際の起動処理の様子 $ systemd-analyze plotSVG 形式で起動時の各 unit の初期化開始・終了のタイ ミングを図示する。23systemd --test 24/lib/systemd/systemd --test --system とすると実際に起動用 の unit や設定を読んで実行するべき job を計算した様子を出 力する。 ( 実際の起動処理はおこないません )主要な Activation のきっかけ 25Activation on boot: 起動処理。通常は default.target unit を有効化する。 Socket-based Activation: systemd が xinetd のようにソケット待ちうけを行 い着信を契機に対応する service の unit を有効化してソケットを引きつぐ。 socket unit と service unit の組み合わせで定義する。 Timer-based Activation: systemd が時刻やタイムアウトなどを契機に service unit などを有効化する。 timer unit と service unit の組み合わせで定義する。 Device-based Activation: linux kernel が検出したハードウェアの挿抜や変更 を契機として device unit を作成し、そこからの依存関係で mount unit や service unit などを有効化する。 fstab での mount 処理などは device-based activation で実施される。 ユーザからの操作 : systemctl systemctl は systemd への主要な操作インタフェース –よく使うコマンド : 見る (show) 、状態 (status) 、有効化 (enable) 、 無効化 (disable) 、開始 (start) 、終了 (stop) 、リロード (reload)例 : cron デーモンに対応する unit crond.service の状態表示 $ systemctl status crond.service crond.service - Command Scheduler Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2020-02-03 14:42:02 JST; 2 days ago Main PID: 1245 (crond) Tasks: 1 (limit: 28413) Memory: 10.0M CPU: 30.309s CGroup: /system.slice/crond.service └─1245 /usr/sbin/crond -n26Feb 06 11:10:01 snake.usersys.redhat.com CROND[147722]: (root) CMD (/usr/bin/redhat-interna>unit の start/stop 操作 systemd が unit を active にする (start) / inactive にする (stop) –systemctl start crond.service–systemctl stop crond.service状態を変える必要がある場合だけ動作を行う –27unit が既に active である場合、 start しようとしても何も実施しな い。 unit が inactive な場合の stop も同様。依存関係があれば指定以外の unit も操作を行う場合がある 実際のシステムで依存関係を見る systemctl list-dependencies –28デフォルトでは起動処理に対応する default.target の依存関係を表示default.target に使われる unit –multi-user.target (runlevel 3 相当 )–graphical.target (runlevel 5 相当 )unit の種類 unit にはいくつかの種類がある。よく使うものを例示する。 –target: 何もしない。依存関係や前後関係を定義する。–service: 何かのプロセスを実行してサービスを提供する–socket: TCP, UDP, IPv4, IPv6, socket, dbus, fifo などで待ちうける––29path: ファイルやディレクトリが存在することや操作されたこと を待ちうける timer: サービス開始時から x 秒経過、前回の timer 起動時から x 秒経過、カレンダー上の日時などの時刻を待ちうける 関連する man page 30systemd(1) Concepts 節 : systemd の主なコンセプト、 unit の種類、 kernel コマンドラインオプション daemon(7) Activation 節 : Activation の説明 systemd.special(7): systemd であらかじめ定義されている特殊 な unit の紹介。 default.target など予約語のように扱われるも のも多い。やってみよう31systemctl list-units で unit の一覧を見るsystemctl status で unit の状態を見るsystemctl show でもっと細かい unit の状態を見るunit file unit の設定ファイル32unit の定義ファイル unit file systemd の unit 群のほとんどはファイルで定義されるunit と unit file は明確に区別されるが慣れるまでは混乱しがち–systemctl list-units (unit の一覧 )–systemctl list-unit-files (unit file の一覧 )–systemctl cat crond.service ( ある service に関連する unit file を出力 )ロードunitsystemd は unit file をロードしてメモリ上の unit を作成する – –33unit filesystemctl daemon-reload ( 全 unit 定義ファイルを読み直す ) 不要な unit は自動的にアンロードされる ( 一覧を見るだけでもロードしなおさ れるので利用時にはあまり意識しない )※unit file を書き変えただけでは動作に影響を及ぼさない場合があるので注意 unit file の例 /lib/systemd/system/httpd.service [Unit] Description=The Apache HTTP Server Wants=httpd-init.service After=network.target remote-fs.target nss-lookup.target httpd-init.service Documentation=man:httpd.service(8) [Service] Type=notify Environment=LANG=C ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND ExecReload=/usr/sbin/httpd $OPTIONS -k graceful KillSignal=SIGWINCH KillMode=mixed PrivateTmp=true34[Install] WantedBy=multi-user.targetunit の例 $ systemctl show httpd35Type=notify Restart=no NotifyAccess=main RestartUSec=100ms TimeoutStartUSec=1min 30s TimeoutStopUSec=1min 30s TimeoutAbortUSec=1min 30s RuntimeMaxUSec=infinity WatchdogUSec=0 WatchdogTimestampMonotonic=0 RootDirectoryStartOnly=no RemainAfterExit=no GuessMainPID=yes MainPID=0 ControlPID=0 FileDescriptorStoreMax=0 NFileDescriptorStore=0StatusErrno=0 Result=success ReloadResult=success CleanResult=success UID=[not set] GID=[not set] NRestarts=0 OOMPolicy=stop ExecMainStartTimestampMonotonic=0 ExecMainExitTimestampMonotonic=0 ExecMainPID=0 ExecMainCode=0 ExecMainStatus=0 ExecStart={ path=/usr/sbin/httpd ; arg ExecStartEx={ path=/usr/sbin/httpd ; a ( 以下略 )unit file の文法 いわゆる INI ファイル形式 Key=Value 形式の行が並ぶ。 Key や 条件により同じ Key を複数回書ける 場合もある。Key はディレクティブと呼ばれる# または ; のあと改行まではコメント改行は \ でエスケープできる36[Section] でセクションを指定時間は” 3min 10sec” のように単位を つけて書ける[Section A] KeyOne=value 1 KeyTwo=value 2 # a comment ; another comment [Section B] Setting="something" "some thing" "..." KeyTwo=value 2 \ value 2 continued TimeoutSec=”1hour 5min”unit と unit file unit には unit file にある情報の他に以下が含まれる –デフォルト値–他 unit からの依存関係の逆向きの依存関係 –37他から After= で参照されていると Before= が作られるなど実行時に決まる情報 ( 時刻、実行結果、 PID など )unit file の enable/disable 操作 unit file に対して enable か、 disable かを指定する ––38enable すると Install 処理が行われる ( 典型的には multiuser.target などからの依存関係を定義する ) disable すると Uninstall 処理を 行い Install での処理を元に戻すchrony-wait Before time-sync chronyd .service Require .service Wants .target Aftercrond .serviceAfterdisable していても unit file はロード .target Before Wants され unit が作成される。他から依存 network-pre After NetworkManager .service .target されていれば active になる場合もある networkWants cups .pathWants multi-user .targetWantsunit file の mask/unmask 操作 どういう依存関係を経由しても unit file から unit を作りたくないと きは mask を行う –mask すると systemd はその unit file を無視してロードしない–unmask すると元にもどすmask した unit file で定義される unit へ、他の unit が依存していると 依存関係を満たせず失敗する場合もあるchrony-wait Before time-sync chronyd .service Require .service Wants .target After After network .targetWants cups .path Before Wantsnetwork-pre .target39crond .serviceAfterNetworkManager .serviceWants multi-user .targetWantsunit file の参照パス 40systemd が unit file を探すディレクトリは複数ある RHEL の場合 systemd 関連の設定ディレクトリは以下。 同名のファイルがあれば上が優先 /etc/systemd ( 管理者による設定 ) → /run/systemd ( 実行時の自動設定など ) → /lib/systemd (rpm パッケージで提供 ) 設定変更がパッケージ更新で壊される事故が防げる unit file のカスタマイズ unit file の典型的なカスタマイズはファイル内容の編集ではなく、別 ファイルの配置やシンボリックリンクの作成でおこなう。foo.service という unit file に対して以下のディレクトリが使える ––41foo.service.d (drop-in ディレクトリ。 *.conf のファイル名で設定を 書くと foo.service の同じディレクティブを上書きしたものとして解 釈される ) foo.service.wants, foo.service.requires ( このディレクトリ内に他 の unit file へのシンボリックリンクを置くことで Wants= および Requires= に追記したものとして解釈される ) drop-in の利用例 /lib/systemd/system/httpd.service [Unit] Description=The Apache HTTP Server Wants=httpd-init.service After=network.target remote-fs.target nsslookup.target httpd-init.service Documentation=man:httpd.service(8) [Service] Type=notify Environment=LANG=C/etc/systemd/system/httpd.service.d/limit.conf [Service] LimitNOFILE=10240 ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND ExecReload=/usr/sbin/httpd $OPTIONS -k graceful # Send SIGWINCH for graceful stop KillSignal=SIGWINCH KillMode=mixed PrivateTmp=true 42[Install] WantedBy=multi-user.targetdrop-in には追加または変更したい部 分だけ書く systemctl cat httpd.service のようにすると指定した unit に関係 する設定ファイルを出力する (*.wants, *.requires は systemctl cat の出力に含まれない ) systemd-delta コマンドは unit カ スタマイズの概要を表示する関連する man page systemd.syntax(7): unit file の文法systemd.time(7): systemd 内で使う日時で使える構文43systemd.unit(5): unit file 、 unit file のロードやアンロードの 条件、依存関係や全 unit 共通の設定、設定ファイルを配置す る path などsystemd.directives(7): ディレクティブから man ページへの索 引 ( 既存の unit file を読むときに便利 ) やってみよう 44systemctl list-units と systemctl list-unit-files を比較 systemctl cat sys-subsystem-net-devices-eno1.device のようにして対応す る unit file が存在しないことを確認する systemctl cat default.target と systemctl show default.target を比較 パッケージに含まれる /lib/systemd/system/ 以下の unit file と、自動または 手動で作られる /etc/systemd/systemd/ 以下のリンクやファイルを見比べて systemctl show の出力と比較する systemctl enable/disable crond.service で unit を install/uninstall する。こ の操作による設定が /etc/systemd/system/ 以下に反映されていることを確認 する target unit 同期ポイントを提供45target unit target unit 自体は何もしないで状態が変わるだけの unit –システム起動、シャットダウン、異常時のレスキューなどで典 型的に必要な target はあらかじめ定義されている。自由に追加 することもできる。 –46前後関係や依存関係をまとめるために利用されるsystemd.special(7) を参照multi-user.target の例 multi-user.target は主なサービス群を動作させたい従来のラン レベル 3 に相当する。 定義ファイルを見ると説明と依存関係のみ /lib/systemd/system/multi-user.target47(略) [Unit] Description=Multi-User System Documentation=man:systemd.special(7) Requires=basic.target Conflicts=rescue.service rescue.target After=basic.target rescue.service rescue.target AllowIsolate=yes multi-user.target の依存関係 systemctl show multi-user.target で unit を見ると多数のサービスにWants と After で依存。 active にすることで多数のサービスが起動する。 $ systemctl show multi-user.target (略) Requires=basic.target Wants=dbus.service plymouth-quit.service chronyd.service sshd.service tuned.servicersyslog.service sssd.service pmlogger.service plymouth-quit-wait.service rhsmcertd.service systemd-ask-password-wall.path pmcd.service systemd-user-sessions.service atd.service systemdlogind.service firewalld.service NetworkManager.service remote-fs.target getty.target dnfmakecache.timer crond.service auditd.service systemd-update-utmp-runlevel.serviceRequiredBy=graphical.target Conflicts=shutdown.target rescue.service rescue.target Before=shutdown.target systemd-update-utmp-runlevel.service graphical.target After=atd.service sshd.service sssd.service rescue.target chronyd.service tuned.serviceNetworkManager.service pmlogger.service systemd-logind.service plymouth-quit-wait.service plymouth-quit.service rsyslog.service firewalld.service crond.service pmcd.service systemd-usersessions.service dnf-makecache.timer dbus.service basic.target rhsmcertd.service getty.target rescue.service48(略)Install 処理での依存関係の追加 multi-user.target の unit file に存在しない Wants は以下のディレクトリにあるリンク群 に由来する tuned multi-user /lib/systemd/system/multi-user.target.wants/ .service Wants .target /etc/systemd/system/multi-user.target.wants/ –/etc/systemd/system/*.wants および *.requires ディレクトリは unit file を拡張する –49unit file の enable 時に [Install] 節の定義によりリンクを作成する 例 : シンボリックリンク multi-user.target.wants/tuned.service は Wants=tuned.service の追記に相当。 さらに依存関係解決により、 tuned.service 内の Requires= で参照している dbus.service, polkit.service も multi-user.target の Wants= へ追加される ネットワーク初期化での .target 例 RHEL 8 ではネットワーク初期化に NetworkManager(NM) を使うが、各種 サービスは直接 NetworkManager に依存せず network.target や networkonline.target へ依存している。 network-pre.target –NM networkネットワーク初期化をはじめる準備ができた (NM.service の After= に指定 )network.target –ネットワーク初期化をはじめた (NM.service の Before= に指定 )network-online.target –ネットワークで外部と通信できるようになった (NM-wait-online.service の Before= に指定 )ネットワーク初期化に NM 以外を利用する場合も、 target は同じなので サービスは target との依存関係だけを持てばよい。 50networkpreNM-wait-online networkonline関連する記事 Running Services After the Network is up https://www.freedesktop.org/wiki/Software/systemd/NetworkTar get/関連する man page51systemd.target(5): target unit の説明systemd.special(7): systemd がデフォルトで提供する *.target の説明bootup(7) : systemd がデフォルトで提供する *.target の依存関係を表現した図やってみよう systemctl list-units -t target -a でどんな target があるか一覧を見る –※LOAD 欄が not-found となっている unit は他から参照されているが unit file は存在しない 52例 :syslog.target は systemd 202 よりあとには存在しないsystemctl show shutdown.target の ConflictedBy= 行を見る。 shutdown.target を active にしようとするとここに記載された unit 群は競 合するため inactive にされる。/etc/systemd/system/multi-user.target.wants/ 以下にあるリンクか ら中身を見て、 [Install] 節を確認する。 service unit サービス管理53systemd の service って何するの ?54サービスが動作する環境を用意サービスを起動 ( 通常何らかのプロセスを開始 )起動したサービスを監視 –stop やシャットダウン時などの操作–異常終了の検出、再起動などの対応サービスのイメージ図 1. systemd は fork() して、子プロセスを cgroup に入れる systemd cgroup, 環境変数 , uid etc. systemd → プロセス2. 環境を設定してから exec() でプロセス実行 プロセス child proces553. プロセスがデーモン化や子プロセスの 生成などを行っても cgroup で追跡する サービスが動作する「環境」って ?56環境変数ulimitUID, GIDnice 値numa ポリシーCPU アフィニティCapabilitySELinux contextcgroups でのリソース制御 –CPU, メモリ , ストレージ I/O, 直 接アクセスできるデバイスseccomp でのシステムコール制限 namespace を使ってファイルシス テムの一部を bind mount して変更 サービス毎の firewall 設定 などサービスを起動 基本的にはプロセスを fork(), exec() するプログラムによりいろいろな動作があるのでいくつかの type を提供している – –57デーモン化するもの、デーモン化しないもの プロセスが生き続けてサービスを提供しつづけるもの、最初と最後に設定変更をす るだけで動作しつづけるプロセスがないもの–(systemd がうまく扱える ) socket や dbus で待ちうけするもの–systemd に対応して通知してくれるもの、 systemd への対応はないものservice は type により「どうなったら active とみなすか」の詳細がことなるdaemon 利用時のイメージ systemd cgroup, ulimit, namespace, 環境変数 etc. service process1. exec() して環境の中でサービス 実行開始 . daemon 化して exit() main processpidfile2. PID ファイルなどで main process を systemd が推定child process 58おさらい : daemon 化による端末との切り離し rccd / stdin, stdout, stderr を /dev/null へ接続 fork() setsid()termservice process termdaemon 化をする目的は端末から 切り離して(SIGPIPE, SIGSTOP, SIGTERM などの ) シグナルを受けない画面にゴミを出さない59端末から影響されないmain process pidfile child processsystemd の環境と daemon 化 60systemd は service に割りあてる stdin, stdout, stderr をどう構成するか明示 的に設定できる –/dev/null, 端末 , socket, ファイル , journal など–systemd.exec(5) の Logging and Standard Input/Output可能 ( プログラムに“ foreground mode” などのオプションがある ) であれば Type=simple を使う。この場合 stdout や stderr へメッセージを出すなら journal に接続して記録できる。 daemon 化を避けられない場合は Type=forking にして PIDFile で監視するべ きプロセスを伝える。 stdout や stderr はセットアップされるが、 daemon 化で /dev/null へ接続されなおすのでその後は使われない。 Type=notify 利用時のイメージ systemd socket1. fork, exec しただけでは unit は active ではなく activating 2. libsystemd の sd_notify() 関数で準備完了のメッセージを 受けとると active にするREADY=1main processchild process61よく使われる type と使い分け よく使う service の type は以下 3 種類simple –service実行されたプロセスがさらに子プロセスを作 りデーモン化するタイプ。古典的な daemon はこの type 。 stdout,stderr 経由の log が使え ないため選べるなら simple のほうがよい。simple childserviceforking main childoneshot –62forkforking –実行されたプロセスが直接サービスを提供す るタイプのプログラム。systemd–何かの初期化など一時的に実行するだけです ぐに終了するもの。 長期間 ( 数分〜 ) 動作するものには向かない。serviceoneshot childservice が active になる条件 Activating InactiveActiveexec: fork(), exec() が成功すると activeforking: プロセスを起動して main process の PID を確認すると active63simple, idle: fork() が成功すると activeoneshot: fork() が成功して activating 、 exit() すると inactive 。 active にならない。 oneshot(RemainAfterExit つき ): fork() したプロセスの exit() が成功だと activedbus: dbus 上で指定した名前の待ちうけが行われると activenotify: sd_notify で通知が行われると active startservice の起動フロー fail前処理 : ExecStartPre ↓ fail 本体 : ExecStart ↓ 確認等 : ExecStartPost fail上から順に実行して、成功の場合は次へ進む –ExecStartPost の実行開始タイミングはいつかが異 なる どこまで起動が進んでいるかにより ExecStop, ExecStopPost を実行する –stop本体終了 : ExecStop ↓ 後処理 : ExecStopPost 64running の場合service の type によりいつ active になるか、終了処理で失敗すると unit は failed 状態になる起動・終了それぞれにデフォルトで 1 分 30 秒の タイムアウトが設定されている。どの Exec* も 失敗する可能性があるreload設定リロード : ExecReload active な service の終了検出 systemd は service の終了を検出できる ––65プロセスが exit() した場合、 systemd は PID 1 なので SIGCHLD を受信し、 signal や exit code がわかる。 systemd の notify に対応している場合 定期的な watchdog 送信sd_notify でのサービス状態の通知ExecStopPost= での終了処理を行う。その後 Restart= により 再度 service を active にするよう指定もできる。 service への start 以外の操作 ( 一部 ) stop –service が active であれば ExecStop, ExecStopPost を実行する–service が signal などで殺された場合 ExecStopPost を実行する–service が inactive であれば何もしない (sysvinit と異なる振舞いなので注意 )kill –cgroup を利用して指定した service に関係するプロセス全てへシグナルを送る 例 ) libvirtd が dnsmasq を起動して stop しても dnsmasq はそのまま残る。関連プロセス を全て落とすには kill を使う。66reload –設定ファイルを読み直す–ExecReload= を指定している場合のみ実行可 起動終了処理のカスタマイズ 条件確認 ––ExecCondition= ( 何かを実行して実行結果で条件確認 )–StartLimit*= (start の頻度に制限をかける )実行の成功失敗判断 ––67Condition*= ( 各種条件を確認し、条件を満たす場合に実行する。 ConditionPathExists=, ConditionVirtualization= など )Exec*= に指定する実行ファイル名の直前に” -” をつけると exit code を無視し て成功とみなす。 SuccessExitStatus= exit code のうち成功とみなすものと、受信シグナルのうち 成功とみなすものを追加する サービスの起動順序が意図と違う ? “ 意図通りの実行順にならない” ケースの多くは以下 2 つ 依存関係 (Wants, Requires) で指定したものが先に実行されると思った – –これらで指定したものが起動しているか、サービス開始しているかは関係な くサービスを起動しはじめる前後関係 (After, Before) がサービス開始の前後関係であると思った – –68指定されたものの activation は job queue に入る ( だけ ) 。プロセス起動の前後関係は守られる サービス開始したかはわからない (Type=dbus か notify でなければプロセス が起動したことしか見ていないから ) 余談 : 前後関係でループができたら? 設定ミスによりループができるケースがある このような場合 systemd は (Wants= だけで 参照されているような ) 必須ではない unit を 無視・停止してループを消そうとする。69B.serviceAfter AfterA.serviceプロセスの起動順序ではなく サービスの起動順序を守らせるには ? 短時間 ( 〜 1 分 ) で初期化が終わる場合は ExecStartPost で対応 きっちりやる派 ( 確認プロセスの終了ステータスで伝える ): Type=forking または simple にして ExecStartPost= でサービス検出を確認したあと exit(0) する。 –70service unit のタイムアウトによる失敗処理があるので確認プロセスは無限に 待つ素朴なプログラムでよいだいたい動けば OK 派 ( ちょっと待つ ): ExecStartPost=/usr/bin/sleep 30sプロセスの起動順序ではなく サービスの起動順序を守らせるには ?( 続 ) プロセスの起動からサービス開始まで長時間 (1 分〜 ) かかる場合はプロセス 起動のタイムアウト処理とサービス開始の検出を分離する。 プログラムを systemd に対応させる派 (notify) –サービスの動作確認をするだけの別サービスを用意する NetworkManager.service→NetworkManager-wait-online.service –71Type=notify として、サーバプロセスから準備 OK の通知をもらう ( 起動され るプログラム内での対応が必要 )Type=oneshot で TimeoutStartSec=infinity, RemainAfterExit=true として ExecStart= でサービスの動作を確認するプロセスを起動する プロセスの実行環境 ( 再掲 ) 1. systemd は fork() して、子プロセスを cgroup に入れる systemd cgroup, 環境変数 , uid etc. systemd → プロセス2. 環境を設定してから exec() でプロセス実行 プロセス child proces723. プロセスがデーモン化や子プロセスの 生成などを行っても cgroup で追跡する systemd-run でサービスの実行環境を調べる 例「 PrivateTmp=yes とされている環境ではどうなるか調べたい場合」systemd-run -S -p PrivateTmp=yes –echo $$ で自分の PID を確認する–lsns -t mnt で shell が独自の mnt namespace に入っていることを確認する–/tmp, /var/tmp に何もないことを確認する 73/var/tmp は /var/tmp/systemd--/tmp を bind mount される。 /tmp は外から見えない関連する man page 74systemd.service(5): service についての unit file でデフォルトで含ま れる依存関係、各 type の動作、 Exec* で使える特殊な記法、 service 特有のディレクティブ、コマンドライン風のパイプおよびリダイレク ト処理 systemd.exec(5): (cgroup 以外の ) プログラムの実行環境を準備する ためのディレクティブ sd_notify(3): systemd へ通信をする C 言語での関数 systemd-notify(1): sd_notify() を呼ぶコマンド。シェル等で利用す る。 やってみよう 75/lib/systemd/system/ で simple, oneshot, forking, notify の例を探す。 ※ Type= がない service はデフォルトの simple Type=oneshot の例をコピー・改変して /bin/echo foobar するだけの unit file foobar.service を作成する –/etc/systemd/system に配置、 systemctl daemon-reload で読み込み–systemctl start foobar.service してログに foobar が記録されていることを確認する–systemctl status foobar.service して状態を確認–RemainAfterExit= の設定を反対にして同じことを繰り返して動作の違いをみる複雑な service unit の例として nfs-server.service か libvirtd.service を見てみる socket, path, timer unit イベントによる service 起床76何かのイベントを契機に service を起動する systemd では「イベントを契機にして service を起動する」仕組 みを提供し、 unit の組み合わせとして管理する socket, path, timer イベントを検出するとデフォルトでは 同じ名前の .service を active にする –77Service= で任意の service unit を指定できるfoo .socketAfterfoo .servicesocket unit systemdsystemd が socket を listen する。 –socket プロセス–socket–socket .target*.socket78foo 起動 bar 起動 baz 起動起動のごく初期に listen を開始するので一 般のプロセスによるランダムなポート open との競合を回避する socket の listen と service の立ち上げを分 離すると、 socket だけ先に作っておけば 互いに socket で通信する service 群の起動 を並列化できる。 socket の利用を開始すれば自動的に相手 を待つ。対応する service unit の起動時に socket をひきつぐ。 –foobar.socket には foobar.service が対応別名の service への対応づけも指定可 –「 socket の引き継ぎ」ってどうやるの ? systemd が socket unit で待ちうけていた socket を service unit へ渡す方法は…… ? 79inetd 方式 : service unit で StandardInput=socket として標準 入出力を socket とのやりとりに使う libsystemd 方式 : systemd が service 実行時に fd 群を設定す る。サービスを実行するプロセスは sd_listen_fds() 関数で fd の 数や属性を取得する。socket activation 起動時は socket unit だけ active にしておき service は起動しないsystemd が socket への着信を検出すると service を起動する –801 回目の通信には遅延が発生するservice の [Install] 節に通常の service と同様 WantedBy= 等を挿入するよ う定義することで、ユーザーが unit の動作を選べるようにもできる –enable してシステム起動時の起動対象にする–disable して socket activation の対象にするservice で Also= として socket をインストールすることで service の enable/disable とセットで socket の確保を指定できる path unit systemd が path の存在・変更等を inotify() で監視する socket activation と同様に指定されたイベントがあれば対応する service を起動する service は特にイベントを受信したり共有したりはしない systemd inotifyプロセスfile,dir file,dir file,dir 81timer unit timer はいくつかのイベントから xx マイクロ秒経過のような表現で定義 socket activation と同様に指定されたイベントがあれば対応する service を起動する timer unit は複数のタイマーイベントを定義できるsystemdtimer 82プロセス余談 : timer の扱うイベント timer はさまざまなイベントからの相対時間を指定できる 83OnBootSec= システムが起動してからの相対時間 OnStartupSec= systemd が起動してからの時間。 system 全体を管理する systemd では OnBootSec とほぼ同じ。ユーザセッションではログイン直後のタ イミングからの相対時間になる。OnActiveSec= timer unit 自身が active にされてからの相対時間OnUnitActiveSec= timer に対応する service unit が active になってからの時間OnUnitInactiveSec= timer に対応する service unit が inactive になってからの時間OnCalendar= カレンダー上の時刻、 cron 的な指定ができる timer の繰り返し (1) 繰り返し実行させたい場合には、 OnBootSec= と OnUnitInactiveSec= を組みあわせて実現システム 起動84OnUnitInactiveSecOnUnitInactiveSecOnBootSecservice 実行service 実行service は Type=simple 実行終了して inactive になった servictimer の繰り返し (2) 繰り返し実行させたい場合には、 OnBootSec= と OnUnitActiveSec= を組 みあわせて実現。実行時間が OnUnitActiveSec より短いことが必須。OnBootSec システム 起動OnUnitActiveSecservice 実行OnUnitActiveSecservice 実行service が active になった 85OnUnitActiveSecservice 実行servictimer をわざとずらす 86AcculacySec= timer で指定された時刻をどれだけの精度で実 現するか。デフォルトでは 1 分。すこしずつ異なるタイマーイ ベント群にもとづくアクションの実施タイミングを揃えること で消費電力削減に役立つ。 RandomizedDelaySec= timer で指定された時刻をランダムに 最大どれだけ遅延させるか。デフォルトは 0 。クラスタ環境で 同じプログラムが同時に実行されることにより、負荷が過度に 集中することを避けるために利用する。 path, socket, service 連携例 cupsd (1) cups.service [Unit] Description=CUPS Scheduler After=sssd.service network.target ypbind.service [Service] ExecStart=/usr/sbin/cupsd -l Type=notify Restart=on-failure [Install] Also=cups.socket cups.path WantedBy=printer.target 87ExecStart= 起動時に実行するコマンド。 ExecStop 等の指定がないので終了時はここで作成さ れたプロセスへ「 SIGTERM 送信→タイムアウト待ち→ SIGKILL 送信」による終了処理を行う Also= この unit を有効にする時に指定した unit も有効化する WantedBy= enable すると依存関係を挿入する。 printer.terget を有効化すると cups.service も有効化の対象になる path, socket, service 連携例 cupsd (2) Socket Activation cups.socket [Unit] Description=CUPS Scheduler PartOf=cups.service [Socket] ListenStream=/var/run/cups/cups.sock [Install] WantedBy=sockets.targetListenStream= 指定したソケットを systemd が待ちうける systemd がソケットの listen を行い、はじめて接続がおこなわれた時点で対応する サービスを起動する (Socket Activation) –88この例では cups.socket を待ちうけて着信があれば cups.service を起動 path, socket, service 連携例 cupsd (3) Path based Activation cups.path [Unit] Description=CUPS Scheduler PartOf=cups.service [Path] PathExists=/var/cache/cups/org.cups.cupsd [Install] WantedBy=multi-user.target 89ファイルやディレクトリの存在や変更を検知して対応するサービスを起動す る (Path based activation) この例では /var/cache/cups/org.cups.cupsd が存在すれば cups.service を 起動する timer と service の連携例 systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.timer [Unit] Description=Daily Cleanup of Temporary Directories Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) [Timer] OnBootSec=15min OnUnitActiveSec=1d 90時間の経過を契機として service を起動する この例ではシステム起動後 15 分、その後 1 日おきに systemd-tmpfilesclean.service を起動する関連する man page systemd.socket(5), systemd.path(5), systemd.timer(5): 各種 類の unit の説明、利用できるディレクティブなどsystemd.time(7): 時刻、時間の表記法systemd for Developers I –91http://0pointer.de/blog/projects/socket-activation.htmlやってみよう lsof -p 1 で systemd が多くの socket を待ちうけていることを確認 –92systemctl -t socket で socket unit を確認して照らしあわせるsystemctl list-timers で今予定されている timer の起床一覧を見る fstrim.timer は 1 週間に 1 回 service を起動する設定になってい る。 drop-in を使って 1 日に 1 回に書き換えてみる。 /lib/systemd/system/*.timer を見ていろいろなタイマーの使い方のパ ターンを見るdevice unit と mount unit ファイルシステムの mount93デバイス検出から mount まで94主な登場人物 : linux kernel, udev daemon, systemd, fstab関係する unit –.device–.mount–local-fs.target, remote-fs.targetデバイス検出から .device unit 作成まで *.rulesudevuevent/sys/devsystemd/run/udevカーネル ハードウェア 951)ハードウェアの追加や変 更を kernel が検出 2)uevent を udev が受信 3)udev の *.rules により /dev 以下にファイルを作成し たり、 systemd 用の情報 を付加したりする 4)systemd は udev によ り” systemd” と TAG が付 与されたデバイス の .device unit を作成するfstab から mount unit を生成 systemd-fstab-generator –/etc/fstab を解析して、対応する mount unit 定義を生成する例 : fstab の ” /dev/mapper/snake-home /home xfs 生成された /run/systemd/generator/home.mountdefaults 0 0” という行から# Automatically generated by systemd-fstab-generator [Unit] SourcePath=/etc/fstab Documentation=man:fstab(5) man:systemd-fstab-generator(8) Before=local-fs.target96[Mount] Where=/home What=/dev/mapper/snake-home Type=xfs余談 : systemd-fstab-generator 実行タイミング systemd の起動直後と、 systemd daemon-reload 実行時に systemd-fstab-generator は実行される。つまり…… – –97initramfs 内での起動直後、 initramfs 内の fstab から unit 生成 root directory を変更して systemd を読み直した直後、 /etc/fstab から unit 生成fstab を編集して手動で mount を行う操作はひきつづき有効なの で利用していても特に今までと違いはない余談 : systemd-mount, systemd-umount systemd の mount unit はちょっと賢い –親ディレクトリの mount や、ネットワーク接続を待つ–automount–98その他任意の unit の依存関係が書けるので mount が成功するのを待ってサービ スを起動する等賢いのはいいが使うのに都度 fstab に書いて generator を実行して systemctl start foobar.mount するのは面倒なので…… → 専用コマンド systemd-mount が用意されているlocal-fs.target, remote-fs.target local-fs.target: ネットワーク不要で mount できるファイルシス テム –remote-fs.target: ネットワーク初期化後に mount できるファイ ルシステム –99fstab に書かれている local fs はこの target から依存されるfstab で _netdev オプションが書かれているとこの target から依存 される関連する man page 100systemd.mount(5): fstab に書くオプションについてここに記 載されている systemd.device(5): udev rules で定義する変数について記載が ある udev(7), udevadm(8): udev database を見たりイベントをモニ タしたいときは udevadm コマンドで見るやってみよう root fs のデバイスを ROOT として。 –udevadm info ROOT の出力をみる 101TAGS= に systemd があることを確認する/lib/udev/rules.d/99-systemd.rules を眺める –対応しそうな device unit を systemctl show して照らしあわせる (DEVPATH と Description 等 )printer サブシステムのデバイスがあると SYSTEMD_WANTS 経由で printer.target を要求USB メモリを挿入したり loopback device を作成して対応する device unit がで きていることを確認する slice, service, scope unit cgroup 管理102cgroup って何 ? linux が持つプロセスをグループ化する仕組み control group の略 – –103主にリソース管理を行うために利用する 全てのプロセスはいずれかのグループに所属し、子プロセスは明示的な 移動がなければ親プロセスと同じグループに所属しつづけるcgroup の利用シーン例 104特定サービス , VM, コンテナだけで特定 CPU を占有、他のサービスや ユーザセッションでは利用できる CPU を制限 主にサービスを実施するマシンで –ユーザの対話セッションで利用できるリソースを制限–Ansible や Puppet が使うリソースを制限サービスに最低限必要なメモリ量を設定 (cgroup v2 が必要 )コンテナ毎に利用できる IOPS を設定 (cgroup v2 が必要 )cgroup によるリソース管理の特徴 cgroup はツリー状にグループを分類して、グループに対してリソースを割り当てる各グループ毎にリソース制御の設定が可能–CPU 使用率の比–IO スループット–メモリ +Swap の割り当て量の上限、下限–アクセスできるデバイス–ネットワークのクラス付け などcgroup v1 と、 cgroup v2 が存在する。 2020 年時点では cgroup v2 への過渡期。 –105systemd は cgroup v1 および v2 の両方に対応しているが、 cgroup のバージョンにより使 えるディレクティブが異なる。 systemd と cgroup の関係 各 unit には対応する cgroup がありリソース管理以外でも活用される – –リソース管理を行わない場合にも、各 unit から実行されるプログラムは対 応する cgroup 内で実行される。そのためあるプロセスがどの unit に所属 しているかが linux kernel により自動的に追跡される。systemd は cgroup を管理してリソース管理をおこなう –– 106systemd は各 unit を初期化する時にグループの作成・設定をおこなうcgroup のパスは slice, scope, service, socket, mount, swap のいずれかの unit に対応づけられ、各 unit の設定としてリソース制御の指定をおこなう systemd が直接管理する他に、部分的に管理を移譲する API がある cgroup と関連してよく使う 3 種類の unit slice: cgroup のグループ分け用の unit –service: systemd が管理するプロセスおよびその子プロセス群 –各 service は自動的に対応する名前の cgroup の中に入れられるscope: systemd 以外のプロセスが fork&exec& 管理をおこなう ものを API で登録する –107slice を単体で宣言する他 service の Slice= で指定して作成systemd-run, gdm, gnome-launchd などは作成したプロセスを scope で作成した cgroup の中に入れる デフォルトの 3 種類の slice User slice –ユーザセッションに対応するプロセスや service を格納する–各ユーザ用の systemd ( 後述 ) を起動–各ユーザの service を起動System slice –Machine slice –108システムサービスを格納する仮想マシン、コンテナ等を格納する user slice, system slice, machine slice109前述の 3 つの slice の下にユーザ毎、サービス毎、 VM 毎などツリー状に分類される systemd-cgls cgroup の階層構造と所属プロセスを一覧表示する110-.slice ├─user.slice │ └─user-11956.slice │ ├─session-3.scope │ │ ├─1909 gdm-session-worker [pam/gdm-password] │ │ ├─4727 /usr/bin/gnome-keyring-daemon --daemonize --login │ │ ├─7654 /usr/libexec/gdm-x-session --run-script /usr/bin/gnome-session │ │ ├─7663 /usr/libexec/Xorg vt2 -displayfd 3 -auth /run/user/11956/gdm/Xauth> │ │ ├─8020 /usr/libexec/gnome-session-binary │ │ └─8225 /usr/bin/ssh-agent /bin/sh -c exec -l /bin/bash -c "/usr/bin/gnome> │ └─user@11956.service │ ├─gsd-xsettings.service │ │ └─8764 /usr/libexec/gsd-xsettings │ ├─gvfs-goa-volume-monitor.service │ │ └─8657 /usr/libexec/gvfs-goa-volume-monitor │ ├─gsd-power.service │ │ └─8723 /usr/libexec/gsd-powersystemd-cgtop cgroup 毎に CPU, メモリ ,I/O 使用量を表示する。ここで表示 するには accounting(cgroup での資源利用追跡 ) が有効になっ ている必要がある。111Control Group / /system.slice /system.slice/NetworkManager.service /system.slice/atd.service /system.slice/auditd.service /system.slice/boot.mount /system.slice/chronyd.service /system.slice/cockpit.socket /system.slice/crond.service /system.slice/dbus.service /system.slice/dev-hugepages.mount /system.slic…-mapper-rhel\x2dswap.swapTasks 213 57 3 1 4 1 1 2 -%CPU -Memory 2.9G 2.7G 7.5M 644.0K 9.2M 92.0K 3.1M 2.2M 2.2G 28.8M 632.0K 456.0KInput/s Output/s -ユーザのリソース制限 各ユーザは user-.slice により定義されるので、この unit に対して定義を おこなうことでユーザのリソースを制限できる – –/etc/systemd/system/user-.slice.d/resources.conf 全ユーザに対して同じ設定を指定したい場合は、 user-.slice がテンプレートなので そこへ定義を記述することで全ユーザへ影響するログイン時に pam_systemd により cgroup の作成・切り替えが行われる –su や sudo では cgroup は切り変わらないのでリソース管理上は同じユーザのまま–cgroup を変えたい場合は systemd-run を使うか ssh などでログインしなおす設定例 : /etc/systemd/system/user-1000.slice.d/resources.conf 112[Slice] CPUQuota=120%関連する man page systemd.resource-control (5): unit に対する cgroup でのリ ソース制御。 cgroup v1 、 v2 の違いは Unified and legacy control group hierarchies 節にまとまっている。 RHEL7 ドキュメント「リソース管理ガイド」https://access.redhat.com/documentation/ja-jp/red_hat_enterpri se_linux/7/html-single/resource_management_guide/index 113linux kernel ドキュメント「 Control Group v2 」 https://www.kernel.org/doc/Documentation/cgroup-v2.txt やってみよう systemd-run -p CPUQuota=10% -t yes > /dev/null として (--user では制限されないので注 意) –top で CPU 消費量を見てリソース制限されていることを確認する–/proc//cgroup を見て cgroup 名を確認–/system.slice/run-u492.service のような名前なので、 /sys/fs/cgroup/cpu,cpuacct/ 以下から 対応するディレクトリをみつけ、 cpu.cfs_period_us, cpu.cfs_quota_us を比較する/etc/systemd/system/user-.slice.d/resources.conf に以下を記述する [Slice] CPUQuota=123%114ログインしなおしてから systemd-cgtop で消費の様子をみるsu, sudo 前後で /proc/self/cgroup を見て追跡されている様子を確認する ユーザセッション用 systemd115ユーザセッション管理 systemd-logind がユーザセッションを追跡する。 –116どのユーザが、いつから、どの seat( 通常ひと組のディスプレイ、キー ボード、 USB ポート等 ) からアクセスしているか、関係するプロセスは どれかの追跡–周辺装置の権限管理–idle 検出、サスペンド・レジューム、電源断–セッションのロック、強制終了、有効化、終了時の関連プロセス kill などloginctl コマンドで操作する systemd の仕組みをユーザ用に使う 各ユーザで systemd を利用できると便利 ユーザごとの service 管理 – –117tmux 、 emacs server 、デスクトップ環境での各種サービス等 ハードウェア利用 ( スマートカードBluetooth アダプタ等 ) の ためのサービス起動–システムのサービスと同じ仕組みで管理–今まで混在していたり失われていたログを journald へ蓄積 ユーザ用 systemd 各ユーザの systemd インスタンス (service manager) は systemd --user として起動される。 –– 118/lib/systemd/user , ~/.local/share/systemd/user, /etc/systemd/ user, ~/.config/systemd/user から unit file をロードする 起動時は default.target を active にするよう動作するsystemd のインスタンスと配下の service 群は「セッション 毎」には対応せず「ユーザ毎」に対応する。そのためログアウ トのタイミングで終了するとは限らない。 ユーザ用 systemd からのリソース管理 デフォルトではユーザ用 systemd から cgroup によるリソース管 理は memory, pid しか利用できない。他のリソースを管理するに はシステムの systemd から管理を移譲する設定が必要。 –例 : /etc/systemd/system/user@.service.d/override.conf [Service] Delegate=yes–119cat /sys/fs/cgroup/user.slice/user-11956.slice/cgroup.controllers のようにして cgroup の設定を確認ログイン時の動作 ログイン認証時にから pam_systemd を 動作させる。 pam_systemd はログインにあたりいくつ かの作業をおこなう –ユーザ用の /run/user// を作成–対話セッションに対応する番号を作成–120セッション毎の scope を作成、まだ存在 していなければユーザ毎の systemd イン スタンスを起動する ユーザプロセスの自動 kill ユーザ用 systemd で管理される service および scope 内のプロセスは以下の条件で kill される セッション終了した場合の動作は logind.conf 内の KillUserProcess= 設定による : – –No( デフォルト ): 特に何もしない Yes: ユーザがログアウトした時にそのセッションに対応する scope 以下のプロセスを kill する。さらに特定ユーザのみ kill 対象、特定ユーザのみ kill 対象外の設定も可。 121kiosk 端末などで便利だが tmux, nohup 等のログアウト後に実行を継続されてほし いソフトウェアにとっては想定と異なる動作になるユーザ用 systemd が SIGTERM, SIGINT をうけると shutdown.target が有効化され (Conflict しているサービスが終了す ) る。 loginctl enable-linger とは ? 英語の linger は「居残る、長引く、なかなか消えない」というような意味 ユーザは自分の全セッションが終了しても scope, service を自動で kill しないよう指 定することができ、 loginctl enable-linger がその指示を行うコマンド。 運用管理者は PolicyKit の設定で linger の指定を禁止することもできる。そのため実 用上は以下の 3 種類から動作を選べる – ––122enable-linger を禁止してセッション終了時に全プロセスを kill する (kiosk 端末など ) enable-linger したユーザのプロセスは kill しない、していないユーザのプロセスは kill する セッション終了をきっかけとして kill はしない ( デフォルト )関連する man page 123systemd-logind(8), logind.conf(5), loginctl(1): セッション管 理を行うサービス、設定ファイル、管理コマンド pam_systemd(8): ユーザログイン時の動作を行うための PAM モジュール systemd-user.conf(5): ユーザ用 systemd の設定は /etc/ systemd/user.conf に分けて定義される。記述内容はシステム 用のものと同じ。 やってみよう loginctl で現在のセッションを見たあと ––124loginctl show-session < セッション ID> で systemd-logind が追 跡しているものを見る loginctl seat-status でシートに対応づけられているデ バイスを見る。これらのデバイスは対応するセッションのユーザ からアクセスできるよう設定される。systemctl --user list-unit-files などを行いユーザセッション用 の unit file を眺める アドホックなコマンド実行の管理125systemd-run アドホックにプログラムを実行する際にも systemd の仕組みを使いたい –– 126ulimit, cgroup, ロギングなどの環境準備を自作しなくても systemd に任せられ る シャットダウン時に”正しいお作法で”終了してくれるsystemd-run で実行すると使い捨ての service または scope を作成する –service を activate する path,timer,socket も特定オプション作成–systemd-run --uid=apache /usr/bin/foobarsystemd-run 利用例 systemd-run ls –systemd-run --uid=apache -t ls –uid を apache にし、標準入出力を端末へ接続して ls する。systemd-run --scope ls ––127一時的な service を作成して systemd が実行。環境変数や標準出力が journal に記録されること、 CWD が / になることなども service と同様。一時的な scope を作成して systemd-run がその中に入って ls を実行。 cgroup でリソース管理される以外は直接実行するのとほとんど変わらない。 scope の場合は systemd から実行されない点が大きく異なる systemd-run の service と scope の違い service 親プロセスsystemdsystemd-run を実行したプ ロセス ( 多くの場合 sh)標準入出力journal 。 -t オプションな どで変更できる。親プロセスをひきつぐ環境変数systemd の service 用設定 に従う親プロセスをひきつぐcgroup, 環境変数、 uid,gid などなどcgroup だけsystemd-run -Ssystemd-run --scope bashsystemd が制御する環境 実験用 shell 起動 128scopesystemd-run を nohup のかわりに使う systemd-runnohupコマンド例loginctl enable-linger systemd-run foobarnohup foobar 2>&1 &ログ出力先journal オプションで任意ファイルnohup.logsystemd に unit 終了のログnohup.log を open しているプロ セスを fuser で探す終了確認SIGSTOP, SIGTERM SIGSTOP, SIGTERM シャットダウン時 -p ExecStop= 指定で任意コマン ド実行 129taskset, ulimit 等のかわりに systemd-run を使う systemd-run の -p オプションでリソース制御ができる。 -t オプションで端末との接続 を維持する。 systemd-run -t -p BlockIOWeight=10 count=10000 oflag=direct –core 出力サイズを抑制して実行するsystemd-run -t -p CPUAffinity=0-1 ./mytest –130cgroup v1 で IO のスロットリングをしつつ実行する。 cgroup v2 では BlockIOWeight ではなく IOWeight を使うsystemd-run -t -p LimitCORE=0 ./mytest –dd if=infile of=outfile実行可能 CPU を 0 と 1 だけに制約して実行する 関連する man page systemd-run(1): systemd-run の利用例などsystemd.resource-control(5): cgroup に指定できる項目131systemd.exec(5): プロセス実行で指定できる項目。 scope で は指定できない。やってみよう 132systemd-run env と systemd-run --user env で環境変数が異なる ( 後者はデスクトップ環境や X の設定なども含む ) ことを確認する systemd-run -S で service のコンテキストで shell を実行す る。 echo $PPID が 1 であること、 systemd-cgls で shell から実 行されているプロセスも含めて service 内にあることを確認する systemd-run -S -p PrivateTmp=yes -p ProtectHome=yes のよう なセキュリティ設定をおこなって /tmp や /home がどのように見 えるか確認する systemd-journald によるロギング133従来の syslog にあった課題 時刻が秒単位で粗いため複数ホストのログを集約すると前後関係がよくわか らない。時刻が localtime で夏時間がある地域では 1 時間隙間が進んだり戻ったりする接続元、指定された優先度などのメタデータが失われる134syslogd が起動する前の boot 直後のログを統合できない構造化されていないため DB などに投入しにくい。 NUL などを含むデータを ログに入れられないなど制限が厳しい。 保存されたログへのアクセス制御が粗いsystemd とログを統合したい 135systemd はシステム起動のごく初期から起動される →同じ基盤でログの仕組みを作れば起動直後から記録できる unit 用に用意する環境の標準出力 (stdout, stderr) を /dev/null へ捨てる のではなくログへ保存して簡単にサービスを作りたい systemd が管理している環境についての ID 各種 (machine ID, boot ID, cgroup 名、 unit 名など ) の情報をログに付与して、イメージを複製し た複数 VM や再起動の前後、どの unit からのログかを識別したいsystemd-journald によるロギング 詳細なメタデータ付加 –136マイクロ秒 64bit でのタイムスタンプ , 起動からの時間 , ログの優先度 , systemd の unit, cgroup, SELinux コンテキスト , boot ID, machine ID 等独自バイナリフォーマット –圧縮・インデクシング・改竄検出対応–テキスト log が必要な場合 rsyslog と併用する (RHEL 7, 8 でのデフォルト )自動的かつ透過的な rotatejournalctl コマンドによるクエリデフォルトでは journald のログは揮発性だが /var/log/journal を作ると永続化 systemd-journald イメージ図バイナリログ uid 毎に分離 ACL で本人には 読み込み許可/dev/kmsg syslog 対応 ソフト/dev/loguser-1000.journalsystemdjournaldsystem.journalunit 内 stdout, stderr journald 対応 ソフトアーカイブrsyslog/var/log/* 137journalctlテキストログ保存 従来との一貫性、互換性system@ 〜 .journalクエリにより journal file 群から ログを検索・整形・表示journald で付加されるメタデータ例138カーソル位置 時刻 (usec) 起動からの経過時間 BOOT ID ログ優先度 UID GID マシン ID ホスト名 syslog の ID と ファシリティ ソースコード 内の行番号 内の関数名 メッセージの ID 接続方法 PID コマンド名 実行ファイル CAPABILITY CGROUP コマンドライン SELinux コンテキスト systemd の unit 名 メッセージ本文 出力時の時刻 (usec)__CURSOR=s=70bed981e31a46e2a70c56776402e0eb;i=342;b=982826bc22454f079fd46ec94e2b __REALTIME_TIMESTAMP=1407830743019247 __MONOTONIC_TIMESTAMP=1792842 _BOOT_ID=982826bc22454f079fd46ec94e2b67fc PRIORITY=6 _UID=0 _GID=0 _MACHINE_ID=025b7ff2c8af43f78513996f06584c4c _HOSTNAME=localhost.localdomain SYSLOG_IDENTIFIER=systemd SYSLOG_FACILITY=3 CODE_FILE=src/core/unit.c CODE_LINE=1115 CODE_FUNCTION=unit_status_log_starting_stopping_reloading MESSAGE_ID=7d4958e842da4a758f6c1cdc7b36dcc5 _TRANSPORT=journal _PID=1 _COMM=systemd _EXE=/usr/lib/systemd/systemd _CAP_EFFECTIVE=1fffffffff _SYSTEMD_CGROUP=/ _CMDLINE=/usr/lib/systemd/systemd --switched-root --system --deserialize 20 _SELINUX_CONTEXT=system_u:system_r:init_t:s0 UNIT=rsyslog.service MESSAGE=Starting System Logging Service... _SOURCE_REALTIME_TIMESTAMP=1407830743019153journalctl によるクエリ例 journalctl コマンドのオプションで条件を指定。絞り込んで必要なログだけを抽出 139ログレベルの制限 journalctl -p 4 ← warning(4) 以上の priority であること ユニットの制限 journalctl -u NetworkManager.service ← 指定した unit からの出力であること 起動ごとの制限 journalctl -b -1← 直近の boot より 1 つ前の起動以降のログ 時間の制限 journalctl --since “2020-02-01” --until “2020-02-05” ← 時間ウィンドウの指定 任意のフィールド journalctl _COMM=dhclient ← < フィールド名 >=< 値 > で任意の値で絞り込み journald のディレクトリ構造 /run/log/journal ( 揮発 ) または /var/log/journal ( 永続 ) 以下に収集する –システムのログ .//system.journal–各ユーザのログ .//user-.journal ログを uid で分離せずにまとめる設定も可能だが journalctl は透過的にまとめて検索する ため ( ユーザが自分のログを見られなくなる以外に ) 特に使い勝手などの変化はない。–ローテートされたログ .//system@suffix.journal–ローテートされたログ .//user-@suffix.journal–140ACL で各ユーザ本人へのアクセス許可を付与しているログの破損を発見すると、 ログは *.journal~ という拡張子に変更され、新しいログへ書き出 しを行う journald の設定 /etc/systemd/journald.conf で設定する 圧縮有無、 Seal 処理 ( ハッシュを利用したログ改竄検出 ) 、ストレージへの同期間 隔、ログ出力レート制限 コンソールや syslog などへの forward の有無、ストレージへの保存や出力先毎の loglevel 指定 ログローテート –141サイズ毎、時間毎の rorate 処理アーカイブ削除 –journal 全体のサイズ上限、ストレージの残容量下限による削除、指定時間以上経過で削除–削除はローテートされたアーカイブファイル単位で行われる ログ保存とローテートの設定 journald のデフォルトではサイズベースでのローテートが指定 される –ファイルシステムの 10% か 4GB の内小さい容量をログに使用 – 142ログファイルはその 1/8 のサイズ (4GB の場合 512MB)ファイルシステムの 15% か 4GB の内小さい空き容量を残すMaxFileSec= を指定してで毎日 / 毎月などのタイミングでも ローテートを指定できる rsyslog への forward rsyslogd の imjournal plugin が journald の ログを監視して保存します –143逆に rsyslogd の出力先を journald にする omjournal plugin も提供されているuser-1000.journal system.journalRHEL のデフォルトでは journald は揮発性、 rsyslogd で永続化 記録される情報としては journald が多いが、 既存の syslog 対応ツールや外部との連携は rsyslog が便利なケースが多い imjournal rsysloglogger コマンドからのログ書き出し例144$ logger --journald <graphviz で指定 unit 群の依存関係を図示する。 systemd-analyze verify → lint 的に unit file をチェックする。ディレ クトリや Exec* の対象存在有無など。 systemd-analyze condition → 各 condition のチェックと全 体としての成否を表示する。 その他時刻や時間の表現がどう認識されるかテストする機能もあります。 systemd の log level を変える systemd がどこと通信して何をしているか見たいときは debug レベルのログを見ます 起動時の問題であれば grub で kernel のオプションに systemd.log_level=debug のように指定する ( 起動時はログがとても多いので注意 ) /etc/systemd/system.conf に LogLevel=debug と記述して systemctl daemonreload します。他の設定ファイルも全て読み直します。 実行中に loglevel だけを変更するには以下のように systemd-analyze コマンドで指 定する –現在の log level 確認 ( デフォルトは info): systemd-analyze log-level–log level を debug にする :systemd-analyze log-level debug 158unit の状態でシェルスクリプトの動作を変える unit の状態でシェルスクリプトなどの動作を変えたい場合は、 systemctl のサブコマンド is-active, is-enabled, is-failed, issystem-running を使う –159systemctl status や systemctl show だと sysvinit のスクリプト の状態が最新でない場合がある出力はテキストと exit code 。 --quiet オプションをつけると テキスト出力は抑制される。余談 : systemd-sysv-install 160sysvinit 形式のスクリプトがある場合の is-enabled の確認は /lib/systemd/systemd-sysv-install で行われる。 /lib/systemd/systemd-sysv-install は RHEL/Fedora では chkconfig の別名で enable/disable を sysvinit のディレクトリ の状態から返す systemctl status や systemctl show では unit の状態を返すが systemd-sysv-install によるチェックを行わないので最新でな い場合がある 関連する man page 161systemd-analyze(1): systemd-analyze の各サブコマンドの実 行例、出力例あり systemctl(1): is-enabled などの節には出力と対応する exit code の表ありやってみよう graphviz を入れて unit 間の依存関係画像を見てみる $ systemd-analyze dot 'cockpit.*' | dot -Tsvg >cockpit.svg $ eog cockpit.svg162systemd-analyze calendar daily として、 timer unit などで ’ daily’ と指定があると実際に何時何分を指しているのかを確認する systemd の log level を debug に変更し、 systemctl などのコマン ドを実行した時にどのようなログが出るのか見る周辺ツールなど163systemd-tmpfiles 一般に永続的でないディレクトリが多数ある /dev, /run, /tmp など –systemd-tmpfiles は起動時、または定期的なセットアップと掃除をこれらの ディレクトリに対して行う –ファイルやディレクトリ、リンク、パイプ、デバイスファイルなどを作成–特定ファイルを特定のコンテンツで初期化––164live CD のような環境では /var 以下も永続的であるとは限らない特定のディレクトリ以下のファイルを削除したり、ファイルそのものは残して truncate したり、 uid,gid, 権限 , ACL などを変更する 最終アクセスから指定時間以上経過していたら削除 など systemd-tmpfiles systemd-tmpfiles --cat-config で全設定を出力 – ––パッケージに含まれる設定を変更したい場合は /etc/tmpfiles.d 以下に /usr/lib/tmpfiles.d と同じ名前のファイルを作るとファイル単位で上書きされる 作成 ( または削除 ) されるディレクトリやファイルに親子関係があれば適切な順序 で実行する実行は systemd-tmpfiles-*.service, systemd-tmpfiles-*.timer から行われる。 –165設定は宣言的に行う。設定内容の読み方については tmpfiles.d(5) を参照デフォルトでは起動時に /dev/ とそれ以外に分けて setup を呼びだし、 起動から 15 分後に 1 回目の clean 、その後 1 日 1 回 clean を繰り返す hostnamectl ホスト名の設定 –hostnamectl set-hostname host.domain.example.orgホスト名の確認 $ hostnamectl$ hostnamectl Static hostname: rhel8.example.com Icon name: computer-vm Chassis: vm Machine ID: 21975e50f73142d5b4a92eb05209e9fa Boot ID: 83c339091094442b8645fec5699c7e11 Virtualization: kvm Operating System: Red Hat Enterprise Linux 8.1 (Ootpa) CPE OS Name: cpe:/o:redhat:enterprise_linux:8.1:GA Kernel: Linux 4.18.0-147.0.3.el8_1.x86_64 Architecture: x86-64166machine id と boot id 各ホストや起動・再起動での一意性を確保したいので UUID のよう な ID があると便利 ––167Machine ID: /etc/machine-id にある id 。ホスト毎に生成されランダ ム。 /etc/machine-id を削除して再起動すると自動で新しい乱数が生成 される。 Boot ID: /proc/sys/kernel/random/boot_id にある id 。起動毎に生成 されランダム。利用例 : journald によるログには machine id, boot id が含まれてい る。 関連する man page 168systemd-tmpfiles(8), tmpfiles.d(5): systemd-tmpfiles と設定 ディレクトリ、設定ファイルについて hostnamectl(1): ホスト名の設定、確認について random(4): linux kernel の random モジュールについて。 boot_id もこのモジュールが生成している。参考資料169man page 今まで参照した man page 群でわかるとおり systemd は多数の充 実した man page が提供される 170systemd.index(7) - systemd の man page 一覧 systemd.directives(7) - unit file の directive がどの man page に書かれているかの一覧。既存 unit file 読解時に便利ホームページ、 blog https://systemd.io/ systemd project ホームページ https://www.freedesktop.org/wiki/Software/systemd/ 旧 systemd ホームページ –171特に運用管理者には The systemd for Administrators Blog Series を推奨しますスライド資料 Demystifying systemd –2018 年版 : RHEL7 ベース。これから使いはじめる人むけ https://www.redhat.com/files/summit/session-assets/201 7/S103870-Demystifying-systemd.pdf–1722019 年版 : RHEL8 ベース。新機能に興味がある人むけ https://www.redhat.com/files/summit/session-assets/201 9/T4D2A2.pdf