作者简介:

赵君|南京爱福路轿车科技有限公司基础设施部云原生工程师,曩昔一向从事 java 相关的架构和研制作业。现在主要担任公司的云原生落地相关作业,担任 F6 基础设施和事务中心运用全面上云和云原生化改造。

徐航|南京爱福路轿车科技有限公司基础设施部云原生工程师,曩昔一向担任数据库高可用以及相关运维和调优作业。现在主要担任研制效能 DevOps 的落地以及事务体系云原生可观测性的改造。

随着散布式架构逐步成为了架构设计的干流,可观测性(Observability)一词也日益被人频繁地提起。

2017 年的散布式追寻峰会(2017 Distributed Tracing Summit)完毕后,Peter Bourgon 撰写了总结文章《Metrics, Tracing, and Logging》体系地阐述了这三者的界说、特征,以及它们之间的关系与差异。文中将可观测性问题映射到了如何处理方针(metrics)、追寻(tracing)、日志(logging)三类数据上。

其后,Cindy Sridharan 在其著作《Distributed Systems Observability》中,进一步讲到方针、追寻、日志是可观测性的三大支柱(three pillars)。

企业实践|分布式系统可观测性之应用业务指标监控

到了 2018 年, CNCF Landscape 首先呈现了 Observability 的概念,将可观测性( Observability )从控制论( Cybernetics )中引进到 IT 范畴。在控制论中,可观测性是指体系能够由其外部输出,来揣度其内部状况的程度,体系的可调查性越强,咱们对体系的可控制性就越强。

可观测性能够处理什么问题?Google SRE Book 第十二章给出了简洁明快的答案:快速排障

There are many ways to simplify and speed troubleshooting. Perhaps the most fundamental are:

  • Building observability—with both white-box metrics and structured logs—into each component from the ground up
  • Designing systems with well-understood and observable interfaces between components. Google SRE Book, Chapter 12

而在云原生时代,散布式体系越来越复杂,散布式体系的改变是十分频繁的,每次改变都或许导致新类型的毛病。运用上线之后,假如短少有用的监控,很或许导致遇到问题咱们自己都不知道,需求依靠用户反馈才知道运用出了问题。

本文主要讲述如何树立运用事务方针Metrics监控和如何完成精准告警。Metrics 能够翻译为衡量或者方针,指的是对于一些要害信息以可聚合的、数值的方式做守时统计,并绘制出各种趋势图表。透过它,咱们能够调查体系的状况与趋势。

技术栈挑选

咱们的运用都是 Spring Boot 运用,并且运用 Spring Boot Actuator 完成运用的健康检查。从 Spring Boot 2.0 开端,Actuator 将底层改为 Micrometer,提供了更强、更灵活的监测才能。Micrometer 支撑对接各种监控体系,包含 Prometheus。

所以咱们挑选 Micrometer 搜集事务方针,Prometheus 进行方针的存储和查询,经过 Grafana 进行展示,经过阿里云的告警中心完成精准告警。

方针搜集

对于整个研制部门来说,应该聚集在能够实时体现公司事务状况的最中心的方针上。例如 Amazon 和 eBay 会盯梢销售量, Google 和 Facebook 会盯梢广告曝光次数等与收入直接相关的实时方针。

Prometheus 默许选用一种名为 OpenMetrics 的方针协议。OpenMetrics 是一种根据文本的格局。下面是一个根据 OpenMetrics 格局的方针表明格局样例。

# HELP http_requests_total The total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="post",code="200"} 1027
http_requests_total{method="post",code="400"}    3
# Escaping in label values:
msdos_file_access_time_seconds{path="C:\DIR\FILE.TXT",error="Cannot find file:\n"FILE.TXT""} 1.458255915e9
# Minimalistic line:
metric_without_timestamp_and_labels 12.47
# A weird metric from before the epoch:
something_weird{problem="division by zero"} +Inf -3982045
# A histogram, which has a pretty complex representation in the text format:
# HELP http_request_duration_seconds A histogram of the request duration.
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.05"} 24054
http_request_duration_seconds_bucket{le="0.1"} 33444
http_request_duration_seconds_bucket{le="0.2"} 100392
http_request_duration_seconds_bucket{le="0.5"} 129389
http_request_duration_seconds_bucket{le="1"} 133988
http_request_duration_seconds_bucket{le="+Inf"} 144320
http_request_duration_seconds_sum 53423
http_request_duration_seconds_count 144320
# Finally a summary, which has a complex representation, too:
# HELP rpc_duration_seconds A summary of the RPC duration in seconds.
# TYPE rpc_duration_seconds summary
rpc_duration_seconds{quantile="0.01"} 3102
rpc_duration_seconds{quantile="0.05"} 3272
rpc_duration_seconds{quantile="0.5"} 4773
rpc_duration_seconds{quantile="0.9"} 9001
rpc_duration_seconds{quantile="0.99"} 76656
rpc_duration_seconds_sum 1.7560473e+07
rpc_duration_seconds_count 2693

