AlmaLinux 9 のサーバー運用において、サービスの起動・停止・自動起動の管理は日常業務の中核です。RHEL 7 以降、サービス管理は SysVinit(service / chkconfig)から systemd(systemctl)に完全移行しました。本記事では、systemctl の基本操作からユニットファイルの自作、ドロップインによるカスタマイズ、依存関係の制御、セキュリティディレクティブ、cgroup v2 によるリソース制限まで、企業のサーバー運用で必要になる systemd の知識を実行例つきで体系的にまとめています。
- コマンド早見表
- 前提条件
- 1. systemd の基礎概念
- 2. systemctl 基本操作
- 3. ユニット一覧の確認
- 4. ターゲット(旧ランレベル)
- 5. マスク(mask / unmask)
- 6. ユニットファイルの構造と自作
- 7. ExecStart 周辺のディレクティブ
- 8. 環境変数の設定
- 9. サービスの再起動制御
- 10. 起動順序と依存関係
- 11. ドロップインファイルによるカスタマイズ
- 12. セキュリティディレクティブ
- 13. リソース制限(cgroup v2)
- 14. systemd-analyze(起動時間の分析)
- 15. トラブルシューティング
- AlmaLinux 9 総合リファレンスガイド シリーズ一覧
コマンド早見表
| やりたいこと | コマンド |
|---|---|
| サービス起動 | 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 |
前提条件
| 項目 | 値 |
|---|---|
| OS | AlmaLinux 9.6(Sage Margay) |
| systemd | 252(252-51.el9.alma.1) |
| cgroup | v2(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 start | systemctl start httpd |
| サービス停止 | service httpd stop | systemctl stop httpd |
| サービス再起動 | service httpd restart | systemctl restart httpd |
| 状態確認 | service httpd status | systemctl status httpd |
| 自動起動の有効化 | chkconfig httpd on | systemctl enable httpd |
| 自動起動の無効化 | chkconfig httpd off | systemctl disable httpd |
| 自動起動の確認 | chkconfig –list httpd | systemctl 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 |
| .slice | cgroup によるリソース管理の単位 | 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)
- Active:
active (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.target | 0 | シャットダウン |
| rescue.target | 1 | シングルユーザーモード(メンテナンス) |
| multi-user.target | 3 | CUI マルチユーザー(サーバーの標準) |
| graphical.target | 5 | GUI マルチユーザー(デスクトップ用途) |
| reboot.target | 6 | 再起動 |
| 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:サービス起動時に実行するコマンド
- ExecReload:
systemctl reload時に実行するコマンド - Restart:プロセスが異常終了したときの再起動ポリシー
- User / Group:サービスを実行するユーザーとグループ
[Install] セクション
- WantedBy:
systemctl enable時にシンボリックリンクが作成されるターゲット。multi-user.targetを指定すると、CUI マルチユーザーモードで自動起動する
Type の種類と選び方
| Type | 動作 | 使用例 |
|---|---|---|
| simple(デフォルト) | ExecStart のプロセスがメインプロセス。フォアグラウンドで動作する前提 | Go / Node.js 製アプリなどフォアグラウンド実行のプロセス |
| forking | ExecStart のプロセスが子プロセスを fork して終了する。子プロセスがメインプロセスになる | Apache httpd(-DFOREGROUND なし)、従来型デーモン |
| oneshot | ExecStart のプロセスが完了するまで待機。完了後に active 状態になる | 初期化スクリプト、一度だけ実行するタスク |
| notify | サービスが sd_notify() で準備完了を通知するまで待機 | sshd, httpd(systemd 対応ビルド) |
| exec | ExecStart のバイナリが実行された時点で起動完了とみなす | 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 を送信します。ExecReload は systemctl 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 | 最強 | このユニットも起動しない。さらに依存先が停止したらこのユニットも停止する |
通常は Wants と After の組み合わせを使います。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/myapp | ProtectSystem=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 はソフトリミットで、この値を超えるとメモリ回収が積極的に行われますが、プロセスは強制終了されません。MemoryHigh を MemoryMax より低い値に設定することで、ハードリミットに到達する前に緩やかなメモリ制御が行われます。
タスク数制限
[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'.
対処手順は以下の通りです。
journalctl -xeu myappで根本原因(設定ミス、ポート競合、権限不足など)を特定する- 原因を修正する
systemctl reset-failed myappで失敗カウンターをリセットするsystemctl start myappでサービスを起動する
Type の誤設定でサービスが即座に failed になる
fork して子プロセスで動作するデーモンに Type=simple を指定すると、親プロセスが fork 後に終了した時点で systemd がサービスの終了と判断し、ステータスが failed になります。
対処方法は、アプリケーションの動作方式に合った Type を設定することです。
- フォアグラウンドで動作する:
Type=simpleまたはType=exec - fork してバックグラウンドで動作する:
Type=forking(PIDFileの指定も推奨) - 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
