どうも、cloudpack の 自称 HAProxy 王子の かっぱ@inokara)です。

はじめに…

前回「HAProxy チートシート(acl や SSL サポート等)」の続きです。
前回の記事で URI パスによる振り分けを検証した際に…以下のようなプロキシが出来なくて残念な気分になっていたら…
HAProxyチートシート(ACLによるリクエストパスの振り分け): 構成図

reqirep を利用してバックエンド側でパスを書き換えるという方法があったので試してみましたとさ。

reqirep を使わない場合

制限

上記のようにクライアントから以下のようにアクセスがあり、acl を利用してパス毎のサーバーを振り分けたい場合に…

http://example.com/hoge

バックエンド側のアプリケーションサーバー側で以下のように設定する必要がありました。

${Document_Root}/hoge

むむ…これはちょっと…

reqirep を使う

reqirep とは…

ドキュメントには以下のように書かれています。

Replace a regular expression with a string in an HTTP request line

HTTP のリクエストを正規表現で書き換える機能で以下のように記述します。

reqirep   [{if | unless} ]   (ignore case)

例えば、/user/ というアクセスがあった場合にbackend のホスト側で / に書き換えたい場合には以下のように設定します。

reqirep ([^ ]*) /user/(.*) 1 /2

haproxy.cfg

クライアントからアクセスが /hoge/ であった場合には backend hoge_bk${Document_Root}/ にルーティングさせる場合には以下のように設定します。

frontend ha-test
  bind 0.0.0.0:80
  acl hoge path_beg /hoge
  acl fuga path_beg /fuga
  use_backend hoge_bk if hoge
  use_backend fuga_bk if fuga
  default_backend hoge_bk

backend hoge_bk
  reqirep ([^ ]*) /hoge/(.*) 1 /2
  server web01 ${backend_host01}:80 check
backend fuga_bk
  reqirep ([^ ]*) /fuga/(.*) 1 /2
  server web01 ${backend_host02}:80 check

同様に /fuga/ でアクセスがあった場合には backend fuga_bk${Document_Root}/ にルーティングさせる場合にも同様です。

ということで…

reqirep を利用することで下図のような動作を HAProxy で行わせることが出来るようになります。
HAProxyチートシート(ACLによるリクエストパスの振り分け): reqirepを使った構成図

やったー。HAProxy サイコー。

demo

ということで…

デモってみます。

仕込み(HAProxy)

frontend balancer-test
  bind 0.0.0.0:80
  stats enable
  stats uri /haproxy?stats
  acl hoge path_beg /hoge
  acl fuga path_beg /fuga
  use_backend hoge_bk if hoge
  use_backend fuga_bk if fuga
  default_backend hoge_bk

backend hoge_bk
  reqirep ([^ ]*) /hoge/(.*) 1 /2
  reqadd Use_HAProxy_backend: hoge_bk
  server web01 backend_host:8001 check

backend fuga_bk
  reqirep ([^ ]*) /fuga/(.*) 1 /2
  reqadd Use_HAProxy_backend: fuga_bk
  server web02 backend_host:8002 check

/hogehoge_bk に振り分けを行い、/fugafuga_bk に振り分けを行います。また、ちゃんとバックエンドが振り分けられているかを識別する為に以下のようにヘッダを追加しています。

  reqadd Use_HAProxy_backend: [hoge_bk|fuga_bk]

バックエンド側の Nginx のログに記録させます。

仕込み(バックエンドの Nginx)

server {
    listen       [8001|8002];

    location / {
        root   [html_8001|html_8002];
        index  index.html index.htm;
    }
  }

それぞれの DocumentRoot 以下に hoge 又は fuga と書いた index.html を配置しました。

また、Nginx のログに HAProxy 側で付加した Use_HAProxy_backend ヘッダを記録するように以下のように設定を追加しています。

    underscores_in_headers on;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '$ssl_protocol/$ssl_cipher'
                      '"$http_user_agent" "$http_x_forwarded_for"'
                      ' "$http_use_haproxy_backend"';

underscores_in_headers on;_ 付きのリクエストヘッダを容認します。また、Use_HAProxy_backend は変数の $http_use_haproxy_backend に記録されます(オリジナルヘッダの場合には $http_ を付けるようです)。

アクセス

/hoge にアクセスしてみましょう
% curl ec2-xx-xx-xx-xxx.ap-northeast-1.compute.amazonaws.com/hoge/
hoge

Nginx のログには以下のように記録されました。

xxx.xxx.xxx.xxx - - [16/Nov/2014:08:46:27 +0000] "GET / HTTP/1.1" 200 5 "-" -/-"curl/7.30.0" "-" "hoge_bk"

ちゃんとhoge_bk に振り分けられて ${DocumentRoot}/index.htmlにアクセスされています。

/fuga にアクセスしてみましょう

% curl ec2-xx-xx-xx-xxx.ap-northeast-1.compute.amazonaws.com/fuga/
fuga

Nginx のログには以下のように記録されました。

xxx.xxx.xxx.xxx - - [16/Nov/2014:08:49:28 +0000] "GET / HTTP/1.1" 200 5 "-" -/-"curl/7.30.0" "-" "fuga_bk"

ちゃんと fuga_bkに振り分けられて ${DocumentRoot}/index.html にアクセスされています。

お疲れ様でした

HAProxy があればー、なんでも出来るー(アン◯ニオ猪木風)って感じです。
あと、Nginx でオリジナルヘッダを記録する方法を知ることが出来て良かったデス!

元記事はこちらです。
HAProxy チートシート(ACL を使ったリクエストパスによる振り分けについて)