シェルスクリプトの死活監視設計|curlとncコマンドでサービス状態を自動確認してアラートを送る方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトの死活監視設計|curlとncコマンドでサービス状態を自動確認してアラートを送る方法
「3時間サーバーが落ちていたのに、ユーザーからの問い合わせで初めて気づいた」——サーバー管理者なら、こういう経験は一度や二度ではないでしょう。

死活監視の仕組みがあれば、問題が起きた直後に自動でアラートを受け取ることができます。Zabbix・Prometheus・Nagios といった本格的な監視ツールは強力ですが、「特定のサービスだけを素早く監視したい」「ツール導入の手間をかけたくない」という現場では、シェルスクリプトで十分に機能する監視の仕組みが作れます。

この記事では、curlnc(netcat)を組み合わせたシェルスクリプトの死活監視設計を解説します。HTTP/HTTPS の応答確認・TCP ポートチェック・メールアラート送信・アラート連射防止の冷却設計まで、RHEL 9.4 / Ubuntu 24.04 LTS で動作確認した実践例を中心に紹介します。

この記事のポイント

・curlの終了コードとHTTPステータスでWeb サービスの死活確認がワンライナーで書ける
・ncコマンドでTCPポートチェックを行い、Postfix など非HTTP系サービスも監視できる
・アラート連射を防ぐ「冷却期間設計」を組み込まないと障害復旧前にメールが爆発する
・cronで5分おきに実行しても負荷はほぼゼロで、1台でも多複数サービスを監視できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜシェルスクリプトで死活監視を組むのか

「監視ツールを入れればいい」という声はよく聞くが、実際の現場ではそれが難しい状況もある。

セキュリティポリシー上エージェントのインストールが許可されない閉域網サーバー、予算の関係でツール購入が難しい小規模な VPS、単一のアプリケーションだけ監視したいケース——こういった場面ではシェルスクリプトによる監視が現実解になる。

シェルスクリプト監視の主な利点は次の通りだ。

追加インストール不要:curl・nc・mail など多くの Linux 環境に標準で入っているコマンドだけで動作する
負荷が極めて低い:5分おきに実行しても CPU / メモリへの影響はほぼゼロ
カスタマイズが容易:監視対象・判定条件・アラート先をコードで自由に制御できる
バージョン管理できる:スクリプト自体を git リポジトリに入れて変更履歴を追跡できる

一方で設計を間違えると「1回の障害でメールが100通届く」「監視スクリプト自体が落ちて誰も気づかない」という問題が起きる。本記事ではこれらを防ぐ設計パターンを合わせて解説する。

死活監視スクリプトの基本設計

1. curlでHTTP/HTTPSサービスを監視する

curl は HTTP ステータスコードを変数に取得できるため、Web サービスの死活確認に使いやすい。

# HTTP ステータスコードを取得して判定する STATUS=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time 10 https://www.example.com/) if [ "$STATUS" -ge 200 ] && [ "$STATUS" -lt 400 ]; then echo "[OK] HTTP $STATUS" else echo "[NG] HTTP $STATUS" >&2 fi

オプションの意味を整理しておく。

-s:進行状況を出力しない(silent モード)
-o /dev/null:レスポンス本文を捨てる
-w '%{http_code}':HTTP ステータスコードだけを標準出力へ出力する
--connect-timeout 5:TCP 接続のタイムアウト秒数
--max-time 10:レスポンス全体のタイムアウト秒数(これを設定しないと curl がハングする)

実際にサーバー上で実行した出力例を示す(ホスト名・IP アドレスはマスク済み)。

$ curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time 10 https://web-srv-01.example.local/ 200 $ curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time 10 https://web-srv-02.example.local/ 000 # 000 は接続失敗(サーバーダウン・ネットワーク不通など)

ステータスコード 000 は接続そのものが失敗したことを示す。curl の終了コードも 0 以外になるため、$? でも検知できる。

