目次Category
AWSでEC2を運用していると、CPU使用率の上昇やWebサーバーのエラーなど、 異常をできるだけ早く検知したい場面があります。
そのようなときに役立つのが、 Amazon CloudWatchです。
この記事では、CloudWatchを初めて利用する方に向けて、 EC2のCPU使用率とApacheのエラーログを監視し、 異常を検知した際にメール通知を送る方法を解説します。
この記事でできること
- CloudWatchの基本的な役割を理解する
- EC2のCPU使用率を監視する
- CPU使用率がしきい値を超えた際にメール通知を送る
- CloudWatch Agentを使ってApacheのエラーログを監視する
- アラートメールを日本語化する仕組みを理解する
この記事の対象者
- AWSを学び始めた方
- CloudWatchを初めて利用する方
- EC2の監視方法を知りたい方
CloudWatchとは?
Amazon CloudWatchは、 AWSリソースやアプリケーションの状態を監視するサービスです。
EC2では、CPU使用率やネットワーク通信量などの情報を標準で確認できます。 また、CloudWatch Agentを導入することで、 EC2内のメモリ使用率やApacheのログなどもCloudWatchへ送信できます。
ポイント
CloudWatchは「監視」、CloudWatch Alarmは「条件の判定」、 SNSは「通知」を担当します。
今回構築する構成
今回は、EC2のCPU使用率とApacheのエラーログを監視し、 異常を検知した場合にメール通知を送信します。
↓
CloudWatch / CloudWatch Agent
↓
CloudWatch Alarm
↓
SNS
↓
メール通知
メール本文を日本語に整形する場合は、 CloudWatch AlarmからLambdaを呼び出し、 Lambdaが日本語に整形した内容をSNSへ送信します。
使用するAWSサービス
| サービス | 役割 |
|---|---|
| EC2 | 監視対象となるサーバー |
| CloudWatch | CPU使用率やログなどを監視する |
| CloudWatch Alarm | しきい値をもとに状態を判定する |
| CloudWatch Agent | EC2内のログなどをCloudWatchへ送信する |
| SNS | 登録したメールアドレスへ通知する |
| Systems Manager | CloudWatch Agentの導入と設定に使用する |
| Lambda | 通知内容を日本語に整形する |
1. CPU使用率の監視とアラーム作成
はじめに、EC2のCPU使用率を監視します。 今回は、CPU使用率が50%以上になった場合に、 登録したメールアドレスへ通知するアラームを作成します。
手順1:SNSトピックを作成する
まず、CloudWatchからの通知を受け取るために、 SNSトピックを作成します。
設定内容
タイプ:Standard
トピック名:ojt-sns-topic-[名前]-01
- AWSマネジメントコンソールで「SNS」を検索します。
- 左側のメニューから「トピック」を開きます。
- 「トピックの作成」を選択します。
- タイプで「Standard」を選択します。
- トピック名を入力して作成します。
手順2:メールアドレスを登録する
- 作成したSNSトピックを開きます。
- 「サブスクリプションの作成」を選択します。
- プロトコルで「Email」を選択します。
- エンドポイントに通知先のメールアドレスを入力します。
- 「サブスクリプションの作成」を選択します。
登録したメールアドレスに確認メールが届きます。 メール内の「Confirm subscription」を選択して、 サブスクリプションを承認してください。
注意
メールアドレスを登録しただけでは通知を受信できません。 必ず確認メールからサブスクリプションを承認してください。
手順3:CloudWatchアラームを作成する
- AWSマネジメントコンソールで「CloudWatch」を検索します。
- 左側のメニューから「アラーム」を選択します。
- 「すべてのアラーム」を開きます。
- 「アラームの作成」を選択します。
- 「メトリクスの選択」を選択します。
- 「EC2」から「インスタンス別メトリクス」を開きます。
- 監視対象のインスタンスIDを確認し、「CPUUtilization」を選択します。
アラーム条件
| 項目 | 設定値 |
|---|---|
| 統計 | 平均 |
| 期間 | 1分 |
| しきい値の種類 | 静的 |
| 条件 | CPUUtilizationが50以上 |
| 評価するデータポイント | 1/1 |
この設定では、1分間の平均CPU使用率が50%以上になると、 アラーム状態へ変化します。
通知先には、手順1で作成したSNSトピックを選択してください。
手順4:CPUに負荷をかけて動作確認する
EC2へ接続し、以下のコマンドを実行します。
cat /dev/zero | sha1sum &コマンドを実行すると、EC2のCPUに負荷がかかります。 設定内容によって多少時間は異なりますが、1〜2分程度待つと、 CloudWatchのアラーム状態が 「OK」から「アラーム状態」へ変化します。 登録したメールアドレスに通知が届けば成功です。
動作確認が終わったら、以下のコマンドでCPU負荷を停止します。
killall sha1sum注意
&を付けて実行した処理はバックグラウンドで動作します。 動作確認が完了したら、必ず停止コマンドを実行してください。
2. Apacheログの監視とアラーム作成
次に、EC2内のApacheエラーログを監視します。
CloudWatchの標準メトリクスだけでは、 EC2内に保存されているApacheのログを取得できません。 そのため、EC2へCloudWatch Agentを導入します。
CloudWatch Agentとは?
EC2内のログなどを収集し、 CloudWatchへ送信するためのソフトウェアです。
↓
CloudWatch Agent
↓
CloudWatch Logs
手順1:EC2用のIAMロールを作成する
EC2がCloudWatchへログを送信し、 Systems Managerから操作できるようにIAMロールを作成します。
設定内容
ロール名:ojt-ec2-roll-[名前]-01
ポリシー:CloudWatchAgentServerPolicy
ポリシー:AmazonSSMManagedInstanceCore
ロールを作成したら、AWSマネジメントコンソールで監視対象のEC2を選択し、 「アクション」→「セキュリティ」→「IAMロールを変更」を開きます。 その後、作成したIAMロールを選択して、「IAMロールを更新」を押します。

