一、先把问题说清楚:为什么 Kubeflow 要单独做一套监控

机器学习的平台跑起来之后,大家关心什么?关心模型训练有没有挂,关心推理接口是不是变慢了,关心 GPU 有没有被白白占用,关心半夜有没有报警。这些问题落到 Kubeflow 上,就绕不开它的两大核心部分:Pipeline(流水线)和推理服务(InferenceService)。前者管的是训练任务怎么编排,后者管的是模型上线后怎么对外提供预测。两者跑在不同的组件上、生成不同类型的日志和指标,所以监控策略也必须分开考虑。

从实用角度出发,最常用的配套方案是 Prometheus 加 Grafana。Prometheus 负责拉取和存储指标,Grafana 负责把指标画成图表。我们下面所有示例都以这套组合为基础。我们会先解决“指标从哪里来”的问题,然后一步一步把采集链路打通,最后告诉你哪些地方容易踩坑。整个过程我会尽量用买菜做饭的大白话来讲,保证你跟着操作一遍,就能在自己环境里跑起来。

二、先认识 Prometheus:采集指标的基本姿势

Prometheus 的工作方式像一个小区物业巡检:它定期到各个“监控点”去敲门,问一句“你这边有什么指标要报告吗?”。每个“监控点”只要按照约定格式提供一个 HTTP 接口,把指标文本返回出来就行。不需要你主动上报,一切靠 Prometheus 拉取(Pull)。

这种拉取模式最大的好处是省心。只要你的服务能访问到,Prometheus 就能定期去采集。万一某个服务临时挂了,Prometheus 按重试机制隔一段时间再去拉一次,服务恢复了数据又会自动接上。

2.1 四类我们最常用的指标模型

在动手写代码之前,先认识一下最常用的四种指标类型。这四种你搞明白了,后面看任何配置都不会觉得晕。

Counter(计数器):只增不减,比如累计请求数。它是累计值,用来看趋势用的。比如“今天到现在一共调用了多少次推理接口”,这个数字只会变大,不会变小。

Gauge(仪表盘):可增可减,比如当前内存占用、当前活跃连接数。它反映的是某一时刻的状态。

Histogram(直方图):把数据分桶统计,适合看耗时分布。比如“推理耗时在 50 毫秒以内的有多少次,在 100 毫秒以内的有多少次”,它能画出类似“大多数请求都很快,少数请求很慢”的图像。我们通常用它来做 P99 百分位的计算。

Summary(摘要):和直方图类似,但更节省资源。理解起来就用前三种就够日常用了。

2.2 先写一个最简单的指标出口

为了让你更直观地理解,我们做个最简单的演示。用一个 Python 写的 HTTP 服务,暴露一个指标接口。这个示例不依赖 Kubeflow,纯粹为了让你知道“被 Prometheus 采集的服务长什么样”。

# 技术栈:Python 3.9 + prometheus_client

from prometheus_client import start_http_server, Counter, Histogram, Gauge
import random
import time
import threading

# 定义一个计数器,名字叫 http_requests_total,帮忙统计接口被调了多少次
REQUEST_COUNT = Counter(
    'http_requests_total',
    '总共接收到的请求数量',
    ['method']  # 用 method 标签区分是 GET 还是 POST
)

# 定义一个直方图,用来统计处理耗时
REQUEST_DURATION = Histogram(
    'http_request_duration_seconds',
    '接口处理耗时,单位秒',
    ['path']  # 用 path 标签区分不同的路径
)

# 定义一个仪表盘,用来表示当前排队中的任务数量
QUEUE_SIZE = Gauge(
    'http_queue_size',
    '当前排队中的任务数量'
)

def process_request():
    # 模拟一个耗时操作,费时 0.1 到 0.5 秒
    time.sleep(random.uniform(0.1, 0.5))

def run_server():
    # 启动一个 HTTP 服务,监听 8000 端口
    # 这个服务会自动提供一个 /metrics 路径供 Prometheus 拉取
    start_http_server(8000)
    print("指标接口已经启动,请访问 http://localhost:8000/metrics 查看结果")

