ローカル環境でVagrant+nginxで気軽にリバースプロキシする

サーバーサイドのアプリケーションがあって、静的コンテンツだけHTTPサーバーで配信したいっていうユースケースがあると思います。

サーバー上で動かす場合にはだいたいこんな感じにHTTPサーバーでリバースプロキシしてアプリケーションと静的コンテンツを振り分けますよね。

f:id:masato47744:20140828015950p:plain

じゃあ、これをローカルの開発環境のときはどうする?っていう話です。

ローカル環境のホストOSにはHTTPサーバーはたてたくない

ローカル環境で同じことするには、同じようにHTTPサーバーをたてればいいんですが、複数のプロジェクトをかけもちなんかしたりしてるとそれ用のvirtualhostを書いたり、hostsでホスト名を書いたりしてなんかダサい感じになってしまいます。あとは、複数人で開発なんかすると、Windowsの人もいれば、Macの人もいるし、各自色々なプロジェクトもってたりで、こっちでは動くけど、あっちでは動かないとか嫌な感じになります。

そこで、VagrantでHTTPサーバーたてて振り分けられないかなと思って試してみました。

この図の左側みたいに、XAMPPとかでホスト側にHTTPサーバーたてるんじゃなく、右側みたいにVagrantを使ってホスト側はきれいなままにしておきたいっていう気持ちです。このプロジェクトを開発するなら、このVagrantをupするだけみたいにしたい!

f:id:masato47744:20140828023046p:plain

右側のVagrantを使ったやつの説明をすると、

  • Vagrantで立てた仮想サーバー内にNginxが入っててNginxは80番ポートで待ち受ける
  • 仮想サーバーはポートフォワーディングでホストの8000番ポートをゲストのnginxの80番ポートに転送
  • 仮想サーバーはVagrantの共有ファイル機能を使って、ホスト側の静的コンテンツをNginxに配信させる
  • ホスト側でサーバーアプリケーションを起動(この図の場合はJavaで9000番ポートで起動)
  • Nginxは静的ファイル以外のリクエストならホストのJavaアプリケーションにリバースプロキシする

これで、ブラウザからは、http://localhost:8000/でアクセスすれば、ホスト側のサーバーアプリケーションにつながるし、静的コンテンツはそのまま配信されるという感じ。

これをするなら、Javaのアプリケーションも仮想マシン上で動かしちゃえば?ってことになるんだろうけど、仮想マシンにあんまりメモリとか割り当てたくないし、ホスト側のCPUとかメモリを使いたいなって思ったのでこうしました。

ということで、実際に書いた設定ファイルの説明をしていきます。

Vagrantfile

CentOS6.5でinitする。chef/centos-6.5

vagrant init chef/centos-6.5

Vagrantfileができるので、設定を書き換えます。
ポートフォワーディングの設定と、静的コンテンツを共有フォルダとして、/var/www/html/におく。プロビジョニングはchef分からないのでシェルです。(chef勉強しないと。。)

# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # Every Vagrant virtual environment requires a box to build off of.
  config.vm.box = "chef/centos-6.5"

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  config.vm.network "forwarded_port", guest: 80, host: 8000

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  config.vm.synced_folder "/path/to/your/static/resources/", "/var/www/html"

  config.vm.provision "shell", path: "provision.sh"
end

provision.sh

次に、プロビジョニングするshの内容です。
nginxインストールして、ローカル環境だからとりあえず、ファイアウォール切る。

function install_nginx() {
    rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
    yum -y install nginx
    cp -f /vagrant/default.conf /etc/nginx/conf.d/default.conf
    service nginx start
    chkconfig nginx on
}

function stop_iptables() {
    service iptables stop
    chkconfig iptables off
}

install_nginx
stop_iptables

default.conf (nginx)

nginxの設定ファイルはこのプロビジョニングの時に、cp -f /vagrant/default.conf /etc/nginx/conf.d/default.confという感じでこのVagrantfileが置いてある場所にconfを用意しといて上書きする。
ここで、ちょっと一工夫があって、Vagrantで作った仮想サーバーのデフォルトゲートウェイは必ず、10.0.2.2になるようなので、リバースプロキシ先は、10.0.2.2としとく。あと、rootは、共有フォルダとしておいた/var/www/html/

proxy_buffering    off;
proxy_set_header   X-Real-IP $remote_addr;
proxy_set_header   X-Scheme $scheme;
proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   Host $http_host;
proxy_http_version 1.1;

upstream playframework {
    server 10.0.2.2:9000;
}

server {
    listen       80;
    server_name  localhost;
    root         /var/www/html;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        if (-f $request_filename) {
            break;
        }

        proxy_pass http://playframework;
        proxy_redirect default;
    }

    #error_page  404              /404.html;
// 以下省略

これで、ホスト側のブラウザから、http://localhost:8000/ってアクセスすれば、ホスト側のJavaアプリケーションにつながって、静的ファイルが存在すれば、静的ファイルが配信されます。

まとめ

これで、ホスト側のhostsとか書き換えなくても気軽にリバースプロキシがたてられました!
個人的にこのVagrant+nginx方式のいいところはもっとあって、全ての設定ファイルがソース管理にコミットできることです。しかも、Vagrantで余計なプラグインも入れてないから、Windowsだろうが、Macだろうが、OSのRubyのバージョンがなんであろうと、なんならOSにRubyが入ってなかろうがみんな同じように動くところがとてもいいと思ってます。