手順2:設定ファイルをParameter Storeに保存する
CloudWatch Agent設定ファイルである config.jsonをテキストエディタで編集します。
CloudWatch Agentが収集するログファイルの場所や、 CloudWatch Logsに作成するロググループ名などを config.jsonに記述します。
Parameter Storeへ保存することで、 Run CommandからCloudWatch Agentの設定を読み込めるようになります。
config.jsonの内容
今回は、ApacheのアクセスログとエラーログをCloudWatch Logsへ送信するため、 以下の内容をconfig.jsonとして使用します。
{
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "root"
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/httpd/access_log",
"log_group_name": "ojt-apache-accesslog-fujita-01",
"log_stream_name": "fujita",
"retention_in_days": 7
},
{
"file_path": "/var/log/httpd/error_log",
"log_group_name": "ojt-apache-error-log-fujita-01",
"log_stream_name": "fujita",
"retention_in_days": 7
}
]
}
}
}
}主な設定内容
metrics_collection_interval:Agentの収集間隔を60秒に設定します。run_as_user:CloudWatch Agentをrootユーザーとして動かします。file_path:収集対象となるApacheログの保存場所です。log_group_name:CloudWatch Logsに作成するロググループ名です。log_stream_name:ログストリーム名です。retention_in_days:ログの保存期間を7日間に設定します。
自分の環境に合わせて変更する箇所
log_group_nameとlog_stream_nameは、 自分の名前や命名規則に合わせて変更してください。 また、Apacheのログファイルが別の場所にある場合は、file_pathも変更します。
手順3:Run CommandでCloudWatch Agentを導入する
Systems ManagerのRun Commandを利用して、 EC2へCloudWatch Agentをインストールします。
インストール後、Parameter Storeに保存した設定を読み込ませ、 CloudWatch Agentへ設定を適用します。
主な設定値
ドキュメント名:AmazonCloudWatch-ManageAgent
Action:configure
Mode:ec2
Configuration Source:ssm
Configuration Location:AmazonCloudWatch-linux
Restart:yes
手順4:CloudWatch Logsへの出力を確認する
CloudWatchの「ロググループ」を開き、 設定したApacheのエラーログが出力されていることを確認します。
ロググループ内のログストリームを開き、 ログイベントにApacheのログが表示されていれば成功です。
手順5:メトリクスフィルターとアラームを作成する
ログの中から特定の文字列を検知するために、 メトリクスフィルターを作成します。
今回は、Apacheのエラーログに含まれる errorという文字列を検知するように設定します。
メトリクスフィルター作成画面のフィルターパターンに、 以下の文字列を入力します。
errorポイント
ログ内でerrorに一致するイベントが発生した際に、 メトリクス値として1を記録します。 そのメトリクスをCloudWatch Alarmで監視します。
メトリクスフィルターを作成したら、 そのメトリクスを監視するCloudWatchアラームを作成し、 通知先としてSNSトピックを指定します。
手順6:エラーログを書き込んで動作確認する
EC2へ接続し、以下のコマンドでApacheのエラーログへ errorという文字列を追記します。
echo "error" | sudo tee -a /var/log/httpd/error_logCloudWatch Logsに追記した内容が表示され、 CloudWatchアラームが「アラーム状態」へ変化し、 メール通知が届けば成功です。
補足
viでログファイルを直接編集する方法もありますが、 初心者向けの記事では、ログの末尾へ追記できる tee -aを使用しています。
3. アラートメールの本文を日本語にする
CloudWatchからSNSを通して送信される標準のアラートメールには、 JSON形式の情報や英語の項目名が含まれます。
内容を確認することはできますが、 初めて見る人には少し分かりにくい場合があります。
そこで、CloudWatch AlarmからLambdaを呼び出し、 Lambdaが必要な情報を日本語に整形してからSNSへ送信します。
↓
Lambda
↓
日本語に整形
↓
SNS
↓
メール通知
Lambdaで整形する主な情報
- アラーム名
- アラームが発生した時刻
- 監視対象
- アラームが発生した理由
- 現在のアラーム状態
手順1:Lambda関数を作成する
- AWSマネジメントコンソールで「Lambda」を検索します。
- 「関数の作成」を選択します。
- 「一から作成」を選択します。
- 任意の関数名を入力します。例:
ojt-cloudwatch-mail-fujita-01 - ランタイムで「Python 3.12」を選択します。
- 「関数の作成」を選択します。
手順2:Lambdaへコードを記述する
作成したLambda関数の「コード」タブを開き、 lambda_function.pyへ以下のコードを貼り付けて、「Deploy」を押します。
import os
from datetime import datetime, timedelta, timezone
import boto3
sns = boto3.client("sns")
SNS_TOPIC_ARN = os.environ["SNS_TOPIC_ARN"]
def lambda_handler(event, context):
"""
CloudWatch Alarmの状態変化を受け取り、
必要な情報を日本語に整形してSNSへ送信する。
"""
alarm_data = event.get("alarmData", {})
state = alarm_data.get("state", {})
configuration = alarm_data.get("configuration", {})
alarm_name = alarm_data.get("alarmName", "取得できませんでした")
state_value = state.get("value", "UNKNOWN")
reason = state.get("reason", "取得できませんでした")
occurred_at = state.get("timestamp") or event.get("time", "")
region = event.get("region", "取得できませんでした")
account_id = event.get("accountId", "取得できませんでした")
alarm_arn = event.get("alarmArn", "取得できませんでした")
target = get_monitoring_target(configuration)
occurred_at_jst = convert_to_jst(occurred_at)
state_ja = {
"ALARM": "アラーム状態",
"OK": "正常",
"INSUFFICIENT_DATA": "データ不足",
}.get(state_value, state_value)
subject = f"【CloudWatch】{alarm_name}:{state_ja}"
message = f"""CloudWatchアラームの状態が変化しました。
■ アラーム名
{alarm_name}
■ 現在の状態
{state_ja}({state_value})
■ 発生時刻
{occurred_at_jst}
■ 監視対象
{target}
■ 発生理由
{reason}
■ AWSリージョン
{region}
■ AWSアカウントID
{account_id}
■ アラームARN
{alarm_arn}
AWSマネジメントコンソールで詳細を確認してください。
"""
response = sns.publish(
TopicArn=SNS_TOPIC_ARN,
Subject=subject[:100],
Message=message,
)
print(f"SNSへの送信に成功しました: {response['MessageId']}")
return {
"statusCode": 200,
"message": "日本語のアラート通知を送信しました。",
}
def get_monitoring_target(configuration):
"""アラーム設定から監視対象のメトリクスとディメンションを取得する。"""
metrics = configuration.get("metrics", [])
if not metrics:
return "取得できませんでした"
metric_stat = metrics[0].get("metricStat", {})
metric = metric_stat.get("metric", {})
namespace = metric.get("namespace", "不明")
metric_name = metric.get("name", "不明")
dimensions = metric.get("dimensions", {})
dimension_text = ", ".join(
f"{key}={value}" for key, value in dimensions.items()
)
if dimension_text:
return f"{namespace} / {metric_name} / {dimension_text}"
return f"{namespace} / {metric_name}"
def convert_to_jst(timestamp_text):
"""CloudWatchから受け取った時刻を日本時間へ変換する。"""
if not timestamp_text:
return "取得できませんでした"
try:
normalized = timestamp_text.replace("Z", "+00:00")
if (
len(normalized) >= 5
and normalized[-5] in ("+", "-")
and normalized[-3] != ":"
):
normalized = normalized[:-2] + ":" + normalized[-2:]
timestamp = datetime.fromisoformat(normalized)
jst = timezone(timedelta(hours=9))
return timestamp.astimezone(jst).strftime(
"%Y年%m月%d日 %H時%M分%S秒"
)
except ValueError:
return timestamp_textコードの主な処理
- CloudWatch Alarmからアラーム名・状態・発生理由・発生時刻を取得します。
- 発生時刻を日本時間へ変換します。
- アラーム状態を「正常」「アラーム状態」「データ不足」の日本語へ変換します。
- 監視対象のメトリクスやEC2インスタンスIDを取得します。
- 日本語へ整形した内容をSNSトピックへ送信します。
手順3:環境変数を設定する
Lambda関数の「設定」→「環境変数」→「編集」を開き、 次の環境変数を追加します。
環境変数
キー:SNS_TOPIC_ARN
値:通知先として使用するSNSトピックのARN
SNSトピックのARNは、Amazon SNSで作成したトピックの詳細画面から確認できます。
手順4:Lambdaの実行ロールへSNS送信権限を追加する
LambdaからSNSトピックへメッセージを送信するため、 Lambdaの実行ロールにsns:Publish権限を追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "通知先SNSトピックのARN"
}
]
}ポイント
Resourceには、環境変数SNS_TOPIC_ARNへ設定したものと同じSNSトピックのARNを指定します。
手順5:CloudWatch AlarmからLambdaを呼び出す
- CloudWatchで対象のアラームを開きます。
- 「アクション」からアラームの編集画面を開きます。
- 通知またはアクションの設定でLambdaアクションを追加します。
- 作成したLambda関数を選択します。
- 設定を保存します。
アラームの状態が変化するとLambdaが実行され、 日本語へ整形したメッセージがSNSを通じてメールで送信されます。
うまくいかない場合の確認ポイント
メールが届かない場合
- SNSのサブスクリプションを承認しているか
- 迷惑メールフォルダに入っていないか
- アラームに正しいSNSトピックを設定しているか
- CloudWatchアラームが「アラーム状態」になっているか
CPUアラームが発生しない場合
- 監視対象のEC2インスタンスIDが正しいか
- CPUUtilizationを選択しているか
- しきい値が50%に設定されているか
- 期間が1分、評価するデータポイントが1/1になっているか
- CPU負荷をかけるコマンドが動作しているか
ログがCloudWatchに表示されない場合
- EC2にIAMロールをアタッチしているか
- CloudWatch Agentが起動しているか
config.jsonのログファイルパスが正しいか- Parameter Storeの名前を正しく指定しているか
- 監視対象のログファイルが存在しているか
- Systems Managerで対象EC2がマネージドノードとして認識されているか
ログアラームが発生しない場合
- メトリクスフィルターのパターンが
errorになっているか - CloudWatch Logsに
errorを含む新しいログが出力されているか - 正しいカスタムメトリクスをアラームで選択しているか
- 期間やしきい値の設定が正しいか
日本語メールが届かない場合
- Lambdaの環境変数
SNS_TOPIC_ARNが正しいか - Lambdaの実行ロールに
sns:Publish権限があるか - CloudWatch Alarmに正しいLambda関数を設定しているか
- Lambdaのコードを変更した後に「Deploy」を押しているか
- LambdaのCloudWatch Logsにエラーが出ていないか
- SNSトピックのメールサブスクリプションが承認済みか
まとめ
今回実施した内容
- CloudWatchによるEC2のCPU使用率の監視
- CloudWatch Alarmによるしきい値の判定
- SNSを利用したメール通知
- CloudWatch Agentを利用したApacheログの監視
- メトリクスフィルターを利用したログアラームの作成
- Lambdaを利用したアラートメールの日本語化
CloudWatchを活用すると、AWSリソースやログの異常を早期に発見できます。 初めて設定する場合は、まずCPU使用率の監視から始め、 その後にログ監視や通知内容のカスタマイズへ進むと理解しやすくなります。
今回の記事では、CPU使用率の監視、Apacheログの監視、 メール通知、日本語通知の仕組みまで紹介しました。
同じカテゴリーの記事
同じカテゴリの記事を読む