【実践シェルスクリプト講座 第1回】サーバーが止まるその前に…。「監視」と「掃除」を自動化して安眠を手に入れよう
こんにちは!「リナックス先生」です。
今回から全5回にわたり、現場で本当に役立つ「実践シェルスクリプト講座」を開講します。
教科書的なコマンドの説明ではなく、「実際に起きるトラブル」を解決するための自動化ツールを、一緒に作り上げていきましょう。
コウ君、最近サーバーの管理はどう?手動で毎日チェックしてる?
先生!それが…実は昨日、テストサーバーが突然動かなくなって焦りました。
原因を調べたら、ログファイルが肥大化してディスクがパンパンになっていて…。
「No space left on device」ってエラーが出て、SSHすら繋がりませんでした(泣)。
典型的な「ディスクフル障害」ね。
サーバーエンジニアが最初に経験する洗礼のようなものよ。
でも、人間が毎日 df コマンドを打って監視するのはナンセンス。そんな単純作業こそ、スクリプトに任せるべきよ!
記念すべき第1回は、サーバー運用の基本中の基本である「ディスク容量の監視」と、その原因となる「ログファイルの自動ローテーション(掃除)」を行うスクリプトを作成します。
最初からプロの現場で通用する「エラー処理(安全性)」を意識したコードを書いていきますよ。
本講座のカリキュラム(全5回)
本シリーズでは、以下のステップで「最強の自動化サーバー」を構築します。
- 【今回】サーバー停止の恐怖から解放!「ディスク容量監視」と「ログ自動整理」の完全自動化
- データ消失をゼロにする!「堅牢バックアップ」と「Slack通知」の構築
- 手作業を撲滅せよ!「ユーザー一括作成」と「SSH鍵配布」の自動化スクリプト
- 攻撃者を秒でブロック!ログ解析による「SSH総当たり攻撃」自動防御システム
- プロの品質へ!スクリプトの「安全性向上(デバッグ)」と「トラブルシューティング」
1. そもそも、なぜ「監視」と「ローテーション」が必要なのか?
サーバーは24時間365日動き続け、常に何らかのデータを書き込んでいます。
特に「ログファイル」は、放っておくと数GB、数TBへと成長し、物理的なディスク容量を食いつぶします。
💡 ディスクがいっぱいになると起こる悲劇
- データの喪失: 新しいデータベースの書き込みができず、データが消える。
- サービスの停止: Webサーバーがセッションファイルを作れず、サイトが閲覧不能になる。
- システムダウン: OS自体が一時ファイルを作れず、コマンド操作すら受け付けなくなる。
こうなってから対応するのでは遅いのです。
「危なくなる前に通知する」仕組みと、「定期的にゴミを片付ける」仕組みの2つが不可欠です。
2. ディスク使用率監視スクリプトを作る
まずは、現在のディスク使用量をチェックし、設定した閾値(いきち)を超えていたら警告を出す disk_alert.sh を作成します。
Step 1: コマンドで数値を取り出す
まずは手動でコマンドを組み立ててみましょう。df -h コマンドは見やすいですが、スクリプト処理には向きません。単純な df を使い、テキスト処理ツール awk と sed で「使用率(%)」の数字だけを抜き出します。
[root@localhost ~]# df / | tail -1 | awk '{print $5}' | sed 's/%//'
84
この「84」という数字が取れれば、あとは if 文で比較するだけですね。
Step 2: 安全なスクリプトを書く(disk_alert.sh)
vim disk_alert.sh を作成します。
今回は最初から「変数の未定義エラー」などを防ぐ安全策(set -u)を入れておきます。
#!/bin/bash
# --- 安全設定 ---
# set -u : 未定義の変数を使おうとしたらエラーで停止する(誤動作防止)
set -u
# ==========================================
# 設定エリア
# ==========================================
# 監視対象のマウントポイント
TARGET="/"
# 警告を出す閾値(パーセント)
THRESHOLD=80
# ログファイル(cron実行時の確認用)
LOG_FILE="/var/log/disk_alert.log"
# ==========================================
# メイン処理
# ==========================================
# ログ出力用関数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# ディスク使用率を取得
# 実行結果を変数に入れるときは $(...) を使う
current_usage=$(df "$TARGET" | tail -1 | awk '{print $5}' | sed 's/%//')
# 数値チェック(空だったり数字以外だった場合の安全策)
if ! [[ "$current_usage" =~ ^[0-9]+$ ]]; then
log "【ERROR】ディスク使用率の取得に失敗しました。"
exit 1
fi
# 閾値と比較
# -ge : Greater than or Equal (以上)
if [ "$current_usage" -ge "$THRESHOLD" ]; then
log "【DANGER】ディスク容量が危険水準です! 使用率: ${current_usage}% (閾値: ${THRESHOLD}%)"
# ★ここにメール送信やSlack通知のコマンドを追記可能
# echo "Disk Alert" | mail -s "Warning" admin@example.com
else
log "【OK】ディスク容量は正常です。 使用率: ${current_usage}%"
fi
Step 3: 動作確認
実行権限を与えて動かしてみましょう。
[root@localhost ~]# chmod +x disk_alert.sh [root@localhost ~]# ./disk_alert.sh [root@localhost ~]# cat /var/log/disk_alert.log [2024-01-25 10:00:00] 【OK】ディスク容量は正常です。 使用率: 45%
これで、1時間に1回このスクリプトを動かせば、気づかぬうちに100%になる事故は防げます。
3. ログローテーションスクリプトを作る
監視ができたら、次は「掃除」です。
Linuxには logrotate という標準機能がありますが、自作アプリのログや、特定の一時ファイルを柔軟に管理したい場合、シェルスクリプトで書けると非常に強力な武器になります。
仕様を決定する
今回は以下の要件で log_rotate.sh を作成します。
- 対象:
/var/log/myapp/app.log - 処理:現在のログを
app.log.YYYYMMDDにリネームして退避。 - 再生成:空の
app.logを新しく作る。 - 圧縮:退避したログを
gzipで圧縮する。 - 削除:30日以上経過した古い圧縮ログを削除する。
スクリプトの実装(log_rotate.sh)
ここでも、現場で必須の「エラーハンドリング(set -e)」を導入します。
#!/bin/bash
# --- 安全設定 ---
# set -e : コマンドが1つでも失敗したらスクリプトを即座に停止する
# set -u : 未定義変数エラーチェック
set -eu
# ==========================================
# 設定エリア
# ==========================================
LOG_DIR="/var/log/myapp"
LOG_FILE="app.log"
RETENTION_DAYS=30
TODAY=$(date +%Y%m%d)
# ==========================================
# メイン処理
# ==========================================
# ディレクトリへ移動
# もしディレクトリ変数が間違っていても、set -uで検知できる
# 移動に失敗したら set -e で止まるので、誤爆を防げる
cd "$LOG_DIR"
# 対象ログファイルがなければ終了(正常終了とする)
if [ ! -f "$LOG_FILE" ]; then
echo "対象ファイル($LOG_FILE)がないためスキップします。"
exit 0
fi
echo "--- ローテーション開始: $TODAY ---"
# 1. リネーム(退避)
mv "$LOG_FILE" "${LOG_FILE}.${TODAY}"
echo "退避完了: ${LOG_FILE}.${TODAY}"
# 2. 空ファイル再作成
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
echo "新規作成完了: $LOG_FILE"
# 3. 圧縮
gzip "${LOG_FILE}.${TODAY}"
echo "圧縮完了: ${LOG_FILE}.${TODAY}.gz"
# 4. 古いログの削除
# find -mtime +30 : 30日より前のファイルを検索
echo "保存期間(${RETENTION_DAYS}日)経過ログを削除中..."
find . -name "${LOG_FILE}.*.gz" -mtime +$RETENTION_DAYS -delete -print
echo "--- 処理完了 ---"
先生、set -e ってそんなに大事なんですか?
書いてなくても動きますよね?
いい質問ね。
例えば、ディスクがいっぱいで mv (リネーム) に失敗したとしましょう。set -e がないと、スクリプトはそのまま次の行に進んで、まだ移動できていないログファイルに touch で上書きしたり、中途半端な状態で圧縮を始めたりして、ログが消失する大事故になるの。
「失敗したらすぐに止まる」のは、システムを守るための鉄則よ!
4. Cronによる定期実行の設定
最後に、これらをCronに登録して完全自動化します。
ここで初心者が最もハマる罠が「環境変数PATH」です。
Cronの鉄則
Cronで動くときは、ターミナルで操作している時とは違い、最小限のコマンドしかパスが通っていません。
そのため、スクリプト内で gzip や df が「command not found」になることがあります。
これを防ぐには、スクリプトの先頭でPATHを定義するか、Cron側で定義します。
今回は、crontab -e で編集する際にPATHを指定する方法を紹介します。
# 環境変数を定義(これ以下の行に適用される) PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin # 毎時0分にディスク容量監視 0 * * * * /root/scripts/disk_alert.sh >> /var/log/cron_disk.log 2>&1 # 毎日AM 3:30 にログローテーション 30 3 * * * /root/scripts/log_rotate.sh >> /var/log/cron_rotate.log 2>&1
2>&1 は「エラーメッセージも一緒にログファイルに保存する」という意味です。
これを忘れると、スクリプトが動かなかった時に原因がわからなくなります。
まとめ:自動化で「時間」を作ろう
今回は第1回として、サーバー管理の基本「監視と掃除」を自動化しました。
| スクリプト | 役割 | プロのテクニック |
|---|---|---|
| disk_alert.sh | ディスク容量の監視 | awkでの数値抽出、set -uでの変数保護 |
| log_rotate.sh | ログの掃除と圧縮 | set -eでの異常時停止、find -delete |
これらをセットしておけば、コウ君のように「朝起きたらサーバーが死んでいた」という悪夢を見る確率はグッと下がります。
空いた時間で、新しい技術の勉強や、サービスの改善に取り組みましょう。
次回予告:データ消失の悲劇を防ぐバックアップ術
ディスクの監視はできましたが、サーバーには「ハードウェア故障」や「操作ミスによる削除」というリスクがつきまといます。
次回は、「tarコマンドによる世代管理バックアップ」と、「rsyncによる高速同期」、そしてスクリプトが失敗した時にSlackやメールに通知を飛ばす「通知システム」を構築します。
「バックアップ取ってませんでした…」で人生を棒に振らないために、次回も必見です!
今回のスクリプトを試すときは、いきなり本番環境ではなく、まずは手元の仮想環境やテスト用のVPSで試してね。
失敗して覚えることも多いけど、本番データだけは守りましょう!
▼スクリプトの実験場に最適!推奨VPS


コメント