OpenTelemetry でPHP の観測に挑戦してみた
こんにちは。
ご機嫌いかがでしょうか。
"No human labor is no human error" が大好きな吉井 亮です。
PHP で作成されたサイト運営に少しだけ関わることがあったので、OpenTelemetry を用いた Observability を試してみました。
構成は以下になっています。OpenTelemetry Collector、Tempo、Promtail、Loki、Grafana で構成しました。
OpenTelemetry と PHP
OpenTelemetry PHP のステータスは Traces、Metrics、Logs ともに Stable となっています。
自動計装用の Extension が用意されています。既存コード修正が必要最低限に抑えられるのは嬉しいことです。
やってみた
今回は minikube を使ってローカル環境で構築しました。こちらで yaml 等を公開しています。
デモアプリ
デモアプリは OpenTelemetry のサンプルをそのままつかいました。
こちらが Dockerfile です。依存関係や SDK をインストールしています。少々過剰な気がしながらも追加追加していたらこのようになりました。
コンテナ起動時に OpenTelemetry のいつもの環境変数を足してあげれば OK です。自動計装は便利ですね。
env:
- name: OTEL_PHP_AUTOLOAD_ENABLED
value: "true"
- name: OTEL_SERVICE_NAME
value: "php-demo"
- name: OTEL_TRACES_EXPORTER
value: "otlp"
- name: OTEL_METRICS_EXPORTER
value: "none"
- name: OTEL_LOGS_EXPORTER
value: "console"
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: "grpc"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://opentelemetry-collector:4317"
- name: OTEL_PROPAGATORS
value: "baggage,tracecontext"
OpenTelemetry Collector
Collector の主だった設定は以下のようになっています。なんの変哲も無いです笑
exporters:
debug: {}
otlp:
endpoint: tempo:4317
tls:
insecure: true
extensions:
health_check: {}
memory_ballast: {}
processors:
batch: {}
memory_limiter:
check_interval: 5s
limit_percentage: 80
spike_limit_percentage: 25
receivers:
otlp:
protocols:
grpc:
endpoint: ${env:MY_POD_IP}:4317
http:
endpoint: ${env:MY_POD_IP}:4318
service:
extensions:
- health_check
- memory_ballast
pipelines:
traces:
exporters:
- debug
- otlp
processors:
- memory_limiter
- batch
receivers:
- otlp
Promtail
OpenTelemetry Log を標準エラー(StdErr)に出しそれを Promtail で収集しています。
標準エラーだと JSON ではなく、ただのテキストがつらつら出てしまうので、改行と空白を削除し1行にまとめています。苦肉の策です。
scrapeConfigs: |
- job_name: kubernetes-pods
pipeline_stages:
- match:
selector: '{container="php-demo"}'
stages:
- docker: {}
- multiline:
firstline: '^\x{007B}'
timeout: 3s
- replace:
expression: '(\n)'
replace: ""
- replace:
expression: '(\s)'
replace: ""
Grafana
データソースの設定から Loki と Tempo を抜き出しました。
こうしておくと Grafana でトレースを表示させた際に該当のログとリンクさせることが可能です。
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
jsonData:
timeout: 60
maxLines: 1000
derivedFields:
# Field with internal link pointing to data source in Grafana.
# datasourceUid value can be anything, but it should be unique across all defined data source uids.
- datasourceUid: tempo
matcherRegex: "traceID=(\\w+)"
name: TraceID
# url will be interpreted as query for the datasource
url: '$${__value.raw}'
# optional for URL Label to set a custom display label for the link.
urlDisplayLabel: 'View Trace'
- name: tempo
type: tempo
url: http://tempo:3100
isDefault: false
access: proxy
basicAuth: false
jsonData:
tracesToLogsV2:
# Field with an internal link pointing to a logs data source in Grafana.
# datasourceUid value must match the uid value of the logs data source.
datasourceUid: 'Loki'
spanStartTimeShift: '1h'
spanEndTimeShift: '-1h'
tags: [
{ key: 'app'}
]
filterByTraceID: false
filterBySpanID: true
customQuery: false
参考
A language-specific implementation of OpenTelemetry in PHP
Quick start for Tempo
Grafana Loki documentation
Promtail agent
Discussion