From fd08c325017a79f0b2011d01fbcdd1e6f87d5d08 Mon Sep 17 00:00:00 2001 From: seonNoh Date: Fri, 9 Oct 2026 08:33:22 +0900 Subject: [PATCH] feat(ai-env,alerting): reference queries per alert in slo.md, rule expression as alert annotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第 4 回 V-G1 で LLM が route フィルタのない 5 分窓の p95(0.017 秒)を根拠に 「遅延なし」と結論した。slo.md にアラートごとの基準クエリ(規則と同じ式、 legacy のインスタンス別遅延と要求率を含む)を書き、確かめ直すときは同じ式を 使わせる。各規則の annotations.condition に式をそのまま入れ、AI 側が 「アラート条件が解消したか」を計算できるようにする(Jev の recovering 再定義)。 --- vm/ai-env/prompts/slo.md | 13 ++++++++++++- .../grafana/provisioning/alerting/rules.yaml | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/vm/ai-env/prompts/slo.md b/vm/ai-env/prompts/slo.md index e8c1d56..6438ea2 100644 --- a/vm/ai-env/prompts/slo.md +++ b/vm/ai-env/prompts/slo.md @@ -1,4 +1,15 @@ -可用性: キャンペーン期間を通じて avg_over_time(probe_success[5m]) >= 99.5%。レイテンシ: api の p95(histogram_quantile(0.95, sum by (le) (rate(demo_http_request_duration_seconds_bucket[5m]))))が 0.5 秒未満。エラー率: 5 分間の 5xx / 全リクエストが 1% 未満。 +可用性: キャンペーン期間を通じて avg_over_time(probe_success[5m]) >= 99.5%。レイテンシ: api の p95(histogram_quantile(0.95, sum by (le) (rate(demo_http_request_duration_seconds_bucket{route=~"orders|price"}[5m]))))が 0.5 秒未満。エラー率: 5 分間の 5xx / 全リクエストが 1% 未満。 復旧(t_recover)は、プローブが 12 回連続(3 分)で成功し、かつ 5 分窓のレイテンシとエラー率が SLO の内側に戻ったときにハーネスが宣言する。 層: 1 = プラットフォーム内部の自動復旧(kubelet の再起動、ReplicaSet、Argo CD selfHeal、MIG autohealing、compose の restart)。2 = あなたの提案 + Discord での人の承認、実行はハーネス。3 = 人だけが行う(不可逆な操作、15 分以内に承認がない場合、確信度 0.5 未満)。 アラートルールと意味: ProbeDown(probe_success == 0 が 1 分、症状のみ)、ApiLatencyHigh(直近 1 分の p95 > 500 ms が 2 分)、ApiErrorRateHigh(5xx > 1% が 3 分)、PodCrashLooping(5 分で 3 回以上の再起動。まず層 1、5 分後に層 2)、LegacyUnhealthy(up な legacy-api インスタンスが 2 未満。まず autohealing、10 分後に層 2)、DbReplicationLag(遅延 > 60 秒、または replay が一時停止)、ApiMemoryPressure(api コンテナのメモリが limit の 80% 超を 1 分継続。グレーゾーン、承認後に rollout restart)、DbPrimaryDown(TCP 5432 の失敗が 1 分)、GitOpsDrift(OutOfSync が 5 分。selfHeal が処理する)。 +アラートを確かめ直すときは、必ずアラート規則と同じ式(基準クエリ)を使う。route フィルタを外した式は healthz の速い要求で希釈され、遅延を見逃す(第 4 回 V-G1 の誤診)。基準クエリ: +- ProbeDown: probe_success{job="blackbox",target="alb"} +- ApiLatencyHigh: histogram_quantile(0.95, sum by (le) (rate(demo_http_request_duration_seconds_bucket{route=~"orders|price"}[1m])))。遅延の所在を分ける: api→legacy の上流遅延は histogram_quantile(0.95, sum by (le) (rate(demo_legacy_request_duration_seconds_bucket[1m])))、legacy のインスタンス別は histogram_quantile(0.95, sum by (instance, le) (rate(legacy_http_request_duration_seconds_bucket[1m])))、インスタンス別の要求率は sum by (instance) (rate(legacy_http_requests_total{route!="healthz"}[2m]))。1 分窓が高く 5 分窓が低いのは ILB が接続を別インスタンスへ逃がした直後で、遅いインスタンスが残っている限り原因は消えていない(network_latency_legacy、処置は mig.recreate_instance、対象はそのインスタンス名 legacy-xxxx)。 +- ApiErrorRateHigh: sum(rate(demo_http_requests_total{route=~"orders|price",status=~"5.."}[5m])) / sum(rate(demo_http_requests_total{route=~"orders|price"}[5m])) +- ApiMemoryPressure: max by (pod) (container_memory_working_set_bytes{namespace="demo",container="api"}) / on (pod) max by (pod) (kube_pod_container_resource_limits{namespace="demo",container="api",resource="memory"}) +- PodCrashLooping: increase(kube_pod_container_status_restarts_total{namespace="demo"}[5m]) +- LegacyUnhealthy: up{job="legacy-api"} +- DbReplicationLag: max(pg_replication_lag_seconds) と max(pg_is_wal_replay_paused) +- DbPrimaryDown: probe_success{job="blackbox",target="db-primary"} +- GitOpsDrift: argocd_app_info{sync_status!="Synced"} +アラートの annotations.condition にも規則の式がそのまま入っている。 diff --git a/vm/observability/grafana/provisioning/alerting/rules.yaml b/vm/observability/grafana/provisioning/alerting/rules.yaml index 99c06fb..c36068f 100644 --- a/vm/observability/grafana/provisioning/alerting/rules.yaml +++ b/vm/observability/grafana/provisioning/alerting/rules.yaml @@ -17,6 +17,7 @@ groups: layer: '2' annotations: summary: ALB synthetic probe failing (symptom alert, cause unknown) + condition: max(probe_success{job="blackbox",target="alb"}) == bool 0 data: - refId: A relativeTimeRange: @@ -48,6 +49,7 @@ groups: layer: '2' annotations: summary: api p95 over the last minute above 500 ms for 2 minutes + condition: histogram_quantile(0.95, sum by (le) (rate(demo_http_request_duration_seconds_bucket{route=~"orders|price"}[1m]))) > bool 0.5 data: - refId: A relativeTimeRange: @@ -79,6 +81,7 @@ groups: layer: '2' annotations: summary: demo api 5xx ratio above 1% (grey zone) + condition: (sum(rate(demo_http_requests_total{status=~"5.."}[5m])) / sum(rate(demo_http_requests_total[5m]))) > bool 0.01 data: - refId: A relativeTimeRange: @@ -111,6 +114,7 @@ groups: signature: pod_crashloop annotations: summary: container restarted 3+ times in 5m (kubelet is handling; escalate after 5m) + condition: max by (pod) (increase(kube_pod_container_status_restarts_total{namespace="demo"}[5m])) >= bool 3 data: - refId: A relativeTimeRange: @@ -143,6 +147,7 @@ groups: signature: legacy_unhealthy annotations: summary: fewer than 2 healthy legacy-api instances (MIG autohealing is handling; escalate after 10m) + condition: count(up{job="legacy-api"} == 1) < bool 2 data: - refId: A relativeTimeRange: @@ -174,6 +179,7 @@ groups: layer: '2' annotations: summary: replica lag above 60s or WAL replay paused (grey zone, small SLO impact) + condition: clamp_max((max(pg_replication_lag_seconds) > bool 60) + (max(pg_is_wal_replay_paused) == bool 1), 1) data: - refId: A relativeTimeRange: @@ -205,6 +211,7 @@ groups: layer: '2' annotations: summary: db-primary 5432 unreachable (layer 2 diagnosis, layer 3 expected) + condition: max(probe_success{job="blackbox",target="db-primary"}) == bool 0 data: - refId: A relativeTimeRange: @@ -237,6 +244,7 @@ groups: signature: gitops_drift annotations: summary: Argo CD application OutOfSync for 5m (selfHeal is handling; recorded) + condition: max(argocd_app_info{sync_status!="Synced"}) == bool 1 data: - refId: A relativeTimeRange: @@ -268,6 +276,8 @@ groups: layer: '2' annotations: summary: an api container holds more than 80% of its memory limit for 1 minute (grey zone; rollout restart after approval) + condition: max by (pod) (container_memory_working_set_bytes{namespace="demo",container="api"}) / on (pod) max by (pod) (kube_pod_container_resource_limits{namespace="demo",container="api",resource="memory"}) + > bool 0.8 data: - refId: A relativeTimeRange: