監視方針
概要
本番の AWS リソースを CloudWatch アラームで監視し、発報を Slack へ届けるための方針をまとめる。リージョンは ap-northeast-1(東京)で、管理下のアラームは 60 件である。
このページは「何をどう監視するか」の方針を扱う。適用・手動実行・切り戻しといった手を動かす手順は h2t/infrastructure の docs/ にある手順書へ分けてある(「手順書の置き場所」を参照)。
方針
要件は次の 2 つである。
| # | 要件 | 具体化したもの |
|---|---|---|
| 1 | 止まっていたら Slack へ通知する | サイト(ALB から見た正常なホスト数)とサーバー(EC2 のステータスチェック)の死活を見る |
| 2 | 負荷が 90% でもアラートを出す | CPU・メモリ・ディスクの使用率を 90% で見る。DB は加えて空き容量の枯渇も見る |
設計はリソースの種別ではなくシステム単位で立てている。全システムがサーバー 1 台構成であり、EC2 が止まればそのシステムは全停止するためである。1 台構成であることは「正常なホスト数が 1 台を下回ったら異常」という閾値の根拠にもなっている。
死活でも負荷 90% でもない項目(応答時間・レイテンシ・レプリカ遅延など)は監視しない。何が起きたかを掘り下げるための指標であり、止まっているか否かの判断には使わないためである。
発報(ALARM)と復帰(OK)の両方を通知する。復帰まで届くことで、対応が済んだのか放置されているのかを Slack の履歴だけで追える。
監視の対象
システムごとの内訳は次のとおりである。件数は管理下 60 件の内訳であり、合計は 60 になる。
| システム | サイト(ALB) | サーバー(EC2) | DB(RDS のインスタンス) | 件数 |
|---|---|---|---|---|
| GMAC | gmac-production | gmac-app-production | gmac-db-productiongmac-db-production-ap-northeast-1a | 12 |
| GCOR | gcor-production | gcor-app-production | gcor-db-production-cluster-instance-1 | 9 |
| PIPIT | kingmeo-production | kingmeo-app-production | kingmeo-db-production-cluster-instance-1-ap-northeast-1c | 9 |
| Kuchikomi One | kuchikomi-one-production | Kuchikomi One | kuchikomi-one-db-production | 9 |
| meo-times | meotimes-elb | meo-times | meotimes-instance-1-ap-northeast-1a | 9 |
| smart-meo | smart-meo-listingplus | smart-meo-listingplus | なし | 6 |
| スクレイピング基盤 | — | — | scraper-instance-1-ap-northeast-1c | 5 |
| 通知基盤 | — | — | — | 1 |
PIPIT のリソース名は kingmeo-* である。名前とサービス名が一致しないため、meo を含む meo-times と取り違えないよう注意する。
スクレイピング基盤は EC2 を持たず、Lambda(sync_database / fetch_search_ranking)と Aurora(scraper)で動く。通知基盤は Lambda h2t-alarm-notifier だけである。
監視の対象にしないもの
| 対象 | 扱い | 理由 |
|---|---|---|
GMAC ステージング(gmac-staging) | 監視しない | 検証用であり、夜間・土日祝は自動で停止する。停止をもって異常とは扱えない |
社内基盤の踏み台(h2-dev-bastion) | 計測だけ行い、アラームは作らない | 本番のサービスを担っていない。CloudWatch agent は入れてあり、取れたメモリの実測はインスタンスサイズの適正化(h2t/infrastructure の Issue #38)の判断材料として使う |
以前の棚卸し(Issue #30)で挙がっていた検証環境の places-app-staging / places-db-staging は現存しないため、対象に含めていない。停止中の環境を抱えたまま監視をどう扱うかを決める必要は、いまのところ生じていない。
監視項目と閾値
層ごとの内訳である。「欠測」はデータ点が無い評価枠の扱い(treat_missing_data)を指す。
| 層 | 監視する内容 | メトリクス | 統計 | 条件 | 欠測 | 件数 |
|---|---|---|---|---|---|---|
| サイト | 正常なホスト数 | AWS/ApplicationELB の HealthyHostCount | Average | 1 未満 | 異常 | 6 |
| サーバー(死活) | ステータスチェック | AWS/EC2 の StatusCheckFailed | Average | 0 を超える | 判断しない | 6 |
| サーバー(死活・自動復旧) | システム側のステータスチェック | AWS/EC2 の StatusCheckFailed_System | Maximum | 0.99 以上 | 判断しない | 6 |
| サーバー(負荷) | CPU 使用率 | AWS/EC2 の CPUUtilization | Average | 90% を超える | 判断しない | 6 |
| サーバー(負荷) | メモリ使用率 | H2T/EC2 の mem_used_percent | Average | 90% を超える | 判断しない | 6 |
| サーバー(負荷) | ディスク使用率 | H2T/EC2 の disk_used_percent | Maximum | 90% を超える | 判断しない | 6 |
| DB(負荷) | CPU 使用率 | AWS/RDS の CPUUtilization | Average | 90% を超える | 判断しない | 7 |
| DB(枯渇) | 空きメモリ | AWS/RDS の FreeableMemory | Average | 100 MB 未満 | 判断しない | 7 |
| DB(枯渇) | 空きローカルストレージ | AWS/RDS の FreeLocalStorage | Average | 200 MB 未満 | 判断しない | 7 |
| スクレイピング | 起動の途絶 | AWS/Lambda の Invocations | Sum | 1 時間の枠が 16 回続けて 0 | 異常 | 1 |
| スクレイピング | 実行時間 | AWS/Lambda の Duration | Maximum | 150 秒を超える | 正常 | 1 |
| 通知基盤 | エラー数 | AWS/Lambda の Errors | Sum | 0 を超える | 正常 | 1 |
評価の間隔は 5 分(自動復旧の 6 件は 1 分、起動の途絶は 1 時間)である。ディスクだけ統計を Maximum にしているのは、1 台に複数のファイルシステムの系列があり、Average では 1 つの逼迫が薄まって発報しないためである。
閾値の根拠
| 閾値 | 値 | 根拠 |
|---|---|---|
| 負荷(CPU・メモリ・ディスク) | 90% | 「負荷 90% でもアラートを出す」という要件そのものである。EC2 と RDS で値をそろえてある |
| 正常なホスト数 | 1 未満 | 全システムがサーバー 1 台構成であり、1 台でも欠ければサイトが応答しない |
| DB の空きメモリ | 100 MB | GMAC の DB に元からあった値を、他の DB へもそろえた |
| DB の空きローカルストレージ | 200 MB | 同上 |
| スクレイピングの実行時間 | 150 秒 | 正常時の実測は 98.6〜106.5 秒、Issue #41 の障害時は 180.9〜185.0 秒であった。両者の間に置いてある |
| スクレイピングの起動の途絶 | 16 時間 | 定期実行は 08:00 と 17:00(JST)で、最大の間隔は 15 時間である。1 時間の余裕を足した |
| 通知基盤のエラー | 1 件以上 | 通知が壊れると他のすべての監視が届かなくなるため、1 件でも異常とする |
起動の途絶だけはメトリクス数式で評価する。起動が無い時間帯はデータ点そのものが出ないため、素の欠測評価に任せると発報が 16 時間より遅れ得る。Invocations の 1 時間ごとの Sum に FILL(m1, 0) を掛けて各枠へ明示的な 0 を作り、16 枠続けて 0 =最後の起動から最大 16 時間で確実に発報する形にしている。
欠測の扱い
データ点が無い枠をどう評価するかは 3 通りに分かれる。
| 扱い | 件数 | 対象 | そう決めた理由 |
|---|---|---|---|
異常(breaching) | 7 | サイトの HealthyHostCount 6 件と、スクレイピングの起動の途絶 1 件 | 欠測は「正常なホストが数えられない」「メトリクスごと消えた」状態であり、死活の観点では異常に倒す |
正常(notBreaching) | 2 | スクレイピングの実行時間と、通知基盤のエラー数 | 対象の関数が疎にしか動かず、実行の合間は必ず欠測になる。データが無い状態は異常を意味しない |
判断しない(missing) | 51 | 上記以外 | 常時データが出る系列であり、欠測は起きない前提である |
アラーム名と閾値が食い違う 3 件
EC2.gmac-app-production.CPUUtilization_over_85 と RDS の ..._over_80 2 件は、名前と裏腹に実際の閾値が 90% である。アラーム名は Terraform への取り込み(import)の鍵であり、改名すると削除と再作成になって発報の履歴まで失われるため、名前は変えない方針を採っている。閾値の正はコード(terraform/monitoring-alarms/alarms.tf)であり、名前ではない。
通知の流れ
CloudWatch アラームが SNS トピックへ発報し、トピックが Lambda を呼び、Lambda が本文を組み立てて Slack へ投稿する。
| 経路の要素 | 決めごと |
|---|---|
| 通知先の指定 | 60 件すべてが AlarmActions と OKActions の両方に h2t-alarm-notification を指す。発報も復帰も届く |
| 投稿先 | Slack の Incoming Webhook。届く先のチャンネルは Webhook 側で決まる |
| Webhook URL | SSM Parameter Store の /h2t/alarm-notifier/slack-webhook-url(SecureString)に置き、Lambda が実行のたびに復号して読む。コード・Terraform の state・ログには残さない |
| 差し替え | SSM のパラメータを書き換えるだけでよい。Terraform を適用し直す必要はない |
Slack の本文は日本語で組み立てる。見出しで状態を区別するため、一覧を眺めるだけで発報と復帰を見分けられる。
| 状態 | 見出し |
|---|---|
ALARM | 🔴 ALARM 発生 |
OK | 🟢 OK 復旧 |
上記以外(INSUFFICIENT_DATA を含む) | ⚠️ と、受け取った値をそのまま |
見出しに続けて、説明・状態の遷移(JST)・理由・対象(名前空間/メトリクス名/ディメンション)・条件(統計/比較演算子/閾値/間隔/評価回数)・リージョンを並べる。ComparisonOperator などの API の列挙値は原文のまま出す。CloudWatch の画面と突き合わせられるようにするためである。
自動復旧の 6 件(awsec2- で始まるもの)は、通知先に加えて arn:aws:automate:ap-northeast-1:ec2:recover を併記している。発報すると Slack へ届くと同時に、実際にインスタンスの復旧処理が走る。
発報したときの動き
Slack の通知を受けた担当者が、アラーム名から層を読み取って次の順に確認する。復帰(🟢 OK 復旧)が届くまで見届ける。
| 通知されたアラーム | 最初に確認すること |
|---|---|
ELB.*.HealthyHostCount_under_1 | 該当システムのサイトが応答するか。応答しなければサーバー(EC2)の状態へ進む |
EC2.*.StatusCheckFailed | インスタンスが動いているか。停止・再起動中でないか |
awsec2-*(自動復旧) | 復旧処理が走っているため、復旧が終わってサービスが戻ったかまで見る |
EC2.*.CPUUtilization_over_* / mem_used_percent_over_90 | 何が使っているかをプロセス単位で見る。恒常的に高いならサイズの適正化(Issue #38)の対象になる |
EC2.*.disk_used_percent_over_90 | ログやアップロードの肥大を疑う。空けても再発するなら容量そのものを見直す |
RDS.*.CPUUtilization_over_90 | 重いクエリと接続数を見る |
RDS.*.FreeableMemory_under_100m / FreeLocalStorage_under_200m | 枯渇はサイトの停止に直結する。空き容量の回復を最優先で行う |
Lambda.sync_database.Duration_over_150s / Lambda.fetch_search_ranking.Invocations_under_1_16h | スクレイピングの起動が途絶えていないか。sync_database のログと VPC エンドポイントの状態を見る |
Lambda.h2t-alarm-notifier.Errors_over_0 | **通知そのものが壊れている。**他の発報が Slack へ届いていない可能性があるため、CloudWatch のアラーム一覧を直接確認する |
sync_database と fetch_search_ranking の発報は、Issue #41 と同じ壊れ方(外部へ到達できずに待たされる)を捉えるためのものである。切り分けの具体的な手順は h2t/infrastructure の docs/monitoring-alarms.md の「スクレイピングの検知が発報したときの調べ方」にまとめてある。
収集の仕組み
メモリとディスクは標準メトリクス(AWS/EC2)に含まれない。OS の中から送る必要があるため、EC2 8 台に CloudWatch agent を入れ、60 秒ごとに名前空間 H2T/EC2 へ送っている。
| 種別 | メトリクス名 | ディメンション |
|---|---|---|
| CPU | cpu_usage_active / cpu_usage_idle | InstanceId |
| メモリ | mem_used_percent / mem_used / mem_total | InstanceId |
| ディスク | disk_used_percent / disk_free / disk_used | InstanceId + path + device + fstype |
ディスクだけディメンションが 4 つになるのは、agent がファイルシステムごとに path / device / fstype を自動で付けるためである。disk_used_percent には InstanceId だけの系列が存在せず、アラーム側で 4 つとも指定しなければ永久に INSUFFICIENT_DATA のままになる。device と fstype は台ごとに違うため、他の台の値を写して埋めてはならない。
導入状況
| インスタンス | OS | agent | アラーム |
|---|---|---|---|
gmac-app-production | Amazon Linux 初代 | 導入済み | あり |
gcor-app-production | Amazon Linux 2 | 導入済み | あり |
kingmeo-app-production | Amazon Linux 2 | 導入済み | あり |
Kuchikomi One | Amazon Linux 初代 | 導入済み | あり |
meo-times | Amazon Linux 初代 | 導入済み | あり |
smart-meo-listingplus | Amazon Linux 2 | 導入済み | あり |
h2-dev-bastion | Amazon Linux 2 | 導入済み | 作らない(監視対象外) |
gmac-staging | Amazon Linux 初代 | 導入済み(mem_used と mem_total が欠けている) | 作らない(監視対象外) |
この名前空間に依存しているもの
H2T/EC2 を読んでいるのはアラームだけではない。 費用のレポート(Lambda h2t-cost-report)と日次の健全性チェック(Lambda daily-health-check)も同じ名前空間とメトリクス名を読む。どちらも系列が見つからなければその項目を省いて投稿するだけでエラーにならないため、設定を変えると誰も気づかないまま値だけが欠けた投稿が続く。
| 変えたもの | 起きること |
|---|---|
名前空間(H2T/EC2) | 2 つの Lambda とアラーム 12 件が一斉に値を失う |
メトリクス名(mem_used_percent など) | 該当の項目だけが投稿から消える。日次レポートは他の行がそのまま出るため気づきにくい |
append_dimensions を増やす | 系列が別物になり、既存の指定では引けなくなる |
| 収集間隔(60 秒) | 値は届き続けるが、5 分単位で評価するアラームの点の数が変わる |
したがって 1 台だけ設定を変えるということはしない。 変えるときは 8 台すべてを同じ内容にそろえ、アラームと 2 つの Lambda の側も併せて見直す。
運用
対象の増減
監視の対象を増やす・減らすときは、コンソールで直接触らず h2t/infrastructure の terraform/monitoring-alarms/ を直して適用する。コンソールで変えると次回の適用で打ち消され、変えたこと自体も記録に残らない。
| 場面 | 直すもの |
|---|---|
| 新しくアラームを作る | alarms.tf に定義を足し、scripts/alarm_classification.json の added へ名前を足す |
| 既存のアラームを管理下へ入れる | alarms.tf と imports.tf の両方に足し、alarm_classification.json の keep へ名前を足す |
| 管理下から外して消す | alarms.tf(取り込んだものは imports.tf も)から取り除き、alarm_classification.json からも取り除く |
いずれも件数の期待値を持つ検査があり、片方だけ直すと検査で止まる。具体的なコマンドは docs/monitoring-alarms.md の「対象の増減」を参照する。メモリ・ディスクのアラームを足すには、その台に CloudWatch agent が入っていて H2T/EC2 へ系列が届いていることが前提になる。
疑似発報のやり方
通知が Slack まで届くことは、障害の発生を待たずに確かめられる。aws cloudwatch set-alarm-state でアラームを ALARM へ落とし、Slack に届いたことを確認してから OK へ戻す。種別ごとに 1 件以上を選んで行う。
awsec2- で始まる 6 件では行わない。 ec2:recover が併記されており、状態を ALARM にすると実際に復旧処理が走るためである。
変更するときに崩れる前提
| 変えるもの | 併せて見直すもの |
|---|---|
| スクレイピングの定期実行の時刻(08:00 / 17:00 JST) | 起動の途絶を 16 時間で捉える前提が崩れる。Lambda.fetch_search_ranking.Invocations_under_1_16h の評価回数を見直す |
| CloudWatch agent の名前空間・メトリクス名・ディメンション | アラーム 12 件と 2 つの Lambda が黙って値を失う |
SNS トピック h2t-alarm-notification の削除 | 60 件の通知先が宛先の無い ARN になる。Issue #30 の状態へ戻る |
手順書の置き場所
手を動かす手順は h2t/infrastructure の docs/ にある。
| 手順書 | 扱う範囲 |
|---|---|
docs/monitoring-alarms.md | アラームの適用・差分の見方・疑似発報・対象の増減・不要なアラームの削除と切り戻し |
docs/alarm-notification.md | 通知経路の適用・Webhook の差し替え・届かないときの確かめ方 |
docs/cloudwatch-agent.md | agent の設定・導入・確認 |
経緯
2026 年 8 月、通知先の SNS トピックが存在せず、本番の多くのリソースに監視が付いていない状態が見つかった(h2t/infrastructure の Issue #30)。ここを起点に、通知経路を作り直し、既存のアラームを取捨選択して Terraform の管理下へ移し、不足していた監視を足し、メモリとディスクを取れるようにするところまでを段階的に整えた。その結果が本ページに記した 60 件である。
| Issue | 行ったこと |
|---|---|
| #30 | 監視が機能していないことの把握(起点) |
| #43 | 通知経路の新設(SNS + Lambda + Slack) |
| #44 | 既存アラームの整理。実体の無いものの削除と通知先の差し替え |
| #45 | 未監視だった本番リソースへのアラーム追加 |
| #46 | CloudWatch agent の導入。メモリ・ディスクを取れるようにした |
| #47 | Zabbix の残骸の撤去(進行中) |
| #50 | スクレイピングが実行されないことの検知 |