AWS ECS And Node.js Monitoring


https://docs.aws.amazon.com/AmazonECS/latest/developerguide/available-metrics.html
https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/using-metric-math.html
https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/

1. Alert Standard#

Severity Alert Metric Namespace Statistic Period Definition Evaluation Alarm After Why Monitor
P0 Service unavailable DesiredTaskCount - RunningTaskCount + probe_success ECS/ContainerInsights + blackbox metric math + min 60s task_gap > 0 and /health failed 3/3 datapoints and for 3m ~3m 同时证明调度层和用户视角都异常,应立即处理
P1 Running task too low DesiredTaskCount - RunningTaskCount ECS/ContainerInsights metric math 60s > 0 5/5 datapoints ~5m 发现部署失败、任务崩溃、容量不足或镜像启动失败
P1 HTTP 5xx high http_requests_total app metrics rate 5m 5xx_rate >= 5% for 5m ~5m 直接反映用户请求失败和发布/依赖问题
P1 P95 latency high http_request_duration_seconds_bucket app metrics p95 5m p95 >= 1s or > 2 * 1h avg for 10m ~10m 发现用户体验下降、依赖慢或服务饱和
P2 CPU high CPUUtilization AWS/ECS Average 60s >= 80% 15/15 datapoints ~15m 提前发现容量不足和自动扩缩容风险
P2 Memory high MemoryUtilization AWS/ECS Average 60s >= 85% 15/15 datapoints ~15m 防止 OOM kill、重启循环和性能抖动
P2 Event loop lag high nodejs_eventloop_lag_seconds_bucket app metrics p95 5m >= 0.2s for 10m ~10m 发现同步阻塞、CPU 密集代码或 GC 压力
P2 Heap pressure nodejs_heap_size_used_bytes / nodejs_heap_size_total_bytes app metrics ratio 5m >= 85% for 10m ~10m 提前发现内存泄漏和 GC 频繁导致的延迟

Evaluation rule:

Period 是每个 datapoint 的统计窗口,不是告警持续时间。
Alarm After 约等于 Period * EvaluationPeriods;应用指标的 `for` 直接表示持续时间。
rate/histogram window 是计算窗口,不等于告警 for。

明显升高 的统一定义:

current_5m > absolute_threshold
and
current_5m > 2 * avg_over_time(metric[1h])
for 10m

2. CloudWatch Metrics#

Metric Meaning Why Monitor Namespace Statistic Period
CPUUtilization ECS service/task 的 CPU 使用率 判断计算资源是否饱和和扩容是否需要触发 AWS/ECS Average 60s
MemoryUtilization ECS service/task 的内存使用率 判断 OOM、内存泄漏和任务重启风险 AWS/ECS Average 60s
RunningTaskCount 当前处于运行状态的 task 数 判断服务实际容量是否达到期望 ECS/ContainerInsights or AWS/ECS if enabled Average 60s
DesiredTaskCount ECS service 期望运行的 task 数 与 RunningTaskCount 对比发现调度或启动失败 ECS/ContainerInsights or AWS/ECS if enabled Average 60s

查询 running task 是否低于 desired task:

[
  {
    "Id": "running",
    "MetricStat": {
      "Metric": {
        "Namespace": "ECS/ContainerInsights",
        "MetricName": "RunningTaskCount",
        "Dimensions": [
          { "Name": "ClusterName", "Value": "prod-cluster" },
          { "Name": "ServiceName", "Value": "api" }
        ]
      },
      "Period": 60,
      "Stat": "Average"
    },
    "ReturnData": false
  },
  {
    "Id": "desired",
    "MetricStat": {
      "Metric": {
        "Namespace": "ECS/ContainerInsights",
        "MetricName": "DesiredTaskCount",
        "Dimensions": [
          { "Name": "ClusterName", "Value": "prod-cluster" },
          { "Name": "ServiceName", "Value": "api" }
        ]
      },
      "Period": 60,
      "Stat": "Average"
    },
    "ReturnData": false
  },
  {
    "Id": "task_gap",
    "Expression": "desired-running",
    "Label": "desired minus running",
    "ReturnData": true
  }
]
aws cloudwatch get-metric-data \
  --start-time 2026-06-02T00:00:00Z \
  --end-time 2026-06-02T01:00:00Z \
  --metric-data-queries file://ecs-running-vs-desired.json

CloudWatch Alarm 条件:

