RHEL 基礎 #4 systemd 入門 — サービス、target、journalctl

読了 10分

#3 でパッケージのインストールができるようになりました。インストールしたものを 起動 / 停止 / 起動時に自動で上げる / ログを見る 作業はすべて systemd の上で起きます。RHEL 7 からの標準で、Ubuntu をはじめほぼすべてのモダン Linux が同じモデルを使います。

RHEL 基礎 シリーズでこの記事の位置:

systemd とは何か #

Linux のブートが終わると、ユーザー領域で最初に立ち上がるプロセスが PID 1 です。昔は init (System V init)、今はほぼ全ディストリビューションで systemd です。

PID 1 の確認
$ ps -p 1
   PID TTY          TIME CMD
     1 ?        00:00:01 systemd

$ ls -l /sbin/init
lrwxrwxrwx. 1 root root ... /sbin/init -> ../lib/systemd/systemd

systemd は単純な init ではなく システム全体を管理するマネージャ です。仕事を一行ずつ整理すると:

  • ブート順序を依存関係グラフで組み、並列で立ち上げる
  • サービス (デーモン) の起動 / 停止 / 再起動 / 自動復旧
  • マウント / スワップ / タイマー / ソケットなどの他のリソースも同じモデルで管理
  • すべての子プロセスのログを journald で一か所に集める

旧 init は /etc/init.d/<サービス> start のようなシェルスクリプトでサービスを立ち上げていました。直列で動いて遅く、デバッグも難しかったです。systemd は 宣言型 です。「このサービスはどんな形で立ち上がるべきか」をテキストファイル (unit) に書いておけば、残りは systemd が処理してくれます。

unit の種類 #

systemd が扱う基本単位が unit です。いくつかの種類があります。

拡張子意味
.serviceデーモン / コマンド (一番よく使う)nginx.servicesshd.service
.socketソケット起動cockpit.socket
.target複数 unit の束 (グループ)multi-user.target
.timer時間ベースのトリガー (cron 代替)dnf-makecache.timer
.mountファイルシステムのマウントhome.mount
.pathファイル変化の監視cups.path
.slice / .scopeリソースグループ (cgroup)user-1000.slice

この記事では一番よく出会う servicetarget を本格的に扱い、それ以外は流れだけ押さえます。

systemctl — systemd と対話するコマンド #

systemctl 一つでほぼすべての systemd 操作ができます。

状態を見る — status #

サービスの状態
$ systemctl status sshd
● sshd.service - OpenSSH server daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-04-12 10:01:32 JST; 1h ago
       Docs: man:sshd(8)
   Main PID: 942 (sshd)
      Tasks: 1 (limit: 4632)
     Memory: 5.2M
        CPU: 30ms
     CGroup: /system.slice/sshd.service
             └─942 "sshd: /usr/sbin/sshd -D ..."

Apr 12 10:01:32 rhel9-lab systemd[1]: Starting OpenSSH server daemon...
Apr 12 10:01:32 rhel9-lab sshd[942]: Server listening on 0.0.0.0 port 22.
Apr 12 10:01:32 rhel9-lab systemd[1]: Started OpenSSH server daemon.

status 一回で見えるもの:

  • Loaded — unit ファイルのパス、enabled (起動時に自動起動) かどうか
  • Active — 現在の動作状態 (active (running)inactive (dead)failed…)
  • Main PID — メインプロセスの PID
  • CGroup — 子プロセスがどの cgroup に属しているか
  • 直近 10 行ほどの ログ — 最近何が起きたか

問題診断の最初のコマンドはほぼ常に systemctl status <サービス> です。

起動 / 停止 / 再起動 #

lifecycle
$ sudo systemctl start nginx       # 起動
$ sudo systemctl stop nginx        # 停止
$ sudo systemctl restart nginx     # 再起動
$ sudo systemctl reload nginx      # 設定だけ再読込 (対応する場合)

