写点什么

可观测性架构实践

用户头像
郑印
关注
发布于: 刚刚

随着技术栈向微服务的发展,可观测性架构是绕不开的一个问题,笔者在最近几年微服务的实践中也接触和实践了各种可观测的方案,最近随着公司核心数据上云,也开始同步考虑将一些微服务的基础设施使用云上的解决方案,可观测就是其中的一部分,本文描述了基于阿里云 SLS 实现可观测性架构的过程。

可观测性的三板斧


  • 日志 logger


通过日志可以详细展示系统的运行状态,但前提是合理的日志记录。通常在业务的关键节点都应该有日志,关键数据的变更也应该有日志,同时可以通过 debug 记录一些过程日志,需要注意的是通过 debug 记录过程日志如果在流量比较大的场景下,还需要考虑当开启 debug 日志后对服务的影响,可以考虑在 debug 的基础上在做一些流控的配置参数,比如按照百分之一取模,或者特殊的调试标识等。


  • 度量  metrics


通过度量可以观察系统的状态和趋势,如同股票的走势图,可以看出涨跌。那么在我们的应用中,可以观察某些指标的变化情况,比如服务的流量,响应时间,错误率等,当某一刻突然增长或以一个趋势不断增长或下跌时都应该引起我们的警觉。


metrics 通常只能做到接口级别的问题定位,不能做到代码级的问题定位。


  • 调用链 tracing


前文所提到的都是在于对单个应用本身的观测,然后在微服务的运行环境中,服务于服务,服务于组件之间的依赖是必然的,通常客户端的一个请求会途径多个微服务与组件,如何对这个过程进行观测,就是调用链要做的事情。


tracing 问题定位能力取决于追踪的细致程度,开源的组件提供的 agent 通常能够追踪到跨组件的调用,比如 java 应用程序对 redis 的调用,更细致的追踪需要自行埋点。tracing 链路包含大量的信息因此也非常占用资源,全量采样通常系统无法承载,基于概率的采样可能会漏过错误请求,需要设计一些机制来保证有效的进行采样

秒杀系统案例观测实践

下面我们模拟一个秒杀系统的案例,来看下各种观测方式的实践,一个用户的请求涉及到了多个环节,本文讨论的观测域聚焦在网关与应用服务环节,如下图所示。


我们的微服务采样 Spring Clound 技术栈



在我们设计的秒杀场景下,一个完整的秒杀流程,可以分为了 4 个步骤,如下图。此文章不在于说明秒杀系统,因此我们选取第四个步骤来看下各环节的观测如何做。



将第四个环节展开分别是:


用户携带 token,发起下单请求,当请求没有到网关之前,不是我们此文讨论的观测域范畴


  1. 网关接收到请求,根据路由转发到秒杀服务; /miaosha-service/miaosha/{itemId}/order

  2. 秒杀服务验证 token,通过验证后调用下单接口;/order-service/order

  3. 订单服务处理下单请求,并调用库存服务扣除库存;/inventory-service/inventory/{itemId}/deduct

观测项采集

  1. 网关、应用、接口的 QPS,相应时间,错误率等

  2. 各应用的 JVM 运行情况,资源占用情况

  3. 秒杀下单的链路分析

  4. 各应用日志

网关、应用、接口的 QPS,相应时间,错误率指标采集


网关通过接收 Client 请求通过路由规则将它们转发到对应的服务,如果你用过 Spring cloud gateway 很容易就能完成此功能配置,但 Spring cloud gateway 实际并不知道有哪些接口,它只知道有哪些接口的路由规则。


在我们的架构中,通过服务暴露出它本身的接口定义信息,在由网关去拉去,并将信息存储在网关中,当客户端请求网关时,网关通过客户端请求的信息,就能够识别出具体命中的接口了。



最终将接口指标日志存储到本地文件,数据格式类似于下面的样例,再由阿里云 logtail 进行采集存储。


使用 | 杠分割日志可以减少日志的空间占用


gateway|miaosha-service||com.izhengyin.miaosha.controller.MiaoShaController#createOrder(long, Map)|200|177|127.0.0.1|nil
复制代码


Metrics 定义


@Data@ToStringpublic class Metrics {    /**     * 网关名     */    private String gateway;    /**     * 服务名     */    private String service;    /**     * 自定义接口名(通过 spring @XXXMapping(name = xxx) 指定 )     */    private String name;    /**     * 处理接口请求的 Java method     */    private String jMethod;    /**     * spring 接口描述的mapping     */    private String mapping;    /**     * 请求的 http method     */    private String httpMethod;    /**     * 请求 uri     */    private String uri;    /**     * 响应状态码     */    private Integer httpCode;    /**     * 请求响应时间     */    private Long resTimeMs;    /**     * UA     */    private String userAgent;    /**     * 请求来源IP     */    private String clientIp;    /**     * 当发送错误时的类别     */    private String errorType;    /**     * 当发送错误时的描述     */    private String errorMessage;    /**     * 链路追踪 traceId     */    private String traceId;    /**     * 链路追踪 spanId     */    private String spanId;    /**     * 是否开启了采样     */    private Boolean sampled;    /**     * 请求时间     */    private Date timestamp;
/** * 使用 | 杠分割日志可以减少日志的空间占用 * @return */ public String toDelimiterString(){ return gateway + "|" + service + "|" + name + "|" + mapping + "|" + jMethod + "|" + uri + "|" + httpCode + "|" + resTimeMs + "|" + userAgent + "|" + clientIp + "|" + errorType + "|" + errorMessage + "|" + traceId + "|" + spanId + "|" + Optional.ofNullable(sampled).filter(Boolean::booleanValue).map(b -> 1).orElse(0); }}
复制代码


  • logtail 配置



具体事项细节,请参考;扩展SpringCloudGateway-让网关帮助你管理接口


除了上面这些基础的指标外,网关还可以将一些日常运行指标集成到 metrics 中,比如限流情况,当网关对接口进行限流时,有多少流量受限,有多少流量未受限,这也是一个可观测的项。


读者感兴趣可以参考;扩展SpringCloudGateway-让网关支持更丰富的限流功能

各应用的 JVM 指标采集

通过 Spring boot 集成增加下面的依赖,spring boot 暴露的端点 /actuator/prometheus 会输出,jvm 运行信息,以及一些线程池与数据库连接信息等。


        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>        <dependency>            <groupId>io.micrometer</groupId>            <artifactId>micrometer-registry-prometheus</artifactId>        </dependency>
复制代码


/actuator/prometheus 输出:


...system_cpu_usage 0.17242482630835854# HELP tomcat_sessions_expired_sessions_total# TYPE tomcat_sessions_expired_sessions_total countertomcat_sessions_expired_sessions_total 0.0# HELP jvm_buffer_count_buffers An estimate of the number of buffers in the pool# TYPE jvm_buffer_count_buffers gaugejvm_buffer_count_buffers{id="direct",} 10.0jvm_buffer_count_buffers{id="mapped",} 0.0...
复制代码


将这些信息进行通过采集 Logtail 进行采集到 metrics store 进行存储。


  • logtail 配置


链路信息采集

我们基于 OpenTelemetry 做链路信息的采集,OpenTelemetry 可以通过 SDK 手动埋点或者 java Agent 的方式自动采集数据,并提供 OpenTelemetry Collector 用于数据的导出,而存储与分析可以是开源组件或者云厂商的来实现。

应用节点的采样

对于应用节点(比如 miaosha-service 所在的节点)而言,比较简单,我们只需要在启动是指定 OpenTelemetry 的 agent 即可。如下所示


-javaagent:/opt/app/opentelemetry-javaagent-all.jar -Dotel.traces.sampler=parentbased_traceidratio -Dotel.traces.sampler.arg=0
复制代码


除了 Agent 这里有两个参数用于控制链路的传播性,单个链路通常是没有分析价值的,因此这里的配置的传播性是以父级为是否采样为基准,同时关闭了自身的采样。需要注意的是这样配置应用服务的后台定时任务,比如通过 @Scheduled 配置的任务是不会去追踪的。


传播性配置参考: https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#propagator

网关的采样

在网关上我们通过 SDK 的方式进行采样,并且通过网关的一些开发去动态的控制采样开关。


采样的策略


  1. 每分钟每个接口采样一次,保证至少一分钟内有可分析的数据

  2. 当接口出现错误或者相应时间边长时,调整为连续进行多次采样(考虑到采样的消耗,可能加重应用节点负担,不能全量采样)

  3. 按照接口采样的配置概率进行采样


这种方式带来的诸多好处


  1. 这种方式当接口出现错误时,虽然首次出现链路可能无法追踪到信息,但由于第二个规则的存在,在次出现错误就能够进行捕获。

  2. 同时日常每分钟的采样在可控的资源消耗情况下,可以保障至少每分钟都有可分析的数据。

  3. 最后在结合自定义的采样配置,可以方便的进行数据采样率的调节。


笔者在支付宝可观测实践的一篇分享文章中描述基于事后采样的方式,可以解决首次错误出现时对链路的捕获,但是这种方式自研成本巨大,并非每个公司的适合。


具体事项细节,请参考;扩展SpringCloudGateway-让网关支持集成链路追踪

日志的采集

日志采集比较简单,就是本地日志 + logtail 的收集方式,但需要注意的时,我们在日志中要记录下当前的 traceId 等链路追踪信息,便于我们时候进行分析。


<?xml version="1.0" encoding="utf-8"?><configuration>    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%X{trace_id:x},%X{span_id:x},%X{trace_flags:0}] [%15.15t] %-40.40logger{39} : %m%n</pattern>        </encoder>    </appender>    <root level="info">        <appender-ref ref="console"/>    </root></configuration>
复制代码


  • logtail 配置正则模式来进行识别


(\d+-\d+-\d+\s\S+)\s(\w+)\s(\d+)\s\[(\S+),(\S+),(\S+)\]\s\[(\S+)\]\s(\S+)\s+:([\S\s\n]*)
复制代码


可视化

流量总览

流量总览大屏用于观测整体经过网关的流量情况,通过阿里云日志的可视化功能,可以根据网关 metrics 配置多种流量监控图表。


