6

微服务系列之服务监控 Prometheus与Grafana - CL静淡

 1 year ago
source link: https://www.cnblogs.com/saltlight-wangchao/p/16993346.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

微服务系列之服务监控 Prometheus与Grafana

1.为什么需要监控服务
  监控服务的所属服务器硬件(如cpu,内存,磁盘I/O等)指标、服务本身的(如gc频率、线程池大小、锁争用情况、请求、响应、自定义业务指标),对于以前的小型单体服务来说,确实没什么必要,但对于中大型项目,尤其那些群集部署显得尤为重要、尤其是现在的微服务架构,服务众多,而且很多服务都是群集部署,我们更是需要实时知道每一个服务所属的实例(pod,服务器)的运行、请求异常、自定义业务监控指标等情况。

image

2.监控方案
  监控方案因公司而异,没有固定的套路,不一定复杂就好用,越复杂学习、维护、支出成本就越高,适合自己团队就好,抛砖引玉我们之前使用过的方案:

  1. 自己的服务器windows群集,.net core微服务架构,使用Telegraf在每台机服务器上进行采集cpu,内存,磁盘IO等等硬件指标数据和sqlserver、redis、rabbitmq等指标信息,使用Prometheus来采集.net core服务的请求相关的数据,无论是硬件指标还是软件指标,统一发送到时序数据库InfulxDB种,使用开源的报表程序Grafana来进行实时报表监控,通过Grafana的Alert条件触发webhook来将报警的数据信息组织好格式后,发送到我们自己写的一个服务接收,然后通过我们研发的通知订阅中心来发送给订阅者邮件、短信、企业微信、桌面等告警提示;

  2. 腾讯云Linux服务器,.net core微服务架构,基于k8s编排管理的docker集群,既然是云,很多硬件指标都是自带的监控,使用Prometheus监控集群下的所有服务的异常请求,Rabbitmq,Redis等等中间件指标信息,这次是基于Prometheus自带的时许数据库,使用其altermanger组件进行告警,全部Prometheus配置和自动发现新pod能力,都由牛逼的运维团队完成。

3.Prometheus
  本文中,我们主要来说说目前非常流行的监控中间件Prometheus,基于docker的搭建、配置、基本指标采集、自定义业务指标采集。

  Prometheus是一个开源的现代化的、支持云原生的系统监控与告警系统,2012年由前谷歌员工开发并做为社区开源项目开发,2015年正式发布,2016年加入云原生计算基金会CNCF,热度仅次于K8S。git地址

  已经很多知名的三方厂商已经基于Prometheus做成了导入器node exporter,比如linux系统,MYSQL数据库,Redis等等非常多的中间件,有了这些导入器,我们直接可以使用,非常丰富的监控指标人家已经为我们做好了。等下,我们演示以下使用linux的导入器来监控linux系统指标。

image

  上面是官方给出的生态组件图,Prometheus整个生态圈组成主要包括prometheus server,Exporter,pushgateway,alertmanager,grafana,Web ui界面,Prometheus server由三个部分组成,Retrieval,Storage,PromQL;

  1. Retrieval 负责在活跃的target主机上抓取监控指标数据;
  2. Storage 存储主要是把采集到的数据存储到磁盘中;
  3. PromQL是Prometheus提供的查询语言模块;

  工作流程,Prometheus server定期从活跃的目标主机上通过http pull的方式拉取指标数据,目标主机可以通过prometheus的配置文件进行配置或者通过服务发现方式来发现目标主机;也可以通过pushGateway组件,从目标主机推送该组件,Prometheus server再定时从该组件拉取指标数据;

4.Prometheus基于docker的搭建和监控

  1. 拉取linux系统的导入器node exporter
docker pull prom/node-exporter
  1. 启动linux监控容器
docker run --name=node-exporter -p 9100:9100 -itd prom/node-exporter
  1. 创建prometheus配置文件
mkdir /opt/prometheus
cd /opt/prometheus/
vim prometheus.yml
  1. 修改配置文件
global:
  scrape_interval:   60s
  evaluation_interval: 60s

scrape_configs:
  - job_name: linuxNode1 --目标任务名称
    static_configs:
      - targets: ['ip:9100'] --可以是多台
        labels:
          instance: linux1    --指标维度

5.启动prometheus容器

docker run -d -p 9090:9090 -v /opt/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml --name prometheus prom/prometheus:latest

image
启动成功后,prometheus的webui也可以使用了,浏览器输入http://IP:9090/targets

image

