前面两篇我们分别演示并介绍了如何基于 OpenTelemetry 进行可观测。
OpenTelemetry to Jeager 、Grafana、ELK
作为经典的可观测架构,将不同类型的数据存储在不同的平台,比如log 存放在ELK,trace 存放在 Jaeger这类的APM,metric 保存在 prometheus 并通过grafana进行视图展示。
OpenTelemetry to Grafana
Grafana Tempo和 Loki 结合,能够让我们直观的看到日志链路情况,但 Loki 的特性也决定了它并不能为大型生产系统提供很好的日志处理分析能力。日志链路也只是可观测的一部分,仅仅是通过日志链路查询并不能解决大部分问题,特别是在微服务云原生架构时代,问题的多样性需要我们从多方面结合分析。如用户访问卡顿,不一定是程序问题,也有可能是当前系统网络、cpu等其他综合因素导致。在用户多云情况下,Grafana 不能有效的支撑其业务发展。

观测云是一个集指标数据、日志数据、APM 、RUM、基础设施、容器、中间件、网络性能等多种数据统一收集管理平台。使用观测云可以为我们全方位观测应用,而不仅仅是日志链路之间的观测。更多观测云信息请跳转到产品优势阅读。

DataKit 是观测云的前置 gateway, 若要把数据打给观测云, 需要正确的配置 DataKit , 而且利用 DataKit 有以下优势:
1. 主机环境下, 每个主机都有一个 datakit , 数据先打给本地的 datakit , 由 datakit 缓存,预处理,然后上报, 避免了网络抖动的同时,附带了边缘处理能力, 给后台数据处理缓解压力.

  1. k8 环境下, 每个 node 都有一个 DataKit 的 daemonset, 通过利用k8s 的 local traffic机制, 让每个 node所在 pod 的数据都先发送本地 node 的 DataKit , 避免网络抖动的同时, 给 apm 数据增加了 pod 和 node 标签, 分布式环境下便于定位.

由于 DataKit 是接收 oltp 协议的, 所以可以绕过 collector 直接打给 DataKit , 也可以把 collector 的 exporter 设置为 oltp(DataKit )

架构

image.png
架构上仍然有两套方案供大家选择。

datakit 采集日志有多种方式,本次最佳实践主要是通过 socket 方式进行日志收集。springboot 应用主要是通过 Logback-logstash 将日志推送到 datakit。

方案一

1、应用 server 和 client 将 metric 、trace 数据通过 otlp-exporter push 到 otel-collector
2、front-app 为前端链路,将链路信息 push 到 otel-collector,并访问应用服务 API
3、otel-collector 对数据进行收集、转换后,将 metric 和 trace 数据通过otlp-exporter 传输到 datakit
4、同时应用 server 和 client 将 logs push 到 datakit。

Exporter

otel-collector 配置了 1 个 exporter:otlpExporter.

  1. otlp:
  2. endpoint: "http://192.168.91.11:4319"
  3. tls:
  4. insecure: true
  5. compression: none # 不开启gzip

参数说明

endpoint :”http://192.168.91.11:4319“ #当前填写的是datakit opentelemetry 采集器地址,协议为 GRPC。
tls.insecure : true # 关闭tls 安全校验
compression: none # 不开启gzip,默认开启

注意

所有的应用都部署在同一个机器上,机器 ip 为 192.168.91.11。如果应用和一些中间件单独分开部署,则注意修改对应的 IP。如果是云服务器,则注意开放相关端口,以免访问失败。

方案二

方案二其实是直接用 DataKit 换掉 otel-collector
后端 server 和 client 启动时,修改 otel.exporter.otlp.endpoint地址,直接指向 datakit 。

-Dotel.exporter.otlp.endpoint=http://192.168.91.11:4319

前端改动

  1. const otelExporter = new OTLPTraceExporter({
  2. // optional - url default value is http://localhost:55681/v1/traces
  3. url: 'http://192.168.91.11:9529/otel/v1/trace',
  4. headers: {},
  5. });

安装部署

以下安装方案一进行部署。

安装配置 DataKit

安装 DataKit

  • <安装 DataKit>
  • DataKit 接入版本 >=1.2.12

    开启 OpenTelemetry 采集器

  1. 开启 OpenTelemetry 插件,复制 Sample 文件

    1. cd /usr/local/datakit/conf.d/opentelemetry
    2. cp opentelemetry.conf.sample opentelemetry.conf
  2. 修改 opentelemetry.conf ```toml [[inputs.opentelemetry]]

    在创建’trace’,Span’,’resource’时,会加入很多标签,这些标签最终都会出现在’Span’中

    当您不希望这些标签太多造成网络上不必要的流量损失时,可选择忽略掉这些标签

    支持正则表达,注意:将所有的’.’替换成’_’

    When creating ‘trace’, ‘span’ and ‘resource’, many labels will be added, and these labels will eventually appear in all ‘spans’

    When you don’t want too many labels to cause unnecessary traffic loss on the network, you can choose to ignore these labels

    Support regular expression. Note!!!: all ‘.’ Replace with ‘_’

    ignoreattribute_keys = [“os“,”process_“]

Keep rare tracing resources list switch.

If some resources are rare enough(not presend in 1 hour), those resource will always send

to data center and do not consider samplers and filters.

keep_rare_resource = false

Ignore tracing resources map like service:[resources…].

The service name is the full service name in current application.

The resource list is regular expressions uses to block resource names.

[inputs.opentelemetry.close_resource]

service1 = [“resource1”, “resource2”, …]

service2 = [“resource1”, “resource2”, …]

Sampler config uses to set global sampling strategy.

priority uses to set tracing data propagation level, the valid values are -1, 0, 1

-1: always reject any tracing data send to datakit

0: accept tracing data and calculate with sampling_rate

1: always send to data center and do not consider sampling_rate

sampling_rate used to set global sampling rate

[inputs.opentelemetry.sampler]

priority = 0

sampling_rate = 1.0

[inputs.opentelemetry.tags]

key1 = “value1”

key2 = “value2”

[inputs.opentelemetry.expectedHeaders]

如有header配置 则请求中必须要携带 否则返回状态码500

可作为安全检测使用,必须全部小写

ex_version = xxx

ex_name = xxx

grpc

[inputs.opentelemetry.grpc]

trace for grpc

trace_enable = true

metric for grpc

metric_enable = true

grpc listen addr

addr = “127.0.0.1:4317”

addr = “0.0.0.0:4319”

http

[inputs.opentelemetry.http]

if enable=true

http path (do not edit):

trace : /otel/v1/trace

metric: /otel/v1/metric

use as : http://127.0.0.1:9529/otel/v11/trace . Method = POST

enable = true

return to client status_ok_code :200/202

http_status_ok = 200

  1. [inputs.opentelemetry.grpc] 参数说明
  2. - trace_enabletrue #开启grpc trace
  3. - metric_enable true #开启grpc metric
  4. - addr: 0.0.0.0:4319 #开启端口
  5. 3. 重启 DataKit
  6. ```shell
  7. datakit --restart

开启日志采集

  1. 开启 Logging 插件,复制 Sample 文件

    1. cd /usr/local/datakit/conf.d/log
    2. cp logging.conf.sample logging-socket-4560.conf
  2. 修改 logging-socket-4560.conf

    1. [[inputs.logging]]
    2. ## required
    3. # logfiles = [
    4. # "/var/log/syslog",
    5. # "/var/log/message",
    6. # ]
    7. sockets = [
    8. "tcp://0.0.0.0:4560"
    9. ]
    10. ## glob filteer
    11. ignore = [""]
    12. ## your logging source, if it's empty, use 'default'
    13. source = "otel"
    14. ## add service tag, if it's empty, use $source.
    15. service = "otel"
    16. ## grok pipeline script path
    17. pipeline = "log_socket.p"
    18. ## optional status:
    19. ## "emerg","alert","critical","error","warning","info","debug","OK"
    20. ignore_status = []
    21. ## optional encodings:
    22. ## "utf-8", "utf-16le", "utf-16le", "gbk", "gb18030" or ""
    23. character_encoding = ""
    24. ## The pattern should be a regexp. Note the use of '''this regexp'''
    25. ## regexp link: https://golang.org/pkg/regexp/syntax/#hdr-Syntax
    26. # multiline_match = '''^\S'''
    27. ## removes ANSI escape codes from text strings
    28. remove_ansi_escape_codes = false
    29. [inputs.logging.tags]
    30. # some_tag = "some_value"
    31. # more_tag = "some_other_value"

    参数说明

  • sockets #配置socket信息
  • pipeline: log_socket.p # 日志解析
  1. 配置pipeline

    cd pipeline vim log_socket.p

  1. json(_,message,"message")
  2. json(_,class,"class")
  3. json(_,serverName,"service")
  4. json(_,thread,"thread")
  5. json(_,severity,"status")
  6. json(_,traceId,"trace_id")
  7. json(_,spanId,"span_id")
  8. json(_,`@timestamp`,"time")
  9. set_tag(service)
  10. default_time(time)
  1. 重启 DataKit
    1. datakit --restart