Webサーバー(Apache/httpd)を内部からチェックする場合は http://localhost/ を監視対象にすると外部ネットワークの影響を排除できる。Apache の設定についてはhttpd の基本操作も参考にしてほしい。

2. ncコマンドでTCPポートを監視する

HTTP 以外のサービス(SSH・SMTP・MySQL など)は nc(netcat)でポート到達確認を行う。

# TCP ポートが開いているか確認する # -z: ポートスキャンモード(接続確立後すぐ切断) # -w: タイムアウト秒数 nc -z -w 5 192.168.10.20 25 echo "終了コード: $?" # 0 = 開いている / 1 = 閉じているまたは到達不能

Postfix(SMTP: ポート 25)やデータベース(MySQL: ポート 3306)など、HTTP 以外のサービスはこの方法で確認する。ポートを手動で確認したい場面では ss コマンドや lsof が役立つ。ポート確認のコマンド一覧は「Linux ポート確認の全コマンド」にまとめている。

3. 監視関数として再利用できる形にまとめる

死活確認ロジックを関数化しておくと、監視対象を増やす際にコードが読みやすくなる。

# HTTP チェック関数(0=正常, 1=異常) check_http() { local url="$1" local timeout="${2:-10}" local status status=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time "$timeout" "$url") [ "$status" -ge 200 ] && [ "$status" -lt 400 ] } # TCP ポートチェック関数(0=到達可, 1=不到達) check_port() { local host="$1" local port="$2" local timeout="${3:-5}" nc -z -w "$timeout" "$host" "$port" }

関数の戻り値は bash の慣例に従い「0 = 成功、0 以外 = 失敗」で統一する。これを守ることで if ! check_http "..."; のような直感的な書き方が可能になる。

実務パターン:複数サービスを一括監視するスクリプト

実際に運用で使っているスクリプトに近い形を示す。

#!/bin/bash # ------------------------------------------------------- # service-monitor.sh — 複数サービス死活監視スクリプト # 動作確認環境: RHEL 9.4 / Ubuntu 24.04 LTS # cron: */5 * * * * /usr/local/bin/service-monitor.sh # ------------------------------------------------------- set -uo pipefail ALERT_MAIL="ops@example.com" LOG="/var/log/service-monitor.log" TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') # ログ関数 log_info() { echo "[$TIMESTAMP] INFO $*" >> "$LOG"; } log_error() { echo "[$TIMESTAMP] ERROR $*" | tee -a "$LOG" >&2; } # アラートメール送信関数(Postfix 経由) send_alert() { local subject="$1" local body="$2" echo "$body" | mail -s "$subject" "$ALERT_MAIL" } # HTTP チェック(0=正常, 1=異常) check_http() { local url="$1" local status status=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time 10 "$url") [ "$status" -ge 200 ] && [ "$status" -lt 400 ] } # TCP ポートチェック(0=到達可, 1=不到達) check_port() { nc -z -w 5 "$1" "$2" } # --------[ 監視実行 ]-------- errors=0 # Web サービスの監視 if ! check_http "https://www.example.com/"; then log_error "www.example.com HTTP 異常" send_alert "[監視] Web サービスダウン検知" \ "www.example.com が HTTP 応答しません。サーバーを確認してください。" errors=$((errors + 1)) else log_info "www.example.com OK" fi # SMTP (Postfix) の監視 if ! check_port "mail.example.local" 25; then log_error "Postfix SMTP (port 25) 不到達" send_alert "[監視] Postfix ダウン検知" \ "mail.example.local の SMTP ポート(25)に到達できません。" errors=$((errors + 1)) else log_info "Postfix (port 25) OK" fi log_info "チェック完了 (エラー数: $errors)" exit "$errors"

このスクリプトを実際に RHEL 9.4 サーバーで5分おきに動かした際のログ出力例を示す(ホスト名・メールアドレスはマスク済み)。

# tail -10 /var/log/service-monitor.log [2026-07-24 10:00:01] INFO www.example.com OK [2026-07-24 10:00:01] INFO Postfix (port 25) OK [2026-07-24 10:00:01] INFO チェック完了 (エラー数: 0) [2026-07-24 10:05:01] INFO www.example.com OK [2026-07-24 10:05:01] ERROR Postfix SMTP (port 25) 不到達 [2026-07-24 10:05:01] INFO チェック完了 (エラー数: 1) [2026-07-24 10:10:01] INFO www.example.com OK [2026-07-24 10:10:01] ERROR Postfix SMTP (port 25) 不到達 [2026-07-24 10:10:01] INFO チェック完了 (エラー数: 1)

メールアラートの送信には Postfix が必要だ。Postfix の基本設定については「Postfix の基本設定の解説」を参照してほしい。

アラート連射を防ぐ冷却期間の設計

上記のスクリプトには致命的な問題がある。障害が30分続いた場合、5分おきに実行されるので 6通のメールが届く。長時間の障害なら何十通にもなる。これが「アラート連射」問題だ。

対策は「冷却期間(クールダウン)フラグファイル」の設計だ。

#!/bin/bash # 冷却期間付きアラート設計のコアロジック COOLDOWN_FILE="/tmp/monitor_alert_cooldown" COOLDOWN_MINUTES=30 # 冷却期間中かどうか判定(0=送信してよい, 1=冷却中) is_in_cooldown() { if [ ! -f "$COOLDOWN_FILE" ]; then return 1 # フラグなし = 冷却期間外 fi local file_age file_age=$(( $(date +%s) - $(stat -c %Y "$COOLDOWN_FILE") )) [ "$file_age" -lt $(( COOLDOWN_MINUTES * 60 )) ] } # 冷却期間を考慮したアラート送信 send_alert_with_cooldown() { local subject="$1" local body="$2" if is_in_cooldown; then log_info "冷却期間中のためアラートスキップ" return 0 fi echo "$body" | mail -s "$subject" "$ALERT_MAIL" touch "$COOLDOWN_FILE" # フラグファイルを更新 log_info "アラート送信: $ALERT_MAIL" } # 全サービスが正常に戻ったら冷却フラグをリセット clear_cooldown() { rm -f "$COOLDOWN_FILE" log_info "冷却フラグをリセット(全サービス正常)" }

stat -c %Y でファイルの最終更新タイムスタンプを秒単位で取得し、現在時刻との差分が冷却期間(ここでは 30 分)を超えたときだけ再送する。フラグファイルは /tmp/ に置くことでサーバー再起動時に自動クリアされる設計にしている。

実際のログ上では次のような動きになる。

[2026-07-24 10:05:01] ERROR Postfix SMTP (port 25) 不到達 [2026-07-24 10:05:01] INFO アラート送信: ops@example.local [2026-07-24 10:10:01] ERROR Postfix SMTP (port 25) 不到達 [2026-07-24 10:10:01] INFO 冷却期間中のためアラートスキップ [2026-07-24 10:15:01] ERROR Postfix SMTP (port 25) 不到達 [2026-07-24 10:15:01] INFO 冷却期間中のためアラートスキップ [2026-07-24 10:35:01] INFO Postfix (port 25) OK [2026-07-24 10:35:01] INFO 冷却フラグをリセット(全サービス正常)

死活監視の設計と合わせて、シェルスクリプト全体の設計力を体系的に身につけたい方は、シェルスクリプト実践講座(Linux Master Pro)もご覧ください。現場で使える設計パターンを体系的に学べます。

cronに組み込んで定期実行する

1. crontabへの登録


# crontab -e で設定する(5分おきに実行する例) */5 * * * * /usr/local/bin/service-monitor.sh # スクリプトに実行権限を付与する chmod 700 /usr/local/bin/service-monitor.sh

cron で実行する場合、スクリプト内で使うコマンド(curl・nc・mail)のパスを /usr/bin/curl のようにフルパスで指定するか、スクリプト冒頭で PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin を宣言しておく。cron の環境変数は対話シェルより大幅に少ないため、PATH が通っておらずコマンドが見つからないエラーが起きやすい。

