LoginSignup
1
2

More than 5 years have passed since last update.

Apacheのmod_proxy_balancerでhttpとwebsocketの両方に対応したバランシングをする方法

Last updated at Posted at 2018-05-30

はじめに

Apacheを使ったリバースプロキシ構成で、バランシングをしない単純なproxyでバックエンドとhttpとwebsocketの両方に対応するにはmod_proxy_httpとmod_proxy_wstunnelを使えば実現できます。

しかし、mod_proxy_balancerと組み合わせると、そのままではhttpとwebsocketの両方に対応できず、苦労したので、その実現方法を記事にします。

実行環境

  • OS : CentOS 7.3
  • Apache:v2.4.28

mod_proxy_balancerの一般的な設定の場合

バランシング対象のproxy先のメンバが2つある場合を想定して設定ファイルを書くと次にようになります。

balancer.conf
ProxyRequests off

ProxyPass /sample/ balancer://samplecluster/sample/ stickysession=sampleID
ProxyPassReverse /sample/ balancer://samplecluster/sample/

<Proxy balancer://samplecluster/sample/>
    BalancerMember http://localhost:6091 loadfactor=1 route=r1
    BalancerMember http://localhost:6092 loadfactor=1 route=r2
</Proxy>

mod_proxy_balancerの仕様に従い、BalancerMemberに定義されるバックエンド側でstickysessionのcookieが設定される前提です。

実行結果

  • 404(NotFound)が返ってくる。

  • http通信は問題なくバランシングされるのですが、Upgradeされてwsスキーマ(ws://)になるとBalancerMemberに合致せず、また、自身にそのようなリソースもないためです。

設定変更して試してみる

BalancerMemberのスキーマをいじってみます。

wsスキーマの場合

balancer.conf
ProxyRequests off

ProxyPass /sample/ balancer://samplecluster/sample/ stickysession=sampleID
ProxyPassReverse /sample/ balancer://samplecluster/sample/

<Proxy balancer://samplecluster/sample/>
-    BalancerMember http://localhost:6091 loadfactor=1 route=r1
-    BalancerMember http://localhost:6092 loadfactor=1 route=r2
+    BalancerMember ws://localhost:6091 loadfactor=1 route=r1
+    BalancerMember ws://localhost:6092 loadfactor=1 route=r2
</Proxy>

この設定の場合、httpスキーマの時にバランシングされません。

httpとwsの両方のスキーマのメンバを定義した場合

balancer.conf
ProxyRequests off

ProxyPass /sample/ balancer://samplecluster/sample/ stickysession=sampleID
ProxyPassReverse /sample/ balancer://samplecluster/sample/

<Proxy balancer://samplecluster/sample/>
    BalancerMember http://localhost:6091 loadfactor=1 route=r1
    BalancerMember http://localhost:6092 loadfactor=1 route=r2
+    BalancerMember ws://localhost:6091 loadfactor=1 route=r1
+    BalancerMember ws://localhost:6092 loadfactor=1 route=r2
</Proxy>
  • 各メンバが独立して認識されるため、同じrouteでも別の接続と認識されてしまう
  • 最大接続数を1のように制限したい場合に、2つまで許容されてしまう

現状把握(実装調査)

mod_proxyのフック実行順をモジュールの実装で確認しました。

mod_proxyのproxy_handlerの主要なところの実装確認

mod_proxy_balancerとmod_proxy_wstunnelはmod_proxyのproxy_handler関数内から次のように呼び出されます。

  1. ap_proxy_pre_request呼び出し(mod_proxy_balancerが実装)
    • 適切なworkerを取得する
  2. proxy_hook_scheme_handler呼び出し(mod_proxy_wstunnelが実装)
    • scheme(今回の場合はwebsocket)に対応するのに必要な処理を実施

mod_proxy_balancerでは単純にconfファイルに書いてある内容のみ実施していて、http→wsのアップグレードで特別な処理はしていないように理解をしました。

スキーマ特有の操作はmod_proxy_wstunnelなどのスキーマに応じた別のモジュールのscheme_handler内で処理しているはずです。

実装を参考にすると、2.が呼び出されるまでの間にhttpスキーマをwsスキーマに変更すればmod_proxy_wstunnelでwebsocketの処理ができそうです。

対応策

スキーマの変更ということで、mod_rewriteを組み合わせます。

websocketの特徴であるhttpヘッダのupgradeフィールドとconnectionフィールドを手掛かりに、websocketと判断出来た場合にsticky session cookieの内容に応じたメンバのURLへ書き換えます。

次のすべての条件を満たしたときにRewriteRuleでURLを書き換えます。

  • httpヘッダのupgradeフィールドが"WebSocket"である
  • httpヘッダのconnectionフィールドが"Upgrade"である
  • cookieの値(今回の例ではsmapleID)がrouteに定義している値と同じである

今回は次のようにrewrite_ws.confというrewrite用のconfファイルを作成し、balancer.confからIncludeOptionalで読み込むようにしました。

balancer.conf
ProxyRequests off

ProxyPass /sample/ balancer://samplecluster/sample/ stickysession=sampleID
ProxyPassReverse /sample/ balancer://samplecluster/sample/

<Proxy balancer://samplecluster/sample/>
    BalancerMember http://localhost:6091 loadfactor=1 route=r1
    BalancerMember http://localhost:6092 loadfactor=1 route=r2
</Proxy>

+ IncludeOptional rewrite_ws.conf
rewrite_ws.conf
RewriteEngine On

RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
RewriteCond %{HTTP_COOKIE} ^.*(sampleID)=([^=]*)\.r1 [NC]
RewriteRule .* ws://localhost:6091%{REQUEST_URI} [P,L]

RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
RewriteCond %{HTTP_COOKIE} ^.*(sampleID)=([^=]*)\.r2 [NC]
RewriteRule .* ws://localhost:6092%{REQUEST_URI} [P,L]

このrewriteにより、cookieを手掛かりにmod_rewriteでURLを書き換えることで、バランシングが可能となります。

参考情報

httpsをwssへ書き換える事例ですが、今回のhttpをwsに書き換える対応として参考になりました。

おわりに

今回のようなmod_proxy_balancerでhttpとwsの両方に対応するというものは、それほど珍しくないユースケースだと個人的には考えていますが、web上にはそのものズバリな対応がなかったので記事にしてみました。

同じような対応で困っている人の助けになれば幸いです。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2