プログラムを組んでいると、「遅いな?でもどこが?」ということが起きてきます。「推測するな、測定せよ」という言葉の通り、ちゃんと遅い部分を見つけだしてから対処する必要があります。
ネットワーク経由でAPI呼び出しをしていたりするので、プロファイラーではなく分散トレーシングの方がいいでしょう。自分の処理はシロだったりすることもあります。
今回、Hit and Blowに分散トレーシングを組み込んでみました。ネットワーク経由の処理はないので、ちょっとオーバースペックかもしれませんが、練習ということで。
AWS X-Rayも分散トレーシング
以前、やっていました。
今回はOpenTelemetryを使います。「APIとか、ベンダーニュートラルだったらアプリ直さずにトレースのサービス切りかえられるよね?」ってことなのかと。
分散トレーシングで出てくる基礎知識や単語は、このスライドがまとまっています。
Jaegerでトレースデータを見る
先に出口、データを見る部分をどうするか、決めてしまいます。
お試しなのでX-RayやCloud Traceといった有料サービスはパス。手元のPCで簡単に動かせるものを…ということで、Jaeger All in Oneを使います。コンテナ1個でOK。
OpenTelemetry?Jaeger?混乱しそうですが、JaegerはOpenTelemetryの情報を受け付けます。
ですので、プログラムのトレース情報をOpenTelemetryで取得して、その情報をJaeger All in Oneに送るようにします。そうすれば、どのタイミングでどんな順番で処理しているのか、わかりやすくグラフ表示されます。
All in Oneの公式サイトにオプションが載っていますが、今回はOpenTelemetryの情報を受け付けられるようにするので、
docker run -d --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14269:14269 \
jaegertracing/all-in-one:1.38
として立ち上げます。 http://localhost:16686/
にアクセスすると、Jaegerの画面が出てきます。
Auto Instrumentationsで半自動的にOpenTelemetryを導入
Auto Instrumentationsはおすすめの導入方法らしいです。が、ライブラリにパッチを当てるので、サポートしていないライブラリとか、自分で書いたコードの部分とかは見れないです。見れないというか、呼び出しを別のスパンに分けることができないです。
今回はお試しなので、Auto Instrumentationsで導入してみます。
Flaskアプリケーションの起動方法
Flaskアプリケーションの起動方法にもお作法があるようで、flask run
として起動しないと、トレースできないようです。
ということで、既にDocker化していたコードに対する差分がこれ。Dockerfile
の修正の他、OpenTelemetry関連のライブラリをrequirements.txt
に追加しています。
diff --git a/Dockerfile b/Dockerfile
index 20cb606..a3b9199 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,10 @@ FROM python:3.10-slim
COPY templates/ /app/templates/
COPY main.py requirements.txt /app/
-RUN pip install -r /app/requirements.txt
+RUN pip install -r /app/requirements.txt && \^M
+ opentelemetry-bootstrap -a install^M
EXPOSE 5000
-CMD ["python3", "/app/main.py"]
+ENV FLASK_APP app.main.py^M
+CMD opentelemetry-instrument --traces_exporter otlp --metrics_exporter console \^M
+ --service_name hit-and-blow --exporter_otlp_protocol grpc flask run --host=0.0.0.0^M
diff --git a/requirements.txt b/requirements.txt
index 946ad91..7448eaa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1,3 @@
Flask==2.1.0
+opentelemetry-distro
+opentelemetry-exporter-otlp
Pythonのアプリケーション(main.py)には変更がないのがポイントですね。DockerfileのCMDの部分はsh経由で起動することになってしまうので、要改善ではあるのですが、力尽きました。
コード全体はGitHubにあります。
これでbuildして、起動する時はこんな感じ。
docker run -p 5000:5000 -e OTEL_EXPORTER_OTLP_ENDPOINT=http://172.17.0.2:4317 -e OTEL_EXPORTER_OTLP_TRACE_INSECURE=true hit-and-blow_otel
172.17.0.2はJaeger All in Oneのコンテナに割り当てられたIPアドレスです。環境によってIPアドレスが異なる可能性があるので、コンテナ起動時に環境変数として渡しています。
トレースする
Hit and Blowで少し遊んでからJagerの画面を見ると、トレースがでています。※serviceからhit and blowを選んでください。
jinja2の処理時間が長いように見えますが、そもそも全体の処理時間が短いので気にしなくてよいでしょう。
内部の処理をスパンとして切り出したいのであれば、自分でOpenTrace用のコードを追加する必要があります。このページが参考になります。
OpenTelemetryの中立性とポータビリティ
分散トレーシングを行おうとすると、スパンの設定など、本来なすべき処理以外のコードがアプリケーションに入ってしまいます。これがベンダー依存だと、ベンダーを変更するためにアプリケーションを変更するという、嬉しくない事態が発生します。
OpenTelemetryを使用することで、こういったことを大幅に減少できるのではないでしょうか。トレーシングだけでなく、メトリクスやロギングについても期待しています。