これは mod_mruby ngx_mruby Advent Calendar 2014 の16日目(12/16)の投稿です。
今回は mod_mruby でのリバースプロキシして、Amazon S3 上の静的リソース(JavaScipt/CSS/HTML/画像)を配信する方法について書きます。S3 の WEBホスティングに対して HTTP でリソースを取得しにいっているので、S3 でなくても別にどのサーバに対しても、向き先をかえれば応用できます。
前提となる環境は、アドベントカレンダーの3日目に書きましたので、何かあればそちらを参照ください。
⇒ mod_mruby を Amazon EC2、Apache2.4 へ導入する
⇒ Apache のバージョンは2.4.10
、mod_mruby のバージョンは1.9.7
です。また、Apache へのディレクティブ設定、および、mod_mruby スクリプトのちょっとした例については 10日目に書きました。
⇒ mod_mruby の可能性とディレクティブ設定/スクリプトの例
実現する構成
図にするとこんな感じです。
最初からクライアントのデバイスから S3 の URL を参照しておけばいいじゃんという意見があるかもしれませんが、この構成は次のようなメリットがあると思っています。
- ① WEBサーバが複数ある場合に、静的リソースを S3 で一元管理できる。
- 静的なリソースのデプロイはアプリケーションと切り離して S3 だけで完結する。
- ② リリース後でも、クライアントへのインタフェースはそのままに、後から素材の場所(S3 から S3 以外へ)を変更したり、例えば 画像以外は やっぱりWEBサーバに置く、画像だけ別のサーバに置く、というような変更ができる。
- ③ 上記①と被るかもですが、HTTP レスポンスヘッダ
Last-Modified
およびEtag
の値をどの WEBサーバへのリクエストでも同じものにすることができる。- クライアントブラウザにキャッシュさせるときに重要。
- WEBサーバへデプロイする際に揃えるという方法もあるにはある。
- ④ URL のドメインを WEBサーバ(APIサーバ)と 静的リソースで同じにできる。
- 気分や管理の問題もありますが、それ以外には JavaScript アプリケーションでクロスオリジン(クロスドメイン)対策する必要がなくなる。
本当は プロキシした静的リソースに対して、mod_mruby で画像変換や JavaScript/CSS ファイルの縮小(minify)や結合(concat)、gzip 圧縮、WEBサーバへのキャッシュなどまでできれば強力なのですが、それは別の機会にということで.. (gzip や キャッシュは mod_deflate や mod_cache と組み合わせれば何となくいけそう。)
自分の職業柄、スマホ向けの通信の最適化などは意識している分野なので。通信の最適化については、こちらのアドベントカレンダーに書きました。
⇒ モバイルフロントエンドで通信を削減する戦略についての考察
悪用すれば、任意のWEBサイトの静的リソースを勝手に(厳密には動的 API も)自ドメインから配信できてしまうのですが^^; それはやらないでください。
また今回の構成だと静的リソースのみリバースプロキシしていますが、自サービスのバックエンドの動的 API にもリバースプロキシは可能です。
Apache での設定方法
とっても簡単です。
まず前提として、Apache で HTTP プロトコルの Proxy が使えるようにしておきます。下記がコメントアウトされていたら(デフォルトでは恐らくコメントアウトされている)、コメントアウトを外します。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
適当な名称で mod_mruby スクリプトを書きます。リクエストされたファイルの拡張子を見て、該当するものであればリバースプロキシします。ソースはこれだけ。
REMOTE_HOST = 'http://s3-hoge.amazonaws.com'
r = Apache::Request.new
if /\.(css|js|gif|jpe?g|png)$/ =~ r.filename
r.reverse_proxy REMOTE_HOST + r.uri
end
Apache::return(Apache::OK)
書いた mod_mruby スクリプトを .conf ファイルで Apache の処理フェーズにフックさせます。
mrubyFixupsLast /usr/local/apache2/mruby/proxy.rb
cache
オプションつけた方が良いかも。
mrubyTranslateNameMiddle
フェーズにフックだと、Proxy の設定がキャッシュされてしまう?感じだったのでmrubyFixupsLast
フェーズにフックしています。
あとは Apache を再起動したら終わりで、この設定を WEBサーバの台数分やります。すると例えば S3 の http://s3-hoge.amazonaws.com/hoge.js
の JavaScript ファイルが http://自ドメイン/hoge.js
で取得できます :smaile:
ただアクセスが多いサイトだと、どこかの層にキャッシュをもった方が良いので、例えば WEBサーバの前段に CroutFront を置くか、もしくは S3 の前段でも良いかと。もしくは上記の mod_mruby スクリプトに追加で実装するか、または mod_cache と組み合わせるとか。
まあ順当に考えて、WEBサーバの前段に CroudFront が手軽かな。ネットワーク帯域がもったいないし。1分のキャッシュでも強力に効くと思います。
おわりに
リバースプロキシを使いこなすと、色々たのしく便利なことが実現できるので、是非チャレンジしてもらえればと思います。明日は @matsumotory さんによる「生成コストの高いオブジェクトを共有するための基本」です!