restartreload は違います。

  • restart — プロセスを殺して立ち上げ直し。短いダウンタイムあり
  • reload — 同じプロセスが SIGHUP のような信号を受けて設定だけを再読込。ダウンタイムなし。すべてのサービスが対応するわけではない (代表的に nginx、sshd は対応)

運用で設定だけ変えたなら reload が安全で、メモリ状態もリセットしたいなら restart

起動時の自動起動 — enable / disable #

enable / disable
$ sudo systemctl enable nginx     # 起動時に自動起動
$ sudo systemctl disable nginx    # 自動起動をオフ
$ sudo systemctl enable --now nginx   # 即座に起動 + 起動時に自動

enablestart は別の動作だと覚えておいてください。

コマンド起動後
start✅ 起動❌ 上がらない
enable❌ 起動しない✅ 自動起動
enable --now✅ 起動✅ 自動起動

--now はよく使うオプションです。「いまインストールしたサービスを今も立ち上げて、次の起動でも上げる」がほぼ常に望む動作です。

一覧を見る #

list-units / list-unit-files
$ systemctl list-units --type=service                  # 現在メモリにあるサービス
$ systemctl list-unit-files --type=service             # ディスクにあるすべての service unit
$ systemctl list-units --state=failed                  # 失敗したサービスだけ

問題追跡の最初の一歩で --state=failed をよく見ます。起動直後に赤い行があるか確認。

target — システムの「モード」 #

旧 init の runlevel (0~6) を systemd は target に置き換えました。複数の unit をまとめた一種の「モード」です。

target旧 runlevel意味
poweroff.target0電源オフ
rescue.target1シングルユーザー応急モード
multi-user.target3テキストマルチユーザー (サーバーのデフォルト)
graphical.target5GUI まで立てたマルチユーザー
reboot.target6再起動
現在の target / デフォルトの target
$ systemctl get-default
graphical.target

$ systemctl is-active graphical.target
active

デフォルト target を変える #

GUI が要らない学習用マシンは multi-user.target で起動するとメモリ・CPU がぐっと軽くなります。

テキストモードで起動
$ sudo systemctl set-default multi-user.target
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.

$ sudo reboot

GUI に戻したくなれば set-default graphical.target。再起動なしで即座にモード切り替え:

即座に切り替え (一回だけ)
$ sudo systemctl isolate multi-user.target     # 今すぐテキストモードに
$ sudo systemctl isolate graphical.target      # GUI に戻す

応急 / 復旧モード #

ブートが壊れたマシンを救うときに使うモードです。GRUB ブートメニューから入ります。

target用途
rescue.targetシングルユーザーモード。ほぼすべてのサービスが立ち上がっていない状態 + root で進入
emergency.targetさらに小さいモード。ファイルシステムも read-only でマウント

GRUB 画面で e で編集し、カーネル行の末尾に systemd.unit=rescue.target を付けて Ctrl+X で起動。詳しいブート復旧は 上級 #1 で扱います。

最初の .service ユニットを直接作ってみる #

人がインストールしたサービスを扱うだけでは systemd の半分。自分で unit を一枚書いてみるとモデル全体が頭に入ります。

小さな練習スクリプト #

まず systemd が起動する小さなスクリプトを作ります。

/usr/local/bin/hello-loop.sh
#!/bin/bash
while true; do
    echo "[$(date +%H:%M:%S)] Hello from systemd"
    sleep 5
done
実行権限
$ sudo chmod +x /usr/local/bin/hello-loop.sh

unit ファイルを書く #

/etc/systemd/system/hello-loop.service
[Unit]
Description=Hello loop demo
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/hello-loop.sh
Restart=on-failure
RestartSec=2
User=nobody

[Install]
WantedBy=multi-user.target

三つのセクションがあります。

[Unit] — このユニット自体のメタデータ。

  • Descriptionsystemctl status で表示される一行説明
  • After — このユニットが起動する前に立ち上がっていてほしい他のユニット。依存関係。(強制依存は Requires)