开启指标采集

  1. 开启 prom 插件,复制 Sample 文件

    1. cd /usr/local/datakit/conf.d/prom
    2. cp prom.conf.sample prom-otel.conf
  2. 修改 prom-otel.conf ```toml [[inputs.prom]]

    Exporter URLs

    urls = [“http://127.0.0.1:8888/metrics“]

    忽略对 url 的请求错误

    ignore_req_err = false

    采集器别名

    source = “prom”

    采集数据输出源

    配置此项,可以将采集到的数据写到本地文件而不将数据打到中心

    之后可以直接用 datakit —prom-conf /path/to/this/conf 命令对本地保存的指标集进行调试

    如果已经将 url 配置为本地文件路径,则 —prom-conf 优先调试 output 路径的数据

    output = “/abs/path/to/file”

    采集数据大小上限,单位为字节

    将数据输出到本地文件时,可以设置采集数据大小上限

    如果采集数据的大小超过了此上限,则采集的数据将被丢弃

    采集数据大小上限默认设置为32MB

    max_file_size = 0

    指标类型过滤, 可选值为 counter, gauge, histogram, summary, untyped

    默认只采集 counter 和 gauge 类型的指标

    如果为空,则不进行过滤

    metric_types = []

    指标名称筛选:符合条件的指标将被保留下来

    支持正则,可以配置多个,即满足其中之一即可

    如果为空,则不进行筛选,所有指标均保留

    metric_name_filter = [“cpu”]

    指标集名称前缀

    配置此项,可以给指标集名称添加前缀

    measurement_prefix = “”

    指标集名称

    默认会将指标名称以下划线”_”进行切割,切割后的第一个字段作为指标集名称,剩下字段作为当前指标名称

    如果配置measurement_name, 则不进行指标名称的切割

    最终的指标集名称会添加上measurement_prefix前缀

    measurement_name = “prom”

    采集间隔 “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”

    interval = “10s”

    过滤 tags, 可配置多个tag

    匹配的tag将被忽略

    tags_ignore = [“xxxx”]

    TLS 配置

    tls_open = false

    tls_ca = “/tmp/ca.crt”

    tls_cert = “/tmp/peer.crt”

    tls_key = “/tmp/peer.key”

    自定义认证方式,目前仅支持 Bearer Token

    token 和 token_file: 仅需配置其中一项即可

    [inputs.prom.auth]

    type = “bearer_token”

    token = “xxxxxxxx”

    token_file = “/tmp/token”

    自定义指标集名称

    可以将包含前缀 prefix 的指标归为一类指标集

    自定义指标集名称配置优先 measurement_name 配置项

    [[inputs.prom.measurements]]

    prefix = “cpu_”

    name = “cpu”

    [[inputs.prom.measurements]]

    prefix = “mem_”

    name = “mem”

    重命名 prom 数据中的 tag key

    [inputs.prom.tags_rename]

    1. overwrite_exist_tags = false
    2. [inputs.prom.tags_rename.mapping]
    3. # tag1 = "new-name-1"
    4. # tag2 = "new-name-2"
    5. # tag3 = "new-name-3"

    自定义Tags

    [inputs.prom.tags]

    some_tag = “some_value”

    more_tag = “some_other_value”

  1. 参数说明
  2. - urls #otel-collector 指标url
  3. - metric_types = []: 采集所有的指标
  4. 3. 重启 DataKit
  5. ```shell
  6. datakit --restart

