Linuxでの定期再起動をシェルスクリプトとcronジョブで自動化する方法と検証結果

## はじめに
サーバーやシステムの安定稼働を維持するために、定期的な再起動が必要な場合があります。

Windows環境では、前回の記事で[バッチファイルとタスクスケジューラを使って自動再起動を設定する方法](https://qiita.com/free-honda/items/b9decb131c4fc20b31c8)をご紹介しました。

Linux環境でも同様の目的を達成するためには、シェルスクリプトとcronジョブを活用することができます。

本記事では、Linuxシステムでの定期的な再起動をシェルスクリプトとcronジョブを使って自動化し、その成功・失敗をログに記録する方法をご紹介します。

## 前提条件
今回の検証を進めるにあたり、以下の環境が必要です。

:::note info
**検証環境**
**Linuxディストリビューション:**
仮想環境や実際のサーバー上でLinux（ここではAlmaLinux 9.3を使用）が稼働していること。
:::

**自宅のVirtualBox環境**
今回も VirtualBox 上で構築した検証環境を使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/70df8950-ddfa-2728-1d71-60373a8276e9.png)


:::note info
※検証には実行中の「AlmaLinux」を使用します。
:::

## 知識整理
今回の検証に必要な知識を簡単に整理します。

**シェルスクリプトとは？**

シェルスクリプトは、Linuxで複数のコマンドを自動で実行するためのスクリプトファイルです。これにより、指定された条件下で再起動を実行し、その結果をログに記録できます。

**cronとは？**

cronは、指定した時間や間隔でスクリプトやコマンドを自動的に実行するためのLinuxのタスクスケジューラです。これを使用して、定期的な再起動の設定が可能です。

:::note warn
さらに詳しく知りたい場合は、以下のリンクも参考にしてください。
**シェルスクリプトについて**
参考サイト：https://sc.megabank.tohoku.ac.jp/ph3-doc/howto/linux/script.html
参考サイト：https://jitera.com/ja/insights/25777

**cronについて**
参考サイト：https://wa3.i-3-i.info/word11748.html
参考サイト：https://www.miraiserver.ne.jp/column/about_cron/
:::

## 構築の流れ

:::note info
ここでは、3つのステップに分けて設定を進めていきます。

**ステップ1：シェルスクリプトの作成**
**ステップ2：cronジョブの設定**
**ステップ3：タスクの実行状況の確認**
:::

※[過去の記事で作成した「**honda test(honda)**」というドメインユーザ](https://qiita.com/free-honda/items/f391a37195ff0e557981)でログインして検証していきます。

今回の再起動検証のディレクトリ構成は、以下の通りです。

```
/home/honda@honda.co.jp/   # ユーザーのホームディレクトリ
├── log/                   # ログファイルを保存するディレクトリ
│   └── reboot_log.txt     # システム再起動に関するログファイル
└── reboot.sh              # 自動再起動を実行するシェルスクリプト
```

## ステップ1：シェルスクリプトの作成

まず、以下のシェルスクリプトを作成し、再起動とログ記録の処理を自動化します。私の環境では、ファイル名を「reboot.sh」として保存しました。

:::note info 
 ログファイルのパス設定は、ご自身の環境に合わせて置き換えてください。
:::

```
vi reboot.sh
```

**reboot.shファイルの中身**
```reboot.sh
#!/bin/bash

# ログファイルのパスを設定
LOG_FILE="/path/to/reboot_log.txt"

# 現在の日時を取得
timestamp=$(date "+%Y-%m-%d %H:%M")

# ログ出力
echo "$timestamp サーバーを再起動しますか？ (Y/N)" >> $LOG_FILE

# 再起動前の確認
read -p "サーバーを再起動しますか？ (Y/N): " choice

# 入力に応じた処理
if [[ "$choice" == "Y" || "$choice" == "y" ]]; then
    # 10秒間のカウントダウン
    echo "サーバーは10秒後に再起動します..." >> $LOG_FILE
    for i in {10..1}; do
        echo "再起動まで $i 秒..." >> $LOG_FILE
        sleep 1
    done

    # 再起動の実行
    echo "$timestamp 再起動中..." >> $LOG_FILE
    sudo shutdown -r now
    echo "$timestamp 再起動が成功しました。" >> $LOG_FILE
else
    # 再起動のキャンセル
    echo "$timestamp 再起動をキャンセルしました。" >> $LOG_FILE
fi
```

このスクリプトは、再起動前に10秒間のカウントダウンを行い、ユーザーの入力に応じて再起動を実行します。

:::note warn
chmodコマンドを使い実行権限を付与することを忘れないでくださいね。

```
chmod +x reboot.sh
```
:::

また、ログファイルには「再起動の確認」「再起動の実行」「再起動をキャンセルする場合」などのメッセージを書き込みます。

## ステップ2：cronジョブの設定

次に、作成したシェルスクリプトをcronジョブで自動実行する設定を行います。

ターミナルを開き、「crontab -e」コマンドでcronジョブの設定ファイルを編集します。エディタが開いたら、次の行を追加します（例として毎日午前8時に実行）。

```
crontab -e
・・・
・・・
0 8 * * * /home/honda@honda.co.jp/reboot.sh
```

:::note info
※「/home/honda@honda.co.jp/～(省略)」の箇所は実行したいシェルスクリプトのパスに置き換えてください。
:::

編集後、設定が保存されていることを確認するため、以下のコマンドで確認します。
ここでは、検証目的のために1分おきにそのスクリプトを実行するcronジョブを設定してみます。
```
[honda@honda.co.jp@HONDA-TEST home]$ crontab -l
0 8 * * * /home/honda@honda.co.jp/reboot.sh
*/1 * * * * /home/honda@honda.co.jp/reboot.sh
```

設定を保存することで、指定した時間に自動的にシェルスクリプトが実行されるようになります。

## ステップ3：タスクの実行状況の確認

設定した時間になると、スクリプトが実行され、「**サーバーを再起動しますか？ (Y/N)」というメッセージが表示され対話形式で再起動することを想定**していました。

しかし、実際のログには再起動がキャンセルされたメッセージだけログに残り、対話形式で聞かれることは実現できいませんでした。

```
[honda@honda.co.jp@HONDA-TEST log]$ cat reboot_log.txt
・・・・・
2024-09-16 11:55 サーバーを再起動しますか？ (Y/N)
2024-09-16 11:55 再起動をキャンセルしました。
・・・・・
2024-09-16 11:56 サーバーを再起動しますか？ (Y/N)
2024-09-16 11:56 再起動をキャンセルしました。
```

手動で実行した場合は、以下のように対話形式で「**サーバーを再起動しますか？ (Y/N)**」というメッセージが表示され、再起動が実行できることが確認できました。

つまり、シェル自体には問題がないことが分かりました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/b1609df2-3ff5-455a-c42f-ba7eebe0bd95.png)

ログにも問題なく再起動前に10秒カウントされ、OSの再起動が実施できていることを確認できました。

```
[honda@honda.co.jp@HONDA-TEST log]$ cat reboot_log.txt
・・・・・
2024-09-16 12:03 サーバーを再起動しますか？ (Y/N)
サーバーは10秒後に再起動します...
再起動まで 10 秒...
再起動まで 9 秒...
再起動まで 8 秒...
再起動まで 7 秒...
再起動まで 6 秒...
再起動まで 5 秒...
再起動まで 4 秒...
再起動まで 3 秒...
再起動まで 2 秒...
再起動まで 1 秒...
2024-09-16 12:03 再起動中...
2024-09-16 12:03 再起動が成功しました。
```

### 原因調査

まず、cronサービスが正しく動作しているかを確認しました。cronサービスが動作していることは確認できました。

```
[honda@honda.co.jp@HONDA-TEST log]$ systemctl status crond
● crond.service - Command Scheduler
     Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-09-16 12:04:00 JST; 23min ago
・・・・・
```

cronジョブが実行されたかどうかは、/var/log/cronを確認する限り、ジョブが1分ごとに実行されていることが確認できました。

```
[honda@honda.co.jp@HONDA-TEST log]$ tail -f /var/log/cron
・・・・・
Sep 16 11:21:04 HONDA-TEST CROND[7507]: (honda@honda.co.jp) CMD (/home/honda@honda.co.jp/reboot.sh)
Sep 16 11:21:04 HONDA-TEST CROND[7464]: (honda@honda.co.jp) CMDEND (/home/honda@honda.co.jp/reboot.sh)
```

cronでジョブが実行されるときに「サーバーを再起動しますか？ (Y/N)」と聞かれない理由は、**cronジョブが非対話モードで実行される**ためです。

これにより、ユーザーからの入力を受け取るための標準入力を持っていません。


:::note alert
そもそも、cronジョブが非対話モードで実行（バックグラウンドで動作）されるため、ユーザーからの入力を受け取るための標準入力を持っていません。
:::

そのため、スクリプトを自動化するためには、**対話的な入力を求める部分を削除し、条件をスクリプト内で直接設定するように変更**しました。

**修正版のreboot.shの中身**

※ログファイルのパス設定は、ご自身の環境に合わせて置き換えてください。

```修正版のreboot.sh
#!/bin/bash

# ログファイルのパスを設定
LOG_FILE="/path/to/reboot_log.txt"

# 現在の日時を取得
timestamp=$(date "+%Y-%m-%d %H:%M")

# ログ出力
echo "$timestamp サーバーを再起動します。" >> $LOG_FILE

# 再起動の実行
echo "サーバーは10秒後に再起動します..." >> $LOG_FILE
for i in {10..1}; do
    echo "再起動まで $i 秒..." >> $LOG_FILE
    sleep 1
done

# 再起動の実行
echo "$timestamp 再起動中..." >> $LOG_FILE
sudo shutdown -r now
echo "$timestamp 再起動が成功しました。" >> $LOG_FILE
```

**ログ確認**
シェルの内容がログにも出力され、無事に再起動されていることが確認できました。

```
[honda@honda.co.jp@HONDA-TEST log]$ cat reboot_log.txt
・・・・・
2024-09-16 12:46 サーバーを再起動します。
サーバーは10秒後に再起動します...
再起動まで 10 秒...
再起動まで 9 秒...
再起動まで 8 秒...
再起動まで 7 秒...
再起動まで 6 秒...
再起動まで 5 秒...
再起動まで 4 秒...
再起動まで 3 秒...
再起動まで 2 秒...
再起動まで 1 秒...
2024-09-16 12:46 再起動中...
2024-09-16 12:46 再起動が成功しました。
```

結果的に、**強制的に再起動する処理に改修しましたが、特定の条件が満たされたときにのみ再起動するようにするシェルの改修も検討**しました。

ただ、時間がかかりそうだったため、今回はこの回避策で対応しました。

## まとめ

本記事では、Linuxシステムでの定期的な再起動をシェルスクリプトとcronジョブを使って自動化する手順をご紹介しました。

途中、**WindowsとLinuxのOSの違い**により苦戦する場面もありましたが、原因調査からシェルの改修までを経て、無事に再起動が自動化できました。この検証が誰かの役立つことを願っています。

次回以降もバッチやシェルスクリプトを使った自動化などの検証を行いますので、よろしくお願いいたします。

## 参考記事
https://office54.net/iot/linux/ubuntu-regular-reboot

https://qiita.com/i13ame/items/bf4b6a0e3485ddc838ce

https://rainbow-engine.com/linux-scheduled-reboot-script/