2. ログのローテーション設定

監視ログを放置するとディスクを圧迫するため、logrotate で管理する。

# /etc/logrotate.d/service-monitor として保存する /var/log/service-monitor.log { daily rotate 14 compress missingok notifempty create 0644 root root }

トラブルシュート:監視が動かない・誤検知するケース

1. curlがSSLエラーで失敗する

自己署名証明書や内部 CA を使っている環境では curl が SSL エラーで終了コード 60 を返す。

# 内部 CA を指定する(推奨) curl --cacert /etc/pki/ca-trust/source/anchors/internal-ca.crt \ -s -o /dev/null -w '%{http_code}' https://internal.example.local/ # 証明書検証をスキップする(開発環境のみ・本番不可) curl -k -s -o /dev/null -w '%{http_code}' https://internal.example.local/

【注意】本番環境では -k(証明書検証スキップ)は絶対に使わない。攻撃者による中間者攻撃(MITM)を見逃す危険がある。CA 証明書を適切に配置して正規の SSL 検証を通す設計にすること。

2. ncコマンドが使えない環境

一部の最小構成インストールでは nc がインストールされていない場合がある。その場合は /dev/tcp で代替できる。

# /dev/tcp を使った TCP ポートチェック(bash 組み込み機能) # nc が使えない環境での代替手段 check_port_bash() { local host="$1" local port="$2" local timeout="${3:-5}" (timeout "$timeout" bash -c "echo >/dev/tcp/$host/$port") 2>/dev/null }

/dev/tcp/ホスト名/ポート番号 は bash の組み込み機能で、OS のファイルとして存在するわけではない。echo >/dev/tcp/... でそのポートへの TCP 接続を試みる。

3. 監視スクリプト自体が落ちたことに気づかない

「監視が動いていない」こと自体を検知する仕組みが必要だ。シンプルな対策は、監視スクリプトが正常終了した際に「ハートビートファイル」を更新し、そのファイルが一定時間更新されていないことを別の仕組みで検知する方法だ。

# service-monitor.sh の末尾に追記するハートビート更新 touch /var/run/service-monitor.heartbeat # ハートビートチェック(別の cron ジョブや別サーバーから実行) HEARTBEAT="/var/run/service-monitor.heartbeat" MAX_AGE=600 # 10分以上更新されていなければ異常 if [ -f "$HEARTBEAT" ]; then age=$(( $(date +%s) - $(stat -c %Y "$HEARTBEAT") )) if [ "$age" -gt "$MAX_AGE" ]; then echo "監視スクリプトが停止している可能性があります" | \ mail -s "[緊急] 監視スクリプト停止疑い" ops@example.com fi fi

本記事のまとめ

シェルスクリプトの死活監視設計をまとめる。

監視対象 コマンド 判定方法
HTTP/HTTPS(Web サービス) curl -s -o /dev/null -w '%{http_code}' URL ステータス 200~399 を正常とみなす
TCP ポート(SSH/SMTP/DBなど) nc -z -w 5 ホスト ポート 終了コード 0 = 到達可
アラートメール送信 echo 本文 | mail -s 件名 宛先 障害検知時に自動送信
アラート連射防止 touch / stat -c %Y でフラグ管理 冷却期間内は送信をスキップ
定期実行 */5 * * * * /path/to/monitor.sh 5分おき(間隔はサービス特性に合わせる)

死活監視で最もよくある失敗は「アラート設計がない」「アラートが来すぎて無視するようになる」の2つだ。今回紹介した冷却期間設計を組み込んで、1回の障害で1通のアラートが届く仕組みを目指してほしい。

まずは監視対象の1サービスだけを選んで5行のスクリプトから始めてみるとよい。「動く仕組みがある」という安心感は、サーバー管理者の夜間の睡眠の質を確実に変えてくれる。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。