方针的数据由方针名(metric_name),一组 key/value 标签(label_name=label_value),数字类型的方针值(value),时刻戳组成。

metric_name [
  "{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
] value [ timestamp ]

Meter

Micrometer 提供了多种衡量类库(Meter),Meter 是指一组用于搜集运用中的衡量数据的接口。Micrometer 中,Meter 的详细类型包含:Timer, Counter, Gauge, DistributionSummary, LongTaskTimer, FunctionCounter, FunctionTimer,and TimeGauge

  • Counter 用来描述一个单调递加的变量,如某个办法的调用次数,缓存命中/访问总次数等。支撑装备 recordFailuresOnly,即只记载办法调用失利的次数。Counter 的方针数据,默许有四个 label:class, method, exception, result。
  • Timer 会一起记载 totalcount, sumtime, maxtime 三种数据,有一个默许的 label: exception。
  • Gauge 用来描述在一个范围内继续动摇的变量。Gauge 一般用于变化的测量值,比方行列中的音讯数量,线程池任务行列数等。
  • DistributionSummary 用于统计数据散布。

运用接入流程

为了方便微服务运用接入,咱们封装了 micrometer-spring-boot-starter。micrometer-spring-boot-starter 的详细完成如下。

  1. 引进 Spring Boot Actuator 依靠
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-prometheus</artifactId>
  <version>${micrometer.version}</version>
</dependency>
  1. 进行初始装备

Actuator 默许敞开了一些方针的搜集,比方 system, jvm, http,能够经过装备关闭它们。其实仅仅是咱们需求关闭,因为咱们现已接了 jmx exporter 了。

management.metrics.enable.jvm=false
management.metrics.enable.process=false
management.metrics.enable.system=false

假如不希望 Web 运用的 Actuator 办理端口和运用端口重合的话,能够运用 management.server.port 设置独立的端口。这是好的实践,能够看到黑客针对 actuator 的攻击,但是换了端口号,不露出公网问题会少很多。

1management.server.port=xxxx
  1. 装备 spring bean

TimedAspect 的 Tags.empty() 是成心的,避免产生太长的 class 称号对 prometheus 形成压力。

@PropertySource(value = {"classpath:/micrometer.properties"})
@Configuration
public class MetricsConfig {
    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry, (pjp) -> Tags.empty());
    }
    @Bean
    public CountedAspect countedAspect(MeterRegistry registry) {
        return new CountedAspect(registry);
    }
    @Bean
    public PrometheusMetricScrapeEndpoint prometheusMetricScrapeEndpoint(CollectorRegistry collectorRegistry) {
        return new PrometheusMetricScrapeEndpoint(collectorRegistry);
    }
    @Bean
    public PrometheusMetricScrapeMvcEndpoint prometheusMvcEndpoint(PrometheusMetricScrapeEndpoint delegate) {
        return new PrometheusMetricScrapeMvcEndpoint(delegate);
    }
}

运用接入时,引进 micrometer-spring-boot-starter 依靠

<dependency>
  <groupId>xxx</groupId>
  <artifactId>micrometer-spring-boot-starter</artifactId>
</dependency>

现在,就能够经过访问 http://ip:port/actuator/prometheus,来检查 Micrometer 记载的数据。

自界说事务方针

Micrometer 内置了 Counted 和 Timed 两个 annotation。能够经过在对应的办法上加上 @Timed 和 @Counted 注解,来搜集办法的调用次数,时刻和是否产生反常等信息。

@Timed

假如想要记载打印办法的调用次数和时刻,需求给 print 办法加上 @Timed 注解,并给方针界说一个称号。

@Timed(value = "biz.print", percentiles = {0.95, 0.99}, description = "metrics of print")
public String print(PrintData printData) {
}

在 print 办法上加上 @Timed 注解之后,Micrometer 会记载 print 办法的调用次数(count),办法调用最大耗时(max),办法调用总耗时(sum)三个方针。percentiles = {0.95, 0.99} 表明核算 p95,p99 的请求时刻。记载的方针数据如下。

