Apache
AWS
AdventCalendar
mruby
mod_mruby

[mod_mruby] 静的リソースをリバースプロキシで配信する

More than 3 years have passed since last update.

これは 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 の可能性とディレクティブ設定/スクリプトの例



実現する構成

図にするとこんな感じです。

スクリーンショット 2014-12-16 21.55.22.png

最初からクライアントのデバイスから 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 が使えるようにしておきます。下記がコメントアウトされていたら(デフォルトでは恐らくコメントアウトされている)、コメントアウトを外します。


***.conf

LoadModule proxy_module modules/mod_proxy.so

LoadModule proxy_http_module modules/mod_proxy_http.so

適当な名称で mod_mruby スクリプトを書きます。リクエストされたファイルの拡張子を見て、該当するものであればリバースプロキシします。ソースはこれだけ。


/usr/local/apache2/mruby/proxy.rb

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 の処理フェーズにフックさせます。


***.conf

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 さんによる「生成コストの高いオブジェクトを共有するための基本」です!