5.Grafana
  官方说明,grafana是用于可视化大型测量数据的开源程序,他提供了强大和优雅的方式去创建、共享、浏览数据。用过之后确实强大,可配置性灵活,现成的主流中间件基于prometheus的报表模板非常丰富。

  1. 拉取grafana镜像
docker pull grafana/grafana
  1. 启动grafana容器
docker run -d -p 3000:3000 --name=jmeterGrafana grafana/grafana

浏览器访问:http://ip:3000(账号密码都是:admin)

image

选择数据源

image
image
image
image
image
image

至此,我们的linux服务器的报表监控好了,接下来,我们来监控.net core服务的基本指标、请求、自定义业务数据。

6..net core集成prometheus

  1. nuget引入prometheus-net.AspNetCore
  2. startup类,管道运行时配置中
 //收集一些服务基本信息,比如线程数,内存使用,句柄,3个GC得回收次数统计
            app.UseMetricServer();
  1. 启动程序访问http://localhost:5000/metrics

    image
  2. 做为API服务,我们当然要收集http请求,请求状态,耗时,次数这些

 //收集一些服务基本信息,比如线程数,内存使用,句柄,3个GC得回收次数统计
 app.UseMetricServer();
 //收集http请求和计数监控,比如总请求数,每次请求得耗时
 app.UseHttpMetrics();
 app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
image
  1. 我们还想收集一些.net core服务运行时一些更深的指标来定位一些困难问题,不用我们写,我们只需要再引入一个别人封装好的包 prometheus-net.DotNetRuntime,就可以得到以下指标:
    • 垃圾回收的收集频率和时间;
    • 服务占用堆大小;
    • 对象堆分配的字节;
    • JIT编译和JIT CPU消耗率;
    • 线程池大小,调度延迟以及增长/缩小的原因;
    • 锁争用情况;
      代码如下:
//Program下main方法,为了减少性能开销,可以设置如下
DotNetRuntimeStatsBuilder
               .Customize()
               //每5个事件个采集一个
               .WithContentionStats(sampleRate: SampleEvery.FiveEvents)
               //每10事件采集一个
               .WithJitStats(sampleRate: SampleEvery.TenEvents)
               .WithThreadPoolStats()
               .WithGcStats()
               .StartCollecting();
  1. 再次运行会看到更多的指标了

    image
  2. 这么多指标,维度的监控在prometheus原生UI上通过过滤可以看到响应的数据和报表,比如我们要看服务运行总内存,通过指标key:dotnet_total_memory_bytes,去webui上搜索:

image
  1. 原生UI多少差点意思,我们去prometheus报表模板市场去看看,有没有基于prometheus的.net core运行时的模板,市场传送门
image
image

我们找到一个模板,复制id用上文grafana导入模板的方法,导入进去,看看:

image

嗯,看上去好像不咋地,很多指标显示不出来,实际项目里,我们知道了指标数据,都是基于grafana自己配置要看的报表,下文我们继续来看看,怎么在.net core中基于promethues自定义一些指标收集,并且利用grafana来配置响应的报表。

7.自定义指标收集与配置报表
  通过上文的metrics指标数据,指标数据其实就是key value结构,只不过key中有label增加维度,并且每一个键值对都有响应的指标类型,prometheus的metrics有以下4种主要类型:

  • Counter:计数器,单调递增,应用启动之后只会增加不会减少
  • Gauge:仪表,和 Counter 类似,可增可减
  • Histogram:直方图,柱形图,Histogram其实是一组数据,主要用于统计数据分布的情况 —— 统计落在某些值的范围内的计数,同时也提供了所有值的总和和个数
  • Summary:汇总,摘要,summary 类似于 histogram,也是一组数据。不同的是,它统计的不是区间的个数而是统计分位数。
  1. 接下来,我们在代码里,使用Counter和Gauge这两种类型,来演示自定义指标收集;
  2. 先定义一个收集中心HUB