biz_print_seconds_count{exception="none"} 4.0
biz_print_seconds_sum{exception="none"} 7.783213927
biz_print_seconds_max{exception="none"} 6.14639717
biz_print_seconds{exception="NullPointerException"} 0.318767104
biz_print_seconds{exception="none",quantile="0.95",} 0.58720256
biz_print_seconds{exception="none",quantile="0.99",} 6.157238272

@Timed 注解支撑装备一些特点:

  • value:必填,方针名
  • extraTags:给方针界说标签,支撑多个,格局 {“key”, “value”, “key”, “value”}
  • percentiles:小于等于 1 的数,核算时刻的百分比散布,比方 p95,p99
  • histogram:记载办法耗时的 histogram 直方图类型方针

@Timed 会记载办法抛出的反常。不同的反常会被记载为独立的数据。代码逻辑是先 catch 办法抛出的反常,记载下反常称号,然后再抛出办法本身的反常:

try {
    return pjp.proceed();
} catch (Exception ex) {
    exceptionClass = ex.getClass().getSimpleName();
    throw ex;
} finally {
    try {
        sample.stop(Timer.builder(metricName)
                    .description(timed.description().isEmpty() ? null : timed.description())
                    .tags(timed.extraTags())
                    .tags(EXCEPTION_TAG, exceptionClass)
                    .tags(tagsBasedOnJoinPoint.apply(pjp))
                    .publishPercentileHistogram(timed.histogram())
                    .publishPercentiles(timed.percentiles().length == 0 ? null : timed.percentiles())
                    .register(registry));
    } catch (Exception e) {
        // ignoring on purpose
    }
}

@Counted

假如不关怀办法履行的时刻,只关怀办法调用的次数,甚至只关怀办法调用产生反常的次数,运用 @Counted 注解是更好的挑选。recordFailuresOnly = true 表明只记载反常的办法调用次数。

@Timed(value = "biz.print", recordFailuresOnly = true, description = "metrics of print")
public String print(PrintData printData) {
}

记载的方针数据如下。

biz_print_failure_total{class="com.xxx.print.service.impl.PrintServiceImpl",exception="NullPointerException",method="print",result="failure",} 4.0

counter 是一个递加的数值,每次办法调用后,会自增 1。

private void record(ProceedingJoinPoint pjp, Counted counted, String exception, String result) {
    counter(pjp, counted)
            .tag(EXCEPTION_TAG, exception)
            .tag(RESULT_TAG, result)
            .register(meterRegistry)
            .increment();
}
private Counter.Builder counter(ProceedingJoinPoint pjp, Counted counted) {
    Counter.Builder builder = Counter.builder(counted.value()).tags(tagsBasedOnJoinPoint.apply(pjp));
    String description = counted.description();
    if (!description.isEmpty()) {
        builder.description(description);
    }
    return builder;
}

Gauge

Gauge 用来描述在一个范围内继续动摇的变量。Gauge 一般用于变化的测量值,例如雪花算法的 workId,打印的模板 id,线程池任务行列数等。

  1. 注入 PrometheusMeterRegistry
  2. 构造 Gauge。给方针命名并赋值。
@Autowired
private PrometheusMeterRegistry meterRegistry;
public void buildGauge(Long workId) {
    Gauge.builder("biz.alphard.snowFlakeIdGenerator.workId", workId, Long::longValue)
            .description("alphard snowFlakeIdGenerator workId")
            .tag("workId", workId.toString())
            .register(meterRegistry).measure();
}

记载的方针数据如下。

biz_alphard_snowFlakeIdGenerator_workId{workId="2"} 2

装备 SLA 方针

假如想要记载方针时刻数据的 sla 散布,Micrometer 提供了对应的装备:

management.metrics.distribution.sla[biz.print]=300ms,400ms,500ms,1s,10s

记载的方针数据如下。

biz_print_seconds_bucket{exception="none",le="0.3",} 1.0
biz_print_seconds_bucket{exception="none",le="0.4",} 3.0
biz_print_seconds_bucket{exception="none",le="0.5",} 10.0
biz_print_seconds_bucket{exception="none",le="0.6",} 11.0
biz_print_seconds_bucket{exception="none",le="1.0",} 11.0
biz_print_seconds_bucket{exception="none",le="10.0",} 12.0
biz_print_seconds_bucket{exception="none",le="+Inf",} 12.0

存储查询

咱们运用 Prometheus 进行方针数据的存储和查询。Prometheus 选用拉取式搜集(Pull-Based Metrics Collection)。Pull 便是 Prometheus 自动从方针体系中拉取方针,相对地,Push 便是由方针体系自动推送方针。Prometheus 官方解说挑选 Pull 的原因。

