コンテンツにスキップ

OpenTelemetry to Guance


前の2つの記事では、OpenTelemetry を使用して可観測性を実現する方法を紹介しました。

OpenTelemetry to Jaeger 、Grafana、ELK

従来の可観測性アーキテクチャでは、異なるタイプのデータを異なるプラットフォームに保存します。例えば、ログは ELK、トレースは Jaeger などの APM、メトリクスは Prometheus に保存し、Grafana で可視化します。

OpenTelemetry to Grafana

Grafana Tempo と Loki を組み合わせることで、ログとトレースの関係を直感的に確認できます。しかし、Loki の特性上、大規模な本番システムに対して優れたログ処理・分析機能を提供できるわけではありません。また、ログとトレースの関連付けは可観測性の一部に過ぎず、ログとトレースのクエリだけではほとんどの問題を解決できません。特にマイクロサービスやクラウドネイティブアーキテクチャの時代においては、問題の多様性から、多角的な分析が必要です。例えば、ユーザーアクセスが遅い場合、プログラムの問題だけでなく、現在のシステムのネットワーク、CPU、その他の複合的な要因が原因である可能性もあります。ユーザーがマルチクラウド環境を利用している場合、Grafana はビジネス発展を効果的にサポートできません。

Guance

は、メトリクスデータ、ログデータ、APM、RUM、インフラストラクチャ、コンテナ、ミドルウェア、ネットワークパフォーマンスなど、多種多様なデータを統一的に収集・管理するプラットフォームです。Guanceを使用することで、ログとトレースの関連付けだけでなく、アプリケーションを全体的に可観測化できます。Guanceの詳細については、製品の優位性をご覧ください。

DataKit は Guance のフロントエンドゲートウェイです。データを Guance に送信するには、DataKit を適切に設定する必要があります。DataKit を利用する主な利点は以下の通りです。

  1. ホスト環境では、各ホストに DataKit が1つ存在し、データはまずローカルの DataKit に送信され、DataKit がキャッシュ、前処理、そしてアップロードを行います。これにより、ネットワークジッターを回避しつつ、エッジ処理能力を付加し、バックエンドのデータ処理負荷を軽減します。

  2. Kubernetes 環境では、各ノードに DataKit の DaemonSet が存在し、Kubernetes の local traffic メカニズムを利用して、各ノードの Pod からのデータをまずローカルノードの DataKit に送信します。これにより、ネットワークジッターを回避するだけでなく、APM データに Pod とノードのラベルが追加され、分散環境での位置特定が容易になります。

DataKit は OTLP プロトコルを受信するため、コレクターを経由せずに直接 DataKit にデータを送信することも、コレクターのエクスポーターを OTLP(DataKit)に設定することもできます。

アーキテクチャ

image.png

アーキテクチャとしては、依然として2つの選択肢があります。

DataKit がログを収集する方法は複数あります。今回のベストプラクティスでは、主にソケット方式でログを収集します。Spring Boot アプリケーションは、主に Logback-logstash を介して DataKit にログをプッシュします。

方案一 (オプション1)

  1. アプリケーションサーバーとクライアントは、メトリクスとトレースデータを OTLP エクスポーターを介して OpenTelemetry Collector にプッシュします。
  2. フロントエンドアプリケーションは、フロントエンドのトレース情報を OpenTelemetry Collector にプッシュし、アプリケーションサービスの API にアクセスします。
  3. OpenTelemetry Collector はデータを収集・変換した後、メトリクスとトレースデータを OTLP エクスポーターを介して DataKit に転送します。
  4. 同時に、アプリケーションサーバーとクライアントはログを DataKit にプッシュします。

Exporter

OpenTelemetry Collector は1つのエクスポーター(otlpExporter)を設定します。

  otlp:
    endpoint: "http://192.168.91.11:4319"
    tls:
      insecure: true
    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 を変更してください。クラウドサーバーの場合は、関連するポートを開放し、アクセス失敗を防いでください。

方案二 (オプション2)

オプション2は、基本的に DataKit で OpenTelemetry Collector を置き換えるものです。

バックエンドサーバーとクライアントの起動時に、otel.exporter.otlp.endpoint アドレスを変更し、直接 DataKit を指定します。

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

フロントエンドの変更

const otelExporter = new OTLPTraceExporter({
  // オプション - url のデフォルト値は http://localhost:55681/v1/traces
  url: 'http://192.168.91.11:9529/otel/v1/trace',
  headers: {},
});

DataKit のインストールと設定