def simulate_requests():
    # 循环模拟产生请求数据
    while True:
        # 每次循环先标记一个请求到来
        REQUEST_COUNT.labels(method='POST').inc()
        # 更新当前队列大小,+1 表示多了一个任务在排队
        QUEUE_SIZE.inc()

        path = '/v1/predict'
        start_time = time.time()

        # 执行模拟任务
        process_request()

        # 记录本次处理的耗时
        REQUEST_DURATION.labels(path=path).observe(time.time() - start_time)

        # 排队人数减一
        QUEUE_SIZE.dec()
        time.sleep(1)

if __name__ == '__main__':
    # 先启动服务
    run_server()
    # 再在另一个线程里模拟请求
    t = threading.Thread(target=simulate_requests, daemon=True)
    t.start()
    # 主线程保持运行
    while True:
        time.sleep(60)

运行上面这段代码后,在浏览器里打开 http://localhost:8000/metrics,你会看到一堆以 # HELP# TYPE 开头的文本。这就是 Prometheus 能读懂的指标格式。关键部分长这样:

# HELP http_requests_total 总共接收到的请求数量
# TYPE http_requests_total counter
http_requests_total{method="POST"} 12.0

这个输出代表:method 为 POST 的请求一共 12 次。Prometheus 拉到这个数据后,会把这个值存储下来,并记录一个时间戳。后面查询的时候,你就能看到一条随时间上涨的折线。

这段代码你不用完全背下来,只要理解一件事:每个被监控的服务,需要自己提供一个 /metrics 接口,里面是文本格式的键值对。

三、Pipeline 指标怎么采集:从零到一的全过程

Pipeline 在 Kubeflow 里主要负责编排多个步骤。你定义好第一步做什么、第二步做什么,平台会帮你调度起来。我们关心的是每一个步骤有没有成功、耗时多久、在哪一步卡住了。通常在这个环节,我们需要监控三类东西:Kubeflow Pipeline 自身的服务状态、Argo Workflow 的任务状态、每一步 Pod 的资源消耗。

3.1 先安装一个轻量 Prometheus 用来做实验

在正式环境里,你可能已经有现成的 Prometheus 集群了。这里我们先在 Kubernetes 里用最简单的方式安装一个测试版,看看数据长什么样,再考虑怎么接入生产。

# 技术栈:Helm 3 + kube-prometheus-stack

# 第一步:添加 prometheus-community 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 第二步:更新仓库索引
helm repo update

# 第三步:在 monitoring 命名空间安装完整监控栈
# 其中包含 Prometheus、Grafana、AlertManager
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set grafana.enabled=true

等安装完成,我们验证一下 Prometheus 是否正常启动:

# 查看安装出来的 Pod 情况
kubectl -n monitoring get pod

# 输出大致长这样:
# NAME                                                     READY   STATUS    RESTARTS   AGE
# kube-prometheus-grafana-xxxxx                            2/2     Running   0          5m
# kube-prometheus-prometheus-xxxxx                         2/2     Running   0          5m
# kube-prometheus-operator-xxxxx                           1/1     Running   0          5m

到这里,一套完整的监控体系已经搭起来了。只不过现在它还只会收集 Kubernetes 集群本身的指标,比如 CPU、内存、网络。我们接下来要做的是把 Kubeflow 的指标也送进去。

3.2 找到 Pipeline 组件的指标入口

Kubeflow Pipeline 系统主要包含几个部分:Pipeline API Server、Argo Workflow Controller、每个训练任务额外地拉起的 Pod。这几个组件里,Argo Workflow 本身就自带了 Prometheus 指标支持,不需要你写代码,它就会暴露一些基础数据。

我们来看看 Argo Workflow Controller 暴露了哪些指标。用端口转发的方式,先把它服务端的 metrics 接口映射出来。

# 技术栈:kubectl 命令

# 先找到 argo-workflows 所在的命名空间
# 一般 Kubeflow 会把相关组件放在 kubeflow 命名空间

# 查看 kubeflow 命名空间下有哪些服务
kubectl -n kubeflow get svc | grep workflow

# 把 argo-server 的 2746 端口映射到本地 8080
kubectl -n kubeflow port-forward svc/argo-server 8080:2746

然后访问 http://localhost:8080/metrics,你就能看到 Argo 自己的指标。其中有几个比较关键的:

argo_workflows_count{status="Running"} 2
argo_workflows_count{status="Error"} 0
argo_workflow_tasks_count{phase="Succeeded"} 15
argo_workflow_tasks_count{phase="Failed"} 1

看到这些指标后,我们的目标就是让 Prometheus 自动采集它们。

3.3 让 Prometheus 自动发现这些指标端点

Prometheus 在 Kubernetes 里采集服务指标,靠的是 Kubernetes 服务发现机制。你不用在每个配置里写死 IP 地址,只要给服务打上对应的标签(annotations),Prometheus 会自动找到它。

下面这段配置解释一下如何给 argo-server 打上采集标签。我们会创建一个 YAML 文件,然后应用它。

# 技术栈:Kubernetes Service 注解配置
apiVersion: v1
kind: Service
metadata:
  name: argo-server
  namespace: kubeflow
  # annotations 是 Prometheus 自动发现的关键部分
  annotations:
    # 打开采集开关,Prometheus 会来这里拉数据
    prometheus.io/scrape: "true"
    # 采集路径,默认是 /metrics
    prometheus.io/path: "/metrics"
    # 端口号,必须和 service 暴露的端口号一致
    prometheus.io/port: "2746"
spec:
  selector:
    app: argo-server
  ports:
    - name: metrics
      port: 2746
      targetPort: 2746

不过这里有个细节:标准的 Service 端口 2746 是 HTTPS 端口,不是纯 HTTP。如果你的 argo-server 启用的是 TLS 加密传输,你还需要在 Prometheus 配置里关闭证书校验。不然 Prometheus 拉取时会报证书错误。

3.4 更常见的是采集每一个 Pipeline 步骤的 Pod 指标

Pipeline 任务跑起来以后,每一个步骤就是一个 Kubernetes Pod。这个 Pod 可能执行的是 Python 训练脚本,也可能是一个数据清洗逻辑。大多数情况下,这种训练脚本内部是不会自己暴露指标的。那怎么办?最实用的办法是通过 Kubernetes 自带的资源监控,用 kube-state-metrics 来获取每个 Pod 的 CPU、内存、重启次数。这些指标已经在 kube-prometheus-stack 里默认安装好了,不需要你做任何额外配置。

你可以先通过下面的命令直观感受一下这些指标:

# 技术栈:kubectl + PromQL 概念演示

# 先查看当前有哪些正在运行的 Pipeline Pod
kubectl -n kubeflow get pod | grep pipeline

# 假设你看到了一个名为 ml-pipeline-xxx 的 Pod
# 在 Prometheus 的查询界面里,你可以输入以下表达式来观察它的 CPU 使用情况
# 这里的 container_cpu_usage_seconds_total 是 kubelet 自己采集的指标
#
# rate(container_cpu_usage_seconds_total{namespace="kubeflow", pod=~"ml-pipeline.*"}[5m])

你会发现,因为我们装的是完整监控栈,刚开始就已经能实时看到每个 Pod 的 CPU 和内存了。这比我们自定义的指标还早一步到位。所以在做 Pipeline 监控时,我给你的第一个建议是:先把 Pod 级别的东西都看一遍,包括状态、重启次数、资源占用。这些数据不用写任何代码就能拿到。

3.5 在训练脚本里埋入自定义指标

上面这些还不够。真实的业务场景里,我们想知道的往往是更偏业务层的数据,例如:

  • 每一轮训练的 loss 变化到多少了。
  • 训练集和验证集准确率差了多少。
  • 某一批数据有多少条被过滤掉了。

