この記事は MySQL Casual Advent Calendar 2013 の 12 日目です。
みんな大好き Nginx + Lua ですが、その Lua から MySQL が叩けるとなると、Nginx だけでウェブアプリケーションが書けちゃうという夢が広がりますね。
難しそうというイメージがあるかもしれませんが、実は OpenResty を使うと、そんな環境が簡単に作れてしまうので、今日はその方法を紹介します。
ngx_openresty のインストール
今回は Ubuntu 12.04 での例ですが、ほとんど同様の手順で CentOS 6.5 でも動くことを確認しています。 *1
$ sudo apt-get -y install gcc make libpcre3-dev libssl-dev perl5 wget $ sudo apt-get -y install libmysqlclient-dev $ wget http://openresty.org/download/ngx_openresty-1.4.3.3.tar.gz $ cd ngx_openresty-1.4.3.3 $ ./configure --with-luajit && make $ sudo make install
上記の手順で、 /usr/local/openresty/nginx/
以下に Nginx がインストールされます。ここでインストールされた nginx には、すでに Lua モジュールや lua-resty-mysql モジュールが含まれているので、すぐに Nginx + Lua + MySQL を使いはじめることができます。
なお今回は手抜きで libmysqlclient-dev パッケージを使っていますが、カジュアル勢の皆様におかれましては秘伝のビルドをリンクさせると良いと思います。
Lua + MySQL のコード例
簡単なアクセスカウンタを作ってみます。
/usr/local/openresty/nginx/conf/nginx.conf
に以下のような記述を追加します。
location /counter { content_by_lua_file conf/counter.lua; }
/usr/local/openresty/nginx/conf/counter.lua
に以下のようなコードを記述します。
-- based on sample code at https://github.com/agentzh/lua-resty-mysql local mysql = require "resty.mysql" local db, err = mysql:new() if not db then ngx.log(ngx.ERR, "failed to instantiate mysql: ", err) return end local ok, err, errno, sqlstate = db:connect{ host = "127.0.0.1", port = 3306, database = "lua", user = "lua", password = "lua", max_packet_size = 1024 * 1024 } if not ok then ngx.log(ngx.ERR, "failed to connect: ", err, ": ", errno, " ", sqlstate) return end res, err, errno, sqlstate = db:query("UPDATE counters SET value = value + 1 WHERE id = 1") if not res then ngx.log(ngx.ERR, "bad result: ", err, ": ", errno, ": ", sqlstate, ".") return end res, err, errno, sqlstate = db:query("SELECT value FROM counters WHERE id = 1") if not res then ngx.log(ngx.ERR, "bad result: ", err, ": ", errno, ": ", sqlstate, ".") return end ngx.say("counter: " .. res[1]["value"]) local ok, err = db:set_keepalive(10000, 100) if not ok then ngx.log(ngx.ERR, "failed to set keepalive: ", err) return end
MySQL の方は、以下のようにユーザとテーブルを作っておきましょう。
GRANT ALL PRIVILEGES ON *.* TO 'lua'@'localhost' IDENTIFIED BY 'lua'; CREATE DATABASE lua; CREATE TABLE lua.counters(id INT PRIMARY KEY AUTO_INCREMENT, value INT NOT NULL); INSERT INTO lua.counters (value) VALUES (0);
Nginx を起動します。
$ sudo /usr/local/openresty/nginx/sbin/nginx
これで、以下のように /counter
でアクセスカウンタが動きます。
$ curl localhost/counter counter: 1 $ curl localhost/counter counter: 2 $ curl localhost/counter counter: 3 $ curl localhost/counter counter: 4
lua-resty-mysql の API
今回利用した API を紹介します。これらはほんの一部なので、詳しくはドキュメントを参照してください。
new
新しい MySQL アダプタオブジェクトを生成します。この時点ではまだ接続しません。
local mysql = require "resty.mysql" local db, err = mysql:new()
connect
MySQL に接続します。後述するコネクションプールが残っていれば、そのコネクションが再利用されるようです。
local ok, err, errno, sqlstate = db:connect{ host = "127.0.0.1", port = 3306, database = "lua", user = "lua", password = "lua", max_packet_size = 1024 * 1024 }
query
SQL を実行します。
local res, err, errno, sqlstate = db:query("SELECT value FROM counters WHERE id = 1")
Result Set は配列で返ってきます。上記の SELECT の結果からは、以下のようにして値を取り出せます。
ngx.say("counter: " .. res[1]["value"])
set_keepalive
コネクションプーリングの設定をします。
local ok, err = db:set_keepalive(10000, 100)
この例では、 10,000ms でタイムアウト、同時に 100 コネクションまでプールする、という設定をしています。
おまけ: Docker でサンプルを動かす
今回のサンプルプログラムを再現できる Dockerfile を作ってみました。以下のリポジトリで公開しています。
https://github.com/mirakui/nginx-lua-mysql-example
Docker 自体のインストール手順については、公式ドキュメントを参照してください。
Docker がインストールされていれば、以下のような手順で、今回の環境が再現できます。
Docker イメージの作成
Dockerfile からイメージを作成します。
$ git clone https://github.com/mirakui/nginx-lua-mysql-example.git $ cd nginx-lua-mysql-example $ sudo docker build -t ngx .
Docker コンテナの起動
作成したイメージを使ってコンテナを起動します。 docker ps
コマンドでコンテナが表示されていれば成功です。
$ sudo docker run -d ngx b32680fbc44ee55d309e8377963f9015fcb3e6733f55dc580276c9eff1952ba5 $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b32680fbc44e ngx:latest /bin/sh -c /usr/loca 4 seconds ago Up 2 seconds 3306/tcp, 80/tcp angry_heisenberg
サンプルコードの実行
立ち上がっているコンテナの IP アドレスを調べてから、その IP アドレスに対して /counter
リクエストを発行してみると、アクセスカウンタが動くはずです。
$ sudo docker inspect b32680fbc44e | jq '.[0].NetworkSettings.IPAddress' "172.17.0.17" $ curl 172.17.0.17/counter counter: 1 $ curl 172.17.0.17/counter counter: 2 $ curl 172.17.0.17/counter counter: 3 $ curl 172.17.0.17/counter counter: 4
おわりに
このように Nginx + Lua + MySQL の環境は、 OpenResty を使えばとても簡単に構築できます。今回のような手順を覚えておけば、 isucon や、 isucon 等で役に立つかもしれません。
明日 12/13 は @meijik さんです。お楽しみに。
*1:Mac OSX でも試したのですが pcre まわりでエラーになってしまい、まだビルドできていません…