PHP 应用监控告警:基于 Prometheus 的 SLO/SLA 指标设计与阈值配置
大家好!今天我们来聊聊如何使用 Prometheus 对 PHP 应用进行监控告警,并基于此设计 SLO/SLA 指标,配置合理的阈值。这将帮助我们更好地保障 PHP 应用的稳定性和性能。
一、监控的重要性与挑战
监控是保障任何应用稳定运行的基础。对于 PHP 应用来说,有效的监控可以帮助我们:
- 及时发现问题: 在问题影响用户之前预警。
- 快速定位问题: 通过监控数据分析问题根源。
- 优化应用性能: 识别性能瓶颈并进行优化。
- 保障服务质量: 确保应用满足服务水平协议 (SLA)。
然而,PHP 应用的监控也存在一些挑战:
- 语言特性: PHP 作为解释型语言,执行过程相对动态,增加了监控的复杂度。
- 框架多样性: 存在多种 PHP 框架,监控方案需要具有一定的通用性。
- 部署环境复杂: 应用可能运行在各种不同的环境,包括容器化环境。
- 指标选择: 如何选择合适的指标来反映应用的健康状况。
二、Prometheus 简介与架构
Prometheus 是一套开源的监控告警系统,特别适用于监控云原生环境。它具有以下特点:
- 多维数据模型: 所有监控数据都以 key-value 对的形式存储,方便查询和分析。
- 强大的查询语言 (PromQL): 用于灵活地查询和聚合监控数据。
- 基于 HTTP 的 pull 模型: Prometheus 定期从目标服务拉取监控数据。
- 高度可扩展性: 支持多种存储后端和集群部署。
- Alertmanager 集成: 用于处理告警规则并发送告警通知。
Prometheus 的基本架构如下:
- Prometheus Server: 负责抓取、存储和查询监控数据。
- Exporters: 用于暴露监控数据的组件,例如 Node Exporter (监控主机资源) 和 MySQL Exporter (监控 MySQL 数据库)。
- Alertmanager: 负责处理告警规则并发送告警通知。
- Pushgateway (可选): 用于接收短期任务的监控数据。
三、PHP 应用监控指标设计
我们需要根据应用的特点和业务需求来选择合适的监控指标。以下是一些常用的 PHP 应用监控指标:
- 请求相关指标:
- 请求总数 (http_requests_total): 记录应用接收到的 HTTP 请求总数。
- 请求延迟 (http_request_duration_seconds): 记录请求的处理时间,通常需要统计不同分位数的延迟。
- 请求状态码 (http_request_status_codes): 记录不同状态码的请求数量,例如 200, 400, 500。
- 每秒请求数 (RPS): 反映应用的吞吐量。
- 资源使用指标:
- CPU 使用率 (cpu_usage): 反映 PHP 进程的 CPU 占用情况。
- 内存使用量 (memory_usage_bytes): 反映 PHP 进程的内存占用情况。
- 磁盘 I/O (disk_io): 反映 PHP 进程的磁盘读写情况。
- PHP 内部指标:
- 错误日志数量 (php_errors_total): 记录 PHP 错误日志的数量,可以按错误级别分类。
- 异常数量 (php_exceptions_total): 记录 PHP 异常的数量。
- Opcache 命中率 (opcache_hit_rate): 反映 Opcache 的效率。
- 数据库查询时间 (db_query_duration_seconds): 记录数据库查询耗时。
- 业务相关指标:
- 用户注册数量 (user_registrations_total): 记录用户注册数量。
- 订单数量 (orders_total): 记录订单数量。
- 支付成功率 (payment_success_rate): 记录支付成功的比例。
指标命名规范:
- 使用有意义的名称,反映指标的含义。
- 使用
_total后缀表示累计值。 - 使用
_seconds后缀表示时间单位为秒。 - 使用
_bytes后缀表示大小单位为字节。
四、Prometheus Exporter 的选择与实现
为了让 Prometheus 能够抓取 PHP 应用的监控数据,我们需要使用 Exporter。 以下是几种常用的方法:
-
使用现有的 Exporter:
- php-fpm_exporter: 监控 php-fpm 的状态,例如活跃进程数、空闲进程数等。
- Third-party application exporters: 针对一些通用的应用程序,比如 Redis,MySQL等,可以使用已经存在的exporter。
-
自定义 Exporter:
如果我们没有找到合适的现有 Exporter,或者需要监控一些特定的指标,我们可以自定义 Exporter。 自定义 Exporter 的基本流程如下:
- 收集指标数据: 在 PHP 代码中收集需要监控的指标数据。
- 暴露 HTTP 接口: 创建一个 HTTP 接口,将收集到的指标数据以 Prometheus 的文本格式 (exposition format) 暴露出来。
- 配置 Prometheus: 配置 Prometheus,使其能够定期从 Exporter 的 HTTP 接口拉取数据。
PHP 代码示例 (自定义 Exporter):
<?php // 收集指标数据 $requests_total = 0; $errors_total = 0; // 模拟请求处理 function handleRequest() { global $requests_total, $errors_total; $requests_total++; // 模拟错误发生 if (rand(0, 9) == 0) { // 10% 的概率发生错误 $errors_total++; error_log("Simulated error occurred."); } // 模拟处理时间 usleep(rand(10000, 50000)); // 10-50 毫秒 } // 处理多个请求 for ($i = 0; $i < 100; $i++) { handleRequest(); } // 暴露 HTTP 接口 if ($_SERVER['REQUEST_URI'] == '/metrics') { header('Content-Type: text/plain'); echo "# HELP http_requests_total Total number of HTTP requests.n"; echo "# TYPE http_requests_total countern"; echo "http_requests_total " . $requests_total . "n"; echo "# HELP php_errors_total Total number of PHP errors.n"; echo "# TYPE php_errors_total countern"; echo "php_errors_total " . $errors_total . "n"; exit; } else { echo "Metrics endpoint: /metrics"; } ?>Prometheus 配置示例 (scrape_configs):
scrape_configs: - job_name: 'php-app' static_configs: - targets: ['localhost:8000'] # 假设 PHP 应用运行在 8000 端口代码解释:
- PHP 代码模拟了请求处理和错误发生,并记录了
http_requests_total和php_errors_total指标。 - 当访问
/metrics路径时,代码会返回 Prometheus 的文本格式的指标数据。 - Prometheus 的
scrape_configs配置指定了 Prometheus 需要抓取的目标地址。
-
使用 PHP 库:
有一些 PHP 库可以帮助我们更方便地创建 Exporter,例如:
- Prometheus PHP Client: 提供了 Metric 类的定义和 Registry 类的实现,可以更方便地管理指标。
使用 Prometheus PHP Client 的代码示例:
<?php require 'vendor/autoload.php'; use PrometheusCollectorRegistry; use PrometheusCounter; use PrometheusRenderTextFormat; // 创建 Registry $adapter = new PrometheusStorageInMemory(); // 使用内存存储,生产环境建议使用 Redis 或其他存储 $registry = new CollectorRegistry($adapter); // 创建 Counter 指标 $counter = $registry->getOrRegisterCounter( 'http_requests', 'total', 'Total number of HTTP requests', ['path'] ); // 创建 Gauge 指标 (用于记录当前活跃连接数) $gauge = $registry->getOrRegisterGauge( 'http_active_connections', 'current', 'Current number of active HTTP connections', ); // 模拟请求处理 function handleRequest(CollectorRegistry $registry, string $path) { global $gauge; $gauge->inc(); //增加活跃连接数 // 增加请求计数 $registry->getCounter('http_requests', 'total')->inc(['path' => $path]); // 模拟处理时间 usleep(rand(10000, 50000)); // 10-50 毫秒 $gauge->dec(); //减少活跃连接数 } // 处理多个请求 for ($i = 0; $i < 10; $i++) { handleRequest($registry, '/example'); } // 暴露 HTTP 接口 if ($_SERVER['REQUEST_URI'] == '/metrics') { header('Content-Type: text/plain'); $renderer = new RenderTextFormat(); $result = $renderer->render($registry->getMetricFamilySamples()); echo $result; exit; } else { echo "Metrics endpoint: /metrics"; }代码解释:
- 使用
PrometheusCollectorRegistry创建 Registry 对象,用于管理指标。 - 使用
getOrRegisterCounter方法创建 Counter 指标,并指定指标的名称、帮助信息和标签。 - 使用
inc()方法增加指标的值。 - 使用
RenderTextFormat类将指标数据转换为 Prometheus 的文本格式。 - 需要使用
composer require promphp/prometheus_client_php安装该包。
五、SLO/SLA 指标设计
SLO (Service Level Objective) 和 SLA (Service Level Agreement) 是衡量服务质量的重要指标。 SLO 定义了我们期望达到的服务水平,而 SLA 则定义了我们承诺提供的服务水平。
常用的 SLO/SLA 指标:
- 可用性 (Availability): 服务正常运行的时间比例。 例如,99.9% 的可用性意味着服务每年最多允许 8.76 小时的 downtime。
- 错误率 (Error Rate): 请求失败的比例。 例如,错误率小于 0.1%。
- 延迟 (Latency): 请求的处理时间。 例如,95% 的请求延迟小于 200 毫秒。
- 吞吐量 (Throughput): 单位时间内处理的请求数量。 例如,每秒处理 1000 个请求。
基于 Prometheus 的 SLO/SLA 指标计算:
我们可以使用 PromQL 来计算 SLO/SLA 指标。
1. 可用性 (Availability):
sum(up{job="php-app"}) / count(up{job="php-app"})
这个 PromQL 查询计算了 php-app 这个 job 的可用性。 up 指标表示服务是否正常运行 (1 表示正常,0 表示异常)。
2. 错误率 (Error Rate):
rate(http_request_status_codes{job="php-app", code=~"5.."}[5m]) / rate(http_requests_total{job="php-app"}[5m])
这个 PromQL 查询计算了 php-app 这个 job 的错误率。 它计算了过去 5 分钟内 5xx 状态码的请求数量与总请求数量的比率。
3. 延迟 (Latency):
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="php-app"}[5m])) by (le))
这个 PromQL 查询计算了 php-app 这个 job 的 95 分位数的请求延迟。 它使用了直方图 (histogram) 指标来统计延迟分布。
4. 吞吐量 (Throughput):
rate(http_requests_total{job="php-app"}[5m])
这个 PromQL 查询计算了 php-app 这个 job 的吞吐量。 它计算了过去 5 分钟内每秒的请求数量。
SLO/SLA 指标表格示例:
| 指标 | SLO | SLA | PromQL 查询 |
|---|---|---|---|
| 可用性 | >= 99.9% | >= 99.5% | sum(up{job="php-app"}) / count(up{job="php-app"}) |
| 错误率 | <= 0.1% | <= 0.5% | rate(http_request_status_codes{job="php-app", code=~"5.."}[5m]) / rate(http_requests_total{job="php-app"}[5m]) |
| 延迟 (P95) | <= 200ms | <= 500ms | histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="php-app"}[5m])) by (le)) |
| 吞吐量 | >= 1000 RPS | >= 500 RPS | rate(http_requests_total{job="php-app"}[5m]) |
六、告警规则配置
Prometheus 使用 Alertmanager 来处理告警规则并发送告警通知。 告警规则定义了在什么情况下触发告警。
告警规则配置示例 (prometheus.yml):
rule_files:
- "alert.rules.yml"
告警规则文件示例 (alert.rules.yml):
groups:
- name: php-app-alerts
rules:
- alert: PhpAppHighLatency
expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="php-app"}[5m])) by (le)) > 0.2
for: 1m
labels:
severity: warning
annotations:
summary: "PHP app latency is high"
description: "PHP app latency (95th percentile) is above 200ms for 1 minute."
- alert: PhpAppHighErrorRate
expr: rate(http_request_status_codes{job="php-app", code=~"5.."}[5m]) / rate(http_requests_total{job="php-app"}[5m]) > 0.01
for: 1m
labels:
severity: critical
annotations:
summary: "PHP app error rate is high"
description: "PHP app error rate is above 1% for 1 minute."
- alert: PhpAppDown
expr: up{job="php-app"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "PHP app is down"
description: "PHP app is down for 1 minute."
告警规则解释:
alert: 告警的名称。expr: PromQL 查询表达式,用于判断是否触发告警。for: 告警持续时间,只有当表达式持续满足条件达到指定时间后才会触发告警。labels: 告警的标签,用于分类和路由告警。annotations: 告警的注释,提供告警的描述信息。
Alertmanager 配置:
Alertmanager 负责接收 Prometheus 发送的告警,并根据配置的路由规则发送告警通知。
Alertmanager 配置示例 (alertmanager.yml):
route:
receiver: 'email-notifications'
repeat_interval: 5m #重复发送间隔
group_wait: 30s #等待多久将告警进行分组
group_interval: 5m #分组后多久再发送告警
receivers:
- name: 'email-notifications'
email_configs:
- to: '[email protected]'
from: '[email protected]'
smarthost: 'smtp.example.com:587'
auth_username: '[email protected]'
auth_password: 'your_password'
tls_config:
insecure_skip_verify: true
配置解释:
route: 定义了告警的路由规则。 所有告警都会路由到email-notifications这个 receiver。receivers: 定义了告警接收器。email-notifications这个 receiver 使用 email 发送告警通知。
七、阈值配置的最佳实践
阈值的配置需要根据应用的特点和业务需求进行调整。 以下是一些最佳实践:
- 从历史数据中学习: 分析历史监控数据,了解应用的正常运行范围,并基于此设置阈值。
- 设置多级阈值: 可以设置 warning 和 critical 两个级别的阈值,分别对应不同的告警级别。
- 使用动态阈值: 可以使用算法来动态调整阈值,例如基于历史数据的平均值和标准差。
- 定期评估和调整: 定期评估告警规则的有效性,并根据实际情况进行调整。
- 考虑业务影响: 在设置阈值时,需要考虑对业务的影响,避免误报。
八、监控仪表盘
监控仪表盘可以帮助我们更直观地了解应用的健康状况。 可以使用 Grafana 等工具来创建监控仪表盘。
Grafana 仪表盘示例:
- PHP 应用整体概览: 显示应用的可用性、错误率、延迟和吞吐量等关键指标。
- 请求监控: 显示请求总数、请求状态码分布、请求延迟分布等指标。
- 资源使用监控: 显示 CPU 使用率、内存使用量、磁盘 I/O 等指标。
- PHP 内部监控: 显示错误日志数量、异常数量、Opcache 命中率等指标。
- 数据库监控: 显示数据库查询时间、连接数等指标。
- 业务监控: 显示用户注册数量、订单数量、支付成功率等指标。
九、实际案例:电商平台的 PHP 应用监控
假设我们有一个电商平台的 PHP 应用,需要监控以下几个方面:
- 用户访问: 监控用户访问量、页面加载时间。
- 商品浏览: 监控商品浏览量、商品详情页加载时间。
- 订单处理: 监控订单创建数量、支付成功率。
- 数据库性能: 监控数据库查询时间、连接数。
指标选择:
http_requests_total: 记录用户访问量和商品浏览量。http_request_duration_seconds: 记录页面加载时间和商品详情页加载时间。orders_total: 记录订单创建数量。payment_success_rate: 记录支付成功率。db_query_duration_seconds: 记录数据库查询时间。db_connections_total: 记录数据库连接数。
告警规则:
- 当页面加载时间超过 2 秒时,发送 warning 告警。
- 当商品详情页加载时间超过 5 秒时,发送 critical 告警。
- 当支付成功率低于 99% 时,发送 warning 告警。
- 当数据库查询时间超过 1 秒时,发送 warning 告警。
- 当数据库连接数超过 100 时,发送 warning 告警。
监控仪表盘:
- 创建一个 Grafana 仪表盘,显示用户访问量、页面加载时间、商品浏览量、商品详情页加载时间、订单创建数量、支付成功率、数据库查询时间和连接数等指标。
十、总结
理解了监控的重要性,Prometheus 的基本架构和工作方式,并深入探讨了如何设计 PHP 应用的监控指标,选择合适的 Exporter,以及如何基于 Prometheus 计算 SLO/SLA 指标,配置告警规则。此外,还探讨了阈值配置的最佳实践,并提供了一个电商平台的实际案例。希望这些内容能帮助你更好地保障 PHP 应用的稳定性和性能。