这时就需要你自己在代码里埋点。这里我们继续使用 Python 技术栈,展示如何在训练脚本中暴露自定义指标,然后让 Prometheus 从训练容器中采集它。

# 技术栈:Python 3.9 + pytorch + prometheus_client
# 这段代码主要展示训练脚本里怎么暴露指标,
# 并不负责完整训练,只引出思路

from prometheus_client import start_http_server, Gauge, Counter, Summary
import torch
import time

# 训练损失,用 Gauge 类型,因为每一轮的值都在变化,可以上下跳动
TRAIN_LOSS = Gauge('train_loss', '当前训练损失值')
# 训练准确率,同样是 Gauge
TRAIN_ACCURACY = Gauge('train_accuracy', '当前训练准确率')
# 已经完成的训练轮数,用 Counter,只会往上涨
EPOCH_COUNT = Counter('train_epoch_total', '已经完成的总训练轮数')

def train_one_epoch(model, data_loader):
    # 这里省略真实的训练代码
    # 我们用休眠模拟一下训练耗时
    time.sleep(3)
    # 模拟返回这一轮的 loss 和 accuracy
    return 0.35, 0.86

def start_metrics_server():
    # 在 8001 端口暴露指标接口
    start_http_server(8001)
    print('训练指标接口已启动在 8001 端口')

def main():
    # 先启动指标上报服务
    start_metrics_server()

    # 模拟初始化模型
    model = torch.nn.Linear(10, 2)

    # 模拟训练 10 个 epoch
    for epoch in range(10):
        loss, acc = train_one_epoch(model, None)

        # 把指标更新到最新值
        TRAIN_LOSS.set(loss)
        TRAIN_ACCURACY.set(acc)
        # 训练轮数加 1
        EPOCH_COUNT.inc()

        # 打印出来给日志看
        print(f"epoch {epoch + 1} loss={loss:.4f} acc={acc:.4f}")

        # 为了让 Prometheus 有时间去拉取,每个 epoch 之间停一下
        # 实际项目中不需要刻意等待,这里是为了演示方便
        time.sleep(10)

if __name__ == '__main__':
    main()

在运行这个脚本之前,需要在同一个容器里启动两个进程:一个是训练主进程,另一个是指标暴露服务。上面的示例里把指标启动写在了主函数内,实际你就可以直接这样用。

然后,你在 Kubernetes 中部署这个训练任务时,用下面这种 Pod 配置来写资源清单:

# 技术栈:Kubernetes Pod YAML 配置
apiVersion: v1
kind: Pod
metadata:
  name: training-job-001
  namespace: kubeflow
  # 这里的关键标签,让 Prometheus 发现这个 Pod 的指标接口
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/path: "/metrics"
    prometheus.io/port: "8001"
spec:
  containers:
    - name: trainer
      image: your-registry/trainer:latest
      ports:
        - name: metrics
          containerPort: 8001
      # 这里省略了具体的训练命令和参数
      # 实际运行时,请用你自己的镜像命令

保存为 training-pod.yaml 以后执行:

# 部署这个训练任务
kubectl apply -f training-pod.yaml

这样 Prometheus 就会自动去 8001 端口拉取训练指标了。之后你在 Grafana 图表里输入 train_loss,就能看到一条随训练时间变化的折线。训练结束 Pod 消失,数据链路也会自动切断,不会对系统造成任何负担。

四、推理服务指标怎么采集:模型上线之后的另一场硬仗

Pipeline 监控解决的是训练阶段的问题。模型训练完成,我们还要把模型部署成在线服务,对外提供实时推理能力。这一环节更贴近我们的直接感受,也很容易出问题。

在 Kubeflow 生态中,最常用的推理服务组件是 KServe。KServe 部署出来的每个模型服务都自带 /v2/metrics 接口。这意味着你不需要改造模型代码,就能拿到标准的监控指标。

