NGINX 公式の OpenTelemetry モジュールを使って NGINX のオブザーバビリティを簡単に実現！

# 概要
　今回 NGINX 公式の Openteremetry モジュールがオープンソースで公開されていたので、検証してみました。導入が簡単になっていてパフォーマンスも改善しているというメリットがありました。
 
 https://github.com/nginxinc/nginx-otel

 　NGINX の OpenTelemetry（OTel）を使った計装の方法は以前 Qiita の記事を作成しましたが、導入時にビルドが必要だったりと導入ハードルが高いものでした。それを理由に諦めていた人はぜひ今回の内容を見て再挑戦してみてください。また、サードパーティのモジュールを使用している人は、パフォーマンス改善の恩恵が大きいので切り替えを検討してください。

:::note info
最新のアップデートの詳細はこちら
[New Relic アップデート一覧
](https://newrelic.com/jp/blog/nerdlog/new-relic-update  "New Relic アップデート一覧")
:::
:::note info

無料のアカウントで試してみよう！

[New Relic フリープランで始めるオブザーバビリティ！](https://qiita.com/ktst79/items/5ee74b34e6adb8a7a6fa)

:::

# 今回のポイント
NGINX 公式の Openteremetry モジュールを使用する利点は、次の3つです。

**1\. NGINX公式のオープンソースモジュール**
　これまではサードパーティのモジュールだったため、ドキュメントが整備されていないなど導入しづらい点もありました。公式のオープンソースモジュールなのでNGINXの公式ドキュメントにも設定のパラメータ情報等が記載されています。また、NGINX Plusとの連携も可能です。
 
**2\. インストール、設定が簡単！**
　**パッケージマネージャ（yum, apt）を使ったインストールに対応**。さらにモジュールがインストールされたDockerイメージも提供されています。（Alpine Linux のパッケージマネージャには対応していないのでイメージの配布は嬉しい）
　モジュールの設定も NGINX のコンフィグファイルに設定を追加するだけなので新しい設定ファイルの追加も必要ありません。
 
**3\.パフォーマンスも大幅に改善**
　サードパーティの Openteremetry モジュールは、リクエスト処理のパフォーマンスが最大 50% 低下します。一方でNGINX 公式のモジュールは、この影響を **約10～15%** に制限しているそうです。

# セットアップ方法
　NGINX 公式のモジュールは、インストールが簡単です。パッケージマネージャを使ってインストールして、 NGINX のコンフィグファイルを編集するという2つのステップで完結します。今回は New Relic の Logs in Context の機能も使いたいのでその方法も併せて紹介します。

セットアップの方法は、GitHub に記載された内容と NGINX の公式ドキュメントを参考にしています。

https://github.com/nginxinc/nginx-otel?tab=readme-ov-file

https://nginx.org/en/docs/ngx_otel_module.html

　また、収集したデータを New Relic に連携するために OpenTelemetry Collector（OTel Collector）　を使用しています。OTel Collectorの設定は、Qiitaの記事で公開していて、今回も同じ設定をそのまま使用しています。

https://qiita.com/MarthaS/items/14da436b6bce5e7d7759#opentelemetry-collector-%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97

![dataflow.png](https://camo.qiitausercontent.com/13fd53fb920ab8ed498eb0751af1c91f51cb8c6b/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f333034333230322f62666163336263652d356364312d306231382d646130362d3563666466356534376264662e706e67)


## OpenTelemetry モジュールのインストール

### RHEL, CentOS および その派生製品
```console :パッケージのインストール
sudo yum install nginx-module-otel
```
### Debian, Ubuntu および その派生製品
```console :パッケージのインストール
sudo apt install nginx-module-otel
```
### Dockerイメージの利用
```Dockerfile :dockerfile
FROM nginx:stable-alpine-otel
```
``***-otel``と記載されているイメージには、OTel モジュールが導入されています。

インストールすることで ``/etc/nginx/modules`` に OTel モジュールの ``ngx_otel_module.so`` が配置されます。

今回はDockerイメージを使って検証を行いました。

## ファイルの設定
### トレースデータの収集 ・ 転送設定
　NGINX の設定ファイルに OTel モジュールの設定を追加すると**トレースデータの収集**と **OTel Collector へのデータ転送**ができます。具体的な設定内容は下記のとおりです。
```diff_conf: nginx.conf
user  nginx;
worker_processes  1;

+ # ngx_otel_module.soをロード
+ load_module modules/ngx_otel_module.so;

error_log  /var/log/nginx/error.log info;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {

+    # OTel Collector へデータを転送する設定
+    otel_exporter {
+        endpoint otel-collector:4317;
+    }

+    # New Relic で表示するエンティティ名の設定
+    otel_service_name nginx-native-eccube-proxy;
+    
    server {
        # Proxy
        location / {
+            # トレースの有効化
+            otel_trace on;

+            # トレース情報を伝搬する方法を設定
+            otel_trace_context propagate;

+            # トランザクション名 ・ スパン名の設定
+            otel_span_name $request_uri;
    
```
それぞれ設定しているのは下記の項目です。
**1\. OTel モジュール ngx_otel_module.so のロード**
　 パッケージマネージャでインストールすると ``/etc/nginx/modules`` 配下にモジュールが配置されるため、設定ファイルからの相対パスでファイルを指定してロードします。
  
**2\. OTel Collector へデータを転送する設定（http配下）**
　 OTel Collector のエンドポイント(endopoint)やデータ転送の間隔（interval）を設定できます。認証情報が設定できないため、New Relic へデータを転送する場合は OTel Collector が必須です。

**3\. エンティティ名の設定（http配下）**
　New Relic で表示されるエンティティ名を設定します。
 
**4\. トレースの有効化（http, server​, location配下）**
　OTel モジュールのトレースを有効(on)または無効(off)にします。
 
**5\. トレース情報を伝搬する方法を設定（http, server​, location配下）**
　traceparent/tracestate ヘッダーを伝播する方法を指定します。下記4つの設定が可能でトレースコンテキストの設定方法には違いがあります。
* **extract**
NGINX が受信したリクエストに付与されたトレースコンテキストを継承します。NGINX 自身がトレースのルートスパンになることはありません。
* **inject**
NGINX 自身が常にトレースのルートスパンになるようにトレースコンテキストを設定します。
* **propagate**
extract と inject の組み合わせで設定されます。トレースコンテキストがない場合は新しく追加し、すでに設定されている場合は継承します。
* **ignore**
トレースコンテキストの処理をスキップします。
 
**6\. トランザクション名 ・ スパン名の設定（http, server​, location配下）**
　OTelモジュールで計装したスパン名を設定します。New Relic 上ではトランザクション名としても使用されます。

### Logs in Context の有効化
　NGINX のログに Service Name, Trace ID, Span ID を追加することで New Relic の Logs in Context の機能が利用できます。分散トレーシングで実行されたトランザクション毎に関連したログを表示する機能でトラブルシュートをする際に大変便利な機能です。
　下記の設定が Logs in Context の機能を使用する際のログの例です。

```diff_conf:nginx.conf
    log_format json escape=json '{'
                                '"time": "$time_iso8601",'
                                '"host": "$remote_addr",'
                                '"vhost": "$host",'
                                '"user": "$remote_user",'
                                '"status": "$status",'
                                '"protocol": "$server_protocol",'
                                '"method": "$request_method",'
                                '"path": "$request_uri",'
                                '"req": "$request",'
                                '"size": "$body_bytes_sent",'
                                '"reqtime": "$request_time",'
                                '"apptime": "$upstream_response_time",'
                                '"user_agent": "$http_user_agent",'
                                '"forwardedfor": "$http_x_forwarded_for",'
                                '"forwardedproto": "$http_x_forwarded_proto",'
                                '"referrer": "$http_referer",'
+                                '"service.name": "nginx-native-eccube-proxy",'
+                                '"trace.id": "$otel_trace_id",'
+                                '"span.id": "$otel_span_id"'
                                '}';

    access_log  /var/log/nginx/access.log  json;

```

 Trace ID, Span ID はそれぞれ ``$otel_trace_id``, ``$otel_span_id`` の変数を使うことで取得可能です。

# New Relicに連携されたデータの確認
　収集した NGINX のデータを New Relic の UI で確認しましょう。APM & Services にエンティティが表示されているので、設定した名前のエンティティを選択して情報を確認していきましょう。
　この記事の中では、フロントエンドやバックエンドと併せて確認できる Service Map と 分散トレーシング、そして NGINX のログで設定した Logs in Context を確認します。
 
## Service Map
　Service Map では発生したリクエストをもとにシステムの構成をマッピングしてくれます。OTel モジュールを使用していない場合は、以下のようにフロントエンドとバックエンドの間にある NGINX が表示されません。
![ServiceMap_noNGINX.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3043202/5f4cef86-7048-ed7c-b657-56663092c354.png)

　OTel モジュールを有効にしてトレースの情報を収集すると以下のようにフロントエンドとバックエンドの間に設置された NGINX がしっかり表示されます。
![ServiceMap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3043202/3f5c4089-ab30-4fe2-1250-fda08f743b4b.png)

## 分散トレーシング
　分散トレーシングでも同様に NGINX が表示されます。これで NGINX がボトルネックになっているのか、バックエンドの処理がボトルネックになっているのか明確に切り分けることができるようになります。
![分散トレーシング.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3043202/a850e1ed-6950-9900-658b-19e4f89ef262.png)

## Logs in Context
　分散トレーシングの画面からトランザクションで出力されたログを確認することができます。今回は NGINX のログにTraceIDを挿入して、以下のように対象のログのみが自動で関連付けて表示されています。NGINX の膨大なログの中から目的のログを探し出してバックエンドの実行ログと突合する作業から解放されますね。
![LogsinContext.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3043202/31b1cfce-7459-ca5e-32e3-6e846943cb63.png)

# 最後に
　NGINX 公式の OpenTelemetry モジュールを使うと簡単に NGINX の計装が可能です。設定もサードパーティのモジュールよりも簡単になっています。コンテナイメージが提供されているため、ちょっとした動作を確認するのも簡単です。New Relic はフリープランを用意しているため、無料で NGINX を計装したデータを確認できます。
　ぜひ New Relic と NGINX の OpenTelemetry モジュールを使って、今運用している環境のオブザーバビリティを高めてください。

:::note info
無料のアカウントで試してみよう！
[New Relic フリープランで始めるオブザーバビリティ！](https://qiita.com/ktst79/items/5ee74b34e6adb8a7a6fa)
:::

 :::note info
[New Relic株式会社のQiita Organization](https://qiita.com/organizations/newrelic)では、
新機能を含む活用方法を公開していますので、ぜひフォローをお願いします。
:::