metric math id: task_gap
comparison: GreaterThanThreshold
threshold: 0
evaluation_periods: 5
datapoints_to_alarm: 5
period: 60
severity: P1

3. PromQL#

YACE metric 名称先用下面的查询确认:

# 用正则先找出当前 YACE 暴露出来的 ECS metric 名称。
# 这里不是告警表达式,只是落地前确认 metric name 和 label name。
{__name__=~"aws_ecs_.*(running|desired|cpu|memory).*"}

具体 PromQL:

# running task 小于 desired task,表示 ECS 想要的任务数没有全部跑起来。
# 这个一般是 deployment 卡住、镜像拉取失败、健康检查失败、容量不足。
aws_ecs_running_task_count_average{cluster_name="prod-cluster", service_name="api"}
  < aws_ecs_desired_task_count_average{cluster_name="prod-cluster", service_name="api"}
# ECS service 平均 CPU 使用率超过 80%。
# 适合做 P2 容量风险,不建议单独作为 P1 可用性告警。
aws_ecs_cpu_utilization_average{cluster_name="prod-cluster", service_name="api"} >= 80
# ECS service 平均内存使用率超过 85%。
# Fargate / container 内存打满通常会导致 OOM kill,所以阈值比 CPU 更敏感。
aws_ecs_memory_utilization_average{cluster_name="prod-cluster", service_name="api"} >= 85

Node.js 应用指标:

# 计算 5 分钟内 HTTP 5xx 错误率:
#   分子 = 5xx 请求速率
#   分母 = 全部请求速率
#   乘以 100 = 百分比
# 结果 >= 5 表示 5xx 错误率达到 5%。
100 *
sum(rate(http_requests_total{service="api",status=~"5.."}[5m]))
/
sum(rate(http_requests_total{service="api"}[5m]))
>= 5
# 计算 HTTP 请求耗时的 p95:
#   http_request_duration_seconds_bucket 是 Prometheus histogram bucket
#   rate(...[5m]) 取最近 5 分钟每个 bucket 的增长速率
#   sum by (le) 保留 bucket 边界 le,再把不同 instance 的 bucket 合并
#   histogram_quantile(0.95, ...) 根据 bucket 估算 p95
# 结果单位是 seconds,>= 1 表示 p95 请求耗时超过 1 秒。
histogram_quantile(
  0.95,
  sum by (le) (rate(http_request_duration_seconds_bucket{service="api"}[5m]))
) >= 1
# 计算 Node.js event loop lag 的 p95:
#   event loop lag 高说明 JS 主线程被 CPU、同步代码或 GC 阻塞
#   结果单位是 seconds,>= 0.2 表示 p95 lag 超过 200ms。
histogram_quantile(
  0.95,
  sum by (le) (rate(nodejs_eventloop_lag_seconds_bucket{service="api"}[5m]))
) >= 0.2
# 计算 V8 heap 使用率:
#   used bytes / total bytes = heap 使用比例
#   >= 0.85 表示 heap 已经用了 85%,需要排查内存泄漏或调大内存。
nodejs_heap_size_used_bytes{service="api"}
/
nodejs_heap_size_total_bytes{service="api"}
>= 0.85

4. vmalert Rules#

groups:
  - name: ecs-nodejs.rules
    rules:
      - alert: ECSServiceRunningTaskTooLow
        # desired > running 时触发,表示 ECS service 没有达到期望任务数。
        expr: aws_ecs_running_task_count_average < aws_ecs_desired_task_count_average
        for: 5m
        labels:
          severity: P1
          component: ecs
        annotations:
          summary: "ECS running task count is below desired count"

      - alert: NodeJSHigh5xxRate
        # 5xx rate = 5xx 请求速率 / 总请求速率 * 100。
        # by (service) 是为了每个服务单独计算,不把多个服务混在一起。
        expr: |
          100 * sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
          /
          sum(rate(http_requests_total[5m])) by (service) >= 5
        for: 5m
        labels:
          severity: P1
          component: app
        annotations:
          summary: "Node.js HTTP 5xx rate is >= 5%"

      - alert: NodeJSHighP95Latency
        # 从 histogram bucket 计算每个 service 的 p95 延迟,单位 seconds。
        expr: |
          histogram_quantile(0.95, sum by (service, le) (rate(http_request_duration_seconds_bucket[5m]))) >= 1
        for: 10m
        labels:
          severity: P1
          component: app
        annotations:
          summary: "Node.js HTTP p95 latency is >= 1s"