4.1 KServe 默认提供的指标有什么用

KServe 默认基于 FastAPI 的推理服务器会暴露这些指标:

# HELP model_request_count 模型请求次数
# TYPE model_request_count counter
model_request_count{model_name="mymodel",version="v1"} 100

# HELP model_request_duration_seconds 请求耗时
# TYPE model_request_duration_seconds histogram
model_request_duration_seconds_bucket{model_name="mymodel",le="0.1"} 10
model_request_duration_seconds_bucket{model_name="mymodel",le="0.5"} 50
model_request_duration_seconds_bucket{model_name="mymodel",le="1"} 80
model_request_duration_seconds_sum{model_name="mymodel"} 67.5
model_request_duration_seconds_count{model_name="mymodel"} 100

这些指标能帮你回答几个很重要的问题:

  • 当前模型服务每秒能响应多少个请求?
  • 请求的平均耗时是多少?P99 耗时是多少?
  • 有没有因为超出并发限制而导致的错误请求?

我们可以先用一个小脚本模拟对推理服务的请求,同时把 KServe 自带的指标拉出来看看:

# 技术栈:curl 命令

# 先部署一个简单的 KServe 模型服务
# 假设它已经跑在 test 命名空间下面
kubectl -n test get inferenceservice

# 获取到模型服务的访问地址
# 假设服务名为 mymodel
# 先用 curl 查看 KServe 的指标接口健康状态
curl http://localhost:8080/v2/metrics 此处省略实际地址

这个命令里 8080 是默认端口,实际请换成你服务地址中的端口。

4.2 为推理服务配置标准的采集规则

KServe 的推理服务本质上是一组 Kubernetes Service 和 Deployment。和前面采集 Pod 指标的方式不同,为它配置采集需要用 ServiceMonitor。ServiceMonitor 是 Prometheus Operator 提供的一种自定义资源,用来描述“需要监控哪些 Service”。

下面这份 YAML 展示了如何为一个名为 mymodel 的推理服务建立一个 ServiceMonitor。

# 技术栈:Prometheus Operator + ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: kserve-models
  # 放在和模型服务同一个命名空间中
  namespace: test
spec:
  # 选择带有名称为 predictor 的 Service
  selector:
    matchLabels:
      serving.knative.dev/service: mymodel
  endpoints:
    - port: metrics
      # KServe 的标准指标路径
      path: /v2/metrics
      # 每 15 秒拉取一次
      interval: 15s

这里的 selector 可能因你的具体部署方式而不同。在我的实践当中,KServe 创建的 Service 标签往往长这样:

selector:
  matchLabels:
    serving.kubeflow.org/inferenceservice: mymodel

部署 ServiceMonitor 之后就完成了一大半工作。还需要让 Prometheus 知道它要处理这个 ServiceMonitor。在默认的 kube-prometheus-stack 里,Prometheus 会通过 prometheus.yml 配置文件中的 serviceMonitorSelector 来决定采集哪些 ServiceMonitor。

一般默认配置会采集所有命名空间下带有指定标签的 ServiceMonitor。如下配置最常见的写法:

# 技术栈:Prometheus CRD 配置
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: kube-prometheus
  namespace: monitoring
spec:
  serviceMonitorSelector:
    matchLabels:
      release: kube-prometheus

这意味着你创建 ServiceMonitor 时,需要在 metadata 里加上对应标签,例如 release: kube-prometheus。所以刚才的 YAML 要改一下:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: kserve-models
  namespace: test
  # 这个标签必须匹配 Prometheus 的 serviceMonitorSelector 才能被采集
  labels:
    release: kube-prometheus
spec:
  selector:
    matchLabels:
      serving.kubeflow.org/inferenceservice: mymodel
  endpoints:
    - port: metrics
      path: /v2/metrics
      interval: 15s

4.3 用 PromQL 看推理服务的真实状态

有了上面这些指标,接下来就是写 PromQL 查询语句了。这些查询语句在 Grafana 里输入后,就能直接画成图表。

看“每秒请求数”:

sum(rate(model_request_count{namespace="test"}[5m])) by (model_name)

看“P99 耗时”:

histogram_quantile(0.99, sum(rate(model_request_duration_seconds_bucket{namespace="test"}[5m])) by (le, model_name))

看“最近5分钟有多少错误请求”:

sum(increase(model_request_count{namespace="test", code=~"5.*"}[5m])) by (model_name)

这些表达式看起来有点复杂,但只需要记住一个逻辑:rate 是求每秒变化率,histogram_quantile 是计算百分位,increase 是求增量。熟练了以后,你看到别人写的表达式基本都能读个七七八八。

五、告警和看板:让数据替你值夜班

数据采集上来了,最后一步是使用它。对我们操作者来说,比较要紧的是看板和告警。看板相当于驾驶舱显示屏,告警相当于报警铃。两者缺一不可。

5.1 Grafana 看板搭建

安装好 kube-prometheus-stack 时,Grafana 已经自带了很多 Kubernetes 相关的看板。我们再补充一个针对模型推理服务的简单看板配置思路。在 Grafana 里,新建 Panel 的时候选择 Prometheus 数据源,输入 PromQL,选择图表类型,就能出图。

一个比较直观的组合是:

  • 用折线图展示模型请求 QPS。
  • 用柱状图展示不同返回码的错误次数。
  • 用热力图展示耗时分布在一天内的变化。

如果你不想从零开始画,社区也有很多现成的 KServe 看板可以直接导入,在 Grafana 中搜索看板 ID 就可以找到。导入后修改一下数据源,就能看到数据和图表了。

5.2 关键告警规则埋点

告警规则的作用是:“当某个数值超过阈值时,自动发消息给值班人员”。我们通过 PrometheusRule 自定义资源来配置规则:

# 技术栈:Prometheus Operator + PrometheusRule
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: kserve-model-alerts
  namespace: test
  labels:
    release: kube-prometheus
spec:
  groups:
    - name: kserve-inference
      rules:
        # 当模型推理接口 5 分钟内错误率超过 5% 时触发告警
        - alert: HighErrorRate
          expr: |
            sum(rate(model_request_count{namespace="test", code=~"5.*"}[5m]))
            /
            sum(rate(model_request_count{namespace="test"}[5m])) > 0.05
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "模型推理错误率超过 5%"
            description: "当前错误率已超过阈值,请及时检查模型版本或资源状态。"

        # 当 P99 耗时超过 2 秒时触发告警
        - alert: HighLatency
          expr: |
            histogram_quantile(0.99, sum(rate(model_request_duration_seconds_bucket{namespace="test"}[5m])) by (le)) > 2
          for: 3m
          labels:
            severity: critical
          annotations:
            summary: "模型推理P99耗时超过2秒"
            description: "用户感知到明显的响应变慢,建议扩容或优化模型。"

应用这个文件:

kubectl apply -f kserve-alerts.yaml

这样,当线上模型出现大面积错误或明显变慢时,你就会收到告警通知。告警消息可以通过 AlertManager 发到钉钉、企业微信、邮件或者 Slack,这里就不展开讲了。

六、常见坑和调优经验

讲了这么多完整流程,最后聊几个容易踩的坑,帮你少走一些弯路。

6.1 不要重复采集同一个指标

Kubeflow 整个链路中有很多替身组件,比如 Pipeline 本身会暴露指标,Argo 也会暴露指标,Pod 级别的资源采集还在重复工作。如果你不加选择地把所有 Service 都加上 prometheus.io/scrape 注解,那么 Prometheus 会收到大量重复数据,导致存储膨胀,查询变慢。一个比较合理的做法是:Pipeline 阶段以 Pod 资源指标做基础,只在训练脚本内部暴露业务指标;推理阶段用 KServe 自带指标 + 少量自定义指标。