DataKit のインストール

OpenTelemetry コレクターの有効化

OpenTelemetry コレクター導入手順 を参照してください。

以下のパラメータを調整

[inputs.opentelemetry.grpc] パラメータ説明

  • trace_enable:true # gRPC トレースを有効にする
  • metric_enable: true # gRPC メトリクスを有効にする
  • addr: 0.0.0.0:4319 # ポートを開放

DataKit を再起動

datakit service restart

ログ収集の有効化

  1. Logging プラグインを有効にし、サンプルファイルをコピーします。
cd /usr/local/datakit/conf.d/log
cp logging.conf.sample logging-socket-4560.conf
  1. logging-socket-4560.conf を変更します。
[[inputs.logging]]
  ## required
#  logfiles = [
#    "/var/log/syslog",
#    "/var/log/message",
#  ]

  sockets = [
   "tcp://0.0.0.0:4560"
  ]

  ## glob filteer
  ignore = [""]

  ## your logging source, if it's empty, use 'default'
  source = "otel"

  ## add service tag, if it's empty, use $source.
  service = "otel"

  ## grok pipeline script path
  pipeline = "log_socket.p"

  ## optional status:
  ##   "emerg","alert","critical","error","warning","info","debug","OK"
  ignore_status = []

  ## optional encodings:
  ##    "utf-8", "utf-16le", "utf-16le", "gbk", "gb18030" or ""
  character_encoding = ""

  ## The pattern should be a regexp. Note the use of '''this regexp'''
  ## regexp link: https://golang.org/pkg/regexp/syntax/#hdr-Syntax
  # multiline_match = '''^\S'''

  ## removes ANSI escape codes from text strings
  remove_ansi_escape_codes = false

  [inputs.logging.tags]
  # some_tag = "some_value"
  # more_tag = "some_other_value"

パラメータ説明

  • sockets #ソケット情報を設定
  • pipeline: log_socket.p # ログ解析

  • Pipeline を設定します。

cd pipeline vim log_socket.p

json(_,message,"message")
json(_,class,"class")
json(_,serverName,"service")
json(_,thread,"thread")
json(_,severity,"status")
json(_,traceId,"trace_id")
json(_,spanId,"span_id")
json(_,`@timestamp`,"time")
set_tag(service)
default_time(time)
  1. DataKit を再起動します。
datakit --restart

メトリクス収集の有効化

  1. Prometheus プラグインを有効にし、サンプルファイルをコピーします。
cd /usr/local/datakit/conf.d/prom
cp prom.conf.sample prom-otel.conf
  1. prom-otel.conf を変更します。
[[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]
        overwrite_exist_tags = false
        [inputs.prom.tags_rename.mapping]
            # tag1 = "new-name-1"
            # tag2 = "new-name-2"
            # tag3 = "new-name-3"

  ## 自定义Tags
  [inputs.prom.tags]
  # some_tag = "some_value"
  # more_tag = "some_other_value"

パラメータ説明

  • urls #OpenTelemetry Collector のメトリクス URL
  • metric_types = []: すべてのメトリクスを収集

  • DataKit を再起動します。

datakit --restart

OpenTelemetry Collector のインストール

ソースコードアドレス

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

otel-collector-config.yaml の設定

コレクターの設定を追加します。1つのレシーバー(OTLP)と4つのエクスポーター(Prometheus、Zipkin、Jaeger、Elasticsearch)を設定します。

receivers:
  otlp:
    protocols:
      grpc:
      http:
        cors:
          allowed_origins:
            - http://*
            - https://*
exporters:
  otlp:
    endpoint: "http://192.168.91.11:4319"
    tls:
      insecure: true
    compression: none # gzip を有効にしない

processors:
  batch:

extensions:
  health_check:
  pprof:
    endpoint: :1888
  zpages:
    endpoint: :55679

service:
  extensions: [pprof, zpages, health_check]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]

docker-compose を使用して OpenTelemetry Collector をインストールします。

version: '3.3'

services:
    # Collector
    otel-collector:
        image: otel/opentelemetry-collector-contrib:0.51.0
        command: ["--config=/etc/otel-collector-config.yaml"]
        volumes:
            - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
        ports:
            - "1888:1888"   # pprof extension
            - "8888:8888"   # Prometheus metrics exposed by the collector
            - "8889:8889"   # Prometheus exporter metrics
            - "13133:13133" # health_check extension
            - "4350:4317"        # OTLP gRPC receiver
            - "55670:55679" # zpages extension
            - "4318:4318"

コンテナの起動

docker-compose up -d

起動状況の確認

docker-compose ps

image.png

Spring Boot アプリケーションの接続(APM & Log)

主に Logstash-logback が提供するソケット方式を使用してログを Logstash にアップロードします。コードに一部変更を加える必要があります。

1. プロジェクトの Maven に Logstash-logback を追加

<dependency>
  <groupId>net.logstash.logback</groupId>
  <artifactId>logstash-logback-encoder</artifactId>
  <version>7.0.1</version>
</dependency>

2. logback-logstash.xml を新規作成

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    <!-- 一部のパラメータは properties ファイルから取得 -->
    <springProperty scope="context" name="logName" source="spring.application.name" defaultValue="localhost.log"/>
    <!-- 設定後ログレベルを動的に変更可能 -->
    <jmxConfigurator />
    <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" />

    <springProperty scope="context" name="logstashHost" source="logstash.host" defaultValue="logstash"/>
    <springProperty scope="context" name="logstashPort" source="logstash.port" defaultValue="4560"/>
    <!-- %m出力情報,%pログレベル,%tスレッド名,%d日付,%cクラスの完全修飾名,,,, -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/${logName}/${logName}.log</file>    <!-- 使用方法 -->
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/${logName}/${logName}-%d{yyyy-MM-dd}.log.%i</fileNamePattern>
            <maxFileSize>64MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- LOGSTASH出力設定 -->
    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <!-- logStash サービスのアドレスを設定 -->
        <destination>${logstashHost}:${logstashPort}</destination>
        <!-- ログ出力エンコーディング -->
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <timeZone>UTC+8</timeZone>
                </timestamp>
                <pattern>
                    <pattern>
                        {
                        "podName":"${podName:-}",
                        "namespace":"${k8sNamespace:-}",
                        "severity": "%level",
                        "serverName": "${logName:-}",
                        "traceId": "%X{trace_id:-}",
                        "spanId": "%X{span_id:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "message": "%message\n%exception"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
        <!-- キープアライブ -->
        <keepAliveDuration>5 minutes</keepAliveDuration>
    </appender>

    <!-- error レベルのみ出力 -->
    <logger name="net.sf.json" level="ERROR" />
    <logger name="org.springframework" level="ERROR" />

    <root level="info">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="LOGSTASH"/>
    </root>
</configuration>

3. application-logstash.yml を新規作成

logstash:
  host: localhost
  port: 4560
logging:
  config: classpath:logback-logstash.xml

4. 再パッケージング

mvn clean package -DskipTests

5. サービスの起動

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

JS アプリケーションの接続(RUM)

ソースコードアドレス

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

OTLPTraceExporter の設定

const otelExporter = new OTLPTraceExporter({
  // オプション - url のデフォルト値は http://localhost:55681/v1/traces
  url: 'http://192.168.91.11:4318/v1/traces',
  headers: {},
});

ここでの url は OpenTelemetry Collector の OTLP 受信アドレス(HTTP プロトコル)です。

server_name の設定

const providerWithZone = new WebTracerProvider({
      resource: new Resource({
        [SemanticResourceAttributes.SERVICE_NAME]: 'front-app',
      }),
    }
);

インストール

npm install

起動

npm start

デフォルトポートは 8090 です。

APM と RUM の関連付け

APM と RUM は主にヘッダーパラメータを介して関連付けられます。一貫性を保つために、統一されたプロパゲーター(Propagator)を設定する必要があります。ここでは RUM が B3 を使用しているため、APM も B3 を設定する必要があります。APM の起動パラメータに -Dotel.propagators=b3 を追加するだけです。

APM と Log の関連付け

APM と Log は、主にログに traceId と spanId を埋め込むことで関連付けられます。ログの取り込み方法によって、埋め込み方は異なります。

Guance

フロントエンドの URL にアクセスすることで、トレース情報が生成されます。

image.png

ログエクスプローラー

guance-log.gif

トレース(アプリケーションパフォーマンスモニタリング(APM))

guance-trace.gif

トレースから対応するログを表示

guance-trace-log.gif

アプリケーションメトリクス

アプリケーションメトリクスは、measurement が otel-service のメジャーメントに格納されます。

guance-metrics.gif

OpenTelemetry Collector メトリクス

guance-otelcol-metrics.gif

OpenTelemetry Collector 統合ビュー

OpenTelemetry Collector 統合ビュー

フィードバック

このページは役に立ちましたか?