演習進捗 0 / 4 未完了
⏱ 約 40 分 · systemd・電源断テスト
Part 3Chapter 01/サーバ運用

systemd でサービスを常駐させる

自作のスクリプトを systemd に登録し、Proxmox から本当に電源を落として入れ直したあとに、サービスが自力で起き上がることを確かめます。「自動起動が設定できている」と「実際に起き上がる」は別物です。

教材は登録なしで読めます。実機でコマンドを試す場合のみ、Google ログインで2時間枠の予約が必要です。
所要時間約 40 分
章タイプstandard
前提知識Part 1 Chapter 6(シェルスクリプトを書いて実行できる)
使用機材踏み台 workstation01 + 演習用サーバ learner01(Proxmox 電源操作あり)
関連章後: Chapter 2「journalctl でログを運用者として読む」/ 後: Chapter 5「ディスクを拡張する」

電源を落として入れ直しても、サービスは自分で起き上がるか

サーバを運用していると、いつか必ずこの場面に出会います。

ある深夜
深夜 2 時。停電アラートが鳴った。UPS が切れて、サーバの電源が落ちた。
朝、電源を入れ直した ── Web サービスは自動で起き上がっているか?

手で python3 app.py &./server.sh を叩いて動かしているだけなら、答えは「起き上がらない」です。誰かが SSH でログインしてコマンドを打つまで、サービスは止まったままになります。

Linux の標準的な解が systemd です。サービスを systemd に「登録」しておけば、電源断や再起動のあとでも OS の起動手順の一部としてサービスが立ち上がります。設定ファイル(ユニットファイル)に WantedBy=multi-user.target と書くだけで、その仲間入りができます。

この章の核心は 本当に電源を切ってみることです。

この章で通す 4 段
① サービスを systemd に登録する(ユニットファイルを書く)
          ↓
② systemctl enable で「OS 起動時に自動起動する」設定を入れる
          ↓
③ Proxmox ホストから電源断相当(qm stop)→ 再投入(qm start)
          ↓
④ SSH で戻り、systemctl is-active が active ── enable が効いている証拠

クラウドの「インスタンスを再起動」ボタンの裏側では、OS が起動するたびにこの仕組みが動いています。ただし、そこで何が起きているかは画面からは見えません。本章では自分でユニットファイルを書き、本物の電源断で確かめます

この章のゴール: 小さなサービス hello-logger を systemd に登録し、Proxmox から電源を落として入れ直したあとに自動起動していることを確認します。章末のクリーンアップで演習前のクリーン状態に戻します。

この章を終えるとできること

サービス常駐の 4 動詞を手に覚えます。

$ systemctl
status svc
Active:
active (running)
① 読む
サービスの状態を
3 つの動詞で見る
status · is-active
[Service]
ExecStart=
/usr/local/bin/…
daemon-reload
② 登録する
スクリプトを
ユニットにする
.service · daemon-reload
$ systemctl
enable --now
$ is-enabled
enabled
③ 仕込む
今すぐ起動と
次回自動起動
enable --now
qm stop <VMID>
qm start <VMID>
$ is-active
active
④ 確かめる
本物の電源断を
越えられるか
qm stop · qm start

systemd のしくみを解きほぐす

1. ユニットファイルの構造:3 つのセクション

systemd は Linux の「最初のプロセス(PID 1)」として起動し、そのあとのすべてのサービス・マウント・タイマーを管理します。管理の単位を ユニット(unit) と呼び、その設定ファイルを ユニットファイル と言います。

サービス用のユニットファイル(.service)は 3 つのセクションでできています。

hello-logger.service(この章で書くもの)
[Unit]  ── このユニット自身の説明と、起動順序の依存関係
Description=Hello Logger - 演習用タイムスタンプ記録サービス
After=network.target

