# 如何用 Grafana 和 Elasticsearch 搭建 API 监控

> 从零搭建生产级 API 监控 —— Elasticsearch 数据源、Lucene 查询、Grafana 面板与告警规则。

- 作者: zhuermu
- 发布: 2023-01-08
- 网页版: https://zhuermu.com/blog/grafana-api-monitoring-elasticsearch/
- 首发于: https://blog.csdn.net/qq258513813/article/details/128715833

---
你的 API 正在生产环境中运行。用户在访问各个端点，服务之间相互调用，一切看起来都很正常 —— 直到出问题为止。某个支付端点开始返回 500 错误；搜索路由的延迟从 200ms 悄悄爬升到 3 秒；某个下游依赖挂掉，错误率从 0.1% 飙升到 12%。如果没有监控，你只能从愤怒的用户口中得知这些问题。而有了合适的仪表盘，你能在几分钟内发现问题，往往在用户察觉之前就已经知道。

本文将带你使用 **Grafana 11.x** 搭配 **Elasticsearch** 作为数据源，构建一套完整的 API 监控仪表盘。我们会讲解整体架构、数据源配置、六个核心仪表盘面板（附带真实的查询语法）、基于 Grafana 统一告警（Unified Alerting）系统的告警配置，以及那些能让仪表盘从"随便瞥一眼"进化为"真正预防故障"的生产实战技巧。

---

## 1. 为什么 API 监控很重要

在深入具体工具之前，值得先用现代运维团队思考可靠性的框架来铺垫一下这个话题：**SLO、SLI 与错误预算**。

**服务水平指标（SLI）** 是你实际测量的指标 —— 请求延迟、错误率、吞吐量。**服务水平目标（SLO）** 是你为这些指标设定的目标 —— 比如"99.5% 的请求成功完成"或"P95 延迟保持在 300ms 以下"。**错误预算** 则是完美与你的 SLO 之间的差距：如果 SLO 是 99.5% 的成功率，那么每个测量窗口内你就有 0.5% 的错误预算。

本文要构建的仪表盘直接测量了 Google SRE 手册中定义的四大黄金信号里的三个：

1. **延迟（Latency）** —— 请求耗时（P50、P95、P99）
2. **流量（Traffic）** —— 每分钟有多少请求流经系统
3. **错误（Errors）** —— 有多少百分比的请求失败

第四个信号 **饱和度（Saturation）** 通常来自基础设施指标（CPU、内存、磁盘），而非访问日志。这四个信号合在一起，能为你勾勒出 API 健康状况的全景图。

它的实用价值立竿见影：当凌晨 3 点因为 `/api/v1/payments` 的错误率超过 2% 而触发告警时，值班工程师打开仪表盘，看到这次飙升恰好始于一次新部署上线，再关联到同一端点上的延迟增长，就有了足够的上下文来决定是回滚还是深入排查具体的故障模式 —— 整个过程只需几分钟。

---

## 2. 架构

监控管线其实很简单：API 流量产生访问日志，这些日志流入 Elasticsearch，Grafana 再查询 Elasticsearch 来驱动仪表盘和告警。

![监控架构](/images/blog/grafana-api-monitoring-elasticsearch/monitoring-architecture.svg)

下面是各个组件的职责：

**API 网关（nginx、AWS ALB、Kong 等）** 作为所有 API 流量的入口。网关为每个请求写入结构化的访问日志，包括 HTTP 方法、路径、状态码、响应时间、客户端 IP 和请求大小。这里的关键决策是日志格式 —— 尽可能使用 JSON，因为它能省去下游复杂的解析规则。

一个典型的 nginx JSON 日志配置如下：

```nginx
log_format json_combined escape=json
  '{'
    '"timestamp":"$time_iso8601",'
    '"method":"$request_method",'
    '"path":"$uri",'
    '"status":$status,'
    '"response_time":$request_time,'
    '"bytes_sent":$bytes_sent,'
    '"remote_addr":"$remote_addr",'
    '"user_agent":"$http_user_agent",'
    '"upstream_response_time":"$upstream_response_time",'
    '"request_id":"$request_id"'
  '}';

access_log /var/log/nginx/api_access.log json_combined;
```