public class PrometheusMetricsHub
    {
        /// <summary>
        /// 监控计数器维度统计容器
        /// </summary>
        public Dictionary<string, Counter> Counters { get; set; } = new Dictionary<string, Counter>();
        /// <summary>
        /// 监控仪表盘维度统计容器
        /// </summary>
        public Dictionary<string, Gauge> Gauges { get; set; } = new Dictionary<string, Gauge>();


        /// <summary>
        /// 创建计数器容器
        /// </summary>
        /// <param name="key"></param>
        /// <param name="desc"></param>
        /// <returns></returns>
        public void CreateCounter(string key, string desc)
        {
            if (!this.Counters.ContainsKey(key))
                this.Counters.Add(key, Metrics.CreateCounter(key, desc));
        }

        /// <summary>
        /// 创建仪表盘容器
        /// </summary>
        /// <param name="key"></param>
        /// <param name="desc"></param>
        /// <returns></returns>
        public void CreateGauge(string key, string desc)
        {
            if (!this.Gauges.ContainsKey(key))
                this.Gauges.Add(key, Metrics.CreateGauge(key, desc));
        }

        /// <summary>
        /// 根据维度类型获取监控实例
        /// </summary>
        /// <typeparam name="TContainer"></typeparam>
        /// <param name="prometheusEnum"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public TContainer GetContainer<TContainer>(PrometheusEnum prometheusEnum, string key) where TContainer : class
        {
            if (prometheusEnum == PrometheusEnum.Counter && this.Counters.ContainsKey(key))
                return this.Counters.GetValueOrDefault(key) as TContainer;
            if (prometheusEnum == PrometheusEnum.Gauge && this.Gauges.ContainsKey(key))
                return this.Gauges.GetValueOrDefault(key) as TContainer;
            return null;

        }
  1. 创建Prometheus注册容器的扩展方法
public static class PrometheusMetricsHubExtenisons
    {
        public static IServiceCollection AddPrometheusMetricsHub(this IServiceCollection services,Action<PrometheusMetricsHub> buildAction)
        {
            var hub = new PrometheusMetricsHub();
            buildAction(hub);
            services.AddSingleton<PrometheusMetricsHub>(hub);
            return services;
        }
    }
  1. 启动类,使用扩展方法,进行对PrometheusHub配置并注入容器
 //注册监控
            services.AddPrometheusMetricsHub(hub =>
            {
                hub.CreateCounter("demoCounter", "测试计数器");
                hub.CreateGauge("demoGauge", "测试仪表盘");
            });
  1. 创建一个中间件,每一次请求,都使用Prometheus指标容器进行累加统计
 public class PrometheusRequestMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly PrometheusMetricsHub _prometheusMetricsHub;
        public PrometheusRequestMiddleware(RequestDelegate next, PrometheusMetricsHub prometheusMetricsHub)
        {
            _next = next;
            _prometheusMetricsHub = prometheusMetricsHub;
        }
        public async Task InvokeAsync(HttpContext context)
        {

            try
            {
                //增加测试数据
                var counter = _prometheusMetricsHub.GetContainer<Counter>(Models.PrometheusEnum.Counter, "demoCounter");
                var gauge = _prometheusMetricsHub.GetContainer<Gauge>(Models.PrometheusEnum.Gauge, "demoGauge");

                counter.Inc();
                gauge.Inc();
                await _next(context);
                
            }
            finally
            {
                
            }
        }
    }
	 app.UseMiddleware<PrometheusRequestMiddleware>();
	
  1. 启动,并查看metrics指标
    image
  2. 配置Grafana报表
    image
image
image

由上图所见,我们自定义的指标监控完成。

8.关于Prometheus高可用
  Prometheus本身不支持群集部署,也就是说本身没法动态水平扩容,其实其本身的性能非常高,很少有撑不住情况,我们担心的是硬盘容量问题,假设真的是超级大量的指标数据,怎么办呢,下面有几个方案,也欢迎留言讨论:

  1. 取舍方案,放弃一些不重要的指标采集,降低指标数据持久化时间,调整采集速率等等一些逻辑优化;
  2. 服务维度拆分方案,也很容易理解,之前一台Prometheus去采集监控100个服务,现在用3台,划分服务,每台采集30多个服务;
  3. 分片方案,这种比较极端,某一个服务,pod规模上千甚至更多,这时候,你单台Promethues想完整采集这些pod的服务,对宽带、硬盘、CPU要求极高极高,性能估计也不咋地,这时候就要分片处理,从一台变多台,分别采集,但是这里有2个问题:
    1.这么多pod,肯定是要利用注册中心,在promethues配置里通过服务发现来动态配置要监控的服务,consul可以,并且promethues服务发现配置也支持consul,然后在服务注册的时候通过区分一个维度注册,好让对应promethues知道哪些节点是当前这台需要采集的。
    2.分片监控一个服务,基于Prometheus本身存储,肯定是没法集中聚合观看了,这里又涉及到统一使用其他数据源,比如InfluxDB,Redis等等,Promethes也支持配置只采集不存储,通过 remote write 方式写入远程存储库。

9.一些问题

  1. Prometheus不是持久化数据的,但是对于大部分指标监控,我们也不需要持久化,这里有一些产品上的业务指标,要想好是否需要持久化,别某台服务重启,之前数据没了。
  2. 报表问题,专业的BI产品经理才行,维度得把控好才行。
  3. .自定义指标收集时候,最好不要侵入性代码,能解耦则解耦。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK