OpenTelemetry to Guance¶
前の2つの記事では、OpenTelemetry を使用して可観測性を実現する方法を紹介しました。
OpenTelemetry to Jaeger 、Grafana、ELK
従来の可観測性アーキテクチャでは、異なるタイプのデータを異なるプラットフォームに保存します。例えば、ログは ELK、トレースは Jaeger などの APM、メトリクスは Prometheus に保存し、Grafana で可視化します。
Grafana Tempo と Loki を組み合わせることで、ログとトレースの関係を直感的に確認できます。しかし、Loki の特性上、大規模な本番システムに対して優れたログ処理・分析機能を提供できるわけではありません。また、ログとトレースの関連付けは可観測性の一部に過ぎず、ログとトレースのクエリだけではほとんどの問題を解決できません。特にマイクロサービスやクラウドネイティブアーキテクチャの時代においては、問題の多様性から、多角的な分析が必要です。例えば、ユーザーアクセスが遅い場合、プログラムの問題だけでなく、現在のシステムのネットワーク、CPU、その他の複合的な要因が原因である可能性もあります。ユーザーがマルチクラウド環境を利用している場合、Grafana はビジネス発展を効果的にサポートできません。
は、メトリクスデータ、ログデータ、APM、RUM、インフラストラクチャ、コンテナ、ミドルウェア、ネットワークパフォーマンスなど、多種多様なデータを統一的に収集・管理するプラットフォームです。Guanceを使用することで、ログとトレースの関連付けだけでなく、アプリケーションを全体的に可観測化できます。Guanceの詳細については、製品の優位性をご覧ください。
DataKit は Guance のフロントエンドゲートウェイです。データを Guance に送信するには、DataKit を適切に設定する必要があります。DataKit を利用する主な利点は以下の通りです。
-
ホスト環境では、各ホストに DataKit が1つ存在し、データはまずローカルの DataKit に送信され、DataKit がキャッシュ、前処理、そしてアップロードを行います。これにより、ネットワークジッターを回避しつつ、エッジ処理能力を付加し、バックエンドのデータ処理負荷を軽減します。
-
Kubernetes 環境では、各ノードに DataKit の DaemonSet が存在し、Kubernetes の local traffic メカニズムを利用して、各ノードの Pod からのデータをまずローカルノードの DataKit に送信します。これにより、ネットワークジッターを回避するだけでなく、APM データに Pod とノードのラベルが追加され、分散環境での位置特定が容易になります。
DataKit は OTLP プロトコルを受信するため、コレクターを経由せずに直接 DataKit にデータを送信することも、コレクターのエクスポーターを OTLP(DataKit)に設定することもできます。
アーキテクチャ¶
アーキテクチャとしては、依然として2つの選択肢があります。
DataKit がログを収集する方法は複数あります。今回のベストプラクティスでは、主にソケット方式でログを収集します。Spring Boot アプリケーションは、主に Logback-logstash を介して DataKit にログをプッシュします。
方案一 (オプション1)¶
- アプリケーションサーバーとクライアントは、メトリクスとトレースデータを OTLP エクスポーターを介して OpenTelemetry Collector にプッシュします。
- フロントエンドアプリケーションは、フロントエンドのトレース情報を OpenTelemetry Collector にプッシュし、アプリケーションサービスの API にアクセスします。
- OpenTelemetry Collector はデータを収集・変換した後、メトリクスとトレースデータを OTLP エクスポーターを介して DataKit に転送します。
- 同時に、アプリケーションサーバーとクライアントはログを DataKit にプッシュします。
Exporter¶
OpenTelemetry Collector は1つのエクスポーター(otlpExporter)を設定します。
パラメータ説明
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 のインストール¶
-
DataKit のバージョンは 1.2.12 以上である必要があります
OpenTelemetry コレクターの有効化¶
OpenTelemetry コレクター導入手順 を参照してください。
以下のパラメータを調整¶
[inputs.opentelemetry.grpc] パラメータ説明
- trace_enable:true # gRPC トレースを有効にする
- metric_enable: true # gRPC メトリクスを有効にする
- addr: 0.0.0.0:4319 # ポートを開放
DataKit を再起動¶
ログ収集の有効化¶
- Logging プラグインを有効にし、サンプルファイルをコピーします。
- 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)
- DataKit を再起動します。
メトリクス収集の有効化¶
- Prometheus プラグインを有効にし、サンプルファイルをコピーします。
- 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 を再起動します。
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"
コンテナの起動¶
起動状況の確認¶
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 を新規作成¶
4. 再パッケージング¶
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',
}),
}
);
インストール¶
起動¶
デフォルトポートは 8090 です。
APM と RUM の関連付け¶
APM と RUM は主にヘッダーパラメータを介して関連付けられます。一貫性を保つために、統一されたプロパゲーター(Propagator)を設定する必要があります。ここでは RUM が B3 を使用しているため、APM も B3 を設定する必要があります。APM の起動パラメータに -Dotel.propagators=b3 を追加するだけです。
APM と Log の関連付け¶
APM と Log は、主にログに traceId と spanId を埋め込むことで関連付けられます。ログの取り込み方法によって、埋め込み方は異なります。
Guance¶
フロントエンドの URL にアクセスすることで、トレース情報が生成されます。
ログエクスプローラー¶
トレース(アプリケーションパフォーマンスモニタリング(APM))¶
トレースから対応するログを表示¶
アプリケーションメトリクス¶
アプリケーションメトリクスは、measurement が otel-service のメジャーメントに格納されます。








