CentOSでngx_mrubyをビルドするDockerfileを書いてみた

# はじめに

[matsumoto-r/ngx_mruby](https://github.com/matsumoto-r/ngx_mruby)をCentOSで試そうと思い、ビルドするためのDockerfileを書いてみました。
[hnakamur/docker-ngx_mruby-centos](https://github.com/hnakamur/docker-ngx_mruby-centos)に置いてあります。MITライセンスです。

# ngx_mrubyのビルド設定

ngx_mrubyではデフォルトで以下のモジュールをビルドするようになっています。

```rb:https://github.com/matsumoto-r/ngx_mruby/blob/1b31fd205abede101dc44ebd042d702e3abbe84b/build_config.rb#L17-L34
  #
  # Recommended for ngx_mruby
  #
  # comment out because of moving error.h
  conf.gem :git => 'git://github.com/iij/mruby-io.git'
  conf.gem :git => 'git://github.com/iij/mruby-process.git'
  conf.gem :git => 'git://github.com/iij/mruby-pack.git'
  conf.gem :git => 'git://github.com/iij/mruby-digest.git'
  conf.gem :git => 'git://github.com/mattn/mruby-json.git'
  conf.gem :git => 'git://github.com/matsumoto-r/mruby-redis.git'
  conf.gem :git => 'git://github.com/matsumoto-r/mruby-vedis.git'
  conf.gem :git => 'git://github.com/matsumoto-r/mruby-memcached.git'
  conf.gem :git => 'git://github.com/matsumoto-r/mruby-sleep.git'
  conf.gem :git => 'git://github.com/matsumoto-r/mruby-userdata.git'
  conf.gem :git => 'git://github.com/mattn/mruby-onig-regexp.git'

  # ngx_mruby extended class
  conf.gem :git => 'git://github.com/matsumoto-r/mruby-ngx-mruby-ext.git'
```

私の書いたDockerfileではbuild_config.rbを以下のように変更しています。

```text:https://github.com/hnakamur/docker-ngx_mruby-centos/blob/febda057d8b6746e653012f1b3b2399f5295786a/Dockerfile#L63-L65
    sed -i.orig "s|conf.gem :git => 'git://github.com/matsumoto-r/mruby-memcached.git'|#&|;/^  conf.gem :git => 'git:\/\/github.com\/mattn\/mruby-onig-regexp.git'/a\
\  conf.gem :git => 'git://github.com/iij/mruby-env.git'" \
      build_config.rb && \
```

* mruby-memcachedはビルド対象から外す
    * [matsumoto-r/mruby-memcached](https://github.com/matsumoto-r/mruby-memcached)はlibmemcachedのソースをビルドして参照するようにしてみたんですが、mruby-memcachedがコンパイルエラーになってしまいました。とりあえず私は使う予定がなかったので外すことにしました。
* mruby-envを追加
    * mrubyのスクリプト内で環境変数を参照したかったので[iij/mruby-env](https://github.com/iij/mruby-env)を追加しました。

# nginxのビルド

[matsumoto-r/ngx_mrubyのBuildのドキュメント](https://github.com/matsumoto-r/ngx_mruby#2-build)には3通りの方法が書かれています。nginxにモジュールを追加したい場合もあるので、3番めの方法にしたかったのですが、mrubyがビルドされずエラーになってしまったので、1番めの方法でmrubyをビルドしておいてから、3番めの方法でビルドするようにしてみたら、うまく行きました。

nginxのconfigureは以下のようにしています。適宜変更してご利用ください。

```text:https://github.com/hnakamur/docker-ngx_mruby-centos/blob/febda057d8b6746e653012f1b3b2399f5295786a/Dockerfile#L100-L125
    ./configure --prefix=/usr/local/nginx \
      --user=nginx \
      --group=nginx \
      --with-file-aio \
      --with-ipv6 \
      --with-http_ssl_module \
      --with-http_realip_module \
      --with-http_addition_module \
      --with-http_xslt_module \
      --with-http_image_filter_module \
      --with-http_geoip_module \
      --with-http_sub_module \
      --with-http_dav_module \
      --with-http_flv_module \
      --with-http_mp4_module \
      --with-http_gzip_static_module \
      --with-http_random_index_module \
      --with-http_secure_link_module \
      --with-http_degradation_module \
      --with-http_stub_status_module \
      --with-md5-asm \
      --with-sha1-asm \
      --with-pcre-jit --with-pcre=/usr/local/src/pcre-8.35 \
      --with-cc-opt='-O2 -g' \
      --add-module=/usr/local/src/ngx_mruby \
      --add-module=/usr/local/src/ngx_mruby/dependence/ngx_devel_kit && \
```


# mrubyとnginxが依存しているもののビルドまたはインストール

* ruby
    * mrubyのビルドに必要。2.1.1をソースからビルド
* [Redis](http://redis.io/)
    * [redis/hiredis](https://github.com/redis/hiredis)のビルドに必要
    * ソースからビルド
* [redis/hiredis](https://github.com/redis/hiredis)
    * [matsumoto-r/mruby-redis](https://github.com/matsumoto-r/mruby-redis)が依存
    * ソースからビルド
* [PCRE - Perl Compatible Regular Expressions](http://www.pcre.org/)
    * JITを使いたい(nginxのconfigureの--with-pcre-jitオプション)ので最新版8.35をソースからビルド
* [GeoIP](https://www.maxmind.com/ja/geolocation_landing)
    * これは依存しているというわけではなく、nginxにGeoIPモジュールを組み込んで(nginxのconfigureの--with-http_geoip_moduleオプション)ビルドしたかったので利用。
    * epelからインストール
    * ソースからビルドする場合は[How to install Nginx GeoIP module](http://www.nginxtips.com/how-to-install-nginx-geoip-module/)が参考になりそうです（試してないです）。

# Dockerイメージのビルド

Dockerfileのあるディレクトリで以下のコマンドを実行します。

```shell-session
docker build -t hnakamur/ngx_mruby .
```

# コンテナの起動

redisは[dockerでlua-resty-redisのコンテナからredisのコンテナに通信するサンプル - Qiita](http://qiita.com/hnakamur/items/1a0c401bdaef88b3f7f5)の記事で作ったコンテナを利用します。以下のコマンドでredisのコンテナを起動します。

```shell-session
docker run -d -p 6379:6379 -name redis hnakamur/redis
```

以下のコマンドでngx_mrubyのコンテナを起動します。

```shell-session
docker run -d -p 80:80 -link redis:redis hnakamur/ngx_mruby
```

# ngx_mrubyからredisにアクセスするサンプル

まずDockerによって環境変数を参照するためにnginx.confに以下の設定を入れています。

```text:https://github.com/hnakamur/docker-ngx_mruby-centos/blob/febda057d8b6746e653012f1b3b2399f5295786a/conf/nginx.conf#L1-L2
env REDIS_PORT_6379_TCP_ADDR;
env REDIS_PORT_6379_TCP_PORT;
```

/redisのlocation設定は以下のようにしています。

```text:https://github.com/hnakamur/docker-ngx_mruby-centos/blob/febda057d8b6746e653012f1b3b2399f5295786a/conf/nginx.conf#L26-L28
        location /redis {
            mruby_content_handler /usr/local/nginx/conf/get_redis.rb;
        }
```

```rb:https://github.com/hnakamur/docker-ngx_mruby-centos/blob/febda057d8b6746e653012f1b3b2399f5295786a/conf/get_redis.rb
host = ENV["REDIS_PORT_6379_TCP_ADDR"]
port = ENV["REDIS_PORT_6379_TCP_PORT"].to_i
db = Redis.new host, port

# Note: a value must be String
# > db.set "key1", 100
# (mirb):1: expected String (TypeError)

db.set "key1", "100"

value = db.get "key1"

r = Nginx::Request.new
r.content_type = "text/html"
Nginx.rputs "host = #{host}, port = #{port}, value is #{value}\n"
```

私の環境では[dockerでlua-resty-redisのコンテナからredisのコンテナに通信するサンプル - Qiita](http://qiita.com/hnakamur/items/1a0c401bdaef88b3f7f5)に書いたように8080→80のポートフォワーディングを設定していますので、OSXで

```shell-session
curl http://localhost:8080/redis
```

でアクセスすると

```
host = 172.17.0.2, port = 6379, value is 100
```

といった出力が得られます。