写在前面
前面系列的文章我们花费了较大篇幅,介绍了 Kubernetes 的各个组件的监控方法。从整个体系来看,Kubernetes 体系的监控还应该包含 Pod 里的应用的监控。
控制面、节点和 Kubernetes 对象监控回答的是“集群本身是否健康”;Pod 里的应用监控回答的是“业务请求是否正常、延迟是否变高、错误是否增加”。如果只做前者,很多应用层问题会被遗漏。
本文主要梳理 3 类方案:
- statsd 埋点:应用通过 SDK 主动把指标推给本地 agent。
- Prometheus 埋点:应用暴露
/metrics,由采集器主动拉取。 - 日志分析:从日志中提取指标或进行中心化查询分析。
App 监控概述
容器里的应用要做监控,大概有两个手段,一个是埋点,一个是日志分析。跨语言的埋点方案有两个非常知名,一个是 statsd,一个是 prometheus。日志分析也有两类,一个是在端上做日志流式处理分析,类似 mtail 的方案,另一个是把日志推到中心,在中心做分析。下面我们拆开来看这几个方案的原理。
几类方案的定位可以先用一张表对齐:
| 方案 | 数据方向 | Kubernetes 中的典型部署 | 适合回答的问题 |
|---|---|---|---|
| statsd | 应用推送到 agent | agent 和应用放在同一个 Pod,采用 sidecar | 请求计数、耗时、业务计数器等自定义指标 |
| Prometheus 埋点 | 采集器主动拉取应用 /metrics |
sidecar 抓取,或通过 Pod annotation 做服务发现 | 标准化指标、服务发现、PromQL 查询 |
| 端上日志分析 | agent 在本地解析日志 | Categraf mtail 等插件随应用部署 | 从日志中生成计数、错误率等指标 |
| 中心日志分析 | 日志集中后处理 | 日志系统统一接收、流式或周期性查询 | 复杂日志检索、聚合分析和后续指标化 |
埋点监控
statsd 和 prometheus 这两类埋点方案相对更通用,不过有些语言会有自己的常用的埋点工具,比如 Java 生态里的 micrometer。一个公司通常有多种语言,跨语言的工具首推 statsd 和 prometheus,下面我们分别做一下概述。
statsd
statsd 开始被大家熟知是由于这篇文章《Measure Anything, Measure Everything image 》Etsy 的工程师使用 NodeJS 开源了这个项目,项目地址在:https://github.com/statsd/statsd。
statsd 对于各种语言都有相应的 SDK,业务程序引入这些 SDK 度量自己的指标。典型的数据流如下:

