本記事には広告(アフィリエイトリンク)が含まれます。

AlmaLinux 9 systemd サービス管理

AlmaLinux 9 のサーバー運用において、サービスの起動・停止・自動起動の管理は日常業務の中核です。RHEL 7 以降、サービス管理は SysVinit(service / chkconfig)から systemd(systemctl)に完全移行しました。本記事では、systemctl の基本操作からユニットファイルの自作、ドロップインによるカスタマイズ、依存関係の制御、セキュリティディレクティブ、cgroup v2 によるリソース制限まで、企業のサーバー運用で必要になる systemd の知識を実行例つきで体系的にまとめています。

コマンド早見表

やりたいことコマンド
サービス起動systemctl start サービス名
サービス停止systemctl stop サービス名
サービス再起動systemctl restart サービス名
設定再読み込み(無停止)systemctl reload サービス名
自動起動の有効化systemctl enable サービス名
自動起動の有効化+即時起動systemctl enable –now サービス名
自動起動の無効化systemctl disable サービス名
稼働状態の確認systemctl status サービス名
自動起動の確認systemctl is-enabled サービス名
稼働中か確認systemctl is-active サービス名
失敗ユニット一覧systemctl –failed
全サービスの自動起動状態systemctl list-unit-files –type=service
稼働中サービス一覧systemctl list-units –type=service
ユニットファイル内容表示systemctl cat サービス名
デフォルトターゲット確認systemctl get-default
サービスのマスクsystemctl mask サービス名
ドロップイン編集systemctl edit サービス名
ユニットファイル変更の反映systemctl daemon-reload
依存関係の表示systemctl list-dependencies サービス名
セキュリティスコア確認systemd-analyze security サービス名
起動時間の分析systemd-analyze blame
cgroup リアルタイム監視systemd-cgtop

前提条件

項目
OSAlmaLinux 9.6(Sage Margay)
systemd252(252-51.el9.alma.1)
cgroupv2(unified)
実行ユーザーroot 権限(sudo su – 済み)

1. systemd の基礎概念

systemd は、Linux の起動プロセスを管理する init システムです。カーネルから最初に起動されるプロセス(PID 1)として動作し、すべてのサービスやマウントポイントの起動・停止・監視を一元管理します。

RHEL 6 までは SysVinit が使われており、サービス管理には service コマンドと chkconfig コマンドを使い分ける必要がありました。RHEL 7 以降は systemd に統一され、systemctl コマンドひとつでサービスの起動・停止・自動起動の管理がすべて行えるようになっています。

SysVinit との対応表

操作SysVinit(RHEL 6 以前)systemd(RHEL 7 以降)
サービス起動service httpd startsystemctl start httpd
サービス停止service httpd stopsystemctl stop httpd
サービス再起動service httpd restartsystemctl restart httpd
状態確認service httpd statussystemctl status httpd
自動起動の有効化chkconfig httpd onsystemctl enable httpd
自動起動の無効化chkconfig httpd offsystemctl disable httpd
自動起動の確認chkconfig –list httpdsystemctl is-enabled httpd

ユニットの種類

systemd はサービスだけでなく、マウントポイントやタイマーなどさまざまなリソースを「ユニット」として統一的に管理します。ユニットの種類は拡張子で区別されます。

拡張子用途
.serviceサービス(デーモン)の管理sshd.service, httpd.service
.socketソケットアクティベーションsshd.socket, cockpit.socket
.targetユニットのグループ化(旧ランレベルに相当)multi-user.target, graphical.target
.mountファイルシステムのマウントhome.mount, boot.mount
.timerタイマーによる定期実行(cron の代替)dnf-makecache.timer
.pathファイル・ディレクトリの監視systemd-ask-password-wall.path
.slicecgroup によるリソース管理の単位user.slice, system.slice
.scope外部プロセスの管理(systemd が直接起動しないプロセス)session-1.scope

AlmaLinux 9 の systemd バージョンは 252 です。バージョンの確認方法は以下の通りです。

実行コマンド:

# systemctl --version

実行結果:

systemd 252 (252-51.el9.alma.1)
+PAM +AUDIT +SELINUX -APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS -FIDO2 +IDN2 -IDN -IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK +XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified

default-hierarchy=unified は cgroup v2(unified モード)が有効であることを示しています。

2. systemctl 基本操作

サービスの起動・停止・再起動は、サーバー運用で最も頻繁に使う操作です。ここでは sshd を例に基本操作を解説します。

start / stop / restart

実行コマンド:

# systemctl start sshd
# systemctl stop sshd
# systemctl restart sshd

restart はサービスを一度停止してから起動します。SSH 接続中のサーバーで systemctl restart sshd を実行しても、既存のセッションは維持されます。ただし、restart の瞬間に新規接続は受け付けられないため、本番環境では後述の reload を優先してください。

reload(設定再読み込み)

reload はプロセスを停止せずに設定ファイルを再読み込みします。ダウンタイムが発生しないため、本番環境での設定変更後はまず reload を試みるのが基本です。

実行コマンド:

# systemctl reload sshd

reload に対応していないサービスがある

すべてのサービスが reload に対応しているわけではありません。reload 非対応のサービスに対して実行すると、エラーが返ります。reload に対応しているかどうかは、ユニットファイルに ExecReload ディレクティブが定義されているかで判断できます。

reload-or-restart

reload-or-restart は、reload に対応しているサービスでは reload を、対応していないサービスでは restart を自動的に選択します。サービスが reload に対応しているか不明な場合に使います。

実行コマンド:

# systemctl reload-or-restart sshd

enable / disable / enable –now

enable は OS 起動時にサービスを自動起動する設定です。enable 単体ではサービスはすぐには起動しません。即座に起動も行いたい場合は --now オプションを付けます。

実行コマンド:

# systemctl enable sshd
# systemctl enable --now sshd

自動起動を無効にする場合は disable を使います。disable もサービスの即時停止は行いません。

# systemctl disable sshd

status(稼働状態の確認)

status はサービスの稼働状態、PID、メモリ使用量、直近のログをまとめて表示します。障害調査の最初のステップとして使うコマンドです。

実行コマンド:

# systemctl status sshd

実行結果:

● sshd.service - OpenSSH server daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-03-24 14:28:59 JST; 9h ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 798 (sshd)
      Tasks: 1 (limit: 24743)
     Memory: 6.9M
        CPU: 2.951s
     CGroup: /system.slice/sshd.service
             └─798 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

出力の読み方は以下の通りです。

  • Loaded:ユニットファイルのパス、自動起動の状態(enabled)、プリセットの状態(preset: enabled)
  • Activeactive (running) は正常に稼働中。inactive (dead) は停止中、failed は異常停止
  • Main PID:メインプロセスの PID(798)
  • Tasks:サービスが使用中のタスク数と上限
  • Memory:サービスが使用しているメモリ量(6.9M)
  • CGroup:サービスが所属する cgroup パス

is-enabled / is-active / is-failed

スクリプトから状態を判定する場合は、以下のコマンドを使います。戻り値(終了コード)で判定できるため、シェルスクリプトの条件分岐に組み込めます。

実行コマンド:

# systemctl is-enabled sshd

実行結果:

enabled

実行コマンド:

# systemctl is-active sshd

実行結果:

active

実行コマンド:

# systemctl is-failed sshd

実行結果:

active

is-failed は、サービスが failed 状態であれば failed を返し終了コード 0 で終了します。正常稼働中であれば active を返し終了コード 1 で終了します。

3. ユニット一覧の確認

サーバーで稼働しているサービスや自動起動の状態を把握するために、一覧表示のコマンドを使い分けます。

list-units(ロード済みユニット)

実行コマンド:

# systemctl list-units --type=service

実行結果(主要部分を抜粋):

auditd.service           loaded active running Security Auditing Service
chronyd.service          loaded active running NTP client/server
crond.service            loaded active running Command Scheduler
firewalld.service        loaded active running firewalld - dynamic firewall daemon
NetworkManager.service   loaded active running Network Manager
rsyslog.service          loaded active running System Logging Service
sshd.service             loaded active running OpenSSH server daemon
systemd-journald.service loaded active running Journal Service

--type=service を付けることで、サービスユニットだけに絞り込んで表示できます。

list-units –failed(失敗ユニット)

実行コマンド:

# systemctl list-units --failed

実行結果:

  UNIT LOAD ACTIVE SUB DESCRIPTION
0 loaded units listed.

失敗ユニットがゼロであれば正常です。ここにサービスが表示された場合は、systemctl status サービス名journalctl -xeu サービス名 で原因を調査します。

list-unit-files(全ユニットファイルの状態)

list-unit-files はインストールされている全ユニットファイルとその自動起動状態を一覧表示します。list-units がロード済みユニットのみを表示するのに対し、list-unit-files はまだロードされていないユニットも含めて表示します。

実行コマンド:

# systemctl list-unit-files --type=service --state=enabled

実行結果(主要部分を抜粋):

auditd.service                     enabled enabled
chronyd.service                    enabled enabled
crond.service                      enabled enabled
firewalld.service                  enabled enabled
NetworkManager.service             enabled enabled
rsyslog.service                    enabled enabled
sshd.service                       enabled enabled

STATE 列の意味は以下の通りです。

  • enabled:自動起動が有効
  • disabled:自動起動が無効
  • masked:手動起動も含めて完全に無効化(第5章で解説)
  • static:[Install] セクションがないため enable/disable できないが、他のユニットから依存関係で起動される

cat / show(ユニットファイルの詳細確認)

systemctl cat でユニットファイルの内容を表示できます。ファイルの配置場所を覚えていなくても、サービス名だけで参照できます。

実行コマンド:

# systemctl cat sshd

systemctl show はユニットの全プロパティを表示します。特定のプロパティだけを取得する場合は -p オプションを使います。

実行コマンド:

# systemctl show sshd -p MainPID

実行結果:

MainPID=798

スクリプトからサービスの PID やメモリ使用量を取得する際に使います。

4. ターゲット(旧ランレベル)

SysVinit ではランレベル(0〜6)でシステムの動作モードを管理していましたが、systemd では「ターゲット」がその役割を担います。

ターゲットとランレベルの対応表

systemd ターゲット旧ランレベル用途
poweroff.target0シャットダウン
rescue.target1シングルユーザーモード(メンテナンス)
multi-user.target3CUI マルチユーザー(サーバーの標準)
graphical.target5GUI マルチユーザー(デスクトップ用途)
reboot.target6再起動
emergency.target最小構成の緊急シェル(root ファイルシステムのみ)

デフォルトターゲットの確認と変更

実行コマンド:

# systemctl get-default

実行結果:

multi-user.target

サーバー用途では multi-user.target が標準です。デフォルトターゲットを変更する場合は set-default を使います。

実行コマンド:

# systemctl set-default multi-user.target

切り戻し方法:

# systemctl set-default graphical.target

isolate(即時切り替え)

isolate は、指定したターゲットに即座に切り替えるコマンドです。緊急メンテナンス時にレスキューモードに切り替える場合に使います。

実行コマンド:

# systemctl isolate rescue.target

rescue.target に切り替えるとネットワークが停止する

isolate rescue.target を実行すると、ネットワーク関連のサービスが停止するため、SSH 接続が切断されます。リモートサーバーで実行する場合は、コンソールアクセス(IPMI、iLO、VNC など)が確保できていることを必ず確認してください。

5. マスク(mask / unmask)

mask は、サービスのユニットファイルを /dev/null へのシンボリックリンクに置き換えることで、手動での start も含めてサービスの起動を完全に拒否する仕組みです。

実行コマンド:

# systemctl mask firewalld

マスクされたサービスを起動しようとすると、以下のようにエラーになります。

# systemctl start firewalld

実行結果:

Failed to start firewalld.service: Unit firewalld.service is masked.

マスクを解除する場合は unmask を使います。

実行コマンド(切り戻し):

# systemctl unmask firewalld

mask と disable の違い

操作自動起動手動 start他ユニットからの起動
disable無効可能可能
mask無効拒否拒否

disable は自動起動を無効にするだけで、手動での起動は許可されます。mask はあらゆる方法での起動を拒否します。別のファイアウォール(iptables-nft など)を使う場合に firewalld を mask するといった使い方をします。

