1. 为什么要在 Istio 里接入 Prometheus-Operator 与 Alertmanager
Service Mesh 落地到生产环境之后,最容易被低估的一件事就是可观测性。Istio 本身自带一套遥测能力,Envoy Sidecar 会把每一次请求的延迟、状态码、上下游信息以指标形式吐出来,但这些指标默认是散的,没有一个统一的采集、存储、告警闭环。很多团队一开始用istioctl dashboard prometheus看看默认面板,觉得“有监控了”,等真正出故障时才发现:指标保留时间短、告警规则缺失、告警发不出去、多集群数据对不上,问题一个接一个。
我这次做的事情,就是把 Istio 的遥测数据接入到一套标准的Prometheus-Operator体系里,并且用Alertmanager把告警链路打通。选 Prometheus-Operator 而不是裸装 Prometheus,核心原因是它用 CRD 把 Prometheus、ServiceMonitor、PrometheusRule、Alertmanager 这些对象都声明式管理起来了,跟 Istio 的声明式配置思路一致,运维起来不会出现“配置漂移”。而且 Istio 官方从 1.7 之后就把 Prometheus-Operator 作为推荐的集成方式,社区资料也最全。
这篇文章适合谁看?如果你正在做 Istio 的生产化落地,或者已经装了 Istio 但监控告警还是半成品,又或者你手上有一套 Prometheus-Operator 想把它和 Mesh 数据打通,那这篇内容基本可以照着抄。我会把整体设计思路、核心配置细节、实操步骤、以及我踩过的坑都摊开讲,尽量让你少走弯路。
需要先说明一点:下面涉及的所有命名空间、集群名、域名都是示例,你实际用的时候替换成自己的即可。Istio 版本我以 1.20 附近的稳定版为参考,Prometheus-Operator 用的是 kube-prometheus-stack 的 Helm Chart,这套组合目前兼容性最好。
2. 整体架构设计与选型考量
2.1 数据从 Envoy 到 Prometheus 的完整链路
先把链路讲清楚,不然后面配置容易懵。Istio 的指标采集分两段:
第一段是Envoy 暴露指标。每个注入 Sidecar 的 Pod 里,Envoy 会在15090端口暴露/stats/prometheus,这是 Envoy 原生格式的指标。同时 Istio 的istio-proxy容器里还有一个pilot-agent,它会把 Envoy 的指标做一次聚合和转换,通过15020端口的/stats/prometheus暴露出来,这个端口出来的指标已经带上了 Istio 的标准标签,比如reporter、source_workload、destination_service这些。
第二段是Prometheus 抓取。Prometheus-Operator 通过ServiceMonitor或PodMonitor这个 CRD 来定义抓取目标。Istio 官方推荐用PodMonitor,因为 Sidecar 是注入到业务 Pod 里的,用 PodMonitor 可以直接按 Pod 标签筛选,不用额外建 Service。
提示:
15020和15090两个端口都能抓,但推荐抓15020,因为它的指标已经做过标准化处理,标签更规整,后续做聚合和告警时省事很多。
2.2 为什么用 PodMonitor 而不是 ServiceMonitor
这是很多人第一次集成时纠结的点。ServiceMonitor 需要有一个对应的 Service 对象,而 Istio 的 Sidecar 是跟着业务 Pod 走的,业务 Pod 不一定有 Service,就算有,Service 的 selector 也未必能覆盖到所有需要抓的 Pod。PodMonitor 直接按 Pod 的 label 匹配,Istio 注入的 Pod 都会带上security.istio.io/tlsMode之类的标签,用 PodMonitor 匹配更自然。
另外 PodMonitor 可以指定podMetricsEndpoints,直接写端口名或端口号,配合relabelings做标签处理,灵活性比 ServiceMonitor 高。实测下来,用 PodMonitor 抓 Istio 指标是最省心的方案。
2.3 Alertmanager 在链路里的角色
Prometheus 负责采集和评估告警规则,Alertmanager 负责去重、分组、静默和路由。两者是解耦的,Prometheus 只负责把触发的告警推给 Alertmanager,具体发给谁、怎么发、发多频繁,全由 Alertmanager 的配置决定。
在 Istio 场景下,告警主要分两类:一类是Mesh 层面的,比如某个服务的 5xx 比例突增、P99 延迟飙升、Sidecar 注入失败;另一类是基础设施层面的,比如 Prometheus 自身抓取目标掉线、Alertmanager 集群脑裂。这两类告警的路由策略通常不一样,Mesh 告警要发给业务负责人,基础设施告警要发给平台团队,所以 Alertmanager 的 route 配置要提前规划好。
2.4 版本兼容性对照
版本这块坑不少,我整理了一张表,是我实测过的组合:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Istio | 1.18 - 1.20 | 1.17 以下 PodMonitor 标签有差异 |
| kube-prometheus-stack | 51.x - 55.x | Chart 版本,对应 Prometheus 2.45+ |
| Prometheus | 2.45+ | 支持 native histogram,对 Istio 延迟指标友好 |
| Alertmanager | 0.25+ | 支持新的路由配置语法 |
注意:Istio 1.20 之后默认关闭了
enablePrometheusMerge,如果你是从旧版本升级上来的,要检查这个开关,否则指标会重复或缺失。
3. 核心配置细节与实操要点
3.1 安装 kube-prometheus-stack 的关键参数
我用 Helm 装,命令大概长这样:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --create-namespace \ --set prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false \ --set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false \ --set prometheus.prometheusSpec.ruleSelectorNilUsesHelmValues=false \ --set prometheus.prometheusSpec.retention=15d \ --set prometheus.prometheusSpec.resources.requests.memory=2Gi \ --set prometheus.prometheusSpec.resources.requests.cpu=500m \ --set alertmanager.alertmanagerSpec.replicas=3这里有几个参数必须解释一下:
podMonitorSelectorNilUsesHelmValues=false这个最关键。默认情况下,Prometheus-Operator 只会选择带有 Helm release 标签的 PodMonitor,你手动创建的 PodMonitor 它不认。设成 false 之后,它会选择所有命名空间下的 PodMonitor,这样 Istio 的 PodMonitor 才能被识别。
retention=15d是保留时间,Istio 指标量很大,默认 10d 往往不够排查问题,15d 是个折中。如果你的磁盘够,可以拉到 30d。
alertmanager replicas=3是为了高可用,Alertmanager 用 gossip 协议做集群,3 副本能容忍 1 个节点故障。
3.2 创建抓取 Istio 指标的 PodMonitor
这是核心配置,我直接贴出来:
apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: name: istio-proxies-monitor namespace: monitoring labels: release: kube-prometheus-stack spec: selector: matchExpressions: - key: istio-prometheus-ignore operator: DoesNotExist namespaceSelector: any: true jobLabel: istio-prometheus podMetricsEndpoints: - path: /stats/prometheus port: http-envoy-prom interval: 15s relabelings: - action: keep sourceLabels: [__meta_kubernetes_pod_container_name] regex: istio-proxy - action: replace sourceLabels: [__meta_kubernetes_pod_label_security_istio_io_tlsMode] targetLabel: security_istio_io_tlsMode逐段拆解:
selector.matchExpressions里用istio-prometheus-ignore这个标签做排除。这是 Istio 官方推荐的写法,如果你某个 Pod 不想被抓,给它打上这个标签就行,不用改 PodMonitor。
namespaceSelector.any: true表示抓所有命名空间的 Pod,因为 Istio 的 Sidecar 可能分布在多个业务命名空间里。
port: http-envoy-prom是端口名,对应15090。这里有个细节:Istio 的 Sidecar 容器里,15090端口的名字就是http-envoy-prom,用端口名比用端口号更稳,因为端口号可能变,端口名是固定的。
relabelings里第一条keep只保留istio-proxy容器的指标,避免抓到业务容器自己的指标。第二条replace是把 Pod 上的security.istio.io/tlsMode标签转成 Prometheus 的标签,这个标签能告诉你这个 Pod 的 mTLS 是 STRICT 还是 PERMISSIVE,排查 mTLS 问题时非常有用。
提示:
interval: 15s是抓取间隔,Istio 指标量大的话可以放宽到 30s,能显著降低 Prometheus 的负载。我实测 15s 在 500 个 Pod 规模下,Prometheus 内存占用大概 4Gi,30s 能降到 2.5Gi 左右。
3.3 配置 Istio 侧的遥测开关
光有 PodMonitor 还不够,Istio 侧的遥测配置要确认。在istio这个 ConfigMap 里,enablePrometheusMerge这个开关控制着是否把 Envoy 指标和业务指标合并暴露。
apiVersion: v1 kind: ConfigMap metadata: name: istio namespace: istio-system data: mesh: | enablePrometheusMerge: true defaultConfig: proxyStatsMatcher: inclusionRegexps: - ".*outlier_detection.*" - ".*circuit_breakers.*" - ".*upstream_rq_retry.*"enablePrometheusMerge: true表示把 Envoy 的指标和业务容器的指标合并到同一个端口暴露,这样 Prometheus 只需要抓一个端口就行。如果你关掉它,就得分别抓15090和业务端口,配置会复杂很多。
proxyStatsMatcher是控制 Envoy 暴露哪些指标的。Envoy 默认会暴露几千个指标,全抓下来 Prometheus 会爆。用inclusionRegexps只保留你关心的,比如熔断、重试、异常检测相关的。这个配置能帮你省掉 70% 以上的指标量。
3.4 告警规则 PrometheusRule 的编写
告警规则我用PrometheusRule这个 CRD 来管理,下面是我实际在用的几条核心规则:
apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: istio-alerts namespace: monitoring labels: release: kube-prometheus-stack spec: groups: - name: istio.rules interval: 30s rules: - alert: IstioHigh5xxRate expr: | sum(rate(istio_requests_total{response_code=~"5.*",reporter="destination"}[5m])) by (destination_service_name, destination_service_namespace) / sum(rate(istio_requests_total{reporter="destination"}[5m])) by (destination_service_name, destination_service_namespace) > 0.05 for: 5m labels: severity: critical team: mesh annotations: summary: "服务 {{ $labels.destination_service_name }} 5xx 比例超过 5%" description: "命名空间 {{ $labels.destination_service_namespace }} 下的服务 {{ $labels.destination_service_name }} 在过去 5 分钟内 5xx 比例达到 {{ $value | humanizePercentage }}"这条规则有几个设计点值得说:
用reporter="destination"而不是source,因为 destination 视角的指标反映的是服务端真实处理结果,source 视角会受客户端重试影响,容易误报。
for: 5m是持续时间,意思是连续 5 分钟都超过阈值才告警,避免瞬时抖动误报。Istio 指标本身有延迟,5m 是个比较稳的值。
annotations里用humanizePercentage把小数转成百分比,告警消息可读性更好。
再补一条延迟告警:
- alert: IstioHighLatency expr: | histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{reporter="destination"}[5m])) by (le, destination_service_name) ) > 1000 for: 10m labels: severity: warning annotations: summary: "服务 {{ $labels.destination_service_name }} P99 延迟超过 1s"注意:
istio_request_duration_milliseconds_bucket这个指标名在不同 Istio 版本里可能不一样,1.20 之后是istio_request_duration_milliseconds,之前是istio_request_duration_seconds。写规则前先用 Prometheus 的查询界面确认一下实际指标名。
3.5 Alertmanager 路由与接收器配置
Alertmanager 的配置我放在 Secret 里,kube-prometheus-stack 会自动加载。核心是 route 和 receivers 两部分:
route: group_by: ['alertname', 'destination_service_namespace'] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: 'default-receiver' routes: - match: team: mesh receiver: 'mesh-team' continue: false - match: severity: critical receiver: 'oncall' continue: true receivers: - name: 'default-receiver' webhook_configs: - url: 'http://alert-webhook.monitoring.svc:8080/alert' - name: 'mesh-team' webhook_configs: - url: 'http://mesh-alert.monitoring.svc:8080/alert' - name: 'oncall' webhook_configs: - url: 'http://oncall-webhook.monitoring.svc:8080/alert'group_by里加上destination_service_namespace,这样同一个命名空间下的告警会被合并成一条,避免告警风暴。
group_wait: 30s是首次告警的等待时间,给 Prometheus 一点时间把同一批告警都推过来,合并后一起发。
repeat_interval: 4h是重复告警间隔,同一个告警 4 小时内只发一次,避免骚扰。
continue: true表示匹配到这条路由后继续往下匹配,这样 critical 告警既能发给 mesh 团队,也能发给 oncall。
4. 完整实操流程与关键环节实现
4.1 环境准备与前置检查
动手之前先做几项检查,能省掉后面很多麻烦。
第一,确认 Istio 已经装好,并且 Sidecar 注入正常。执行kubectl get pods -n istio-system,确保istiod是 Running 状态。然后随便找个注入了 Sidecar 的业务 Pod,kubectl describe pod看看有没有istio-proxy容器。
第二,确认集群里有足够的资源。Prometheus-Operator 全家桶跑起来,至少需要 4 核 8G 的预留。如果集群资源紧张,先把 Prometheus 的 requests 调小,但别低于 1 核 2G,否则抓取会丢点。
第三,确认网络策略。如果集群开了 NetworkPolicy,要确保 monitoring 命名空间能访问所有业务命名空间的15090端口。我遇到过因为 NetworkPolicy 没放行,Prometheus 一直报context deadline exceeded的情况,排查了半天。
4.2 分步安装与配置
第一步,装 kube-prometheus-stack。用上面 3.1 节的命令,装完后kubectl get pods -n monitoring应该能看到 prometheus、alertmanager、grafana、operator 这些 Pod 都 Running。
第二步,创建 PodMonitor。把 3.2 节的 YAML 保存成文件,kubectl apply -f应用。应用后等 1-2 分钟,去 Prometheus 的/targets页面看,应该能看到istio-proxies-monitor这个 job,并且所有 Sidecar Pod 都是 UP 状态。
第三步,配置 Istio 遥测。把 3.3 节的 ConfigMap 改好,kubectl apply之后,istiod 会自动把新配置下发给所有 Sidecar。这个过程是热更新,不用重启 Pod。等 1 分钟左右,去 Prometheus 查询istio_requests_total,应该能看到数据了。
第四步,创建告警规则。把 3.4 节的 PrometheusRule 应用上去,然后在 Prometheus 的/rules页面确认规则已经加载,状态是 OK。
第五步,配置 Alertmanager。把 3.5 节的配置写进 Secret,kube-prometheus-stack 会自动 reload。去 Alertmanager 的/status页面确认配置生效。
4.3 验证数据链路是否打通
验证分三层:
第一层,看 Prometheus 的 targets 页面,确认所有 Istio Sidecar 都是 UP。如果有 DOWN 的,点进去看错误信息,通常是网络不通或端口不对。
第二层,在 Prometheus 查询界面执行istio_requests_total,看有没有数据返回。如果返回空,检查enablePrometheusMerge是否开启,以及 PodMonitor 的 relabelings 是否正确。
第三层,手动触发一条告警。可以把某条规则的阈值临时调低,比如把 5xx 比例阈值改成 0.001,然后制造一些 5xx 请求,看 Alertmanager 是否收到告警。这一步能验证从规则评估到告警发送的完整链路。
4.4 参数调优与性能实测
我实测过几组参数,分享出来供参考:
| 参数 | 默认值 | 调优值 | 效果 |
|---|---|---|---|
| 抓取间隔 | 15s | 30s | Prometheus 内存降 35% |
| 指标保留 | 10d | 15d | 磁盘增 50%,排查更从容 |
| proxyStatsMatcher | 全量 | 正则过滤 | 指标量降 70% |
| Prometheus 副本 | 1 | 2 | 查询性能提升,但存储翻倍 |
proxyStatsMatcher的过滤规则我建议至少包含这几类:outlier_detection、circuit_breakers、upstream_rq_retry、upstream_rq_timeout。这几类指标对排查熔断、重试、超时问题最有用,其他指标可以按需加。
5. 常见问题与排查技巧实录
5.1 Prometheus 抓不到 Istio 指标
这是最高频的问题。排查顺序:
先看 PodMonitor 是否被 Prometheus 识别。去 Prometheus 的/config页面,搜索istio-proxies-monitor,如果没有,说明podMonitorSelectorNilUsesHelmValues没设成 false,或者 PodMonitor 的 label 跟 Prometheus 的 selector 不匹配。
再看 target 状态。如果 target 是 DOWN,点进去看错误。常见错误有connection refused(端口不对)、context deadline exceeded(网络不通)、403(认证问题)。
最后看指标是否被 relabel 掉。在 Prometheus 的/targets页面,展开某个 target,看Labels里有没有__meta_kubernetes_pod_container_name,如果没有,说明 relabelings 配置有问题。
5.2 告警规则不触发
规则不触发通常三个原因:
一是表达式写错。在 Prometheus 查询界面单独执行expr里的表达式,看有没有结果。如果没结果,说明指标名或标签写错了。
二是for时间太长。比如设了for: 1h,那得连续 1 小时满足条件才告警,测试时容易误以为不触发。
三是告警被静默。去 Alertmanager 的/silences页面看有没有匹配的静默规则。
5.3 Alertmanager 收不到告警
先确认 Prometheus 是否把告警推给了 Alertmanager。在 Prometheus 的/alerts页面,看告警状态是不是 Firing。如果是 Firing 但 Alertmanager 没收到,检查 Prometheus 配置里的alerting.alertmanagers地址对不对。
如果 Alertmanager 收到了但没发出去,检查 route 配置。常见问题是match写错,导致告警匹配不到任何 receiver。可以在 Alertmanager 的/status页面看配置是否加载成功。
5.4 指标量过大导致 Prometheus OOM
Istio 指标量确实大,500 个 Pod 的集群,全量指标能有几百万 series。控制方法:
用proxyStatsMatcher过滤 Envoy 指标,这是最有效的。用metric_relabel_configs在抓取时丢弃不需要的指标。降低抓取频率,从 15s 改到 30s 甚至 60s。给 Prometheus 加内存,但这是治标不治本。
我实测下来,一个 500 Pod 的集群,经过过滤后,Prometheus 内存稳定在 4-6Gi,CPU 在 2 核左右,这个资源消耗是可以接受的。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| target DOWN | 端口不对/网络不通 | 看 target 错误信息 |
| 指标为空 | merge 未开启/relabel 错误 | 查 ConfigMap 和 PodMonitor |
| 告警不触发 | 表达式错/for 太长 | 单独执行表达式 |
| 告警不发送 | route 配置错 | 看 Alertmanager status |
| Prometheus OOM | 指标量过大 | 加过滤/降频率 |
提示:排查时善用 Prometheus 的
/targets、/rules、/alerts三个页面,以及 Alertmanager 的/status和/silences页面,90% 的问题都能在这几个页面找到线索。
6. 我在实际落地中的几点体会
这套集成方案我前后在三个不同规模的集群上落地过,从几十个 Pod 的小集群到上千个 Pod 的大集群都跑过。最大的体会是:配置本身不难,难的是指标治理。Istio 默认吐出来的指标太多太杂,如果不做过滤,Prometheus 很快就会被压垮。所以proxyStatsMatcher和metric_relabel_configs这两个配置一定要认真写,这是整套方案能不能长期稳定运行的关键。
另一个体会是告警规则要克制。一开始我写了三十多条规则,结果告警天天响,团队都麻木了。后来精简到十条左右,只保留真正影响业务的,告警才重新变得有意义。告警的价值不在于多,而在于每一条都值得被处理。
最后分享一个小技巧:在 Grafana 里建一个 Istio 的概览面板,把 5xx 比例、P99 延迟、QPS、mTLS 状态这几个核心指标放上去,日常巡检一眼就能看出问题。这个面板配合告警规则,基本能覆盖 80% 的 Mesh 故障场景。