2014年2月23日日曜日

Solaris11 + nginx + Lua + Redis で動的リバースプロキシサーバーを作ってみた

nginxによる動的リバースプロキシサーバー

以前、LXCの勉強会で知ったNginxとRedisを使った動的なプロキシサーバーに興味をそそられて、「これは素晴らしい!」といまさら影響を受け、この辺の資料や記事を漁ってみたもののLinux系の情報は多いのですがなかなかSolaris系の情報が少なかったため、「では、Solarisでやってみましょう」ということでやってみました

やりたいこと

今回のサンプル環境でやりたいことは、URLに含まれるディレクトリが、Redisへ登録されている場合だけ静的コンテンツを表示するwebサーバーへ(server3)転送し、それ以外は動的コンテンツを表示するwebサーバー(server1、server2)へ転送するという処理をnginxで処理させます。

構成

環境として、愛しのGentoo Linuxへインストールしたvmware player上のSolaris11で構築しました、こんな感じです

 

それぞれ5つのzoneを作成して必要なアプリケーションをインストールします。

作成するZone

  • ngx
    • nginx + Lua + Redis
  • server1
  • server2
    • apache + PHP + ZendFramework(動的コンテンツ出力用)
  • server3
    • apache(静的コンテンツ出力用) 
  • iichiko-spec
    • パッケージサーバー(IPS)
以上のZoneをSolaris11上へ作成します

インストール

まず、全部パッケージをビルドするのは面倒なので、パッケージサーバーを作成しそこからインストールします。このパッケージは私がビルドしたものなので、ご利用の際は自己責任でお願いします、検証のために作成したためテストなどは一切行っておりませんのでご注意ください。

iichiko-specゾーンの作成

パッケージインストール用zoneの作成、これは他のzoneで代用しても構いません。

iichiko-spec.cfg
sol11 ~ # cat iichiko-spec.cfg
create -b
set brand=solaris
set zonepath=/rpool/zones/iichiko-spec
set autoboot=false
set ip-type=shared
add net
set address=192.168.254.102/24
set configure-allowed-address=true
set physical=net0
end
iichiko-specゾーンの構築
sol11 ~ # zonecfg -z iichiko-spec -f iichiko-spec.cfg
sol11 ~ # zoneadm -z iichiko-spec install
sol11 ~ # zoneadm -z iichiko-spec boot
sol11 ~ # zlogin -C iichiko-spec
パッケージサーバーを設定
こでで、iichiko-specからnginxとredisなどの必要パッケージをインストールすることが出きるようになります。
root@iichiko-spec:~# zfs create -p -o mountpoint=/var/pkglocal rpool/pkglocal
root@iichiko-spec:~# svccfg -s application/pkg/server setprop pkg/inst_root=/var/pkglocal
root@iichiko-spec:~# pkgsend -s file:///var/pkglocal create-repository --set-property publisher.prefix=iichiko-spec
root@iichiko-spec:~# svccfg -s pkg/server setprop pkg/port=80
root@iichiko-spec:~# svccfg -s svc:/application/pkg/server setprop pkg/readonly=true
root@iichiko-spec:~# svcadm refresh pkg/server
root@iichiko-spec:~# wget http://www.karky7.com/files/ips-2014.02.22.zfs.img.gz
root@iichiko-spec:~# gunzip < ips-2014.02.22.zfs.img.gz | zfs recv -F rpool/pkglocal
root@iichiko-spec:~# svcadm enable pkg/server

http://192.168.254.102にアクセスしてこんな画面がでればOKです


nginxをソースからビルドする場合

今回のnginxへ組み込んだモジュールは
  • nginx-1.5.8
  • ngx_http_redis-0.3.7
  • lua_nginx_module-0.9.5rc2
です、ソースからインストールする方は、configureにモジュールを展開したディレクトリを指定してください

$ wget http://nginx.org/download/nginx-1.5.8.tar.gz
$ tar -xzvf nginx-1.5.8.tar.gz
$ cd nginx-1.5.8
$ ./condifgure \
--add-module=../ngx_http_redis-0.3.7 \
--add-module=../lua_nginx_module-0.9.5rc2
...
...


ngxゾーンの構築

ngx.cfg
create -b
set brand=solaris
set zonepath=/rpool/zones/ngx
set autoboot=false
set ip-type=shared
add net
set address=192.168.254.200/24
set configure-allowed-address=true
set physical=net0
end
ngxゾーンのインストール
sol11 ~ # zonecfg -z ngx -f ngx.cfg
sol11 ~ # zoneadm -z ngx install
ngxゾーンへアプリケーションのインストール
sol11 ~ # zlogin ngx
root@ngx:~# pkg set-publisher -g http://192.168.254.102 iichiko-spec
必要なアプリケーションをインストール

root@ngx:~# pkg install pkg://iichiko-spec/service/redis-28
root@ngx:~# pkg install pkg://iichiko-spec/web/server/nginx
root@ngx:~# pkg install pkg://iichiko-spec/library/lua/resty-redis
root@ngx:~# pkg install pkg://iichiko-spec/library/lua/jit
root@ngx:~# pkg install runtime/lua
root@ngx:~# svcadm enable svc:/application/database/redis_28:default_64bit

nginxからRedisへアクセスする設定

Redisへサンプルとして使うデータをセットする、イメージ的にはこんな感じです
「static1」、「static2」、「static3」へアクセスした場合だけ、静的なコンテンツを出力するwebサーバーへ転送します。

root@ngx:~# redis-cli
127.0.0.1:6379>

127.0.0.1:6379> RPUSH "www.samohan.jp" "static1"
(integer) 1
127.0.0.1:6379> RPUSH "www.samohan.jp" "static2"
(integer) 2
127.0.0.1:6379> RPUSH "www.samohan.jp" "static3"
(integer) 3
127.0.0.1:6379>

nginx + luaの設定を作成

nginxからlua経由でRedisへアクセスするための設定をnginx.confへ書いておきます。
細かい設定は調整してください、私自身も細かいところまで解ってません 笑...
upstream の部分がzendが動いているserver1、server2へ振る設定です、access_by_luaの部分がluaの部分でRedisへアクセスするコードです。luaがライブラリにアクセス出きるようにlua_package_pathの設定を忘れずに書いておいてください。
# /etc/nginx/nginx.conf
#user  nobody;
worker_processes  1;

# error_log  logs/error.log;
#error_log  logs/error.log  notice;

# ここをコメントアウトするとINFOレベルのログの出力が止まります
error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    lua_package_path "/usr/lib/lua/?.lua;;";

    upstream zend {
        server 192.168.254.201:80;
        server 192.168.254.202:80;
    }

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
        set $target '';
        access_by_lua '
            local domain_name = ngx.var.host
            local path = ngx.var.uri
            ngx.log(ngx.INFO, "Get domain_name: ", domain_name)
            ngx.log(ngx.INFO, "Get path: ", path)

            local redis = require "resty.redis"
            local red = redis:new()

            red:set_timeout(1000) -- 1 second

            local ok, err = red:connect("127.0.0.1", 6379)
            if not ok then
                ngx.log(ngx.ERR, "failed to connect to redis: ", err)
                return ngx.exit(500)
            end

            local klen, err = red:llen(domain_name)
            if klen == 0 then
                ngx.log(ngx.ERR, "failed to llen redis key: ", err)
                return ngx.exit(500)
            end
            local maxindex = klen - 1
            ngx.log(ngx.INFO, "Get maxindex: ", maxindex)
                
            local t = {}
            local find = false
            local m = nil
            for i = 0, maxindex do
                t[i] = red:lindex(domain_name, i)
                m = string.match(path, "^\/" .. t[i] .. "\/")
                if m ~= nil then
                    ngx.log(ngx.INFO, "Found : ", m)
                    find = true
                    break
                end
                ngx.log(ngx.INFO, "Get data: ", t[i])
            end

            if find == true then
                ngx.var.target = "192.168.254.203"
            else
                ngx.var.target = "zend"
            end
        ';

        proxy_pass http://$target;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}
設定ができましたらnginxを再起動してください
root@ngx:~# svcadm enable nginx
root@ngx:~# svcs -xv
root@ngx:~#


server1、server2、server3ゾーンの構築

server1とserver2は同じものを作成する、動的なページの生成で利用する、server3は静的ページを表示するために利用。
server1.cfg
sol11 ~ # cat server1.cfg
create -b
set brand=solaris
set zonepath=/rpool/zones/server1
set autoboot=false
set ip-type=shared
add net
set address=192.168.254.201/24
set configure-allowed-address=true
set physical=net0
end
server2.cfg
sol11 ~ # cat server2.cfg
create -b
set brand=solaris
set zonepath=/rpool/zones/server2
set autoboot=false
set ip-type=shared
add net
set address=192.168.254.202/24
set configure-allowed-address=true
set physical=net0
end
server3.cfg
sol11 ~ # cat server3.cfg
create -b
set brand=solaris
set zonepath=/rpool/zones/server3
set autoboot=false
set ip-type=shared
add net
set address=192.168.254.203/24
set configure-allowed-address=true
set physical=net0
end
ゾーン作成
sol11 ~ # zonecfg -z server1 -f server1.cfg
sol11 ~ # zonecfg -z server2 -f server2.cfg
sol11 ~ # zonecfg -z server3 -f server3.cfg
sol11 ~ # zoneadm -z server1 install 
sol11 ~ # zoneadm -z server2 clone server1
sol11 ~ # zoneadm -z server3 clone server1
最終的なゾーンはこの様な形になります
sol11 ~ # zoneadm -z list -vc
  ID NAME             STATUS     PATH                           BRAND    IP
   0 global           running    /                              solaris  shared
  10 iichiko-spec     running    /rpool/zones/iichiko-spec      solaris  shared
  14 ngx              running    /rpool/zones/ngx               solaris  shared
  15 server1          running    /rpool/zones/server1           solaris  shared
  16 server2          running    /rpool/zones/server2           solaris  shared
  17 server3          running    /rpool/zones/server3           solaris  shared

