NginxでWebサーバ間をトレースするrequest_id

## $request_id

Nginx 1.11.0 以降に限りますが、リクエスト毎に発番されるIDの変数として **$request_id** が追加されたようです。
http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_id

この変数を利用することにより、Nginxコアだけでサービス間のトレースを簡単に行うことが可能になります。

## シンプルな例

以下のように、$request_idをログに含めるだけでリクエスト毎のIDを記録できます。

```nginx
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$request_id"';
    access_log  logs/access.log  main;

    # クライアントにも返しておくと調査が楽ですね
    #add_header X-Request-ID $request_id;
```

## プロキシ先でもリクエストIDを引き継ぎたい

プロキシした先にもrequest_idを渡すことにより、同一リクエストかどうかを判別することが可能になります。
プロキシヘッダにX-Request-Id[^1] を付与し、それをプロキシ先でも読み取るには以下のような設定になります。
プロキシ先ではカスタムヘッダーの変数である **$http_x_request_id** を参照しています。

```nginx
###########################################
# プロキシ元 nginx
###########################################
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$request_id"';

    access_log  logs/access1.log  main;
    server {
        listen 80;

        location / {
            proxy_pass http://backend/;
            proxy_set_header X-Request-ID $request_id;
        }
    }
}

###########################################
# プロキシ先 nginx
###########################################
http {
    # http_x_request_id を記録する
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$http_x_request_id"';

    access_log  logs/access2.log  main;
    server {
        listen 80;

        location / {
            # ...
        }
    }
}
```

## プロキシ先でもリクエストIDを引き継ぎつつ、設定も共通化したい

ただ、プロキシ元とプロキシ先で設定変えるのは少し面倒ですね。
そこで **X-Request-Idが渡ってきたときにはそちらを優先し、なければ $request_idを参照する** という処理を共通化してしまえばどの環境でも同一設定かつ、request_idの引き継ぎを行うことができます。

```nginx:x_request_id.conf
# 独自の $_request_id を定義し、設定する
set $_request_id $request_id;
if ($http_x_request_id) {
    set $_request_id $http_x_request_id;
}
```

```nginx:nginx.conf
http {
    # $_request_id を参照する
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$_request_id"';

    server {
    listen 80;
        include ../conf.d/x_request_id.conf;
        access_log logs/access1.log  main;

        # ...
    }
}
```

動作するフルのconfigはgistに置いてあります。
https://gist.github.com/toritori0318/2dc2b64ff696822b02d202bf1fc2f5b2

上記を実行してみたところ、request_id が問題なく引き継ぎ出来ています！

```shell-session
$ tail -1 logs/access1.log
127.0.0.1 - - [08/Apr/2017:16:13:25 +0000] "GET / HTTP/1.1" 200 44 "-" "curl/7.47.1" "-" "8e77df77bdec1eef154d5f819596cb0e"

$ tail -1 logs/access2.log
127.0.0.1 - - [08/Apr/2017:16:13:25 +0000] "GET /fake HTTP/1.1" 200 44 "-" "curl/7.47.1" "-" "8e77df77bdec1eef154d5f819596cb0e"
```

Nginxだけでマイクロサービス間のトレースを行うのがお手軽にできるので良いですね。便利！


[^1]: もちろん、 `X-Request-Id` は任意なのでサービスごとにお好きなヘッダ名に変えてもOKです