[Service] — どう起動するか。

  • Type=simple — 最も一般的な形。ExecStart で立ち上げたプロセスがメインプロセス (他の形: forkingoneshotnotify…)
  • ExecStart — 立ち上げるコマンド。絶対パス
  • Restart=on-failure — 異常終了時に自動再起動 (always / no / on-success も)
  • RestartSec — 再起動の間隔 (秒)
  • User=nobody — どのユーザーで実行するか。セキュリティ上 root 以外 に落とすのが基本

[Install]enable したときの動作。

  • WantedBy=multi-user.target — この target が活性化されたときに一緒に起動。これがないと enable は動作しません。

有効化 #

登録 / 起動 / 確認
$ sudo systemctl daemon-reload                # 新しい unit ファイルを systemd に読ませる
$ sudo systemctl enable --now hello-loop
Created symlink ...

$ systemctl status hello-loop
● hello-loop.service - Hello loop demo
     Loaded: loaded (/etc/systemd/system/hello-loop.service; enabled; ...)
     Active: active (running) since ...
   Main PID: 12345 (hello-loop.sh)
     ...
     CGroup: /system.slice/hello-loop.service
             ├─12345 /bin/bash /usr/local/bin/hello-loop.sh
             └─12389 sleep 5

落とし穴daemon-reload の忘れが一番よくあります。unit ファイルを新しく作ったり修正したら、必ず daemon-reload — でないと systemd が以前の定義で動きます。

unit ファイルをどこに置くかも大事です。

パス用途
/usr/lib/systemd/system/パッケージがインストールした unit (直接編集禁止)
/etc/systemd/system/ユーザーが作る / 上書きする unit
~/.config/systemd/user/ユーザー単位 (user systemd)

同名のファイルが両方にあれば /etc/systemd/system/ が優先 されます。パッケージ unit の一部だけ変えたい場合は systemctl edit <サービス> — drop-in ディレクトリに override ファイルを作ってくれます。

drop-in override
$ sudo systemctl edit nginx
# エディタが開いて /etc/systemd/system/nginx.service.d/override.conf を編集
[Service]
Environment="NGINX_OPTS=-q"
LimitNOFILE=65536

オリジナルを触らずに設定を上書きできます。パッケージ更新でオリジナルが新しくなっても override は残ります。

journalctl — すべてのログが集まるところ #

systemd には journald というログデーモンが一緒に入っています。すべての unit の stdout/stderr、カーネルメッセージ、syslog まで一か所に集まります。

基本
$ journalctl                           # 全部 (Page Down でスクロール)
$ journalctl -e                        # 末尾 (一番最近) へ
$ journalctl -f                        # リアルタイム follow (tail -f のように)
$ journalctl -n 100                    # 直近 100 行だけ

フィルタ #

ユニット / ブート / 時間 / 優先度
$ journalctl -u nginx                  # nginx unit のログだけ
$ journalctl -u nginx -f               # nginx をリアルタイムで

$ journalctl -b                        # 今回の起動のログだけ
$ journalctl -b -1                     # 一つ前の起動
$ journalctl --list-boots              # 保存されている起動の一覧

$ journalctl --since "2026-04-12 10:00" --until "2026-04-12 11:00"
$ journalctl --since "1 hour ago"
$ journalctl --since today

$ journalctl -p err                    # err 以上の優先度
$ journalctl -p warning..err           # 範囲

よく使う組み合わせ #

実戦
$ journalctl -u sshd -f                # sshd だけリアルタイムで見る
$ journalctl -u nginx --since today -p err  # 今日のエラーだけ
$ journalctl _PID=12345                # 特定 PID のログ
$ journalctl _COMM=sudo                # sudo コマンド自体のログ

filter の _PID_COMM_UID のようなキーは journald が自動で埋めるメタデータです。journalctl -F _COMM でどんな値があるか覗けます。

journald の保存 #

デフォルトは 揮発性 で、再起動すると以前のログが消えます。永続保存を有効にするには次のコマンドを実行します。

永続保存
$ sudo mkdir -p /var/log/journal
$ sudo systemd-tmpfiles --create --prefix /var/log/journal
$ sudo systemctl restart systemd-journald

