OpenTelemetry to Jaeger、Grafana、ELK¶
OpenTelemetry には数多くのオープンソースの組み合わせ方式があります。ここでは三つのプラットフォーム/アーキテクチャを通じて、OpenTelemetry が異なる技術アーキテクチャ上でどのようにデプロイされるかを紹介・デモンストレーションします。
OpenTelemetry¶
OTEL は OpenTelemetry の略称であり、CNCF の可観測性プロジェクトです。可観測性分野における標準化を目指し、観測データのデータモデル、収集、処理、エクスポートなどの標準化問題を解決し、ベンダーに依存しないサービスを提供することを目的としています。
OpenTelemetry は、トレース、メトリクス、ログなどの観測データ(将来的に新しい種類の観測データが登場する可能性もあります)を管理するための標準とツールのセットです。現在では業界標準となっています。
OTLP¶
OTLP(OpenTelemetry Protocol)は、OpenTelemetry がネイティブでサポートするテレメトリシグナル転送プロトコルです。OpenTelemetry プロジェクトのコンポーネントの中には、Zipkin v2 や Jaeger Thrift のプロトコル形式を実装したものもありますが、これらはサードパーティのコントリビューションライブラリとして提供されています。OTLP のみが OpenTelemetry 公式がネイティブにサポートする形式です。OTLP のデータモデル定義は ProtoBuf に基づいています。そのため、OTLP テレメトリデータを収集できるバックエンドサービスを実装する必要がある場合、その内容を理解する必要があります。対応するコードリポジトリについては、opentelemetry-proto(https://github.com/open-telemetry/opentelemetry-proto)を参照してください。
OpenTelemetry-Collector¶
OpenTelemetry Collector(以下「otel-collector」)は、テレメトリデータの受信、処理、エクスポート方法について、ベンダーに依存しない実装を提供します。これにより、Jaeger、Prometheus などのオープンソース可観測性データ形式を1つ以上のオープンソースまたは商用バックエンドに送信するために、複数のエージェント/コレクターを実行、運用、保守する必要がなくなります。さらに、コレクターによりエンドユーザーは自らのデータを制御できます。コレクターは、インストルメンテーションライブラリがテレメトリデータをエクスポートするデフォルトの場所です。
OpenTelemetry-Java¶
OpenTelemetry が Java 言語で開発した SDK で、さまざまなエクスポーターを通じてデータを異なる観測プラットフォームにプッシュすることをサポートします。
OpenTelemetry-JS¶
OpenTelemetry が提供する、フロントエンド JS ベースの分散型トレーシングです。
アーキテクチャ¶
アーキテクチャの説明¶
1、アプリケーション server と client は、metric と trace データを otlp-exporter を通じて otel-collector にプッシュします。
2、front-app はフロントエンドのトレースであり、トレース情報を otel-collector にプッシュし、アプリケーションサービスの API にアクセスします。
3、otel-collector はデータを収集・変換した後、データを Jaeger、Zipkin にプッシュします。
4、同時に Prometheus は otel-collector からデータをプルします。
ログの2種類のプッシュ方式:
方式一:OTLP によるログの報告
アプリケーション server と client は、ログを otlp-exporter を通じて otel-collector にプッシュし、さらに otel-collector の exporter から Elasticsearch にプッシュします。OpenTelemetry のログ機能はまだ安定していないため、ログは個別に処理し、otel-collector を経由しないことを推奨します。テスト中に、ログとメトリクスを同時に設定すると競合が発生することが確認されており、主に otel-collector 上で問題が発生します。公式の修正を待ちましょう。
方式二:Logback-logstash によるログの報告
アプリケーション server と client は、ログを Logback-logstash を通じて logstash にプッシュします。
otel-collector には4つのエクスポーターが設定されています。
prometheus:
endpoint: "0.0.0.0:8889"
const_labels:
label1: value1
zipkin:
endpoint: "http://otel_collector_zipkin:9411/api/v2/spans"
format: proto
jaeger:
endpoint: otel_collector_jaeger:14250
tls:
insecure: true
elasticsearch:
endpoints: "http://192.168.0.17:9200"
注意:すべてのアプリケーションは同じマシン(IP:192.168.0.17)にデプロイされています。アプリケーションと一部のミドルウェアを別々にデプロイする場合は、対応する IP を変更してください。クラウドサーバーの場合は、関連するポートを開放しないとアクセスに失敗する可能性があるため、注意してください。
インストールとデプロイ¶
OpenTelemetry-Collector のインストール¶
ソースコードアドレス¶
https://github.com/lrwh/observable-demo/tree/main/opentelemetry-collector-to-all
otel-collector-config.yaml の設定¶
コレクターの設定を追加します。1つのレシーバー(otlp)と4つのエクスポーター(prometheus、zipkin、jaeger、elasticsearch)を設定します。
receivers:
otlp:
protocols:
grpc:
http:
cors:
allowed_origins:
- http://*
- https://*
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
const_labels:
label1: value1
zipkin:
endpoint: "http://otel_collector_zipkin:9411/api/v2/spans"
format: proto
jaeger:
endpoint: otel_collector_jaeger:14250
tls:
insecure: true
elasticsearch:
endpoints: "http://192.168.0.17:9200"
processors:
batch:
extensions:
health_check:
pprof:
endpoint: :1888
zpages:
endpoint: :55679
service:
extensions: [pprof, zpages, health_check]
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [zipkin, jaeger]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [batch]
exporters: [elasticsearch]
docker-compose で otel-collector をインストールします。
version: '3.3'
services:
jaeger:
image: jaegertracing/all-in-one:1.29
container_name: otel_collector_jaeger
ports:
- 16686:16686
- 14250
- 14268
zipkin:
image: openzipkin/zipkin:latest
container_name: otel_collector_zipkin
ports:
- 9411:9411
# Collector
otel-collector:
image: otel/opentelemetry-collector:0.50.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
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP http receiver
- "55670:55679" # zpages extension
depends_on:
- jaeger
- zipkin
prometheus:
container_name: prometheus
image: prom/prometheus:latest
volumes:
- ./prometheus.yaml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
container_name: grafana
image: grafana/grafana
ports:
- "3000:3000"
Prometheus の設定¶
scrape_configs:
- job_name: 'otel-collector'
scrape_interval: 10s
static_configs:
- targets: ['otel-collector:8889']
- targets: ['otel-collector:8888']
コンテナの起動¶
起動状況の確認¶
Docker で ELK をインストール¶
Docker を使用して ELK をインストールすると、簡単かつ便利です。関連コンポーネントのバージョンは 7.16.2 です。
イメージのプル¶
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.16.2
docker pull docker.elastic.co/logstash/logstash:7.16.2
docker pull docker.elastic.co/kibana/kibana:7.16.2
設定ディレクトリ¶
# Linux 固有の設定
sysctl -w vm.max_map_count=262144
sysctl -p
# Linux 設定ここまで
mkdir -p ~/elk/elasticsearch/plugins
mkdir -p ~/elk/elasticsearch/data
mkdir -p ~/elk/logstash
chmod 777 ~/elk/elasticsearch/data
Logstash の設定¶
input {
tcp {
mode => "server"
host => "0.0.0.0"
port => 4560
codec => json_lines
}
}
output {
elasticsearch {
hosts => "es:9200"
index => "springboot-logstash-demo-%{+YYYY.MM.dd}"
}
}
tcp : tcp プロトコルです。 port: tcp ポート codec:json 行解析
Docker-compose の設定¶
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.16.2
container_name: elasticsearch
volumes:
- ~/elk/elasticsearch/plugins:/usr/share/elasticsearch/plugins #プラグインファイルのマウント
- ~/elk/elasticsearch/data:/usr/share/elasticsearch/data #データファイルのマウント
environment:
- "cluster.name=elasticsearch" #クラスター名を elasticsearch に設定
- "discovery.type=single-node" #シングルノードモードで起動
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" #使用する JVM メモリサイズの設定
- "ingest.geoip.downloader.enabled=false" # (Dynamic, Boolean) true の場合、Elasticsearch は ingest.geoip.downloader.endpoint から GeoIP2 データベースの更新を自動的にダウンロードして管理します。false の場合、Elasticsearch は更新をダウンロードせず、ダウンロード済みのデータベースをすべて削除します。デフォルトは true です。
ports:
- 9200:9200
logstash:
image: docker.elastic.co/logstash/logstash:7.16.2
container_name: logstash
volumes:
- ~/elk/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf #logstash の設定ファイルをマウント
depends_on:
- elasticsearch #kibana は elasticsearch 起動後に起動
links:
- elasticsearch:es #es というドメイン名で elasticsearch サービスにアクセス可能
ports:
- 4560:4560
kibana:
image: docker.elastic.co/kibana/kibana:7.16.2
container_name: kibana
depends_on:
- elasticsearch #kibana は elasticsearch 起動後に起動
links:
- elasticsearch:es #es というドメイン名で elasticsearch サービスにアクセス可能
environment:
- "elasticsearch.hosts=http://es:9200" #elasticsearch にアクセスするアドレスを設定
ports:
- 5601:5601
コンテナの起動¶
起動状況の確認¶
Springboot アプリケーションの接続(APM)¶
ソースコードアドレス¶
https://github.com/lrwh/observable-demo/tree/main/springboot-server
server の起動¶
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.logs.exporter=otlp \
-Dotel.propagators=b3 \
-jar springboot-server.jar --client=true
client の起動¶
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.logs.exporter=otlp \
-Dotel.propagators=b3 \
-jar springboot-client.jar
パラメータの説明¶
otel.traces.exporter:otlp # exporter タイプを otlp に設定します。デフォルトは otlp です。
otel.exporter.otlp.endpoint:otlp exporter のエンドポイント(grpc)
otel.resource.attributes:タグを設定します。
otel.metrics.exporter:otlp # metrics exporter タイプを設定します。デフォルトは none です。
otel.logs.exporter:otlp # logs exporter タイプを設定します。デフォルトは none です。
otel.propagators:トレースの伝播器を設定します。
OpenTelemetry のログ機能は未成熟で安定していないため、本番環境での使用は推奨されません。テスト中にもいくつかのバグが発生しました。学習目的としてのみご利用ください。
Springboot アプリケーションの接続(ログ)¶
方式一:OTLP によるログの報告¶
アプリケーション server と client は、ログを otlp-exporter を通じて otel-collector にプッシュし、さらに otel-collector の exporter から Elasticsearch にプッシュします。
起動パラメータの変更¶
アプリケーション起動時にパラメータ -Dotel.logs.exporter=otlp を追加する必要があります。
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.logs.exporter=otlp \
-Dotel.propagators=b3 \
-jar springboot-server.jar --client=true
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.logs.exporter=otlp \
-Dotel.propagators=b3 \
-jar springboot-client.jar
方式二:Logstash-logback によるログの報告¶
主に 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=localhost \
--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.logs.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({
// optional - url default value is http://localhost:55681/v1/traces
url: 'http://192.168.91.11:4318/v1/traces',
headers: {},
});
ここでの url は otel-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 とログの関連付け¶
APM とログの関連付けは、主にログに traceId と spanId を埋め込むことで行われます。ログの接続方式によって、埋め込み方法が異なります。
UI 表示¶
フロントエンド URL にアクセスしてトレース情報を生成します。
ELK ログ表示¶
ELK は ElasticSearch、Logstash、Kibana の略称です。
OTLP によるログの報告¶
展開後のログの source 部分
"_source": {
"@timestamp": "2022-05-18T08:39:20.661000000Z",
"Body": "this is method3,null",
"Resource.container.id": "7478",
"Resource.host.arch": "amd64",
"Resource.host.name": "cluster-ecs07",
"Resource.os.description": "Linux 3.10.0-1160.15.2.el7.x86_64",
"Resource.os.type": "linux",
"Resource.process.command_line": "/usr/java/jdk1.8.0_111/jre:bin: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.logs.exporter=otlp -Dotel.propagators=b3",
"Resource.process.executable.path": "/usr/java/jdk1.8.0_111/jre:bin:java",
"Resource.process.pid": 5728,
"Resource.process.runtime.description": "Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 25.111-b14",
"Resource.process.runtime.name": "Java(TM) SE Runtime Environment",
"Resource.process.runtime.version": "1.8.0_111-b14",
"Resource.service.name": "server",
"Resource.telemetry.auto.version": "1.13.1",
"Resource.telemetry.sdk.language": "java",
"Resource.telemetry.sdk.name": "opentelemetry",
"Resource.telemetry.sdk.version": "1.13.0",
"Resource.username": "liu",
"SeverityNumber": 9,
"SeverityText": "INFO",
"SpanId": "bb890485f7b6ba05",
"TraceFlags": 1,
"TraceId": "b4841a6b3ec9aa93d7f002393a156ff5"
},
otlp プロトコルにより、ログへの traceId と spanId の自動埋め込みが実現されています。
Logstash-logback によるログの報告¶
展開後のログの source 部分
"_source": {
"@timestamp": "2022-05-18T13:43:34.790Z",
"port": 55630,
"serverName": "otlp-server",
"namespace": "k8sNamespace_IS_UNDEFINED",
"message": "this is tag\n",
"@version": "1",
"severity": "INFO",
"thread": "http-nio-8080-exec-1",
"pid": "3975",
"host": "gateway",
"class": "c.z.o.server.controller.ServerController",
"traceId": "a7360264491f074a1b852cfcabb10fdb",
"spanId": "e4a8f1c4606ca598",
"podName": "podName_IS_UNDEFINED"
},
Logstash-logback 方式では、traceId と spanId を手動で埋め込む必要があります。
Prometheus & Grafana メトリクス表示¶
Jaeger、Zipkin トレース表示¶
次の記事 では、OpenTelemetry が grafana 関連コンポーネントに基づいてどのように可観測性を実現するかを紹介します。