应用程序通过 SDK 埋点之后,SDK 会把数据通过 UDP 协议推给监控 agent,很多 agent 都支持 statsd 协议的接收,比如 Telegraf、Datadog-agent。然后监控 agent 把数据做聚合,完事推给监控服务端。
这里使用 Telegraf 来做演示,在 Telegraf 配置文件中搜索 inputs.statsd,可以看到很多注释和样例,这里我提供一个例子:
[[inputs.statsd]]
protocol = "udp"
service_address = ":8125"
percentiles = [50.0, 90.0, 99.0, 99.9, 99.95, 100.0]
metric_separator = "_"
启用如上配置,percentiles 略微有点多,可以配置得少一点,比如 percentiles = [50.0, 90.0, 99.0, 100.0],这样整体计算和存储压力也会小一些。重启 Telegraf,Telegraf 就会在 8125 端口监听 UDP 协议,接收业务埋点数据的上报。
这里我写了一个简单的 Go 程序,使用 statsd 的 SDK(github.com/smira/go-statsd) 做了一个简单埋点,样例如下:
package main
import (
"fmt"
"math/rand"
"net/http"
"time"
"github.com/smira/go-statsd"
)
var client *statsd.Client
func homeHandler(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// random sleep
num := rand.Int31n(100)
time.Sleep(time.Duration(num) * time.Millisecond)
fmt.Fprintf(w, "duration: %d", num)
client.Incr("requests.counter,page=home", 1)
client.PrecisionTiming("requests.latency,page=home", time.Since(start))
}
func main() {
// init client
client = statsd.NewClient("localhost:8125",
statsd.TagStyle(statsd.TagFormatInfluxDB),
statsd.MaxPacketSize(1400),
statsd.MetricPrefix("http."),
statsd.DefaultTags(statsd.StringTag("service", "n9e-webapi"), statsd.StringTag("region", "bj")),
)
defer client.Close()
http.HandleFunc("/", homeHandler)
http.ListenAndServe(":8000", nil)
}
这个 web 服务只有一个根路径,逻辑也很简单,就是随机 sleep 几十个毫秒当作业务处理时间。整体逻辑是这样的:首先,我们要通过 statsd.NewClient 初始化一个 statsd 客户端,参数中指定了 StatsD 的 Server 地址(在本例中就是 Telegraf 的 8125),指定了所有监控指标的前缀是 http.,还指定了两个全局 Tag,一个是 service=n9e-webapi,另一个是 region=bj,通过 TagStyle 指定了要发送的是 InfluxDB 样式(因为数据是发给 Telegraf 的,Telegraf 是 InfluxDB 生态的)的标签。然后,在请求的具体处理逻辑里上报了两个监控指标,一个是 requests.counter,另一个是 requests.latency,并且为这俩指标指定了一个指标级别的标签 page=home,整体看起来还是比较简单的。
上面的 Go 程序编译一下,启动后会作为一个 web server 监听在 8000 端口。周期性请求这个 web server 的地址做测试时,web server 接收到请求之后,就调用 statsd 的 SDK。statsd SDK 的核心逻辑就是把数据发给 Telegraf 的 8125,然后由 Telegraf 处理聚合逻辑,聚合之后的数据每 10s(默认 flush 频率)发给夜莺。
在页面上,应该可以看到http_requests_latency和http_requests_counter打头的相关指标,比如http_requests_latency_mean这个指标,会看到这个指标有如下几个标签:
- ident: VM-0-4-centos 这个标签其实是Telegraf原始的host标签,夜莺的规范里叫ident,所以做了一下rename
- metric_type: timing 这个显然是把statsd的数据类型也做为标签了,其他数据类型还有gauge、counter、set等
- page: home 这是我们代码里附到监控指标后面的标签,Telegraf自动帮解析出来了
- service: n9e-webapi NewClient时候附加的全局默认标签
- region: bj NewClient时候附加的全局默认标签
在 Kubernetes 中,要把支持 statsd 协议的 agent(这里就是 Telegraf),和应用程序放到一个 Pod 中,即 sidecar 架构,这样走 UDP 协议最为稳妥。
statsd 方案的关键点是“本地近距离接收”。UDP 上报本身简单高效,但不适合跨复杂网络链路长距离传输;把 Telegraf 和应用放进同一个 Pod,可以减少丢包和网络不确定性。
prometheus
使用 prometheus 来埋点,也是常见做法。除了埋点 SDK 和 statsd 不同之外,指标暴露方式和数据流向也不同。使用 prometheus 埋点,应用程序会暴露 /metrics 接口,然后等待监控系统来拉取。监控系统拉取又有两种方式:一种是使用 Categraf 这样的工具,和应用程序部署到一起做成 sidecar;另一种是应用程序所在的 Pod 打上特殊的 annotation,用 prometheus agent mode 来做动态服务发现,只抓取那些带有相关 annotation 的 Pod 的监控数据。
Nightingale 的官方文档中提供了一个使用 Prometheus 埋点的样例,链接在这里。
对于如何抓取的问题,sidecar 模式比较简单,不做赘述。重点看一下服务发现的方式,我来演示一下这个过程。
首先,假设某个应用埋点了,并且上线的时候写好了 annotation,那自然是好的,如果没有打上 annotation,也可以现搞,应用无需重启,样例如下:
kubectl annotate pod nightingale-nwebapi-5df5cd7cd6-rc5p4 -n n9e prometheus.io/scrape=true
kubectl annotate pod nightingale-nwebapi-5df5cd7cd6-rc5p4 -n n9e prometheus.io/metric_path=/metrics
kubectl annotate pod nightingale-nwebapi-5df5cd7cd6-rc5p4 -n n9e prometheus.io/scrape_port=18000
这样一来,Pod 就会带有如下注解:
prometheus.io/scrape=true
prometheus.io/metric_path=/metrics
prometheus.io/scrape_port=18000
后面我们就为 prometheus agent mode 增加一个抓取 job,抓取这些带有 prometheus.io/scrape 的 Pod 的监控指标,抓取规则如下:
- job_name: "kubernetes-pods"
kubernetes_sd_configs:
- role: pod
relabel_configs:
# "prometheus.io/scrape = true" annotation.
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
# "prometheus.io/metric_path = <metric path>" annotation.
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_metric_path]
action: replace
target_label: __metrics_path__
regex: (.+)
# "prometheus.io/scrape_port = <port>" annotation.
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_scrape_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: pod
sidecar 的模式和中心服务发现的方式,如何选型呢?大家可以参考这个视频的介绍:Scaling Prometheus Metrics in Kubernetes with Telegraf | DZone.com Webinar
这里的选型可以先按两个问题判断:
- 如果应用和采集器需要强绑定、希望本地抓取后再转发,优先考虑 sidecar。
- 如果团队已经统一使用 Prometheus 服务发现,并且应用能规范打 annotation,优先考虑 prometheus agent mode。
日志监控
日志监控的两种方式,一个在端上,一个是中心,端上的方式采用 Categraf mtail 插件,具体可以参考 categraf mtail introduction。
中心的方式,又有两种,一个是流式处理,日志来了之后,根据预定义的处理规则进行聚合;另一种是周期性查询日志内容生成指标,都是很大的话题,留待后续介绍。
常见问题
Q1:Kubernetes 应用监控应该优先选 statsd 还是 Prometheus? A:如果已有 Prometheus 生态和服务发现,Prometheus 埋点更容易和查询、告警、仪表盘打通;如果应用更适合主动上报,或者希望用本地 agent 聚合 UDP 指标,statsd 也很实用。
Q2:sidecar 和 annotation 服务发现哪个更好? A:没有绝对答案。sidecar 更适合本地强绑定和隔离采集;annotation 服务发现更适合平台统一治理和动态发现。关键是团队能否稳定维护部署规范。
Q3:有了指标埋点还需要日志监控吗? A:需要。指标适合趋势、聚合和告警,日志适合保留上下文和排查细节。应用监控通常要同时使用指标和日志。
相关文章
- Kubernetes监控手册01-体系介绍
- Kubernetes监控手册02-宿主监控概述
- Kubernetes监控手册03-宿主监控实操
- Kubernetes监控手册04-监控Kube-Proxy
- Kubernetes监控手册05-监控Kubelet
- Kubernetes监控手册06-监控APIServer
- Kubernetes监控手册07-监控Controller-manager
- Kubernetes监控手册08-监控Scheduler
- Kubernetes监控手册09-监控ETCD
- Kubernetes监控手册10-使用KSM监控Kubernetes对象
关于作者
本文作者秦晓辉,Flashcat合伙人,文章内容是Flashcat技术团队共同沉淀的结晶,作者做了编辑整理,我们会持续输出监控、稳定性保障相关的技术文章,文章可转载,转载请注明出处,尊重技术人员的成果。