安装 OpenTelemetry-Collector

源码地址

https://github.com/lrwh/observable-demo/tree/main/opentelemetry-collector-to-guance

配置 otel-collector-config.yaml

新增 collecter 配置,配置1个recevier(otlp)、4个exporter(prometheus、zipkin、jaeger 和 elasticsearch。

  1. receivers:
  2. otlp:
  3. protocols:
  4. grpc:
  5. http:
  6. cors:
  7. allowed_origins:
  8. - http://*
  9. - https://*
  10. exporters:
  11. otlp:
  12. endpoint: "http://192.168.91.11:4319"
  13. tls:
  14. insecure: true
  15. compression: none # 不开启gzip
  16. processors:
  17. batch:
  18. extensions:
  19. health_check:
  20. pprof:
  21. endpoint: :1888
  22. zpages:
  23. endpoint: :55679
  24. service:
  25. extensions: [pprof, zpages, health_check]
  26. pipelines:
  27. traces:
  28. receivers: [otlp]
  29. processors: [batch]
  30. exporters: [otlp]
  31. metrics:
  32. receivers: [otlp]
  33. processors: [batch]
  34. exporters: [otlp]

通过 docker-compose 安装 otel-collector

  1. version: '3.3'
  2. services:
  3. # Collector
  4. otel-collector:
  5. image: otel/opentelemetry-collector-contrib:0.51.0
  6. command: ["--config=/etc/otel-collector-config.yaml"]
  7. volumes:
  8. - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
  9. ports:
  10. - "1888:1888" # pprof extension
  11. - "8888:8888" # Prometheus metrics exposed by the collector
  12. - "8889:8889" # Prometheus exporter metrics
  13. - "13133:13133" # health_check extension
  14. - "4350:4317" # OTLP gRPC receiver
  15. - "55670:55679" # zpages extension
  16. - "4318:4318"

启动容器

  1. docker-compose up -d

查看启动情况

  1. docker-compose ps

image.png

Springboot 应用接入(APM & Log)

主要是通过 Logstash-logback 提供的 socket 方式将日志上传到 Logstash 上,需要对代码做部分调整。

1、项目Maven引用 Logstash-logback

  1. <dependency>
  2. <groupId>net.logstash.logback</groupId>
  3. <artifactId>logstash-logback-encoder</artifactId>
  4. <version>7.0.1</version>
  5. </dependency>

2、新增 logback-logstash.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration scan="true" scanPeriod="30 seconds">
  3. <!-- 部分参数需要来源于properties文件 -->
  4. <springProperty scope="context" name="logName" source="spring.application.name" defaultValue="localhost.log"/>
  5. <!-- 配置后可以动态修改日志级别-->
  6. <jmxConfigurator />
  7. <property name="log.pattern" value="%d{HH:mm:ss} [%thread] %-5level %logger{10} [traceId=%X{trace_id} spanId=%X{span_id} userId=%X{user-id}] %msg%n" />
  8. <springProperty scope="context" name="logstashHost" source="logstash.host" defaultValue="logstash"/>
  9. <springProperty scope="context" name="logstashPort" source="logstash.port" defaultValue="4560"/>
  10. <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,,,, -->
  11. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  12. <encoder>
  13. <pattern>${log.pattern}</pattern>
  14. <charset>UTF-8</charset>
  15. </encoder>
  16. </appender>
  17. <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  18. <file>logs/${logName}/${logName}.log</file> <!-- 使用方法 -->
  19. <append>true</append>
  20. <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  21. <fileNamePattern>logs/${logName}/${logName}-%d{yyyy-MM-dd}.log.%i</fileNamePattern>
  22. <maxFileSize>64MB</maxFileSize>
  23. <maxHistory>30</maxHistory>
  24. <totalSizeCap>1GB</totalSizeCap>
  25. </rollingPolicy>
  26. <encoder>
  27. <pattern>${log.pattern}</pattern>
  28. <charset>UTF-8</charset>
  29. </encoder>
  30. </appender>
  31. <!-- LOGSTASH输出设置 -->
  32. <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
  33. <!-- 配置logStash 服务地址 -->
  34. <destination>${logstashHost}:${logstashPort}</destination>
  35. <!-- 日志输出编码 -->
  36. <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
  37. <providers>
  38. <timestamp>
  39. <timeZone>UTC+8</timeZone>
  40. </timestamp>
  41. <pattern>
  42. <pattern>
  43. {
  44. "podName":"${podName:-}",
  45. "namespace":"${k8sNamespace:-}",
  46. "severity": "%level",
  47. "serverName": "${logName:-}",
  48. "traceId": "%X{trace_id:-}",
  49. "spanId": "%X{span_id:-}",
  50. "pid": "${PID:-}",
  51. "thread": "%thread",
  52. "class": "%logger{40}",
  53. "message": "%message\n%exception"
  54. }
  55. </pattern>
  56. </pattern>
  57. </providers>
  58. </encoder>
  59. <!-- 保活 -->
  60. <keepAliveDuration>5 minutes</keepAliveDuration>
  61. </appender>
  62. <!-- 只打印error级别的内容 -->
  63. <logger name="net.sf.json" level="ERROR" />
  64. <logger name="org.springframework" level="ERROR" />
  65. <root level="info">
  66. <appender-ref ref="STDOUT"/>
  67. <appender-ref ref="LOGSTASH"/>
  68. </root>
  69. </configuration>

3、新增 application-logstash.yml

  1. logstash:
  2. host: localhost
  3. port: 4560
  4. logging:
  5. config: classpath:logback-logstash.xml

4、重新打包

  1. mvn clean package -DskipTests

5、启动服务

  1. java -javaagent:opentelemetry-javaagent-1.13.1.jar \
  2. -Dotel.traces.exporter=otlp \
  3. -Dotel.exporter.otlp.endpoint=http://localhost:4350 \
  4. -Dotel.resource.attributes=service.name=server,username=liu \
  5. -Dotel.metrics.exporter=otlp \
  6. -Dotel.propagators=b3 \
  7. -jar springboot-server.jar --client=true \
  8. --spring.profiles.active=logstash \
  9. --logstash.host=192.168.91.11 \
  10. --logstash.port=4560
  1. java -javaagent:opentelemetry-javaagent-1.13.1.jar \
  2. -Dotel.traces.exporter=otlp \
  3. -Dotel.exporter.otlp.endpoint=http://localhost:4350 \
  4. -Dotel.resource.attributes=service.name=client,username=liu \
  5. -Dotel.metrics.exporter=otlp \
  6. -Dotel.propagators=b3 \
  7. -jar springboot-client.jar \
  8. --spring.profiles.active=logstash \
  9. --logstash.host=localhost \
  10. --logstash.port=4560

JS 接入(RUM)

源码地址

https://github.com/lrwh/observable-demo/tree/main/opentelemetry-js

配置 OTLPTraceExporter

  1. const otelExporter = new OTLPTraceExporter({
  2. // optional - url default value is http://localhost:55681/v1/traces
  3. url: 'http://192.168.91.11:4318/v1/traces',
  4. headers: {},
  5. });

此处 url 为 otel-collector 的 otlp 接收地址( http 协议)。

配置server_name

  1. const providerWithZone = new WebTracerProvider({
  2. resource: new Resource({
  3. [SemanticResourceAttributes.SERVICE_NAME]: 'front-app',
  4. }),
  5. }
  6. );

安装

  1. npm install

启动

  1. npm start

默认端口_8090_

APM 与 RUM 关联

APM 与 RUM 主要通过 header 参数进行关联,为了保持一直,需要配置统一的传播器(Propagator),这里RUM 采用的是 B3,所以 APM 也需要配置B3,只需要在 APM 启动参数加上-Dotel.propagators=b3即可。

APM 与 Log 关联

APM 与 Log 主要是通过在日志埋点 traceId 和 spanId。不同的日志接入方式,埋点有差异。

观测云

通过访问前端url产生 trace 信息。
image.png

日志查看器

guance-log.gif

链路(应用性能监测

guance-trace.gif

从链路查看对应的 Log

guance-trace-log.gif

应用Metrics

guance-metrics.gif

Otelcol Metrics

guance-otelcol-metrics.gif

Otelcol 集成视图

火狐截图_2022-05-23T01-25-02.504Z.png