その後 journalctl --list-boots で複数の起動が表示されれば永続保存がオンになっています。ディスク使用量の制限は /etc/systemd/journald.confSystemMaxUse=1G のような値で。

/etc/init.d/ はどこへ行ったか #

古い資料を見ると service nginx start/etc/init.d/nginx start といった表現がよく出ます。RHEL 9 ではどちらも動きますが、内部で systemctl に変換されます。

旧コマンド (互換用)
$ sudo service nginx status        # systemctl status nginx に変換される
$ sudo chkconfig nginx on          # systemctl enable nginx に変換される

新しく覚える方は最初から systemctl で行くべきです。旧コマンドは古い資料を読むときに知っておく程度。

よく使うコマンド一表 #

コマンドする仕事
systemctl status <unit>状態 + 直近ログ
systemctl start/stop/restart/reload <unit>lifecycle
systemctl enable [--now] <unit>起動時自動 (+ 即座に起動)
systemctl disable [--now] <unit>自動起動解除 (+ 即座に停止)
systemctl is-active/is-enabled <unit>一語で答え
systemctl list-units --state=failed失敗した unit だけ
systemctl daemon-reloadunit ファイル修正後に再読込
systemctl get-default / set-defaultデフォルト target の確認 / 変更
systemctl isolate <target>即座に target を切り替え
systemctl edit <unit>drop-in override 編集
journalctl -u <unit> [-f]その unit のログ (+ リアルタイム)
journalctl -b [-1]今回/前回起動のログ
journalctl --since "..." --until "..."時間範囲
journalctl -p err優先度フィルタ

よく出会う落とし穴 #

「ユニットが見えない」 #

新しい unit ファイルを作ったのに systemctl start が “Unit not found” なら、ほぼ常に daemon-reload 忘れ。unit ファイルをディスクに置くだけで systemd が自動認識するわけではありません。

「enable したのに起動後に上がらない」 #

unit ファイルに [Install] セクションがない場合。WantedBy=multi-user.target 一行があって初めて enable に意味があります。systemctl enable が “no installation config” 系のメッセージを出したらこの問題です。

「Active: failed」 #

何が悪いか一番速く確認するには journalctl -u <unit> -e。起動直後にどんなエラーで死んだかを一画面で見せてくれます。

よくある原因:

  • ExecStart のコマンドが 相対パス (絶対パスでなければならない)
  • 実行ファイルに実行権限がない (chmod +x)
  • User= で指定したユーザーがファイルにアクセスできない (権限)
  • Type= が実際の動作と合っていない

「ログが多すぎる」 #

journalctl だけ叩くと起動以降のすべてが出ます。ほぼ常に -u <unit>-b--since のいずれかを付けて絞ります。

まとめ #

この記事で押さえた絵:

  • systemd が RHEL の PID 1 — すべてのブート/サービスを握っているマネージャ
  • unit は systemd が扱う単位。servicetargettimermount など複数の種類
  • systemctl status / start / stop / restart / reload / enable / disable が日常コマンド。enable --now が実際にもっともよく使うオプション
  • target は旧 runlevel の代替。multi-user.target (テキスト) / graphical.target (GUI) / rescue.target (応急)
  • 自分で書いた .service ユニットは /etc/systemd/system/ に置き、daemon-reload 後に enable --now
  • systemctl edit でパッケージ unit を触らずに drop-in override
  • journalctl がすべてのログが集まるところ — -u-b--since-p-f の組み合わせで絞って見る

次 — ユーザーと権限 #

systemd がサービスをどのユーザーで起動するかは unit の User= 一行で決まります。そのユーザーがシステムにどう存在し、どのファイルにアクセスできるかのモデルが次の記事のテーマです。

#5 ユーザー/グループ/権限 — UID/GID、sudo、ACL では /etc/passwd/etc/shadow の形、useradd / usermod / groupadd のコマンド群、ファイル権限 rwx の意味と chmod の二つの表記、もっと細かい権限が必要なときに使う ACL (getfacl/setfacl)、そして sudo/etc/sudoers.d/ までを一度に押さえます。

X