Pulling over HTTP offers a number of advantages:

  • You can run your monitoring on your laptop when developing changes.
  • You can more easily tell if a target is down.
  • You can manually go to a target and inspect its health with a web browser.

Overall, we believe that pulling is slightly better than pushing, but it should not be considered a major point when considering a monitoring system.

Prometheus 也支撑 Push 的搜集方式,便是 Pushgateway。

For cases where you must push, we offer the Pushgateway.

为了让 Prometheus 搜集运用的方针数据,咱们需求做两件事:

  1. 运用经过 service 露出出 actuator 端口,并增加 label: monitor/metrics
apiVersion: v1
kind: Service
metadata:
  name: print-svc
  labels:
    monitor/metrics: ""
spec:
  ports:
  - name: custom-metrics
    port: xxxx
    targetPort: xxxx
    protocol: TCP
  type: ClusterIP
  selector:
    app: print-test
  1. 增加 ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: metrics
  labels:
    app: metric-monitor
spec:
  namespaceSelector:
    any: true
  endpoints:
  - interval: 15s
    port: custom-metrics
    path: "/manage/prometheusMetric"
  selector:
    matchLabels:
      monitor/metrics: ""

Prometheus 会守时访问 service 的 endpoints (http://podip:port/manage/prometheusMetric),拉取运用的 metrics,保存到自己的时序数据库。

Prometheus 存储的数据是文本格局,虽然 Prometheus 也有 Graph,但是不够炫酷,而且功能有限。还需求有一些可视化东西去展示数据,经过规范易用的可视化大盘去获悉当时体系的运转状况。比较常见的处理计划便是 Grafana。Prometheus 内置了强大的时序数据库,并提供了 PromQL 的数据查询语言,能对时序数据进行丰富的查询、聚合以及逻辑运算。经过在 Grafana 装备 Prometheus 数据源和 PromQL,让 Grafana 去查询 Prometheus 的方针数据,以图表的方式展示出来。

  1. grafana 装备 Prometheus 数据源

企业实践|分布式系统可观测性之应用业务指标监控

  1. 增加看板,装备数据源,query 语句,图表样式

企业实践|分布式系统可观测性之应用业务指标监控

  1. 能够在一个 dasborad 增加多个看板,构成监控大盘。

企业实践|分布式系统可观测性之应用业务指标监控

企业实践|分布式系统可观测性之应用业务指标监控

精准告警

任何体系都不是完美的,当呈现反常和毛病时,能在第一时刻发现问题且快速定位问题原因就尤为重要。但要想做到以上这两点,只有数据搜集是不够的,需求依靠完善的监控和告警体系,迅速反响并发出告警。

咱们最初的计划是,根据 Prometheus operator 的 PrometheusRule 创立告警规矩, Prometheus servers 把告警发送给 Alertmanager,Alertmanager 担任把告警发到钉钉群机器人。但是这样运转一段时刻之后,咱们发现这种方式存在一些问题。SRE 团队和研制团队担任人收到的告警太多,一切的告警都发到一个群里,翻开群音讯,满屏的告警标题,告警等级,告警值。其中有需求运维处理的体系告警,有需求研制处理的运用告警,信息太多,很难快速挑选出高优先级的告警,很难快速转派告警到对应的处理人。所以咱们希望运用告警能够精准发送到运用归属的研制团队。

经过一段时刻的调研,咱们最终挑选阿里云的《ARMS 告警运维中心》来担任告警的办理。ARMS 告警运维中心支撑接入 Prometheus 数据源,支撑增加钉钉群机器人作为联系人。

  1. 搜集研制团队的钉钉群机器人的 webhook 地址,创立机器人作为联系人。

企业实践|分布式系统可观测性之应用业务指标监控

  1. 给每个研制团队别离装备告诉战略,告诉战略挑选告警信息里的 team 字段,并绑定对应的钉钉群机器人联系人。\

企业实践|分布式系统可观测性之应用业务指标监控

企业实践|分布式系统可观测性之应用业务指标监控

经过这个方式,完成了运用的告警直接发送到对应的研制团队,节省了信息挑选和二次转派的时刻,提高了告警处理效率。

效果如下:

企业实践|分布式系统可观测性之应用业务指标监控

ARMS 告警运维中心支撑接入 grafana,zabbix,arms 等多种数据源,具有告警分配和认领,告警汇总去重,经过升级告诉方式对长时刻没有处理的告警进行多次提示,或升级告诉到领导,确保告警及时处理。