**日志采集器（Filebeat 或 Fluentd）** 负责读取日志文件、解析它们，并发送到 Elasticsearch。Filebeat 更轻量，且与 Elastic Stack 原生集成。如果你需要把日志路由到多个目的地，Fluentd 则更灵活。

一个用于采集 nginx JSON 日志的最小化 Filebeat 配置：

```yaml
filebeat.inputs:
  - type: filestream
    id: api-access-logs
    paths:
      - /var/log/nginx/api_access.log
    parsers:
      - ndjson:
          target: ""
          add_error_key: true

output.elasticsearch:
  hosts: ["https://elasticsearch:9200"]
  index: "api-access-%{+yyyy.MM.dd}"
  username: "${ES_USERNAME}"
  password: "${ES_PASSWORD}"

setup.ilm.enabled: true
setup.ilm.rollover_alias: "api-access"
setup.ilm.pattern: "{now/d}-000001"
```

**Elasticsearch** 存储访问日志，并提供 Grafana 查询所依赖的聚合引擎。每条访问日志都会成为时序索引（例如 `api-access-2023.01.08`）中的一个文档。正是 Elasticsearch 的聚合框架 —— terms、date histogram、percentiles、filters —— 让"按端点分组统计过去一小时的 P99 延迟"这类指标能够实时计算出来。

**Grafana** 查询 Elasticsearch 以渲染仪表盘面板并评估告警规则。Grafana 的 Elasticsearch 数据源插件原生支持 Elasticsearch 查询 DSL，因此你可以在面板编辑器中使用 Lucene 语法和聚合构建器来配置查询。

---

## 3. 配置 Grafana 与 Elasticsearch

### 3.1 添加 Elasticsearch 数据源

在 Grafana 11.x 中，进入 **Connections > Data sources > Add data source**，选择 **Elasticsearch**。关键配置字段如下：

| 字段 | 值 | 说明 |
|-------|-------|-------|
| **URL** | `https://elasticsearch:9200` | 生产环境请使用 HTTPS |
| **Authentication** | Basic Auth 或 API Key | 绝不要禁用认证 |
| **Index name** | `api-access-*` | 通配符匹配每日索引 |
| **Time field** | `timestamp` | 必须与日志中的时间戳字段一致 |
| **Max concurrent shard requests** | `5` | 防止仪表盘查询压垮 ES |
| **Min time interval** | `1m` | 与日志粒度保持一致 |

如果你使用的是 Amazon OpenSearch Service，配置方式完全相同 —— Grafana 的 Elasticsearch 插件与 OpenSearch 兼容。只需将 URL 指向你的 OpenSearch 域名端点，并通过 Sigv4 auth 选项使用基于 IAM 的认证即可。

### 3.2 索引模式与映射注意事项

要让仪表盘查询正常工作，你的 Elasticsearch 索引映射需要满足几个条件：

- **timestamp** 字段必须是 `date` 类型（而非 `text` 或 `keyword`）。
- **status** 字段应为 `integer` 或 `keyword`。如果是 `text`，聚合将无法工作。
- **response_time** 字段必须是 `float` 或 `double`，才能进行百分位计算。
- **path** 字段应为 `keyword`（而非 `text`），这样 terms 聚合才能返回完整的路径，而不是被分词后的碎片。

你可以用以下命令验证映射：

```bash
curl -s "https://elasticsearch:9200/api-access-*/_mapping" | jq '.[] .mappings.properties | {timestamp, status, response_time, path}'
```

如果字段被错误地映射成了 `text`，可以创建一个索引模板来强制使用正确的类型：

```json
PUT _index_template/api-access
{
  "index_patterns": ["api-access-*"],
  "template": {
    "mappings": {
      "properties": {
        "timestamp": { "type": "date" },
        "method": { "type": "keyword" },
        "path": { "type": "keyword" },
        "status": { "type": "integer" },
        "response_time": { "type": "float" },
        "bytes_sent": { "type": "long" },
        "remote_addr": { "type": "ip" },
        "user_agent": { "type": "text" },
        "request_id": { "type": "keyword" }
      }
    }
  }
}
```

---

## 4. 构建仪表盘面板

![仪表盘布局](/images/blog/grafana-api-monitoring-elasticsearch/dashboard-layout.svg)

仪表盘分为两行，每行三个面板。上面一行展示高层次的健康指标（请求速率、成功率、延迟），下面一行展示详细的拆解（状态码分布、最慢端点、按路由的错误情况）。

### 4.1 请求速率（requests/min）

这个面板展示 API 每分钟处理多少请求，并按状态码类别拆分。

**面板类型：** Time series（时序图）

**Elasticsearch 查询配置：**

- **Metric：** Count
- **Group by：** 对 `timestamp` 做 Date Histogram，间隔 `1m`
- **Group by（第二层）：** 对 `status` 做 Terms（用于按状态码拆分）

在 Grafana 面板编辑器中，查询大致如下：

```
Query: *
Metric: Count
Group by: Date Histogram (timestamp) — Interval: 1m
         Terms (status) — Order: Top, Size: 10
```

如果想要一个更清爽的视图，按状态码类别（2xx、3xx、4xx、5xx）而非单个状态码分组，可以采用多条 **Lucene 查询** 的方式：

- **Query A：** `status:[200 TO 299]` —— 标签："2xx"
- **Query B：** `status:[300 TO 399]` —— 标签："3xx"
- **Query C：** `status:[400 TO 499]` —— 标签："4xx"
- **Query D：** `status:[500 TO 599]` —— 标签："5xx"

每条查询都使用 Count 作为指标，并对 `timestamp` 做间隔为 `1m` 的 Date Histogram。

### 4.2 成功率（%）

成功率是指返回 2xx 状态码的请求所占的百分比。这是你衡量可用性的首要 SLI。

**面板类型：** Stat（用于展示大数字）或 Time series（用于展示趋势）

在 Grafana 中用 Elasticsearch 计算比率需要一些额外配置。有两种方法：

**方法一：使用 Bucket Script（推荐用于 Grafana 11.x）**

配置一条 Elasticsearch 查询：

```
Metric A: Count (all requests)
Metric B: Count with Lucene query filter: status:[200 TO 299]
Pipeline: Bucket Script
  Expression: params.B / params.A * 100
Group by: Date Histogram (timestamp) — Interval: 1m
```

Bucket Script 管线聚合能让你在每个时间桶上计算 `(成功请求数 / 总请求数) * 100`。

**方法二：使用 Grafana Transformations**

创建两条查询 —— 一条统计总请求数（Count，无过滤），另一条统计成功请求数（Count，过滤条件 `status:[200 TO 299]`）。然后添加一个 **Transform > Calculate field** 步骤，操作为 `Query B / Query A * 100`。

对于展示大数字的 Stat 面板，将 **Value options > Calculation** 设为 "Last" 或 "Mean"，取决于你想看当前值还是平均成功率。并在 SLO 边界处设置阈值：

```json
{
  "thresholds": {
    "steps": [
      { "color": "red", "value": null },
      { "color": "orange", "value": 99 },
      { "color": "green", "value": 99.5 }
    ]
  }
}
```

### 4.3 P50/P95/P99 延迟

延迟百分位告诉你请求对中位数用户（P50）、尾部用户（P95）以及极端尾部用户（P99）分别有多快。问题往往就藏在 P99 里 —— 某个端点对大多数用户来说可能感觉很快，但对 1% 的用户却慢得令人痛苦。

**面板类型：** Time series（时序图）

**Elasticsearch 查询配置：**

```
Query: *
Metric: Percentiles on field "response_time"
  Percentile values: 50, 95, 99
Group by: Date Histogram (timestamp) — Interval: 1m
```

Elasticsearch 的百分位聚合使用 t-digest 算法，它是近似计算但内存效率很高 —— 适合处理高基数的时序数据。

要添加一条可视化的 SLO 参考线（例如"P95 必须保持在 300ms 以下"），可以使用 **Dashboard > Annotations > Add annotation query**，或者干脆在面板的 **Thresholds** 配置中添加一条阈值线，设为 `300` 并用红色标示。

### 4.4 HTTP 状态码分布

用饼图展示 HTTP 状态码的整体分布，能帮你一眼发现异常模式 —— 例如 429（限流）或 401（认证失败）突然激增。

**面板类型：** Pie chart（饼图）

**Elasticsearch 查询配置：**

```
Query: *
Metric: Count
Group by: Terms on "status" — Order: Top, Size: 20
```

可以按语义含义覆盖颜色：

| 状态码范围 | 颜色 |
|------------------|-------|
| 200-299 | 绿色 |
| 301、302、304 | 蓝色 |
| 400-499 | 橙色 |
| 500-599 | 红色 |

### 4.5 最慢的端点 Top 榜

这个面板回答了一个问题："现在哪些端点最慢？"它在部署后识别性能回退时格外有价值。

**面板类型：** Table（表格）

**Elasticsearch 查询配置：**

```
Query: *
Metric: Average on field "response_time"
Group by: Terms on "path" — Order by: Average response_time (desc), Size: 15
```

为了让表格更有用，可以添加额外的指标：

```
Metric A: Average on "response_time" (alias: "Avg Latency (ms)")
Metric B: Percentiles on "response_time", value: 99 (alias: "P99 (ms)")
Metric C: Count (alias: "Request Count")
Group by: Terms on "path" — Order by: Metric A desc, Size: 15
```

这样你就能看到每个端点的平均延迟、P99 延迟和请求量 —— 足以区分"这个端点慢是因为某个离群值"和"这个端点一直都很慢"这两种情况。

### 4.6 按端点划分的错误率

这个面板展示哪些具体端点的错误率最高，帮助你确定排查的优先级。

**面板类型：** Bar gauge（水平柱状仪表）

**Elasticsearch 查询配置：**

这需要一个两步聚合。首先计算每个端点的错误数和总数，然后计算比率：

```
Query A: status:[400 TO 599]
  Metric: Count
  Group by: Terms on "path" — Order: Top, Size: 10

Query B: *
  Metric: Count
  Group by: Terms on "path" — Order: Top, Size: 10
```

然后对 `path` 应用 **Transform > Join by field**，再接一个 **Transform > Add field from calculation**，公式为 `Query A / Query B * 100`。或者，也可以在单条查询中使用 Bucket Script 方式：

```
Metric A: Count (all requests per path)
Metric B: Count with inline filter: status:[400 TO 599]
Pipeline: Bucket Script — Expression: params.B / params.A * 100
Group by: Terms on "path" — Order by: Bucket Script desc, Size: 10
```

---

## 5. Grafana 告警

Grafana 9+ 用 **统一告警（Unified Alerting）** 取代了老旧的按面板告警系统。它是一个独立的告警引擎，支持多维度评估、专用的规则编辑器，以及完整的通知管线。如果你还在用旧系统，很值得迁移 —— 统一告警的能力要强大得多。

### 5.1 告警规则

Grafana 中的告警规则会按计划评估一个查询表达式，并在条件满足时触发。下面是一个针对高错误率的告警规则示例：

```yaml
# Alert: API Error Rate > 2%
apiVersion: 1
groups:
  - orgId: 1
    name: api-monitoring
    folder: API Alerts
    interval: 1m
    rules:
      - uid: api-error-rate-high
        title: "API Error Rate Exceeds 2%"
        condition: C
        data:
          - refId: A
            relativeTimeRange:
              from: 300  # last 5 minutes
              to: 0
            datasourceUid: elasticsearch-ds
            model:
              query: "*"
              metrics:
                - type: count
                  id: "1"
              bucketAggs:
                - type: date_histogram
                  field: timestamp
                  id: "2"
                  settings:
                    interval: 1m
          - refId: B
            relativeTimeRange:
              from: 300
              to: 0
            datasourceUid: elasticsearch-ds
            model:
              query: "status:[400 TO 599]"
              metrics:
                - type: count
                  id: "1"
              bucketAggs:
                - type: date_histogram
                  field: timestamp
                  id: "2"
                  settings:
                    interval: 1m
          - refId: C
            datasourceUid: __expr__
            model:
              type: math
              expression: "$B / $A * 100"
              conditions:
                - evaluator:
                    type: gt
                    params: [2]
        for: 5m  # must breach for 5 consecutive minutes
        labels:
          severity: critical
          team: platform
        annotations:
          summary: "API error rate is {{ $value }}%, exceeding 2% threshold"
          dashboard_url: "https://grafana.example.com/d/api-monitoring"
```

对于 **延迟告警**，也适用类似的模式。监控 P99 延迟何时超过你的 SLO：

```yaml
      - uid: api-p99-latency-high
        title: "API P99 Latency Exceeds 500ms"
        condition: B
        data:
          - refId: A
            datasourceUid: elasticsearch-ds
            model:
              query: "*"
              metrics:
                - type: percentiles
                  field: response_time
                  id: "1"
                  settings:
                    percents: ["99"]
              bucketAggs:
                - type: date_histogram
                  field: timestamp
                  id: "2"
                  settings:
                    interval: 1m
          - refId: B
            datasourceUid: __expr__
            model:
              type: threshold
              expression: A
              conditions:
                - evaluator:
                    type: gt
                    params: [500]
        for: 3m
        labels:
          severity: warning
          team: platform
```

### 5.2 联络点（Contact Points）

联络点定义了告警发送到哪里。Grafana 支持 Slack、PagerDuty、邮件、Microsoft Teams、Opsgenie、webhook 等众多渠道。

一个 Slack 联络点配置：

```yaml
apiVersion: 1
contactPoints:
  - orgId: 1
    name: platform-team-slack
    receivers:
      - uid: slack-platform
        type: slack
        settings:
          recipient: "#platform-alerts"
          token: "${SLACK_BOT_TOKEN}"
          title: |
            {{ `{{ .Status | toUpper }}` }} {{ `{{ .CommonLabels.alertname }}` }}
          text: |
            {{ `{{ range .Alerts }}` }}
            *{{ `{{ .Labels.alertname }}` }}*
            {{ `{{ .Annotations.summary }}` }}
            Dashboard: {{ `{{ .Annotations.dashboard_url }}` }}
            {{ `{{ end }}` }}
```

用于 PagerDuty 集成（需要呼叫值班工程师的关键告警）：

```yaml
      - uid: pagerduty-platform
        type: pagerduty
        settings:
          integrationKey: "${PAGERDUTY_INTEGRATION_KEY}"
          severity: "{{ `{{ .CommonLabels.severity }}` }}"
          class: "api-monitoring"
```

### 5.3 通知策略与路由

通知策略根据标签将告警路由到正确的联络点。你就在这里实现"关键告警发往 PagerDuty，警告发往 Slack"这样的逻辑：

```yaml
apiVersion: 1
policies:
  - orgId: 1
    receiver: platform-team-slack  # default receiver
    group_by: ["alertname", "team"]
    group_wait: 30s
    group_interval: 5m
    repeat_interval: 4h
    routes:
      - receiver: pagerduty-platform
        matchers:
          - severity = critical
        continue: true  # also send to the default Slack receiver
      - receiver: platform-team-slack
        matchers:
          - severity = warning
```

### 5.4 静默（Silences）与静音时段（Mute Timings）

在计划内的维护窗口期间，你不希望告警乱响。Grafana 提供了两种机制：

**静音时段（Mute timings）** 定义周期性的窗口（例如"每周六 UTC 时间凌晨 2 点到 6 点"），在此期间抑制告警：

```yaml
apiVersion: 1
muteTimes:
  - orgId: 1
    name: weekly-maintenance
    time_intervals:
      - times:
          - start_time: "02:00"
            end_time: "06:00"
        weekdays: ["saturday"]
```

**静默（Silences）** 是通过 Grafana UI 或 API 为特定维护事件创建的一次性抑制。它们按标签匹配告警，并在指定时长后过期。

---

## 6. 仪表盘即代码（Dashboard as Code）

通过 Grafana UI 手动配置仪表盘在原型阶段没问题，但生产环境的仪表盘应当纳入版本控制并通过代码部署。Grafana 支持三种方式：

**1. JSON Provisioning** —— 把仪表盘 JSON 文件放到 Grafana 的 provisioning 目录（`/etc/grafana/provisioning/dashboards/`）中，Grafana 会在启动时加载它们。这是最简单的方式，也很适合基于 Git 的工作流。