6. ユニットファイルの構造と自作

ユニットファイルは、systemd がサービスを管理するための設定ファイルです。INI ファイルに似た形式で、セクション([Unit]、[Service]、[Install])ごとにディレクティブを記述します。

配置場所と優先順位

パス用途優先順位
/usr/lib/systemd/system/パッケージ(RPM)が提供するユニットファイル
/etc/systemd/system/管理者が作成・カスタマイズするユニットファイル

/etc/systemd/system/ に同名のファイルがある場合、そちらが優先されます。パッケージ提供のユニットファイルを直接編集してはいけません。パッケージ更新時に上書きされるためです。カスタマイズには後述のドロップインファイル(第11章)を使います。

ユニットファイルの実例(sshd.service)

実際に AlmaLinux 9 にインストールされている sshd.service の内容を見てみます。

実行コマンド:

# systemctl cat sshd

実行結果:

[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.target
Wants=sshd-keygen.target

[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

各セクションの役割は以下の通りです。

[Unit] セクション

  • Description:サービスの説明文。systemctl status の出力に表示される
  • Documentation:参照すべきドキュメント(man ページなど)
  • After:このユニットより先に起動するべきユニット。起動順序の制御に使う
  • Wants:弱い依存関係。指定先が起動に失敗しても、このユニットは起動する
  • Requires:強い依存関係。指定先が起動に失敗すると、このユニットも起動しない

[Service] セクション

  • Type:サービスの起動方式(後述の Type 一覧を参照)
  • ExecStart:サービス起動時に実行するコマンド
  • ExecReloadsystemctl reload 時に実行するコマンド
  • Restart:プロセスが異常終了したときの再起動ポリシー
  • User / Group:サービスを実行するユーザーとグループ

[Install] セクション

  • WantedBysystemctl enable 時にシンボリックリンクが作成されるターゲット。multi-user.target を指定すると、CUI マルチユーザーモードで自動起動する

Type の種類と選び方

Type動作使用例
simple(デフォルト)ExecStart のプロセスがメインプロセス。フォアグラウンドで動作する前提Go / Node.js 製アプリなどフォアグラウンド実行のプロセス
forkingExecStart のプロセスが子プロセスを fork して終了する。子プロセスがメインプロセスになるApache httpd(-DFOREGROUND なし)、従来型デーモン
oneshotExecStart のプロセスが完了するまで待機。完了後に active 状態になる初期化スクリプト、一度だけ実行するタスク
notifyサービスが sd_notify() で準備完了を通知するまで待機sshd, httpd(systemd 対応ビルド)
execExecStart のバイナリが実行された時点で起動完了とみなすsimple と似るが、バイナリの実行失敗を検知できる

Type の誤設定に注意

fork して子プロセスで動作するデーモンに Type=simple を指定すると、親プロセスが終了した時点で systemd がサービス停止と判断し、即座に failed になります。デーモンの動作方式に合った Type を選択してください。判断に迷う場合は、まず Type=exec を試し、sd_notify() に対応しているなら Type=notify を使います。

ユニットファイルの自作

自社開発のアプリケーションを systemd で管理する場合のユニットファイル例です。Java アプリケーション(myapp.jar)をサービスとして登録します。

作成するファイル:/etc/systemd/system/myapp.service

[Unit]
Description=MyApp Application Server
After=network.target
Wants=network.target

[Service]
Type=exec
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -jar /opt/myapp/myapp.jar
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=10s
TimeoutStartSec=90s
TimeoutStopSec=30s

[Install]
WantedBy=multi-user.target

ユニットファイルを作成・変更した後は、daemon-reload で systemd にファイルの変更を認識させる必要があります。

実行コマンド:

# systemctl daemon-reload

daemon-reload を忘れるとユニットファイルの変更が反映されない

ユニットファイルを編集した後に daemon-reload を実行しないと、systemd は古い設定のままサービスを管理し続けます。設定変更が反映されない場合は、まず daemon-reload の実行漏れを疑ってください。

ユニットファイルの構文チェックは systemd-analyze verify で行えます。

実行コマンド:

# systemd-analyze verify /etc/systemd/system/myapp.service

エラーがなければ何も出力されません。構文エラーがある場合は、該当箇所とエラー内容が表示されます。

構文チェック後、サービスの有効化と起動を行います。

実行コマンド:

# systemctl enable --now myapp

7. ExecStart 周辺のディレクティブ

ExecStart の前後に処理を追加するディレクティブと、タイムアウトの設定方法を解説します。

ExecStartPre / ExecStartPost

ExecStartPre はサービスの起動前に実行するコマンドです。設定ファイルの存在確認やディレクトリの作成に使います。ExecStartPost は起動後に実行するコマンドで、ヘルスチェックや通知の送信に使います。

[Service]
ExecStartPre=/usr/bin/test -f /etc/myapp/config.yml
ExecStart=/usr/bin/myapp --config /etc/myapp/config.yml
ExecStartPost=/usr/bin/curl -sf http://localhost:8080/health

ExecStartPre が失敗(終了コード 0 以外)した場合、ExecStart は実行されません。設定ファイルの不備による起動失敗を事前に防げます。

ExecStop / ExecReload

ExecStop はサービス停止時に実行するコマンドです。指定しない場合、systemd はメインプロセスに SIGTERM を送信します。ExecReloadsystemctl reload 時に実行するコマンドです。

[Service]
ExecStop=/usr/bin/myapp --graceful-shutdown
ExecReload=/bin/kill -HUP $MAINPID

TimeoutStartSec / TimeoutStopSec

サービスの起動・停止に時間がかかる場合、デフォルトのタイムアウト(90秒)では不足することがあります。Java アプリケーションのように起動に時間がかかるサービスでは、タイムアウト値を調整します。

[Service]
TimeoutStartSec=180s
TimeoutStopSec=60s

TimeoutStartSec を超えても起動完了しない場合、systemd はサービスを failed とマークします。アプリケーションの起動時間を計測したうえで、余裕を持った値を設定してください。

8. 環境変数の設定

サービスに環境変数を渡す方法は 2 つあります。ユニットファイル内に直接記述する方法と、外部ファイルから読み込む方法です。

Environment(直接指定)

[Service]
Environment="JAVA_HOME=/usr/lib/jvm/java-17"
Environment="APP_ENV=production"

変数が少ない場合は Environment ディレクティブで直接指定します。1 行に 1 つの変数を記述します。

EnvironmentFile(外部ファイルから読み込み)

変数が多い場合や、環境ごとに値を切り替えたい場合は EnvironmentFile を使います。

[Service]
EnvironmentFile=/etc/sysconfig/myapp

環境変数ファイルの記述例(/etc/sysconfig/myapp):

JAVA_HOME=/usr/lib/jvm/java-17
APP_ENV=production
APP_PORT=8080

ファイルパスの先頭に -(ハイフン)を付けると、ファイルが存在しなくてもエラーになりません。sshd.service の EnvironmentFile=-/etc/sysconfig/sshd がこのパターンです。

[Service]
EnvironmentFile=-/etc/sysconfig/myapp

9. サービスの再起動制御

サービスが異常終了した際に自動的に再起動させるかどうかは、Restart ディレクティブで制御します。

Restart の設定値

再起動する条件使用場面
no再起動しない手動管理のサービス
on-failure異常終了(終了コード 0 以外、シグナルによる終了)のときだけ再起動一般的なサーバーアプリケーション(推奨)
always正常終了・異常終了を問わず常に再起動停止してはならないサービス
on-abnormalシグナル・タイムアウト・ウォッチドッグ異常時に再起動正常終了時には再起動したくない場合

RestartSec(再起動の間隔)

RestartSec は、再起動までの待機時間です。デフォルトは 100ms です。設定の不備で即座にクラッシュするサービスが高速で再起動を繰り返すことを防ぐため、本番環境では 5 秒以上を推奨します。

[Service]
Restart=on-failure
RestartSec=5s

再起動ループの防止(StartLimitIntervalSec / StartLimitBurst)

サービスが短時間に繰り返し失敗すると、systemd は再起動を停止します。この制御は StartLimitIntervalSec(監視期間)と StartLimitBurst(許容回数)で行います。

[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5

この例では「300 秒以内に 5 回の起動試行」を超えると、systemd はサービスの再起動を停止し、以下のメッセージをログに記録します。

Start request repeated too quickly.

再起動ループに陥った場合は、systemctl reset-failed サービス名 で失敗カウンターをリセットしてから原因を調査してください。

実行コマンド:

# systemctl reset-failed myapp

10. 起動順序と依存関係

サービス間の起動順序と依存関係は、ユニットファイルの [Unit] セクションで制御します。「順序」と「依存」は別の概念であり、それぞれ独立して設定する必要があります。

After / Before(起動順序)

After は「指定したユニットが起動した後に起動する」という順序の指定です。Before はその逆です。これらは起動の順番のみを制御し、依存関係は設定しません。

[Unit]
After=network.target postgresql.service

この例では、network.target と postgresql.service が起動した後に、このサービスが起動します。ただし、postgresql.service が起動しなくても、このサービスの起動は妨げられません(順序のみで依存なし)。

Wants / Requires / BindsTo(依存関係)

ディレクティブ強度依存先が失敗した場合
Wants弱いこのユニットは起動する
Requires強いこのユニットも起動しない
BindsTo最強このユニットも起動しない。さらに依存先が停止したらこのユニットも停止する

通常は WantsAfter の組み合わせを使います。Requires は、依存先が停止するとこちらも停止する副作用があるため、本当に連動が必要な場合にのみ使用します。

[Unit]
After=postgresql.service
Wants=postgresql.service

依存関係の確認

実行コマンド:

# systemctl list-dependencies sshd

このコマンドは sshd が依存しているユニットをツリー構造で表示します。逆方向の依存関係(sshd に依存しているユニット)を確認する場合は --reverse を付けます。

実行コマンド:

# systemctl list-dependencies sshd --reverse

変更の影響範囲を事前に把握する際に使います。sshd を停止したとき、他のどのサービスに影響するかを確認できます。

11. ドロップインファイルによるカスタマイズ

パッケージ提供のユニットファイルをカスタマイズする場合、ファイルを直接編集するのではなく「ドロップインファイル」を使います。ドロップインファイルは元のユニットファイルに対する差分設定で、パッケージ更新でユニットファイルが上書きされてもカスタマイズが保持されます。

systemctl edit(ドロップインファイルの作成)

実行コマンド:

# systemctl edit httpd

このコマンドはエディタが起動し、/etc/systemd/system/httpd.service.d/override.conf を作成します。保存して終了すると、daemon-reload が自動的に実行されます。

記述例(httpd のメモリ上限を追加する場合):

[Service]
MemoryMax=1G

systemctl edit –full(ユニットファイル全体のコピー編集)

ドロップインではなく、ユニットファイル全体を /etc/systemd/system/ にコピーして編集する場合は --full を付けます。

実行コマンド:

# systemctl edit --full httpd

--full を使うと /usr/lib/systemd/system/httpd.service の内容が /etc/systemd/system/httpd.service にコピーされ、そちらが優先されるようになります。ただし、パッケージ更新時に元ファイル側に修正が入っても反映されなくなるため、通常はドロップインファイルを推奨します。

systemd-delta(変更箇所の一覧表示)

システム上でカスタマイズされているユニットファイルの一覧を確認できます。

実行コマンド:

# systemd-delta

出力には、[EXTENDED](ドロップインで拡張)、[OVERRIDDEN](ファイル全体を上書き)、[MASKED](マスク済み)などの種別が表示されます。ドロップインファイルを削除して元に戻す場合の切り戻し手順は以下の通りです。

切り戻し方法:

# rm -rf /etc/systemd/system/httpd.service.d/
# systemctl daemon-reload

12. セキュリティディレクティブ

systemd にはサービスのセキュリティを強化するディレクティブが用意されています。サービスが侵害された場合の被害範囲を限定するために、本番環境では積極的に活用します。

User / Group

サービスを root 以外のユーザーで実行します。サービスが侵害された場合に、root 権限での操作を防ぐための基本的な対策です。

[Service]
User=myapp
Group=myapp

ProtectSystem / ProtectHome

ProtectSystem=strict は、ファイルシステム全体を読み取り専用にします。サービスが書き込みを必要とするディレクトリは ReadWritePaths で個別に許可します。ProtectHome=yes は、/home/root/run/user をサービスからアクセスできなくします。

[Service]
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/log/myapp /var/lib/myapp

その他のセキュリティディレクティブ

ディレクティブ効果
NoNewPrivileges=yesプロセスとその子プロセスが新たな特権を取得できなくなる
PrivateTmp=yesサービス専用の /tmp を作成する。他サービスの /tmp にアクセスできなくなる
ReadOnlyPaths=/etc指定パスを読み取り専用にする
ReadWritePaths=/var/log/myappProtectSystem=strict の環境で書き込みを許可するパスを指定する

セキュリティスコアの確認

systemd-analyze security でサービスのセキュリティスコアを確認できます。スコアは 0.0(最も安全)から 10.0(最も危険)の範囲で表示されます。

実行コマンド:

# systemd-analyze security sshd

セキュリティディレクティブを追加するたびにスコアが改善されるため、チューニングの指標として活用できます。

13. リソース制限(cgroup v2)

AlmaLinux 9 の systemd は cgroup v2(unified モード)を使用しています。cgroup v2 によって、サービスごとに CPU、メモリ、I/O、タスク数の上限を設定できます。1 つのサービスがサーバーのリソースを使い切ることを防ぐための仕組みです。

CPU 制限

[Service]
CPUQuota=50%

CPUQuota=50% は、サービスが使用できる CPU 時間をシングルコアの 50% に制限します。2 コアのサーバーで全コアを使わせたい場合は CPUQuota=200% と設定します。

メモリ制限

[Service]
MemoryMax=512M
MemoryHigh=400M

MemoryMax はハードリミットです。この値を超えると OOM Killer によってプロセスが強制終了されます。MemoryHigh はソフトリミットで、この値を超えるとメモリ回収が積極的に行われますが、プロセスは強制終了されません。MemoryHighMemoryMax より低い値に設定することで、ハードリミットに到達する前に緩やかなメモリ制御が行われます。

タスク数制限

[Service]
TasksMax=256

TasksMax はサービスが生成できるタスク(プロセス・スレッド)の上限です。fork 爆弾(無限にプロセスを生成する攻撃)への対策として有効です。

I/O 制限

[Service]
IOWeight=50

IOWeight は 1〜10000 の範囲でディスク I/O の優先度を設定します。デフォルトは 100 です。バッチ処理など優先度の低いサービスには低い値を設定することで、他のサービスへの I/O 影響を軽減できます。

リソース使用量の確認

特定のサービスの現在のメモリ使用量を確認する方法です。

実行コマンド:

# systemctl show sshd -p MemoryCurrent

実行結果:

MemoryCurrent=7319552

値はバイト単位で表示されます。7319552 バイトは約 7.0MB です。

systemd-cgtop(リアルタイム監視)

systemd-cgtop は、cgroup ごとの CPU・メモリ・I/O 使用量をリアルタイムで監視するコマンドです。top コマンドのサービス版と考えてください。

実行コマンド:

# systemd-cgtop

どのサービスがリソースを多く消費しているかを把握する際に使います。q キーで終了します。

14. systemd-analyze(起動時間の分析)

サーバーの起動が遅い場合、どのサービスがボトルネックになっているかを systemd-analyze で特定できます。

全体の起動時間

実行コマンド:

# systemd-analyze time

実行結果:

Startup finished in 501ms (kernel) + 1.574s (initrd) + 2.227s (userspace) = 4.303s

カーネル、initrd、ユーザースペースの各フェーズにかかった時間と合計時間が表示されます。

blame(サービスごとの起動時間)

実行コマンド:

# systemd-analyze blame

実行結果(先頭5件):

1.886s sys-devices-...-eth2.device
1.886s sys-subsystem-net-devices-eth2.device
1.873s sys-subsystem-net-devices-eth3.device
...

起動時間が長いユニットが上位に表示されます。特定のサービスの起動が遅い場合は、そのサービスの設定やリソース状況を確認します。

critical-chain(クリティカルパス)

特定のサービスが起動するまでのクリティカルパス(最も時間がかかった経路)を表示します。

実行コマンド:

# systemd-analyze critical-chain sshd.service

どのユニットの完了を待って sshd が起動したかがツリー構造で表示されます。起動順序を最適化する際の手がかりになります。

verify(構文チェック)

第6章でも触れましたが、ユニットファイルの構文チェックは systemd-analyze verify で行います。

実行コマンド:

# systemd-analyze verify /etc/systemd/system/myapp.service

構文エラーがある場合はエラーメッセージが表示されます。新規作成したユニットファイルは、daemon-reload の前にこのコマンドで検証する習慣をつけてください。

15. トラブルシューティング

サービスが起動しない

サービスが起動しない場合は、まず systemctl status で状態を確認し、次に journalctl で詳細なログを確認します。

実行コマンド:

# systemctl status myapp
# journalctl -xeu myapp

journalctl のオプションの意味は以下の通りです。

  • -x:カタログからの補足説明を表示
  • -e:ログの末尾にジャンプ
  • -u myapp:指定したユニットのログだけに絞り込み

ログの確認方法の詳細は「ログ管理編」で解説しています。

daemon-reload の実行漏れ

ユニットファイルを変更したのに反映されない場合は、daemon-reload を実行していないことが原因です。systemd は起動時にユニットファイルを読み込み、その後はメモリ上のキャッシュを参照します。ファイルを変更した後に daemon-reload を実行しないと、変更前の設定のまま動作し続けます。

実行コマンド:

# systemctl daemon-reload

なお、systemctl edit を使ってドロップインファイルを編集した場合は、保存時に daemon-reload が自動的に実行されるため、手動での実行は不要です。

再起動ループ(start request repeated too quickly)

サービスが短時間に何度も起動と失敗を繰り返すと、StartLimitBurst の上限に達して以下のメッセージが記録されます。

myapp.service: Start request repeated too quickly.
myapp.service: Failed with result 'start-limit-hit'.

対処手順は以下の通りです。

  1. journalctl -xeu myapp で根本原因(設定ミス、ポート競合、権限不足など)を特定する
  2. 原因を修正する
  3. systemctl reset-failed myapp で失敗カウンターをリセットする
  4. systemctl start myapp でサービスを起動する

Type の誤設定でサービスが即座に failed になる

fork して子プロセスで動作するデーモンに Type=simple を指定すると、親プロセスが fork 後に終了した時点で systemd がサービスの終了と判断し、ステータスが failed になります。

対処方法は、アプリケーションの動作方式に合った Type を設定することです。

  • フォアグラウンドで動作する:Type=simple または Type=exec
  • fork してバックグラウンドで動作する:Type=forkingPIDFile の指定も推奨)
  • sd_notify() で準備完了を通知する:Type=notify

ProtectSystem で書き込みが失敗する

ProtectSystem=strict を設定したサービスでは、ReadWritePaths で明示的に許可していないパスへの書き込みが拒否されます。ログに Read-only file system というエラーが記録されます。

対処方法として、サービスが書き込みを必要とするディレクトリを ReadWritePaths に追加します。ドロップインファイルで設定するのが安全です。

実行コマンド:

# systemctl edit myapp

記述内容:

[Service]
ReadWritePaths=/var/log/myapp /var/lib/myapp

切り戻し方法:

# rm /etc/systemd/system/myapp.service.d/override.conf
# systemctl daemon-reload

AlmaLinux 9 総合リファレンスガイド シリーズ一覧

  1. システム基本情報の確認
  2. 初期設定
  3. ネットワーク設定(nmcli)
  4. パッケージ管理(dnf)
  5. ユーザー・グループ管理
  6. ファイアウォール(firewalld)
  7. SSH設定・鍵認証
  8. systemd / サービス管理(この記事)
  9. ストレージ・LVM
  10. ログ管理(journalctl / rsyslog)
  11. cron / systemd timer
  12. セキュリティ強化
  13. パフォーマンス監視
  14. コンテナ管理(Podman)
  15. バックアップ・リストア