6.2 注意“指标爆炸”的问题

Prometheus 的存储压力很大一部分来自标签基数(cardinality)。如果你在代码里为每个用户ID、每个项目名都设置一个标签,那么标签组合会呈指数级增长。举个例子,一个 Counter 定义了 user_id 标签,有 1 万个用户,那么这个指标在 Prometheus 里就有 1 万个时间序列。在 Kubeflow 场景中,模型名和版本号是合适的标签,但请求里的用户 ID 就千万不要做标签。

6.3 训练脚本的指标接口要设置超时

训练任务通常只在集群空闲时段运行。如果脚本里启动了一个永不退出的指标服务,那么在任务结束时可能会出现资源泄漏。在代码中要给指标服务器加一个关闭机制。比如在训练完成后,显式调用一下 shutdown 函数,或者依靠 Pod 的生命周期自动回收。因为训练容器结束的时候,所有进程都会被杀掉,所以实际影响不大,但如果你的训练任务长期驻留,这个点就需要特别注意。

6.4 使用端口转发做临时排查

生产环境不建议频繁修改 Service 配置。当你想临时测试某个指标接口是否可用时,使用 kubectl port-forward 是最快速的直连方式。等你确认接口正常后,再通过 ServiceMonitor 或注解方式接入正式的采集链路。这样可以避免测试痕迹污染线上配置。

七、优缺点分析和适用场景

聊到这里,我们把整套方案再理性地盘点一下。不要以为用了 Prometheus 就一劳永逸,它也有优点也有缺点。

7.1 优点

这套方案的优点是极其明显。第一,它完全开源,不需要额外购买商业监控软件。第二,它与 Kubernetes 和 Kubeflow 原生结合,很多组件天生就暴露了指标,不需要从头开发。第三,告警和可视化一体化,从数据采集到通知报警的链路很简单,能在短时间内搭建完成。第四,社区非常庞大,遇到的问题大概率都能在网上找到参考方案。

7.2 缺点

缺点也不能回避。第一,Prometheus 的存储是单机阻塞式的,如果不使用 Thanos 或者 VictoriaMetrics 做长期存储,历史数据保存时间很有限。第二,指标采集是拉模式,如果某些推理服务在离线环境或网络隔离的网络中,Prometheus 访问不到服务地址,就采集不了数据。第三,学习曲线相对陡峭,尤其是 PromQL 对新手不太友好。第四,在一些超大规模集群下,指标数量太多会拖垮监控系统自身的性能,对运维能力有一定要求。

7.3 适合用在哪些场景

这套方案特别适合以下几类场景。第一批是内部使用的机器学习平台,团队规模中等,希望能快速看到训练和推理两个阶段的核心状态。第二批是已经在用 Kubeflow 进行全套 ML 流程管理的组织,避免使用过多零散工具增加维护成本。第三批是研究和实验性质的项目,对指标存储周期要求不高,更注重实时观察和快速发现问题。

八、最后的总结

监控体系这件事,不需要做得多么花哨,关键是抓大放小。拿 Kubeflow 来说,只要你把 Pipeline 的任务状态、Pod 资源用量、推理服务的请求量、耗时和错误率放在一块看板上,90% 的日常问题都能一览无余。剩下的细节再靠日志系统去追查。

我们在文章中从 Prometheus 的基础讲起,演示了自定义指标接口的写法,串联了 Pipeline 的采集流程和 KServe 推理服务的采集方式,还配置了告警规则。这一套走通之后,再遇到模型推理变慢、训练任务失败这类问题,你就不会只是干瞪眼等日志了。打开 Grafana,一眼就能定位到是资源不够、代码卡住还是模型包版本有问题。

所有示例你都可以直接复制到自己的环境里跑一下。把你训练任务里的核心指标先暴露出来,部署一个推理服务,拉出它自带的状态指标,然后画一张属于你自己的面板,感受一下手上有数据的踏实感。多试几轮,就会越来越熟练。