Rails3+devise,nginx,fluent,S3構成でのアクセスログ収集と蓄積
ユーザ認証が存在するWebアプリのアクセスログを有効に分析するためには、
・Webサーバからアクセスログを集める
・Webアプリのユーザとアクセスログを紐付ける
の2つを行う必要があります。
この記事では、
・Webサーバ: nginx
・開発フレームワーク: Ruby on Rails (認証プラグインとしてdevise)
・ログ集約: fluentd
・ログの格納先: Amazon S3
という構成のWebアプリケーションで、
「アクセスログの収集と蓄積」を行う流れについて説明します。
サーバOSは、CentOSを利用します。
アクセスログの収集から蓄積までは以下の図の流れになります。
アクセスログの出力:
①ユーザの訪問
②アプリから会員ID情報を送信
→RailsアプリからHTTPヘッダに会員IDを付加して返却
付加したヘッダはnginxでログ出力後に削除(ユーザに付加した情報は見えない)
③アクセスログに会員ID・トラッキング用cookie情報を出力
④tracking用のcookie情報を送信
→会員でない場合でもクライアントを特定できるよう
tracking用のCookie情報を付加
アクセスログの収集・分析:
①ログを監視し、条件に合うログを抽出
②定期的にS3に転送
→fluentdでログを監視、定期的にS3に転送
文章だけではわかりにくいかと思ったので、概要についてスライドにもまとめています。
Rails3+devise,nginx,fluent,S3構成でのアクセスログ収集と蓄積 from Takeshi Mikamiこの構成を構築する手順を次の順で説明していきます。
- CentOSへのnginx、rubyのインストール
- Railsアプリ作成とdeviseによる認証機能の作成
- 詳細なログを出力するためのnginxの設定
- fluentdの導入とログ収集・S3への転送設定
1. CentOSへのnginx、rubyのインストール
EPELの追加
CentOS標準のリポジトリだとnginxが入っていないので、
以下のように、EPELをリポジトリに追加します。
$ sudo wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-5.noarch.rpm
$ sudo rpm -ivh epel-release-6-5.noarch.rpm
参考)CentOS 外部レポジトリの追加(EPEL)
http://www.tooyama.org/yum-addrepo-epel.html
nginxのインストール
EPELの追加が終わったら、以下のコマンドでnginxをインストールします。
$ sudo yum install nginx
Rubyのインストール
ここでは最新版のRubyを利用したいので、ソースからコンパイルします。
まずはコンパイルに必要なパッケージ群をインストールします。
(おそらく、このくらい入れとけば大丈夫じゃないかなと思います)
$ sudo yum install gcc-c++ patch readline readline-devel zlib zlib-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison sqlite-devel mysql-devel libxslt-devel httpd-devel curl-devel ncurses-devel gdbm-devel
$ sudo yum install libyaml-devel
Rubyのソースをダウンロード、コンパイル&インストールします。
$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p125.tar.gz
$ tar zxf ruby-1.9.3-p125.tar.gz
$ cd ruby-1.9.3-p125
$ ./configure --prefix=/usr
$ make
$ sudo make install
2. Railsアプリ作成とdeviseによる認証機能の作成
# このページの流れの大部分はこの記事と同じです。
# 説明を省いているので、不明なところはこちらの記事も参照してください。
# http://takemikami.com/technote/archives/653
Railsのインストール
最新のRuby on Railsをインストールします。
$ gem install rails
== 略 ==
Successfully installed rails-3.2.8
== 略 ==
Railsアプリの作成
rails newでrailsアプリを作成します。
$ rails new app
create
create README.rdoc
create Rakefile
== 略 ==
Gemfileのtherubyracerのgemを以下のようにコメントアウトして、有効化。
== 略 ==
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
gem 'therubyracer', :platforms => :ruby
== 略 ==
bundle installして、rails sでサーバを起動します。
$ bundle install
== 略 ==
Installing therubyracer (0.10.2) with native extensions
== 略 ==
$ rails s
=> Booting WEBrick
=> Rails 3.2.8 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2012-08-21 11:16:47] INFO WEBrick 1.3.1
[2012-08-21 11:16:47] INFO WEBrick::HTTPServer#start: pid=1837 port=3000
この状態で、ブラウザから
http://(ホスト名orIPアドレス):3000/
でアクセスして「Welcome aboard」が表示されることを確認します。
ここまでで、素のrailsアプリ作成が完了。
## deviseによる認証機能の追加
次にdeviseを使って、ユーザの認証機能を追加します。
まずは、Gemfileにdeviseのgemを追記。
```ruby
== 略 ==
gem 'devise'
== 略 ==
その後、bundle install, devise:installしてdeviseをインストールします。 ユーザ用のモデルとビューを生成します。 ここでは「user」というモデルを作っています。
$ bundle install
== 略 ==
Installing orm_adapter (0.4.0)
Installing warden (1.2.1)
Installing devise (2.1.2)
== 略 ==
$ rails generate devise:install
create config/initializers/devise.rb
create config/locales/devise.en.yml
== 略 ==
$ rails generate devise:views
invoke Devise::Generators::SharedViewsGenerator
create app/views/devise/shared
create app/views/devise/shared/_links.erb
== 略 ==
$ rails generate devise user
invoke active_record
create db/migrate/20120821023255_devise_create_users.rb
create app/models/user.rb
== 略 ==
$ rake db:migrate
== DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
-> 0.0059s
-- add_index(:users, :email, {:unique=>true})
-> 0.0138s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0010s
== DeviseCreateUsers: migrated (0.0210s) =====================================
各ページにdeviseに関するアラートを表示するように、 「app/views/layouts/application.html.erb」に 以下のように、notice, alert表示の2行を追記します。
<body>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</body>
ログイン状態に応じて、 ログイン・ログアウトボタンを表示するトップページを作成します。
$ rails g controller home index
create app/controllers/home_controller.rb
route get "home/index"
== 略 ==
「app/views/home/index.html.erb」を以下のように編集し、 ログイン・ログアウトボタンを設置します。
<% if user_signed_in? %>
<%= link_to "ログアウト", destroy_user_session_path, :method => 'delete' %>
<%= link_to "ユーザ情報の編集", edit_user_registration_path %>
<% else %>
<%= link_to "ログイン", new_user_session_path %>
<%= link_to "登録", new_user_registration_path %>
<% end %>
「config/route.rb」に以下の行を追加して、ルートページを「/home/index」にします。
root :to => "home#index"
デフォルトのルートページは、リネームしておきます。
$ mv public/index.html public/index.html.bak
ここまで生成したら、再度「rails s」でrailsを起動。 以下のURLにアクセスすると ログイン・登録と表示されたルーとページが参照できます。 http://(ホスト名orIPアドレス):3000/ 登録から進み、メールアドレスとパスワードを入力して会員登録します。
会員登録後、ログイン・ログアウトができることを確認します。
HTTPヘッダにユーザIDを追加
railsアプリからnginxに対してユーザIDの情報を引き渡すため HTTPヘッダーにユーザIDの情報を付加します。
# ここで追加したヘッダーはnginxで削除してブラウザに返却するので、
# ユーザには見えません(インターネット上の経路には流出しません)。
「app/controllers/application_controller.rb」を以下のように編集します。
class ApplicationController < ActionController::Base
protect_from_forgery
after_filter :add_userid_toheader
def add_userid_toheader
response.headers["X-App-Userid"] = "#{user_signed_in? ? session['warden.user.user.key'][1][0] : nil}"
end
end
以下のコマンドを入力し、「X-App-Userid」ヘッダが返却されることを確認します。
$ curl -I http://(ホスト名orIPアドレス):3000/
HTTP/1.1 200 OK
X-App-Userid:
Content-Type: text/html; charset=utf-8
X-Ua-Compatible: IE=Edge
== 略 ==
3. 詳細なログを出力するためのnginxの設定
railsアプリのフロントにnginxを設置
「/etc/nginx/conf.d/default.conf」を以下のとおり編集し、 railsアプリのフロントにnginxを設置します。
\#
\# The default server
\#
upstream rails_app {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name _;
location / {
proxy_pass http://rails_app/;
\# root /usr/share/nginx/html;
\# index index.html index.htm;
}
error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/html;
}
\# redirect server error pages to the static page /50x.html
\#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginxを再起動して設定変更を有効にします。
$ service nginx restart
この状態で、ブラウザから http://(ホスト名orIPアドレス)/ でアクセスしてrailsアプリのルートが表示されることを確認します。
nginxに詳細ログの出力設定を追加
「/etc/nginx/conf.d/default.conf」を以下のとおり編集し、 nginxで詳細ログを出力するように設定します。 この設定ファイルで、次の設定を行っています。 ①tracking用のCookie情報を付加 →userid on; userid_name uid; userid_path /; userid_expires 365d; ②Railsアプリで付加した会員ID情報を削除 →proxy_hide_header X-App-Userid; ③詳細ログを出力 →log_format detail_accesslog ….. access_log /var/log/nginx/detail_access.log detail_accesslog;
\#
\# The default server
\#
upstream rails_app {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name _;
location / {
proxy_pass http://rails_app/;
\# root /usr/share/nginx/html;
\# index index.html index.htm;
}
error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/html;
}
\# redirect server error pages to the static page /50x.html
\#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
userid on;
userid_name uid;
userid_path /;
userid_expires 365d;
proxy_hide_header X-App-Userid;
log_format detail_accesslog '"$time_local","$remote_addr","$uid_got","$upstream_http_x_app_userid","$remote_user","$request","$status","$body_bytes_sent","$http_referer","$http_user_agent","$http_x_forwarded_for","$gzip_ratio"';
access_log /var/log/nginx/detail_access.log detail_accesslog;
}
nginxを再起動して設定変更を有効にします。
$ service nginx restart
この状態で、ブラウザから http://(ホスト名orIPアドレス)/ でアクセスして、次のようなログが出力されることを確認します。
“21/Aug/2012:14:10:09 +0900”,“192.168.199.1”,“uid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”,“2”,"-",“GET / HTTP/1.1”,“304”,“0”,"-",“Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.79 Safari/537.1”,"-","-"
注目する出力値: ・“uid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX” ← trackingのcookie情報 ・“2” ← 会員ID
ここまでで、nginxからのログの出力設定は完了です。
4. fluentdの導入とログ収集・S3への転送設定
fluentdのインストール
fluentd及びS3用プラグインをインストールします。
$ gem install fluentd
= 略 =
Successfully installed fluentd-0.10.25
= 略 =
$ gem install fluent-plugin-s3
= 略 =
Successfully installed fluent-plugin-s3-0.2.2
= 略 =
fluentdへのログ収集・転送設定
「/etc/fluent/fluent.conf」を以下の通り編集し、 ログの収集とS3への転送を設定します。 ・source部で、収集対象となるログを設定 ・pathに対象ファイルを指定 ・formatで収集する行を正規表現で指定 ・match部で、収集したログの転送先を指定 ・aws_key_id, aws_sec_key, s3_bucket, s3_endpointで転送先bucketを指定 ・path, buffer_path, time_slice_formatで転送先パス・ファイル名を指定
<source>
type tail
format /^"(?<time_local>[^"]*)","(?<remote_addr>[^"]*)","(?<uid_got>[^"]*)","(?<upstream_http_x_app_userid>[^"]*)","(?<remote_user>[^"]*)","(?<request>[^"]*)","(?<status>[^"]*)","(?<body_bytes_sent>[^"]*)","(?<http_referer>[^"]*)","(?<http_user_agent>[^"]*)","(?<http_x_forwarded_for>[^"]*)","(?<gzip_ratio>[^"]*)"$/
path /var/log/nginx/detail_access.log
pos_file /tmp/td-agent/apache-web.pos
tag nginx.access
</source>
<match nginx.access>
type s3
aws_key_id <ここにaccess_key_idを入れる>
aws_sec_key <ここにsecret_access_keyを入れる>
s3_bucket <ここにbucket名を入れる>
s3_endpoint s3-ap-northeast-1.amazonaws.com
path log/
buffer_path /var/log/fluent/s3
time_slice_format /nginx/%Y%m/detail_access.www-web.log.%Y%m%d%H
time_slice_wait 10m
</match>
以下のコマンドで、 posファイルの保存用にテンポラリディレクトリを作ります。
$ mkdir /tmp/td-agent/
fluentdの起動
以下のコマンドを入力しfluentdを起動します。
$ fluentd
2012-08-21 16:54:05 +0900: starting fluentd-0.10.25
2012-08-21 16:54:05 +0900: reading config file path="/etc/fluent/fluent.conf"
= 略 =
この状態で、ブラウザから以下のURLにアクセスします。 http://(ホスト名orIPアドレス)/ アクセスしたログが、 「/var/log/fluent/s3」以下にコピーされていることを確認します。
ここにコピーされたファイルが一時間ごとに、 指定したS3のbucketにコピーされます(最大1時間待って確認します)。