下面这个 SQL 展示了通过阿里云的统计图表,根据网关的 metrics 数据源,统计 95 分位平均响应时间。


* | select  round(diff [1], 2) as today,  round((diff [3] -1.0) * 100, 2) as growth FROM ( SELECT compare(total, 86400) as diff  from (select round(approx_percentile(resTimeMs, 0.95) / 1000.0, 2) as total from gateway-metrics ) )
复制代码


服务总览

服务总览大屏反映的是经过网关的总流量,那么应用大屏反映的就是网关转发到应用的流量,因此应用大屏的数据源也是通过网关的 metrics 进行配,不同的时,需要增加特定的应用筛选项。


下面这个 SQL 展示了通过阿里云的统计图表,查询出服务的平均响应时间变化曲线。


语句前的 service 就是服务的检索项


service : * | select time_series(__time__, '1m', '%H:%i:%s' ,'0') as Time ,avg(resTimeMs) as "平均请求时间" from gateway-metrics group by Time  order by Time limit 240
复制代码


JVM 应用监控

JVM 监控的可视化,我们通过阿里云的时序查询,来实现。在上面的文章中我们将 prometheus 采集的数据通过了 logtail 收集到了 metrics store ,因此此处我们通过阿里云的时序查询方式来查询数据并形成生成图表。


下面展示了查询 jvm 堆区的变化趋势


* | select promql_query_range('sum(jvm_memory_used_bytes{instance="${{instance|}}", area="heap"})*100/sum(jvm_memory_max_bytes{instance="${{instance|}}", area="heap"})') from metrics
复制代码


整个 JVM 的可视化比较大,这里放一个 mini 版,完整的请点击下面的链接查看。



查看完整的JVM大屏

tracing 大屏

阿里云的 Trace 服务自带大屏,从链路查找、依赖分析、到链路追踪都有,因此不用我们自行配置,不过变扭的是入口没有和日志的 project 放在一起,每次使用时都需要单独的去控制台找入口,这点不是很方便。


下图展示了秒杀下单时的一个链路



下图展示了秒杀下单时的一个链路错误的情况


互联互通

我们来看一下线上运行服务出现告警时,的一个问题定位流程。



从这个流程看出,我们通常需要通过多个环节的协助才能完成最终的问题定位,那么各个环节之间互联互通的能力可以极大的减少问题定位时的时间。


笔者在 IDC 使用开源解决方案是,互联互通的问题尤其明显。


阿里云的 SLS 可以通过添加交互行为,实现各个组件、环节直接的跳转,下面这个图演示了从日志跳转到 tracing 详情的交互行为。


基于开源的观测方式

在未使用云解决方案之前,我们在 IDC 中使用了多种开源组件来解决可观测性的问题,下面增对模拟的秒杀场景,做一个简单的对应介绍。

观测的媒介

观测数据来源

基于阿里云日志服务 SLS 的观测方式

虽然叫 SLS 日志服务,但是这个服务能做的不仅仅是可观测三板斧中单日志本身的一项,下面我们还是根据模拟的秒杀场景来看下基于阿里云 SLS 如何做观测。

观测的媒介

观测数据来源

两种不同观测方式的差别

  • 可视化上的差别


  1. 在日志分析与可视化上 kibana 的功能无疑是强大的,kibana 提供了方便的 UI 交互功能以及面板进行数据分析与可视化,同时基于 Lucene 查询语法来进行数据集的检索。从使用上来看阿里云 SLS 可以完成 kibana 上数据分析与可视化的功能, 并且提供了更丰富的图表,但在数据检索上使用了开发者更为熟悉的 SQL,降低了使用的学习成本。

  2. 在指标的分析上,阿里云通过 MetricStore + PromQL 的方式承接 Prometheus + grafana 的组合,在仪表盘的配置上感觉不如 grafana,主要问题是卡顿现象比较明显,但好处是可以组合日志的数据与 metrics 一同展示。

  3. 阿里云 trace 的分析面板要比 zipkin 的好用很多,别捏的是,通过日志 project 没有导航进入到 trace,需要通过控制台进入。


  • 组件的互联互通是阿里云的一大优势


在使用开源方案时,假如在 kibana 上发现了一条错误日志,想看下它的链路,你需要复制 traceId 然后粘贴 zipkin 面板,在进行查看。在 grafana 上分析一个接口的异常流量时,想看见它的详细请求,需要复制到 kibana,然后找到对应索引检索查看。开源组件不能互联互通的还有很多,这些问题在阿里云上都不在是问题,通过配置交互行为都能解决。


  • 绕不开的费用问题


自建费用方面主要在服务器、机柜以及人工运维成本,阿里云按照存储量进行收费。日志的存储量事先可以估算出来的,在自建的环境下,不仅要估算存储,还要估算计算资源,基于此可以考虑一个费用优化的方案,如果计算场景较多,日志多上云,如果只是一些不经常查询的日志,那就算了吧。

用户头像

郑印

关注

还未添加个人签名 2017.10.17 加入

语雀 , https://www.yuque.com/izhengyin

评论

发布
暂无评论
可观测性架构实践