追記(1)
ngx_mruby 作者の @matsumotory さんに以下オンようなコメントを頂きました!
有難うございます!
追記(2)
連載(笑)にしようと思いますのでタイトルに数字つけました。
はじめに
Nginx は設定に if が使えたりとデブオプスのココロを擽る Web サーバーだと思っていますが、細かい制御をしたいなと思った時に設定ファイルをグリグリ書くのはどうもなあと思っていたら mruby でイジれる ngx_mruby があるではないですか!
しかも、事例が既に載っているではないですか!
ということで、自分も試してみたいと思います。(以下、作業中の内容も含まれますのでご注意ください)
やりたいこと
- リバースプロキシへの接続数に応じて処理を変えたい!
- リバースプロキシへの接続数を超えた接続はエラーページを表示させたい
- バックエンドへの接続数を監視したい!
- 監視の結果は別のツールに飛ばして可視化したい
準備
環境の構築
環境の構築にあたっては ngx_mruby の作者である @matsumotory さんが既に Dockerfile を作って頂いているのでそれを利用させて頂きます。
Docker Hub
リリース依頼、あまり騒がれない感じの Docker Hub を使ってコンテナをビルドします。
既に ngx_mruby を fork させて頂いた状態からスタートです。
Docker を動かす器
待つ間にコンテナを動かす器を用意しましょう。
そして先日発表された EBS の SSD なんか使っちゃいます。
バックエンドの Web サーバーは手動で CentOS で作ってしまいます。
以下のような簡単な Dockerfile を用意して…
FROM centos:6.4
RUN yum -y update
ADD nginx.repo /etc/yum.repos.d/nginx.repo
RUN yum -y install nginx
docker build するだけです。
実践
まずやりたいことの(1)から実装してみたいと思います。
その前に
ビルドが完了したコンテナを pull してきます。
docker pull inokappa/ngx-mruby
pull してきたら run します。
docker run -t -i inokappa/ngx-mruby /bin/bash
ログインしたら以下のようにして nginx を起動しましょう。
/usr/local/nginx/sbin/nginx &
以下のようにして Detach モードで起動すると /usr/local/nginx/sbin/nginx が CMD で実行されます。
docker run -t -d inokappa/ngx-mruby
簡単ですね。
何がやりたいのか?
- リバースプロキシとして動作している Nginx への接続数に応じて処理を変えたい!
- 接続数を超えたアクセスに対してエラーページを表示させたい
break down
ドキュメントを見ながらやりたいことをブレイクダウンして見てみたいと思います。
nginx.conf
上記のコンテナから起動した場合には既に nginx.conf は下記のように設定されすぐに ngx_mruby は利用可能な状態になっています。
daemon off;
user daemon;
worker_processes auto;
events {
worker_connections 1024;
}
env PROXY1_PORT_80_TCP_ADDR;
env PROXY1_PORT_80_TCP_PORT;
env PROXY2_PORT_80_TCP_ADDR;
env PROXY2_PORT_80_TCP_PORT;
env PROXY3_PORT_80_TCP_ADDR;
env PROXY3_PORT_80_TCP_PORT;
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /mruby-hello {
mruby_content_handler_code 'Nginx.echo "server ip: #{Nginx::Connection.new.local_ip}: hello ngx_mruby world."';
}
location /mruby-test {
mruby_content_handler /usr/local/nginx/hook/test.rb;
}
location /mruby-proxy {
mruby_set $backend /usr/local/nginx/hook/proxy.rb;
proxy_pass http://$backend;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
接続数の取得
純粋な Nginx が起動してからの累積接続数を取得する場合には Nginx::Var クラスの connection メソッドを利用します。
v = Nginx::Var.new
Nginx.echo v.connection
/usr/local/nginx/hook/test.rb に以下のように追記します。
Nginx.echo "This is test for ngx_mruby"
v = Nginx::Var.new
Nginx.echo v.connection
追記したら curl でアクセスしてみます。
初めてのアクセスなので connection は 1 となります。尚、connection 以外には connection_requests というメソッドも利用出来ます。
Nginx.echo v.connection_requests
これは 1 つのクライアントからのリクエスト数をカウントしているようですので、これにしきい値を設けると DDoS 対策のようなことが出来ると思われます。
v = Nginx::Var.new
if v.connection_requests.to_i < 100 then
Nginx.redirect "/may_be_you_ddos.html"
else
Nginx.echo v.connection
end
接続数に応じた処理
connection メソッドを以下のように接続数が一定数を超えたら 50x.html ページが表示されるようにするには以下のように書くだけで実現可能です。
v = Nginx::Var.new
if v.connection.to_i < 100 then
Nginx.redirect "/50x.html"
else
Nginx.echo v.connection
end
これで累積の接続数が 100 を超えた場合に以下のような 50x.html のページが表示されるようになります。
ただ、これだと累積のアクセス数が 100 を超えてしまったら全てのクライアントが 50x.html となってしまい残念な感じになってしまいます。。。
仕切り直し(やりたいこと(1))
追記にも書かせて頂きましたが @matsumotory さんにご対応頂いて Nginx の HttpStubStatusModule を ngx_mruby に組み込むようにして頂きましたので仕切り直しで試していきたいと思います。
尚、検証に際しては ngx-mruby のコンテナを利用させて頂きました!
同時接続数を取得する
HttpStubStatusModule を ngx_mruby に組み込んで頂いたおかけで以下のように書くことで同時アクセス数を取得出来るようになりました。
r = Nginx::Request.new
Nginx.echo r.var.send r.var.arg_name.to_sym
Nginx.return Nginx::HTTP_OK
リクエストパラメータの name に connection_waiting を渡してあげると接続待ち数が取得出来ます。また、以下のようなパラメータを渡すことでそれぞれの値を取得することも可能です。
- connections_active
- connections_reading
- connections_writing
- connections_waiting
以下のように指定します。
http://xxx.xxx.xxx.xxx/path/to?name=connections_active
しきい値を超えた場合の処理を追加
以下のような簡単なスクリプトだけで処理が実装可能です。
r = Nginx::Request.new
w = r.var.send r.var.arg_name.to_sym
if w.to_i < 99 then
Nginx.redirect "/error.html"
else
Nginx.echo r.var.send r.var.arg_name.to_sym
end
リバースプロキシの設定
動作確認
JMeter を使って動作確認を行いました。
テスト前
スレッドは 100 でいきたいと思います!
接続開始直後
順調に伸びてますな…
しきい値を超えたあたり
おお、しきい値を超えたあたりで以下のようにエラーページが表示されました。
すばらしひ。
これで要件を満たせそうです。
ひとまずまとめ
やりたいこと(2) がまだ出来ていませんが…
今回やったこと
- ngx_mruby と Nginx を利用してコネクション数等を取得した
- 取得したコネクション数に応じた処理を簡単なスクリプトで作った
- 同時接続数をカウントして接続数に応じた処理を簡単なスクリプトで実装した
引き続きの課題
- Nginx をリバースプロキシとして設定して引き続き試す
- やりたいこと(2)がまだ手付かず
- 毎回パラメータをつけてアクセスさせるのは URL の見た目としてはあまり良くないのでパスに対してパラメータ無しの場合(デフォルトは connection_waiting)が出来るかどうかを確認する
@matsumotory さんにご協力頂いてやりたいことを実現しつつあります。@matsumotory さん、本当に有難うございました。引き続き、よろしくお願い致します!(^^ゞ
元記事は、こちら