[Service]  ── プロセスをどう起動・管理するか
Type=simple
ExecStart=/usr/local/bin/hello-logger.sh
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]  ── どの起動フェーズに組み込むか
WantedBy=multi-user.target
セクション主なキー役割
[Unit]After=依存するサービスより後に起動する、という順序の指定
[Service]ExecStart=起動するコマンド(絶対パスで書く
[Service]Restart=異常終了したときに自動で再起動するか
[Install]WantedBy=どの「起動ターゲット」に組み込むか

/etc/systemd/system/ に置いたユニットファイルは、systemctl daemon-reload を実行して初めて systemd に読み込まれます。ファイルを書いただけでは反映されません。

2. enablestart の違い:自動起動と即時起動

ここが最も混乱しやすいところです。名前が似ていますが、まったく別のことをします。

3 つの言い方
systemctl start <svc>         # 今すぐ起動する。再起動したら消える
systemctl enable <svc>        # 次回起動時から自動起動する。今は起動しない
systemctl enable --now <svc>  # 両方まとめて(実務で最もよく使う)
コマンド今すぐ起動する再起動後も起動する
start のみ
enable のみ
enable --now

enable が効いているかは systemctl is-enabled <svc> で確かめます。enabled なら設定済み、disabled なら毎回手で start する必要があります。

3. Restart=enable:似て非なる 2 つの復活

どちらも「勝手に起き上がる」仕組みですが、効く場面が違います

2 つの復活の守備範囲
Restart=on-failure
└── サービスが異常終了したとき、systemd が同じ OS セッションの中で
    自動的に再起動する(電源断・再起動とは無関係)

WantedBy=multi-user.target(= enable の効果)
└── OS が multi-user.target フェーズに達したとき、このサービスを起動する
    (電源断後・再起動後に「OS が起動するたびに」立ち上がる根拠)

電源を切って入れ直したあとにサービスが起き上がるのは、Restart= ではなく enable(=WantedBy=)の効果です。電源が落ちれば systemd 自身も一度死ぬので、Restart= が面倒を見る相手はもういません。

実機価値: enable が本当に効いているかは、実際に電源を切って入れ直すまで分かりませんis-enabledenabled と答えても、それは「設定が入っている」という申告にすぎません。本章では Proxmox の qm stop / qm start で電源断相当の操作を行い、サービスが自力で起き上がる瞬間を SSH で確認します。

試してみよう:サービスを作り、電源断を越えさせる

5 秒おきに時刻を出すだけの小さなサービス hello-logger を作り、systemd に登録し、電源断のあとに自動起動することを確認します。

項目
出発地点踏み台 workstation01
演習対象learner01(サービス作成・systemd 操作)
Proxmox 操作Proxmox ホスト(<Proxmox管理IP>
ゲスト操作ユーザーops(NOPASSWD sudo)
所要時間約 30 分(8 ステップ)
失敗しても安全です: この演習で作るのは /usr/local/bin/hello-logger.sh/etc/systemd/system/hello-logger.service の 2 ファイルだけです。STEP ⑧ のクリーンアップを上から順に実行すれば、どの段階からでもクリーン状態に戻せます。VMID の確認方法: Proxmox ホストで qm list | grep learner01 を実行すると、左端の数字が VMID です。以降 <VMID> と記します。

STEP 01 · 現在の状態を確認する

workstation01 から learner01 に SSH 接続し、いま systemd で動いているサービスを眺めます。

bash · workstation01 → learner01
# workstation01 から learner01 へ接続
ssh ops@learner01

# 今まさに動いているサービスの一覧
systemctl list-units --type=service --state=running
出力例(抜粋)
UNIT                       LOAD   ACTIVE SUB     DESCRIPTION
cron.service               loaded active running Regular background program processing daemon
dbus.service               loaded active running D-Bus System Message Bus
qemu-guest-agent.service   loaded active running QEMU Guest Agent
rsyslog.service            loaded active running System Logging Service
ssh.service                loaded active running OpenBSD Secure Shell server
systemd-journald.service   loaded active running Journal Service
(中略)

いま自分が使っている SSH も ssh.service という 1 つのサービスとして systemd に管理されています。この章の終わりには、ここに hello-logger.service が並びます。

前セッションの残骸チェック: systemctl status hello-logger を実行してみてください。Unit hello-logger.service could not be found. と出ればクリーン状態です。activefailed が出る場合は前回の演習が途中で終わった名残なので、先に STEP ⑧「章末クリーンアップ」を実行してから STEP ① に戻ってください。

STEP 02 · サービスにするスクリプトを作る

5 秒おきに時刻を出力するだけのスクリプトを /usr/local/bin/hello-logger.sh として作ります。

bash · learner01
# スクリプトを作成
sudo tee /usr/local/bin/hello-logger.sh << 'EOF'
#!/bin/bash
while true; do
    echo "$(date '+%Y-%m-%d %T') [hello-logger] alive"
    sleep 5
done
EOF

# 実行権限を付ける
sudo chmod 755 /usr/local/bin/hello-logger.sh

systemd に預ける前に、手で動かして確かめておきます。

bash · learner01
/usr/local/bin/hello-logger.sh

5 秒ごとに時刻付きの行が出れば成功です。Ctrl + C で止めてください。止めないと次の手順に進めません。

ここで確認していること: サービス化したあとに動かないとき、原因が「スクリプト自体」なのか「systemd への登録」なのかを切り分けられなくなります。先に素の状態で動くことを見ておくと、あとの調査が一段楽になります。

STEP 03 · ユニットファイルを書く

/etc/systemd/system/hello-logger.service を作ります。§03 で見た 3 セクションの形です。

bash · learner01
sudo tee /etc/systemd/system/hello-logger.service << 'EOF'
[Unit]
Description=Hello Logger - 演習用タイムスタンプ記録サービス
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/hello-logger.sh
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

ExecStart= には絶対パスを書く: systemd はあなたのシェルとは別の環境でプロセスを起動するため、PATH が同じとは限りません。hello-logger.sh ではなく /usr/local/bin/hello-logger.sh と、必ずフルパスで書いてください。相対パスで書くと起動に失敗します。

STEP 04 · systemd に読み込ませて起動する

ユニットファイルを systemd に認識させてから、サービスを起動します。

bash · learner01
# ユニットファイルの追加・変更を反映する
sudo systemctl daemon-reload

# 「今すぐ起動」と「次回から自動起動」を同時に設定
sudo systemctl enable --now hello-logger

# 状態を確認
systemctl status hello-logger
期待される出力
● hello-logger.service - Hello Logger - 演習用タイムスタンプ記録サービス
     Loaded: loaded (/etc/systemd/system/hello-logger.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-07-28 09:59:34 JST; 17ms ago
   Main PID: 2792 (hello-logger.sh)
      Tasks: 2 (limit: 2255)
     Memory: 596.0K (peak: 780.0K)
        CPU: 3ms

見るところは 2 か所です。Active: 行が active (running) であること、そして Loaded: 行の末尾に enabled と出ていること。この enabled が「次回も起き上がる」という申告です。

bash · learner01
# 自動起動の設定だけを 1 行で確認する
systemctl is-enabled hello-logger
期待される出力
enabled

ここまでで「設定は入った」状態です。本当に効くかどうかは STEP ⑥ 以降で確かめます。

STEP 05 · 壊す(1)── プロセスを強制終了して Restart= を見る

まず小さいほうの復活を見ます。kill -9 でプロセスを叩き落とし、Restart=on-failure が拾ってくれるかを観察します。

bash · learner01
# いまのメインプロセス ID を控える
systemctl status hello-logger | grep "Main PID"

# 強制終了(異常終了させる)
sudo kill -9 $(systemctl show hello-logger --property MainPID | cut -d= -f2)

# RestartSec=5s の待機ぶん待ってから確認
sleep 6
systemctl status hello-logger
期待される出力
● hello-logger.service - Hello Logger - 演習用タイムスタンプ記録サービス
     Loaded: loaded (/etc/systemd/system/hello-logger.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-07-28 09:59:47 JST; 989ms ago
   Main PID: 2867 (hello-logger.sh)
7月 28 09:59:42 learner01 systemd[1]: hello-logger.service: Failed with result 'signal'.
7月 28 09:59:47 learner01 systemd[1]: hello-logger.service: Scheduled restart job, restart counter is at 1.
7月 28 09:59:47 learner01 systemd[1]: Started hello-logger.service - Hello Logger - 演習用タイムスタンプ記録サービス.

Main PID が変わっていることに注目してください(例では 2792 → 2867)。同じサービスですが、中身のプロセスは別物に入れ替わっています。restart counter is at 1 が「1 回目の再起動をやった」という systemd の申告です。

systemctl stop との違い: systemctl stop でサービスを止めた場合、Restart=on-failure があっても再起動しません。こちらは「正常な停止」だからです。on-failure が反応するのは異常終了(kill シグナルや 0 以外の終了コード)に限られます。止めたいのに勝手に起き上がると感じたときは、この違いを思い出してください。

STEP 06 · 壊す(2)── Proxmox から電源を落とす

この章の核心です。Proxmox ホストから VM の電源そのものを落とします。OS のシャットダウン処理を挟まない、コンセントを抜くのに近い操作です。

workstation01別のターミナルを開いて、Proxmox ホストに接続してください。<Proxmox管理IP> は受講環境の案内に記載された IP に読み替えます。

bash · Proxmox ホスト
# 別ターミナルで Proxmox ホストに接続
ssh ops@<Proxmox管理IP>

# VMID を確認(左端の数字)
qm list | grep learner01

# learner01 を停止する(電源断相当)
qm stop <VMID>

qm stop は成功しても何も出力しません。代わりに、さきほどまで使っていた learner01 のターミナルが切れます(Connection to learner01 closed. と出るか、無反応になります)。これが電源が落ちた合図です。

bash · Proxmox ホスト
# 本当に止まったかを確認
qm status <VMID>

# 電源を入れ直す
qm start <VMID>
期待される出力
status: stopped
(qm start の標準出力なし)

ここで「起きるはずのないこと」を確認しています: 電源が落ちた時点で、Restart=on-failure の面倒を見る systemd 自身も消えています。次に hello-logger が動いていたとしたら、それは Restart= ではなく OS の起動手順に組み込まれていたからとしか説明がつきません。

STEP 07 · 戻る ── 自動起動していることを確かめる

qm start からおよそ 20〜30 秒待ってから、workstation01 の元のターミナルで learner01 に接続し直します。

bash · workstation01 → learner01
# 再接続
ssh ops@learner01

# 自分で起き上がっているか
systemctl is-active hello-logger
期待される出力
active

この active の 1 行が、enable が効いていた証拠です。誰も start を打っていないのに、OS が起動の途中で hello-logger を立ち上げました。

bash · learner01
# いつ起動したのかも見る
systemctl status hello-logger
期待される出力
● hello-logger.service - Hello Logger - 演習用タイムスタンプ記録サービス
     Loaded: loaded (/etc/systemd/system/hello-logger.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-07-28 10:00:19 JST; 2min 16s ago
   Main PID: 1084 (hello-logger.sh)
      Tasks: 2 (limit: 2255)
     Memory: 748.0K (peak: 1.3M)
        CPU: 61ms

Main PID にも注目してください。STEP ⑤ では 2867 でしたが、ここでは 1084 と小さい数字に戻っています。PID は OS が起動してから順に振られるので、小さい PID は「起動直後に立ち上がった」印です。OS ごと作り直されたことがここにも表れています。

合格の条件: systemctl is-active hello-loggeractive を返し、かつ systemctl statussince(起動時刻)が qm start の後の時刻になっていること。

STEP 08 · 章末クリーンアップ

この演習で作ったものをすべて撤去し、演習前の状態に戻します。

bash · learner01
# サービスを止めて、自動起動も解除する
sudo systemctl disable --now hello-logger
期待される出力
Removed "/etc/systemd/system/multi-user.target.wants/hello-logger.service".

この出力が enable の正体です。enablemulti-user.target.wants/ というディレクトリにシンボリックリンクを 1 本張るだけの操作でした。OS はこのディレクトリの中身を見て「起動時に何を立ち上げるか」を決めています。

bash · learner01
# ユニットファイルとスクリプトを削除
sudo rm /etc/systemd/system/hello-logger.service
sudo rm /usr/local/bin/hello-logger.sh

# 削除を systemd に反映
sudo systemctl daemon-reload

# 残骸がないことを確認
systemctl status hello-logger

Unit hello-logger.service could not be found. と出れば完了です。これはエラーではなく「もう無い」という意味なので、赤い文字が出ても心配いりません。

クリーンアップ完了の確認
確認項目期待値
systemctl status hello-loggerUnit hello-logger.service could not be found.
/etc/systemd/system/hello-logger.service存在しない(ls で確認)
/usr/local/bin/hello-logger.sh存在しない
電源断テストの結果演習中に is-active = active を確認済み

詰まったら

ハマりポイント

STEP ④ で Active: failed になる

まず journalctl -u hello-logger -n 20 で直近のログを見てください。原因の大半は次の 2 つです。

①スクリプトに実行権限がない: ls -l /usr/local/bin/hello-logger.sh-rwxr-xr-x になっているか確認します。なっていなければ sudo chmod 755 を実行し直してください。

ExecStart= のパスが違う: 相対パスやタイプミスがないか確認します。systemctl cat hello-logger で、systemd が実際に読み込んでいる内容を見られます。

ユニットファイルを直したのに、動きが変わらない

sudo systemctl daemon-reload を忘れている可能性が高いです。systemd はファイルの変更を自動では検知しません。ユニットファイルを触ったら必ず daemon-reload、と覚えてください。

systemctl statusLoaded: 行に changed on disk のような警告が出ていれば、まさにこの状態です。

STEP ⑦ で SSH がつながらない

Connection refused の場合: VM の起動がまだ終わっていません。qm start から 20〜30 秒待ってから再試行してください。

Connection timed out during banner exchange の場合: ping も通り、ポート 22 も開いているのにこれが出ることがあります。sshd がまだ応答を返し始めていない状態です。1〜2 分待ってから再試行してください。ポートが開いていることと、サービスが応答を始めていることは別です。

上記以外の詰まりと対処を一覧にまとめます。

起きたこと対処
daemon-reload 後に Failed to load unit fileユニットファイルの書式エラー。systemctl status hello-logger に該当行が出るので、STEP ③ のファイルを修正してから daemon-reload をやり直す
is-enableddisabled になるenable --now ではなく start だけを実行した可能性。sudo systemctl enable hello-logger を実行する
kill -9 の後に再起動しないRestartSec=5s の待ち時間を過ぎているか確認(sleep 6 を挟む)。それでも戻らなければ systemctl status でエラーを読む
⑥ Proxmox に SSH できない<Proxmox管理IP> が受講環境案内の値と一致しているか確認。proxmox というホスト名では解決できない(workstation01/etc/hosts に登録されていないため)
is-activeinactive になるsystemctl is-enabled hello-logger を確認。disabled なら STEP ④ の enable が漏れている(この場合は演習として正しく「失敗を観測できた」状態)
クリーンアップ後に systemctl status がエラーを出すUnit ... could not be found. は正常。「存在しない」という意味でありエラーではない

AI に相談

AI systemd の詰まりを自由に質問できます
この章の systemd・ユニットファイル・enable を前提に、インフラの基礎をやさしく答えます

修了確認

この章で体験した「2 つの復活」の違いを確認します。

問 1 · enablestart の違い

systemctl start hello-loggersystemctl enable hello-logger の違いを説明してください。また、qm stopqm start(電源断相当)の後にサービスが自動で起き上がったのは、どちらの効果ですか?

解答を見る
コマンド効果再起動後
systemctl start今すぐプロセスを起動する消える(一時的)
systemctl enable次回起動時から自動起動する設定を入れる起き上がる(永続的)

電源断後に自動起動したのは enableWantedBy=multi-user.target)の効果です。start だけでは、電源を入れ直すたびに人がコマンドを打つ必要があります。

enable --now は「今すぐ起動(start)」と「次回から自動起動(enable)」を 1 コマンドでまとめて行う形です。実務ではこれを使う場面が最も多くなります。

問 2 · Restart=enable の守備範囲

今回書いたユニットファイルの Restart=on-failureWantedBy=multi-user.target は、それぞれ何を制御していますか? また、電源断の後に効くのはどちらで、それはなぜですか?

解答を見る
設定制御する対象
Restart=on-failure同じ OS セッションの中で、プロセスが異常終了したときに自動再起動する。kill -9 や 0 以外の終了コードで発動する
WantedBy=multi-user.targetOS が multi-user.target フェーズに達したときに、このサービスを起動する。systemctl enable の実体は、この設定をシンボリックリンクで有効化する操作

電源断の後に効くのは enableWantedBy=です。理由は、電源が落ちた時点で systemd 自身も一度死んでいるからです。Restart= は「動いている systemd が、死んだ子プロセスを拾い直す」仕組みなので、systemd ごと消えてしまえば誰も拾えません。

電源を入れ直すと、OS は起動手順の中で multi-user.target.wants/ の中身を読み、そこに置かれたサービスを立ち上げます。STEP ⑧ で disable したときに表示された Removed "/etc/systemd/system/multi-user.target.wants/hello-logger.service". が、まさにその仕掛けです。

段階ヒント(問 1・問 2 共通)

段階ヒント 1 段目:systemd は「書く → 読ませる → 有効にする」

systemd の操作はこの 3 ステップの順序を守れば迷いません。ファイルを書く → daemon-reload で読ませる → enable / start で動かす。ユニットファイルを編集したら必ず daemon-reload を先に実行してください(忘れると古い設定のまま動き続けます)。状態の確認は常に systemctl status <svc> から始めると、原因が Loaded: 行や Process: 行に出ています。

段階ヒント 2 段目:具体的なコマンド手順
サービス作成から確認まで
# スクリプトに実行権限(STEP ② の tee の後)
sudo chmod 755 /usr/local/bin/hello-logger.sh

# ユニットファイルを読ませる(STEP ③ の tee の後)
sudo systemctl daemon-reload

# 今すぐ起動+次回から自動起動
sudo systemctl enable --now hello-logger

systemctl status hello-logger      # active (running) を確認
systemctl is-enabled hello-logger  # enabled を確認

よくある落とし穴:

  • daemon-reload を忘れる → 古い設定のまま動く(statusLoaded: 行で気づける)
  • ExecStart= に相対パスを書く → 起動できず failed になる
答えを見る(フルコマンド列)
全手順(learner01 + Proxmox ホスト)
# ── learner01(ops)──
sudo chmod 755 /usr/local/bin/hello-logger.sh
sudo systemctl daemon-reload
sudo systemctl enable --now hello-logger
systemctl status hello-logger      # active (running)
systemctl is-enabled hello-logger  # enabled

# Restart= の確認
sudo kill -9 $(systemctl show hello-logger --property MainPID | cut -d= -f2)
sleep 6
systemctl is-active hello-logger   # active(Restart=on-failure で復帰)

# ── Proxmox ホスト(別ターミナル)──
qm list | grep learner01          # VMID 確認
qm stop <VMID>                   # 電源断相当
qm status <VMID>                 # stopped
qm start <VMID>                  # 再投入

# ── workstation01(20〜30 秒後)──
ssh ops@learner01
systemctl is-active hello-logger   # active ← enable が効いた証拠

# ── クリーンアップ(learner01)──
sudo systemctl disable --now hello-logger
sudo rm /etc/systemd/system/hello-logger.service
sudo rm /usr/local/bin/hello-logger.sh
sudo systemctl daemon-reload
systemctl status hello-logger      # could not be found.

このフローで覚えること:

  • enable は「次回起動時から自動起動する」宣言、start は「今すぐ起動する」命令。両方いる
  • Restart= はセッション内の異常終了に効き、enable は電源断の後に効く。別の仕組み
  • ユニットファイルを触ったら必ず daemon-reload を先に

次の章へ

この章では「サービスを常駐させる」ことを扱いました。次の Chapter 2 では、そのサービスがおかしくなったときに原因を追う技術を扱います。systemd は全サービスのログを journal という 1 か所に集めています。次章では本章で作ったサービスをわざと壊し、journalctl で失敗を追跡し、修正し、さらに電源イベントをまたいだログまで読みます。

Chapter 2: journalctl でログを運用者として読む:
  • journalctl -u <svc> でサービス単位にログを絞り込む
  • 失敗したサービスのログから原因を特定し、直して復旧させる
  • 再起動をまたいだログを読み、「いつ落ちたか」を時系列で追う

もっと知りたい方へ