网站建设028郑州网站建设君捷
2026/4/16 19:43:48 网站建设 项目流程
网站建设028,郑州网站建设君捷,网站高并发前端怎么做,专业平台建设1. 从问题出发#xff1a;监控到底要回答什么问题 在设计任何 Exporter 之前#xff0c;先问一个问题#xff1a; 如果这台服务器出了问题#xff0c;你希望监控系统第一时间告诉你什么#xff1f; 这不是技术问题#xff0c;是业务问题。 1.1 USE 方法#xff1a;三个…1. 从问题出发监控到底要回答什么问题在设计任何 Exporter 之前先问一个问题如果这台服务器出了问题你希望监控系统第一时间告诉你什么这不是技术问题是业务问题。1.1 USE 方法三个核心维度Google SRE 总结的 USE 方法给了一个框架Utilization利用率资源用了多少CPU 是否接近 100%内存是否吃紧磁盘是否快满Saturation饱和度是否有排队/拥塞磁盘 IO 队列是否堆积网络带宽是否打满Errors错误是否有明显异常网络错误包磁盘错误这三个维度回答了系统是否健康的问题。1.2 四个观察窗口对 Linux 服务器来说具体就是四个维度CPU这台机器忙不忙忙在 user 模式跑应用忙在 system 模式内核调用还是在等 IOiowait内存是否紧张真正可用的内存有多少MemAvailablecache/buffer 占了多少是否有内存泄漏磁盘是否有风险容量是否快满未来多久会满趋势预测IO 吞吐是否正常网络是否有瓶颈带宽是否打满错误率是否异常1.3 node-exporter 的价值你可能写过这样的指标app_cpu_usage_percent 85.3 memory_usage_percent 72.3 disk_usage_percent 60.1看似直观但问题在于使用率怎么算的时间窗口是多久能按不同维度聚合吗能看历史趋势吗告警阈值怎么定72%的内存使用算高吗要看cache占比node-exporter 的做法完全不同不给你CPU 使用率只给你CPU 累计时间不给你内存使用率给你MemTotal、MemAvailable、Buffers...不给你磁盘使用率给你总容量、可用容量乍一看很麻烦其实这才是最灵活的设计。设计哲学暴露事实不是观点CPU 使用率是观点需要定义过去 5 分钟1 分钟按核心还是整机CPU 累计时间是事实内核记录了多少秒观点会限制用户的灵活性事实才能支撑各种视图。2. 核心设计暴露事实把视图交给 PromQL这一节承接第一篇看看 node-exporter 如何把四种指标类型的哲学落地到实际设计中。2.1 CPU为什么不直接给使用率问题你想知道CPU 是否繁忙常见错误直接暴露使用率# 不好的设计 cpu_usage_percent 85.5问题使用率是怎么算的时间窗口是多久能否按 CPU 核心分别看不能已经聚合了能否看 user/system/iowait 的细分不能已经合并了能否调整时间窗口重新计算不能只有当前值Linux 给你的事实/proc/stat文件记录了每个 CPU 自启动以来在各种模式下累计的时间秒cpu0 123456 0 56789 987654 1234 0 567 0这些数字分别是user、nice、system、idle、iowait...的累计时间。node-exporter 的选择用 Counter 暴露这些累计时间node_cpu_seconds_total{cpu0, modeuser} 123456 node_cpu_seconds_total{cpu0, modesystem} 56789 node_cpu_seconds_total{cpu0, modeidle} 987654 node_cpu_seconds_total{cpu0, modeiowait} 1234为什么这样设计因为使用率是个观点需要定义时间窗口最近 5 分钟1 分钟10 秒聚合方式按 CPU 核心整机平均模式选择user system还是包括 iowait一旦在 Exporter 里固化了使用率这些选择就被锁死了。用户想看不同视图没办法。PromQL从事实到视图# 整机 CPU 使用率最近 5 分钟 100 - (avg by (instance) ( rate(node_cpu_seconds_total{modeidle}[5m]) ) * 100) # 只看 user 模式的使用率 rate(node_cpu_seconds_total{modeuser}[5m]) * 100 # 按核心分别看 rate(node_cpu_seconds_total{modeuser}[5m]) * 100同一份事实支持无限种视图。设计智慧事实 累计时间用户视图1 5分钟使用率用户视图2 1分钟使用率用户视图3 按核心分别看这就是暴露事实不是观点的第一个例子Exporter我告诉你事实累计了多少秒Prometheus我帮你计算视图使用率是多少用户我自由组合想看什么就看什么为什么 CPU 时间用 Counter因为时间是只增不减的累积量系统运行越久累积时间越多进程重启时会归零Counter 允许重置Prometheus 的 rate() 函数会自动处理重置情况计算正确的速率这和第一篇讲的 Counter 设计哲学完全一致。2.2 内存为什么不是一个使用率问题内存是否紧张常见错误一个内存使用率搞定# 不好的设计 memory_usage_percent 72.3问题无法区分 cache/bufferLinux 会用空闲内存做缓存不能用于容量规划72%算高吗cache可以随时释放告警阈值难定不同应用对内存的容忍度不同无法排查问题不知道内存都用在哪了Linux 给你的事实/proc/meminfo提供了一组快照值MemTotal: 16384000 kB MemFree: 2048000 kB MemAvailable: 8192000 kB Buffers: 512000 kB Cached: 4096000 kB ...node-exporter 的选择全部用 Gauge 暴露node_memory_MemTotal_bytes 16777216000 node_memory_MemFree_bytes 2097152000 node_memory_MemAvailable_bytes 8388608000 node_memory_Buffers_bytes 524288000 node_memory_Cached_bytes 4194304000为什么不直接给一个内存使用率因为不同场景需要不同的使用率场景 1快速告警只关心 MemAvailable(node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 10场景 2问题排查需要看 cache 和 buffer 的细节# Free 内存占比 node_memory_MemFree_bytes / node_memory_MemTotal_bytes # Cache 占比 node_memory_Cached_bytes / node_memory_MemTotal_bytes # Buffer 占比 node_memory_Buffers_bytes / node_memory_MemTotal_bytes场景 3容量规划看长期趋势avg_over_time(node_memory_MemAvailable_bytes[7d])一个使用率指标无法同时支持这三种场景。设计智慧这是用多个 Gauge 表达不同子视图的例子不是一个 Gauge内存使用率而是多个 GaugeTotal、Free、Available、Buffers、Cached每个 Gauge 都是一个事实用 PromQL 组合出各种观点为什么用 Gauge因为内存是当前状态可以增加应用申请内存可以减少应用释放内存不是累积量是快照这和第一篇讲的 Gauge 本质当前状态完全一致。2.3 磁盘容量和 IO 的分离设计两类问题磁盘有两类完全不同的问题容量问题是否快满未来多久会满IO 问题吞吐够不够延迟是否变高常见错误混在一起# 不好的设计 disk_usage_percent 80.5 # 容量 disk_io_busy_percent 60.2 # IO繁忙度问题容量和IO是两个完全不同的维度不应该都用百分比无法预测未来容量只有当前值无法看IO的细分读写无法计算IOPS只有繁忙度不知道实际操作数设计拆分容量相关用 Gaugenode_filesystem_size_bytes{mountpoint/} 107374182400 node_filesystem_avail_bytes{mountpoint/} 53687091200IO 相关用 Counternode_disk_read_bytes_total{devicesda} 123456789 node_disk_written_bytes_total{devicesda} 987654321 node_disk_reads_completed_total{devicesda} 12345 node_disk_writes_completed_total{devicesda} 67890为什么分开因为本质不同维度容量IO本质当前状态累积事件类型GaugeCounter函数直接读、predict_linearrate()、increase()PromQL从事实到视图容量使用率(1 - node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100预测 4 小时后是否会满predict_linear(node_filesystem_avail_bytes[1h], 4*3600) 0IO 吞吐MB/srate(node_disk_read_bytes_total[5m]) / 1024 / 1024IOPSrate(node_disk_reads_completed_total[5m])设计智慧一旦识别出容量和流量是两类本质不同的问题设计就自然而然了容量 → Gauge当前状态流量 → Counter累积事件这是第一篇抓住本质哲学的具体应用。2.4 网络统统是 Counter问题网络带宽是否打满错误率是否异常常见错误直接给速率和错误率# 不好的设计 network_bandwidth_mbps 850.5 # 当前带宽 network_error_rate 0.02 # 错误率 2%问题带宽是瞬时值还是平均值时间窗口多久错误率的分母是什么包数字节数无法按接口分别看无法看接收和发送的细分Linux 给你的事实/proc/net/dev提供接口级别的累计统计eth0: 123456789 1000000 100 0 0 0 0 0 987654321 800000 50 0 0 0 0 0分别是接收字节数、接收包数、接收错误...发送字节数、发送包数、发送错误...node-exporter 的选择全部用 Counternode_network_receive_bytes_total{deviceeth0} 123456789 node_network_receive_packets_total{deviceeth0} 1000000 node_network_receive_errs_total{deviceeth0} 100 node_network_transmit_bytes_total{deviceeth0} 987654321 node_network_transmit_packets_total{deviceeth0} 800000 node_network_transmit_errs_total{deviceeth0} 50为什么全是 Counter因为网络统计都是累积事件收了多少字节累积发了多少包累积发生了多少错误累积没有当前状态的概念。PromQL从事实到视图流量速率Mbpsrate(node_network_receive_bytes_total{deviceeth0}[5m]) * 8 / 1000000错误率rate(node_network_receive_errs_total[5m]) / rate(node_network_receive_packets_total[5m])设计智慧网络监控是事件类指标的典型场景所有统计都是累积的关心的是速率和趋势用 Counter rate() 自然表达这再次验证了上一篇文章的结论事件类 → Counter。2.5 设计总结从问题到类型的映射通过 CPU、内存、磁盘、网络四个例子可以总结出一个通用映射问题类型原始事实指标类型典型函数累积时间CPU 各模式的累计秒数Counterrate()当前状态内存当前使用量Gauge直接读、比例计算容量资源磁盘总大小、可用大小Gauge比例、predict_linear累积事件网络累计字节数、错误数Counterrate()、increase()核心设计原则是否是否监控问题是累积的吗?Counter是当前状态吗?Gauge考虑Histogram这个决策树就是 node-exporter 的核心设计智慧也是上一篇文章中四种指标类型在实际设计中的应用。3. 架构剖析三段式的极简设计node-exporter 的架构可以抽象成三段式OS内核数据Collector采集Registry注册HTTP暴露Prometheus抓取3.1 采集层Collector每个 Collector 负责一个领域CPU Collector读/proc/stat解析成 CounterMemory Collector读/proc/meminfo解析成 GaugeFilesystem Collector读/proc/mountsstatfs()解析成 GaugeNetwork Collector读/proc/net/dev解析成 CounterCollector 的职责边界每个 Collector 只做三件事读取 OS 数据从 /proc、/sys 读取原始数据单位换算比如 kB → bytes按规范暴露命名、类型、标签、单位不做的事情不存历史数据不做复杂计算不做聚合这些都交给 Prometheus。3.2 注册层Registry所有 Collector 向同一个 Registry 注册registry : prometheus.NewRegistry() registry.MustRegister(cpuCollector) registry.MustRegister(memoryCollector) registry.MustRegister(filesystemCollector) registry.MustRegister(networkCollector)每次 Prometheus 发起 scrapeHTTP 请求到达/metricsRegistry 调用所有 Collector 的Collect()方法收集当前快照输出 Prometheus 文本格式3.3 暴露层/metricsHTTP handler 非常简单http.Handle(/metrics, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))输出格式# HELP node_cpu_seconds_total Seconds the cpus spent in each mode. # TYPE node_cpu_seconds_total counter node_cpu_seconds_total{cpu0,modeidle} 123456.78 node_cpu_seconds_total{cpu0,modeuser} 45678.90 # HELP node_memory_MemTotal_bytes Memory information field MemTotal_bytes. # TYPE node_memory_MemTotal_bytes gauge node_memory_MemTotal_bytes 167772160003.4 设计智慧这个三段式设计体现了几个关键思想1. 被动采集不主动轮询Exporter 不主动定时采集数据一切由 Prometheus 的 scrape 驱动Prometheus 发起请求 → Exporter 才采集采集频率由 Prometheus 控制scrape_intervalExporter 无状态重启后立即可用2. 职责分离Collector负责采集 Registry负责管理 HTTP Handler负责暴露每层只做自己的事边界清晰。3. 极简主义整个 node-exporter 的核心逻辑不超过几百行代码。复杂的部分是处理各种 Linux 内核接口的细节处理各种边界情况文件不存在、权限不足处理各种 Linux 发行版的差异但核心架构永远是读取 → 注册 → 暴露。4. 设计范式如何复用这套思想理解了 node-exporter就可以把这套思想复用到任何 Exporter 设计中。4.1 从问题类型选择指标类型这是最核心的决策监控对象问题推荐类型HTTP 请求数累积了多少次Counter数据库连接数当前有多少GaugeAPI 响应时间分布如何Histogram错误次数累积了多少次Counter队列长度当前有多少Gauge磁盘使用量当前用了多少Gauge通用规律资源类容量、数量、长度→ Gauge事件类请求、错误、字节→ Counter延迟类响应时间、等待时间→ HistogramHistogram vs Summary 的选择当需要监控分布如响应时间时Histogram在服务端聚合支持多实例聚合可以灵活计算任意百分位优点可以跨实例聚合Prometheus 计算百分位缺点需要预定义 bucket推荐优先使用Summary在客户端预计算百分位如 p50、p90、p99优点精确的百分位无需 bucket性能更好缺点无法跨实例聚合无法在 Prometheus 中重新计算使用场景单实例且性能极端敏感原则除非单机性能极端敏感否则优先用 Histogram。这就是上一篇文章中讲的四种类型在 Exporter 设计中的具体应用。4.2 暴露原始事实不是加工观点这是 node-exporter 最重要的设计原则。反模式直接暴露观点# 不好的设计 app_cpu_usage_percent 85.5 app_memory_usage_percent 72.3 app_disk_usage_percent 60.1问题使用率是怎么算的时间窗口能不能按不同维度聚合不能能不能看历史趋势只能看当前值正确暴露事实# 好的设计 app_cpu_seconds_total{modeuser} 12345 app_cpu_seconds_total{modesystem} 6789 app_memory_bytes{typeused} 8589934592 app_memory_bytes{typetotal} 16777216000然后用 PromQL 组合# 用户想要的观点 rate(app_cpu_seconds_total{modeuser}[5m]) * 100 app_memory_bytes{typeused} / app_memory_bytes{typetotal} * 1004.3 命名、单位、标签的约定命名规范namespace_subsystem_metric_unit示例node_cpu_seconds_total # namespacenode, metriccpu, unitseconds mysql_queries_total # namespacemysql, metricqueries http_request_duration_seconds # namespacehttp, metricrequest_duration, unitseconds单位规范时间_seconds不要用 ms大小_bytes不要用 KB、MB比例_ratio0-1或无单位百分比直接用数字标签规范只用低基数标签# 好低基数 http_requests_total{methodGET, status200} # 坏高基数 http_requests_total{user_id123456}为什么高基数标签会导致时间序列爆炸100 万用户 × 5 个方法 × 10 个状态码 5000 万条时间序列这会直接把 Prometheus 打爆。4.4 模块化 Collector按领域拆分node-exporter 的模块化设计可以复用// 业务 Exporter 示例 registry : prometheus.NewRegistry() // 按业务领域拆分 Collector registry.MustRegister(NewOrderCollector()) // 订单相关指标 registry.MustRegister(NewPaymentCollector()) // 支付相关指标 registry.MustRegister(NewUserCollector()) // 用户相关指标 registry.MustRegister(NewCacheCollector()) // 缓存相关指标 http.Handle(/metrics, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))每个 Collector 只负责一个领域type OrderCollector struct{} func (c *OrderCollector) Describe(ch chan- *prometheus.Desc) { // 注册指标描述 } func (c *OrderCollector) Collect(ch chan- prometheus.Metric) { // 采集当前数据 totalOrders : getOrderCount() ch - prometheus.MustNewConstMetric( orderCountDesc, prometheus.CounterValue, float64(totalOrders), ) }好处职责清晰易于维护可以单独开关某个 Collector可以并行采集如果需要方便测试4.5 极简 Exporter 骨架一个最简单的 Exporter 骨架package main import ( net/http github.com/prometheus/client_golang/prometheus github.com/prometheus/client_golang/prometheus/promhttp ) // 定义指标 var requestsTotal prometheus.NewCounterVec( prometheus.CounterOpts{ Name: app_requests_total, Help: Total number of requests, }, []string{method, status}, ) func init() { // 注册指标 prometheus.MustRegister(requestsTotal) } func main() { // 业务代码 http.HandleFunc(/api, func(w http.ResponseWriter, r *http.Request) { requestsTotal.WithLabelValues(r.Method, 200).Inc() w.Write([]byte(OK)) }) // 暴露指标 http.Handle(/metrics, promhttp.Handler()) http.ListenAndServe(:8080, nil) }这就是 Exporter 的核心结构。node-exporter 做的就是把这个模式复制到几十个 Collector 上然后打磨细节。5. 性能哲学为什么足够轻量node-exporter 能在生产环境大规模使用是因为它足够轻。5.1 不存历史不做重计算Exporter 的职责采集当前数据简单的单位换算暴露指标不做的事情不存历史数据这是 Prometheus 的事不做复杂统计这是 PromQL 的事不做聚合这也是 PromQL 的事结果内存占用小只需要存当前快照CPU 开销低只是读文件和格式化输出可以部署到任何机器包括低配置机器5.2 被动采集成本可控Exporter 是被动的不主动轮询不定时采集一切由 Prometheus 的 scrape 驱动好处采集频率可控调整 scrape_interval可以按需关闭某些 Collector降低开销不会因为监控系统反过来压垮被监控对象5.3 充分利用 OS 接口node-exporter 的数据来源几乎全是/proc、/sys这是 Linux 内核主动暴露的接口读取成本极低内核内存映射不需要额外的内核模块或代理对系统侵入性极低这就是站在巨人肩膀上的设计智慧。5.4 设计智慧node-exporter 的性能哲学可以总结为把重活交给专业的人做Exporter 轻量采集Prometheus 存储和查询Grafana 可视化Exporter我只负责采集轻量简单Prometheus我负责存储和复杂查询专业高效Grafana我负责可视化美观友好这种分工让每个组件都做自己擅长的事整个系统才能高效运转。这也是简单优先哲学的体现不要让 Exporter 变成一个小型数据库。6. 下一步从设计到落地通过 node-exporter我们学到的核心精髓可以浓缩为三条三条黄金法则1. 暴露事实不加工观点给累计时间不给使用率给原始容量不给百分比给累计字节不给速率让 PromQL 负责视图组合2. 按本质选类型累积类事件、时间、字节→ Counter状态类容量、数量、长度→ Gauge分布类延迟、大小→ Histogram识别出问题本质类型选择自然而然3. 命名规范 低基数标签Counter 用_total后缀单位用_seconds、_bytes标签只用低基数维度避免 user_id遵循namespace_metric_unit格式掌握这三条你就能设计出可复用的监控指标。详细设计原则下面是更详细的设计原则和范式核心原则暴露事实不是观点不给加工后的指标使用率给原始数据累计时间、当前容量让 PromQL 负责视图组合从问题到类型的映射累积类 → Counter状态类 → Gauge分布类 → Histogram一旦识别出问题本质类型选择自然而然极简三段式架构Collector 采集Registry 注册HTTP 暴露职责分离边界清晰轻量性能哲学不存历史不做重计算被动采集充分利用 OS 接口设计范式可复用命名、单位、标签规范模块化 Collector 结构低基数标签原则事实 vs 观点的界限这些原则不只适用于服务器监控同样适用于应用监控HTTP、数据库、缓存业务监控订单、支付、用户中间件监控Kafka、Redis、MySQL理解了 node-exporter 的设计智慧你就掌握了 Exporter 设计的精髓。但是有了指标和 Exporter还不够。监控的最终目的是什么及时发现问题并告警。下一篇我们将学习如何设计生产级的告警系统以及如何把这套监控哲学延伸到告警规则的设计中。下一篇《告警的艺术从夜莺引擎学习告警系统设计》附录Exporter 设计清单设计前的问题清单我要监控什么系统核心问题是什么UtilizationSaturationErrors原始数据从哪来/procAPI数据库哪些是事实哪些是观点指标类型选择清单是累积的吗→ Counter是当前状态吗→ Gauge需要看分布吗→ Histogram单实例且需要极致性能吗→ Summary命名规范清单格式namespace_metric_unitCounter 用_total后缀单位用_seconds、_bytes标签只用低基数维度架构设计清单按领域拆分 Collector向 Registry 注册在/metrics暴露不存历史不做重计算被动采集由 Prometheus 驱动性能优化清单避免高基数标签避免频繁的网络调用避免复杂计算提供开关可以关闭某些 Collector设置合理的采集超时

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询