Webuilder240

Nginxでリクエスト毎に発番して、Railsのログに書き込むまで

2018-07-30 20:55:00 +0900

Nginx Rails

いきなりまとめ

  • マイクロサービスとかでいくつものアプリケーションに対してリクエストを行うような構造のアプリケーションはリクエストIDを設定しておき、異なるマイクロサービスでも同一のIDがログ上で記録されるので、ログのチェックなどがはかどって便利。
  • Nginx 1.10以降で利用できる $request_id を使うと簡単に リクエストごとに発番できる。
  • Railsは HEADER X-Request-Id を取得してログにタグを簡単に設定できる。

なぜ必要なのかをもっと詳しく

Railsアプリケーションの手前でNginxでTLSを終端していたり、 複数のアプリケーションをまたいだりしているマイクロサービスでアプリケーションを実装していると、もし大げさなサービスを構築していなくとも、リクエストが多いとログを確認するのが大変なので、 なにかとNginxでリクエストごとに生成したIDを振ったものをProxyしているアプリケーションに渡してやることでログの確認を補助することができる。

やりたいこと・サンプルアプリケーションの構成

  1. 今回は前段にTLS終端を想定した、Nginxでロードバランサを立てる。
  2. 1.のNginxアプリケーション(コンテナ)でNginxでUUIDを割り当て、Proxyする。
  3. 2のコンテナから、ProxyからのアクセスにX-Request-Id が設定されている場合は、それをそのまま X-Request-Id に設定する。設定されていない場合は、3のコンテナでリクエストIDを発番する。
  4. 更にRailsアプリケーション用にProxyして、ProxyHeaderに書き込まれた X-Request-Id をRailsのログにタグとして設定して書き込む
図解

サンプルリポジトリ


github.com
これをGit cloneしてきて、docker-compose up ってすればシュシュッとサンプルのアプリケーションが動くので、よかったら動かしてみて欲しい。
TLS終端用のNginx側の設定
リクエストごとにIDを発番する機構はNginx1.10以上であれば、$request_id を利用して、簡単に設定することができる。
http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_id
  server {
    listen       80;
    server_name  _;
    client_max_body_size 4G;
    keepalive_timeout 5;

    proxy_set_header    Host    $host;
    proxy_set_header    X-Real-IP $remote_addr;
    proxy_set_header    X-Forwarded-Host $host;
    proxy_set_header    X-Forwarded-Server $host;
    proxy_set_header    X-Forwarded-For http;

    location / {
      # X-Request-IdをProxy Headerに設定
      proxy_set_header X-Request-Id $request_id;
      proxy_pass http://nginx_rails_proxy;
    }
  }

上記は設定の抜粋なので、下記にすべて記載したものを掲載しておく。*1
nginx_proxy_uuid_rails/nginx.conf at master · webuilder240/nginx_proxy_uuid_rails · GitHub
Rails Proxy用のNginxコンテナの設定
基本的にはTLS終端用のNginxの設定と同じ。RailsアプリケーションへのProxy設定部分以外で異なるのは、 先程のTLS終端用のコンテナから受け取った X-Request-Id をProxyするアプリケーションに対して ProxyHeaderに設定してあげればOKということ。 もし X-Request-Idがない場合は、このNginxでリクエストIDを生成して設定すればOK。
  server {
    listen       80;
    server_name  _;
    client_max_body_size 4G;
    keepalive_timeout 5;

   # この生成したRequestIdをProxy用のRequest IDに設定。
    set $proxy_request_id $request_id;
    if ($http_x_request_id) {
      # Proxy Headerに `X-Request-Id`が設定されている場合、 Proxy用のリクエストIDに設定する
      set $proxy_request_id $http_x_request_id;
    }

    root /usr/src/app/public;

    try_files $uri/index.html $uri.html $uri @app;

    location @app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      # Header, Proxy Headerに `X-Request-Id` を設定する。
      add_header X-Request-Id $proxy_request_id;
      proxy_set_header X-Request-Id $proxy_request_id;
      proxy_redirect off;
      proxy_pass http://app_server;
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /usr/src/app/public;
    }
  }

nginx_proxy_uuid_rails/nginx.conf at master · webuilder240/nginx_proxy_uuid_rails · GitHub
Railsの設定
Rails側の設定は至って簡単。 application.rbに下記設定を追記すればOK。 なお、X-Request-Idが設定されていない場合は、Rails側で生成したものが反映される。
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module NginxProxyUuid
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2
    config.log_tags = [:request_id]
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
  end
end

ログのフォーマットはこんな感じ。
[43a62d59ee9f05db194539c71e113601] Started GET "/" for 172.20.0.2 at 2018-07-28 05:00:26 +0000
[43a62d59ee9f05db194539c71e113601] Cannot render console from 172.20.0.4! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
[43a62d59ee9f05db194539c71e113601] Processing by Rails::WelcomeController#index as HTML
*1:ここではNginxのアクセスログの設定を省略しているが、ちゃんとRequestIdを記録しておこう https://github.com/webuilder240/nginx_proxy_uuid_rails/blob/master/containers/nginx_tls/nginx.conf#L15 

関連しそうなブログ