```yaml
# /etc/grafana/provisioning/dashboards/api-monitoring.yaml
apiVersion: 1
providers:
  - name: API Monitoring
    folder: Production
    type: file
    options:
      path: /var/lib/grafana/dashboards
      foldersFromFilesStructure: true
```

**2. Terraform Provider** —— [Grafana Terraform provider](https://registry.terraform.io/providers/grafana/grafana/latest) 让你能把仪表盘、数据源、告警规则和联络点当作 Terraform 资源来管理。这与基础设施即代码的工作流天然契合：

```hcl
resource "grafana_dashboard" "api_monitoring" {
  config_json = file("dashboards/api-monitoring.json")
  folder      = grafana_folder.production.id
}

resource "grafana_rule_group" "api_alerts" {
  org_id           = 1
  name             = "api-monitoring"
  folder_uid       = grafana_folder.production.uid
  interval_seconds = 60

  rule {
    name      = "API Error Rate Exceeds 2%"
    condition = "C"
    for       = "5m"
    # ... data and expressions
  }
}
```

**3. Grafonnet（Jsonnet 库）** —— 对于需要管理大量共享同一模式的仪表盘的团队，[Grafonnet](https://github.com/grafana/grafonnet) 提供了一种编程化的方式，从可复用的模板生成仪表盘 JSON。这就避免了手工编辑 JSON 常见的复制粘贴漂移问题。

---

## 7. 生产实战技巧

### 7.1 用仪表盘变量做筛选

在查询中硬编码具体值会让仪表盘变得僵化。使用 **Grafana 模板变量**，让用户能按服务、环境或端点动态筛选。

定义一个环境变量：

```
Name: environment
Type: Query
Data source: Elasticsearch
Query: {"find": "terms", "field": "environment.keyword", "size": 20}
```

定义一个 API 路径变量：

```
Name: path
Type: Query
Data source: Elasticsearch
Query: {"find": "terms", "field": "path.keyword", "size": 100}
```

然后在面板查询中用 Lucene 语法引用它们：

```
environment:$environment AND path:$path
```

这样一来，这一个仪表盘就能服务于每个服务和每个环境。顶部的下拉框让任何人都能精确筛选出自己需要的内容。

### 7.2 来自 CI/CD 部署的注解

当出问题时，第一个问题永远是"有什么变化吗？"**注解（Annotations）** 把部署事件直接叠加在你的时序图上，让部署与指标变化之间的关联一目了然。

从你的 CI/CD 管线推送注解：

```bash
curl -X POST "https://grafana.example.com/api/annotations" \
  -H "Authorization: Bearer ${GRAFANA_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "dashboardUID": "api-monitoring",
    "time": '"$(date +%s)000"',
    "tags": ["deploy", "api-service"],
    "text": "Deployed api-service v2.4.1 (commit: abc123)"
  }'
```

这些注解会以竖线的形式出现在时序面板上，让你能在部署与指标变化之间瞬间建立起可视化的关联。

### 7.3 索引生命周期管理

访问日志可能产生海量数据。配置 Elasticsearch 的 **索引生命周期管理（ILM）**，自动滚动、收缩并删除旧索引：

```json
PUT _ilm/policy/api-access-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50gb",
            "max_age": "1d"
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": { "delete": {} }
      }
    }
  }
}
```

这能让你的热数据保持快速、可查询，同时自动清理旧数据。

### 7.4 仪表盘的组织结构

随着监控规模的增长，组织结构变得很重要。一个实用的结构：

```
Production/
  API Monitoring/
    Overview (the dashboard we built)
    Per-Service Deep Dive
    SLO Tracking
  Infrastructure/
    ECS/EC2 Metrics
    RDS Performance
    ElastiCache Metrics
```

用 **仪表盘链接（dashboard links）** 把相关的仪表盘串联起来。总览仪表盘应当链接到按服务的深度分析页，并预先筛选到相关的服务。这就形成了一条自然的下钻路径：告警触发，值班工程师打开总览，锁定受影响的服务，点击进入深度分析页寻找根因。

### 7.5 规避常见的坑

有几点是我们用血泪教训换来的：

**托管版 Grafana 上的插件安装。** 如果你运行的是托管版 Grafana 实例（例如 Amazon Managed Grafana、Azure Managed Grafana，或某个云厂商托管的产品），插件安装可能会受限。某些未签名的插件需要在 `grafana.ini` 中显式加入白名单：

```ini
[plugins]
allow_loading_unsigned_plugins = goshposh-metaqueries-datasource
```

不过，凭借现代 Grafana 内置的 Bucket Script 和转换能力，对第三方 MetaQuery 插件的需求已基本消失。Bucket Script 管线聚合（见 4.2 节）原生就能处理比率计算。

**数据源连通性。** 配置 Elasticsearch 数据源时，务必确认你使用的主机名或 IP 是从 Grafana 服务器实际可达的地址。在容器化环境中，管理控制台里显示的"内网 IP"可能是负载均衡器的 VIP，而非真正的容器 IP。请始终从 Grafana 容器内部验证连通性：

```bash
# From inside the Grafana container
curl -v "https://elasticsearch-host:9200/_cluster/health"
```

**查询性能。** 那些查询高基数字段（如 `user_agent` 或 `remote_addr`）并带有大 terms 聚合的仪表盘面板可能会很慢。把 terms 聚合中的 `size` 参数保持在合理范围（仪表盘面板用 10-20），并使用数据源配置中的 `Min time interval` 设置来防止查询粒度过细。

---

## 结语

我们构建的这套仪表盘让你对 API 健康状况有了全面的可见性：请求量、成功率、延迟百分位、状态码分布、最慢端点，以及按路由的错误率。再结合 Grafana 的统一告警，你就能在 SLO 面临风险时获得主动通知，并通过正确的渠道送达正确的人。

不过，真正的价值并不在于任何单个面板或告警，而在于它们的组合：当告警触发时，仪表盘为排查提供即时的上下文；当部署出错时，注解精确显示它发生的时刻；当有人问"API 健康吗？"时，你可以直接指向一个 URL，而不必临时跑一堆查询。

如果你是从零开始，先从最重要的三个面板做起 —— 成功率、P99 延迟和按端点的错误率 —— 并为每个配一条告警。你随时可以之后再添加更多面板，但这三个已经能捕获生产环境中绝大多数的问题。先把告警做对，再让仪表盘变得好用，最后再打磨细节。

---

## 常见问题

### 如何用 Grafana 和 Elasticsearch 监控 API 性能？

让 API 网关（nginx、ALB、Kong）输出结构化 JSON 访问日志，通过 Filebeat 或 Fluentd 采集到 Elasticsearch，再在 Grafana 中添加指向时序索引的 Elasticsearch 数据源。然后用 Lucene 查询和聚合构建面板：用 Date Histogram 做请求速率，用 Bucket Script 管线聚合算成功率，对 response_time 字段做 Percentiles 聚合得到 P50/P95/P99 延迟。

### 在 Grafana 中怎么用 Elasticsearch 计算 API 成功率？

有两种方法。Grafana 11.x 推荐在单条查询中使用 Bucket Script 管线聚合：Metric A 统计全部请求数，Metric B 用 status:[200 TO 299] 过滤统计成功请求数，表达式 params.B / params.A * 100 即可得到每个时间桶的成功率百分比。也可以创建两条独立查询，再用 Grafana Transformation 的 Calculate field 做除法。

### API 监控应该先配哪些 Grafana 面板和告警？

先做能捕获绝大多数生产问题的三个面板：成功率、P99 延迟和按端点的错误率，并为每个配一条告警。使用 Grafana 统一告警并设置持续时长（例如错误率超过 2% 且持续 5 分钟才触发）以避免抖动，通过通知策略把关键告警路由到 PagerDuty、警告发到 Slack，之后再逐步扩充仪表盘。


---

## 参考资料

- [Grafana documentation](https://grafana.com/docs/grafana/latest/) — Grafana
- [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) — Elastic
- [Prometheus overview](https://prometheus.io/docs/introduction/overview/) — Prometheus