server1、server2へZendFrameworkをインストール

server2も同じ構成でインストールしてください
sol11 ~ # zlogin server1
root@server1:~# pkg install pkg:/web/server/apache-22
root@server1:~# pkg install pkg:/web/server/apache-22/module/apache-php52
root@server1:~# wget http://packages.zendframework.com/releases/ZendFramework-1.12.3/ZendFramework-1.12.3.tar.gz
root@server1:~# mkdir -p /usr/local/share/php
root@server1:~# tar -xzvf ZendFramework-1.12.3.tar.gz
root@server1:~# cp -rf ZendFramework-1.12.3/library/Zend/ /usr/local/share/php/
php.iniの設定
root@server1:~# emacs /etc/php/5.2/php.ini
include_path = ".:/var/php/5.2/pear:/usr/local/share/php"
ZendFrameworkの設定
server1とserver2へ展開してください
root@server1:~# cd /
root@server1:/# wget http://www.karky7.com/files/zend_sample.tar.gz
root@server1:/# tar -xzvf zend_sample.tar.gz
root@server1:/# chown -R webservd:webservd web
root@server1:/# tree /web
/web
├── application
│   ├── controllers
│   │   ├── ErrorController.php
│   │   ├── IndexController.php
│   │   └── SampleController.php
│   └── views
│       ├── index
│       │   └── index.phtml
│       └── sample
│           └── sample.phtml
└── www
    └── index.
root@server1:/#
続いてhttpd.confの設定
root@server1:~# diff /etc/apache2/2.2/httpd.conf /etc/apache2/2.2/httpd.conf.ORG
113c113
< DocumentRoot "/web/www"
---
> DocumentRoot "/var/apache2/2.2/htdocs"
140c140
< <Directory "/web/www">
---
> <Directory "/var/apache2/2.2/htdocs">
153c153
<     Options All
---
>     Options Indexes FollowSymLinks
160c160
<     AllowOverride All
---
>     AllowOverride None

server3の構築

server3は普通の静的ファイルを返すだけのapacheサーバーとして構築する
sol11 ~ # zlogin server3
root@server3:~# pkg install pkg:/web/server/apache-22

アクセスしてみる

最後に、ドメイン経由でブラウザからアクセスしたいので /etc/hostsへドメインを追加する(DNS立てれないからね)、ちなみにここで言うhostsファイルは、ブラウザでアクセスするホストの奴です、僕で言うとGentooのhostsフィルです。
192.168.254.200はngxゾーンのIPです。
192.168.254.200  www.samohan.jp
ブラウザから以下のURLへアクセスしてみますと、Redisへ登録されているstatic1、static2、static3へのアクセスはserver3へ転送され、それ以外はserver1、server2へ転送される。
  • http://www.samohan.jp/ ・・・ zendサーバーへ、server1、server2
  • http://www.samohan.jp/static1/ ・・・ server3へ
  • http://www.samohan.jp/static2/ ・・・ server3へ
  • http://www.samohan.jp/static3/ ・・・ server3へ
  • http://www.samohan.jp/static4/ ・・・ zendサーバーへ転送されErrorControllerで捕まる

こんな感じで、Redisに保存されている情報を元に色々振り分ける事ができます。
これは素晴らしい
この他にもnginxって色々使えそうでビックリしてる所です、今後、適材適所でnginxを利用していこう思います。

書きすぎでまとまりがありませんが、間違いがありましたら指摘していただければ幸です

最後に謝辞


この度、パッケージングで作成したnginxは @ftnk 先生のspecをカスタマイズして利用させていただきました、ブログからで申し訳ございませんがお礼を申し上げます、ありがとうございました。また色々お世話になると思いますのでよろしくお願いします。


0 件のコメント:

コメントを投稿