一、部署环境
prometheus 目前我们是使用operator 部署的单点实例,部署在k8s集群中的 10.53.7.138节点,这台机器专用于部署prometheus。业务容器不允许调度在这台机器上
二、使用场景
- 采集和存储线上k8s集群的所有监控数据,包含基础服务组件例如etcd、kubelet、coredns等,同时还有最主要的就是pod的监控以及k8s node节点的监控
- 存储业务打点数据,配置serviceMonitor 采集业务监控指标用于实现业务的监控展示以及报警
- 集群外自定义的exporter、例如nginx_exporter、以及非集群内的主机监控
- 告警rules配置
三、问题现象
3.1 prometheus 磁盘存储暴涨
3.2 prometheus 占用大量内存导致触发kubelet驱逐事件
四、优化方案
干货来了~
4.1 prometheus参数调优和数据抓取优化
4.1.1 operator 配置文件优化
我当前的版本是 version: v2.48.1
spec:
# additionalScrapeConfigs: 指定额外的抓取配置,通过 Kubernetes Secret 存储,并通过 key 指定文件名称。
additionalScrapeConfigs:
key: prometheus-additional.yaml
name: additional-scrape-configs
# 节点亲和性配置,要求 Pod 安排到具有 deploy: prometheus 标签的节点上
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: deploy
operator: In
values:
- prometheus
alerting:
alertmanagers:
- apiVersion: v2
name: uk8s-alertmanager
namespace: uk8s-monitor
pathPrefix: /
port: web
containers:
- args:
- --storage.tsdb.max-block-duration=1h
- --storage.tsdb.min-block-duration=30m
- --storage.tsdb.no-lockfile
- --storage.tsdb.wal-compression
- --web.console.templates=/etc/prometheus/consoles
- --web.console.libraries=/etc/prometheus/console_libraries
- --storage.tsdb.retention.size=900GB
- --config.file=/etc/prometheus/config_out/prometheus.env.yaml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=10d
- --web.enable-lifecycle
- --web.external-url=http://prometheus.xxx.cn
- --web.route-prefix=/
- --web.config.file=/etc/prometheus/web_config/web-config.yaml
- --query.timeout=2m
- --query.max-concurrency=20
- --query.max-samples=5000000
- --web.max-connections=512
name: prometheus
enableAdminAPI: false
evaluationInterval: 60s
externalUrl: http://prometheus.xxx.cn
image: quay.io/prometheus/prometheus
listenLocal: false
logFormat: logfmt
logLevel: info
paused: false
podMetadata:
labels:
k8s-app: uk8s-monitor
podMonitorNamespaceSelector: {}
podMonitorSelector: {}
portName: web
replicas: 1
resources:
limits:
cpu: 13
memory: 55Gi
requests:
cpu: 13
memory: 55Gi
retention: 10d
retentionSize: 900GB
routePrefix: /
ruleNamespaceSelector: {}
ruleSelector: {}
scrapeInterval: 60s
scrapeTimeout: 10s
secrets:
- etcd-client-cert
securityContext:
fsGroup: 2000
runAsGroup: 2000
runAsNonRoot: true
runAsUser: 1000
serviceAccountName: prometheus
serviceMonitorNamespaceSelector: {}
serviceMonitorSelector: {}
# 存储配置,使用 PVC(PersistentVolumeClaim)模版
storage:
volumeClaimTemplate:
metadata:
labels:
k8s-app: uk8s-monitor
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1000Gi
storageClassName: ssd-csi-udisk
# 容忍性,允许 Pod 调度到具有 business=sre 污点的节点上,具有 NoSchedule 效应
tolerations:
- effect: NoSchedule
key: business
operator: Equal
value: sre
version: v2.48.1
walCompression: true
重要的参数解释
args启动参数解释
--storage.tsdb.max-block-duration=1h: 设置最大数据块持续时间为1小时。
--storage.tsdb.min-block-duration=30m: 设置最小数据块持续时间为30分钟。
--storage.tsdb.no-lockfile: 禁用锁文件。
--storage.tsdb.wal-compression: 启用 WAL(Write Ahead Log)压缩。
--storage.tsdb.retention.size=900GB: 数据保留大小为900GB。
--storage.tsdb.retention.time=10d: 数据保留时间为10天。
--web.enable-lifecycle: 启用生命周期管理。
--query.timeout=2m: 查询超时时间2分钟。
--query.max-concurrency=20: 最大并发查询数量20。
--query.max-samples=5000000: 单次查询的最大样本数5,000,000。
--web.max-connections=512: 最大 HTTP 连接数512。
全局配置
scrapeInterval: 60s # 全局抓取间隔60秒
scrapeTimeout: 10s # 全局抓取超时10秒
evaluationInterval: 60s # 规则评估间隔时间,为60秒。
4.2 监控数据抓取优化
4.2.1 完整配置如下
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: xxx
namespace: xxx
spec:
endpoints:
- interval: 30s
path: /actuator/prometheus
port: 9203tcp
scheme: http
scrapeTimeout: 10s
relabelings:
- action: labeldrop
regex: 'namespace|container|endpoint|job|user'
sampleLimit: 200000 # 限制每个抓取请求的样本数
namespaceSelector:
matchNames:
- xxx
selector:
matchLabels:
k8sapp: xxx
4.2.2 配置说明
spec: 定义 ServiceMonitor 的详细配置。
endpoints: 定义 Prometheus 如何抓取服务数据的一个或多个终端点。
interval: 30s: 抓取间隔时间,每 30 秒抓取一次数据。
path: /actuator/prometheus: URL 路径,Prometheus 使用这个路径来抓取指标数据。
port: 9203tcp: 服务的端口号,到这个端口发送请求抓取数据。
scheme: http: 使用的协议,这里是 HTTP。
scrapeTimeout: 10s: 抓取超时时间,如果超过 10 秒,请求将会超时。
relabelings: 标签重写规则,用于修改或删除标签。
action: labeldrop: 表示要删除标签。
regex: 'namespace|container|endpoint|job|user': 使用正则表达式匹配标签名称,这里匹配的标签包括 namespace, container, endpoint, job, 和 user。
- spec: 定义
ServiceMonitor
的详细配置。 - endpoints: 定义 Prometheus 如何抓取服务数据的一个或多个终端点。
- interval: 30s: 抓取间隔时间,每 30 秒抓取一次数据。
- path: /actuator/prometheus: URL 路径,Prometheus 使用这个路径来抓取指标数据。
- port: 9203tcp: 服务的端口号,到这个端口发送请求抓取数据。
- scheme: http: 使用的协议,这里是 HTTP。
- scrapeTimeout: 10s: 抓取超时时间,如果超过 10 秒,请求将会超时。
relabelings: 标签重写规则,用于修改或删除标签。
- action: labeldrop: 表示要删除标签。
- regex: 'namespace|container|endpoint|job|user': 使用正则表达式匹配标签名称,这里匹配的标签包括
namespace
,container
,endpoint
,job
, 和user
。
- sampleLimit: 200000: 设置每次抓取请求允许的最大样本数,限制在 200,000 样本内。这是为了防止单次抓取请求产生过多样本,导致性能问题。
4.2.3 关键点总结
-
抓取配置:
- 频率: 每 30 秒抓取一次。
- 路径: 请求
/actuator/prometheus
路径。 - 端口: 使用端口
9203tcp
。 - 协议: 使用 HTTP 协议。
- 超时: 请求超时时长为 10 秒。
-
标签重写:
- 使用
labeldrop
操作和正则表达式删除不需要的标签namespace
,container
,endpoint
,job
,user
以减少标签的基数。
- 使用
-
样本限制:
sampleLimit
配置限制每次抓取请求的样本数,以防止因单一抓取请求导致的过高开销。
这份 ServiceMonitor
配置定义了 Prometheus 如何从指定服务中抓取指标数据,如何处理标签以减少存储压力,以及如何限制抓取请求的样本数来优化性能和资源使用。通过这些配置,可以确保 Prometheus 高效地监控符合条件的服务并合理管理资源。
4.3 业务方要遵守以下规则
4.3.1 基本命名规范
指标命名
- 简洁明了:指标名称应当简洁、描述性强。避免使用复杂、难以理解的名称。
http_requests_total
- 使用标准前缀和单位:遵循 Prometheus 社区推荐的命名惯例,例如
_total
表示计数器,_duration_seconds
表示时间度量。request_duration_seconds
4.3.2 标签(Label)设计
标签名称
- 规范化名称:标签应使用规范化的名称,避免大小写混用。
service, region, method
控制基数
- 避免高基数标签:不要使用会产生高基数的标签值,例如用户ID、IP地址、唯一请求ID等。高基数标签会导致大量时间序列,增加存储和检索的负担。
// Bad: method is too granular status_code{method="/users/123/profile", service="user-service"} // Good: use generalized method status_code{method="GET", service="user-service"}
4.3.3. 衡量类型和用途
计数器(Counter)
计数器是单调递增的度量,用于记录事件的总数量。
httpRequestsTotal := prometheus.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
})
仪表(Gauge)
仪表用于记录能够增加和减少的数值,例如当前的内存使用。
memoryUsage := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "memory_usage_bytes",
Help: "Current memory usage in bytes.",
})
直方图(Histogram)和摘要(Summary)
直方图和摘要用于分布统计,特别适合记录请求持续时间或响应大小等度量。
requestDurationHist := prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests in seconds.",
Buckets: prometheus.LinearBuckets(0.01, 0.05, 10), // 0.01s to 0.51s
})
4.3.4 合理模块化与封装
封装指标创建
将指标的创建与初始化封装在一个独立的模块中,使代码更具可维护性和重用性
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
)
var (
HttpRequestsTotal = prometheus.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
})
MemoryUsage = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "memory_usage_bytes",
Help: "Current memory usage in bytes.",
})
...
)
func Init() {
prometheus.MustRegister(HttpRequestsTotal, MemoryUsage)
}
4.3.5 优化打点性能
缓存和批处理
根据实际情况,适当缓存数据并进行批处理,减少对指标的频繁操作。
// Example of batch processing
func ProcessDataBatch(batch []Data) {
start := time.Now()
for _, data := range batch {
process(data)
metrics.DataProcessed.Inc()
}
duration := time.Since(start).Seconds()
metrics.BatchProcessDuration.Observe(duration)
}
延迟操作
仅在必要时更新指标,尽量减少对性能的影响。
if needToUpdateMetrics {
metrics.HttpRequestsTotal.Inc()
}
4.3.6 文档与注释
注释与文档
为每个指标添加适当的注释和文档,帮助其他开发者理解指标的用途和含义
// HttpRequestsTotal counts the total number of HTTP requests received.
var HttpRequestsTotal = prometheus.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests received.",
})