WebSocket(node + socket.io)をstunnel + HAProxyでプロキシする - その2 - HAProxyの設定

Nginxとnode.jsをHAProxy+stunnelでまとめる方法についてまとめています。

サーバーの構成は以下のようになります。

前回の記事はHAProxyのインストールまで紹介しました。
http://d.hatena.ne.jp/hrendoh/20120328/1332917793

今回は、HAProxyでNginxとnode.jsのサーバーに振り分ける設定方法と、各種パラメータについて実際の設定例を基にまとめてみます。

設定のリファレンスは、公式サイトのDocumentページにリンクがあるテキストのものと、Google codeにも同じ内容のものがまとまっています。

Google codeのドキュメントの方が見やすいのでお薦めです。

haproxy.cfgの設定

HAProxyの設定は /etc/haproxy/haproxy.cfg に記述します。
今回の例は、以下のようになります。

global
    maxconn 4096
    daemon
    nproc 1
    log 127.0.0.1 local1 debug

defaults
    mode http
    option httplog
    log global

frontend all 0.0.0.0:80
    timeout client 86400000
    default_backend www_backend
    acl is_websocket hdr(Upgrade) -i WebSocket
    acl is_websocket path_beg /socket.io/

    use_backend socket_backend if is_websocket

backend www_backend
    balance roundrobin
    option forwardfor # This sets X-Forwarded-For
    option httpclose
    timeout server 30000
    timeout connect 4000
    server server1 localhost:81 weight 1 maxconn 1024 check

backend socket_backend
    balance roundrobin
    option forwardfor # This sets X-Forwarded-For
    option httpclose
    timeout queue 5000
    timeout server 86400000
    timeout connect 86400000
    server server1 localhost:3000 weight 1 maxconn 1024 check

以下、これを踏まえてhaproxy.confの設定について説明していきます。

設定ファイルの構成

まず、"global"セクションに、HAProxyプロセス全体で共通の設定を記述し、続けて、以下の4つのプロキシ設定セクションに、リッスンする各ポートの設定やバックエンドサーバーへの振り分け設定などを記述していきます。

  • defaults: 後述するfrontend、backend、listenのデフォルトパラメータを指定します。
  • frontend: クライアントからの接続を受け入れるソケットのパラメータを指定します。
  • backend: プロキシするサーバーに関わるパラメータを指定します。
  • listen: frontendとbackendを完全にプロキシする設定を記述します。

プロセス設定

設定例の"global"セクションに記述されている各パラメータは以下の通りです。

  • daemon: デーモンで起動するときに指定します。
  • nproc: daemonモードで起動するプロセス数を指定します。
  • maxconn: プロセスごとの最大同時接続数
  • log: ログ出力について設定します。後述の「ログ出力」で説明します。

プロキシ設定

プロキシセクションに設定するパラメータは、"defaults"、"frontend"、"backend"、"listen"のいづれのセクションでも使用可能なものと、専用のものがあります。
各パラメータと利用可能なセクションの一覧がドキュメントの「Proxy keywords matrix」にあります。

以下、大まかに分けてよく使うパラメータについて説明しています。

Keep-Aliveの設定(すべてのセクション)
  • option httpclose: 各リクエストでHTTPヘッダー"Connection: close"をチェックし、なければレスポンスヘッダに追加します。つまり、Keep-Aliveが無効になります。HAProxyは、デフォルトではHTTP接続の最初のリクエストのみ検査するため、HTTPヘッダーの操作や、ヘッダーやパラメータによって振り分けを行う場合は"httpclose"オプションが必要になります。
  • option http-server-close: 上記の"httpclose"との違いは、クライアント側はKeep-Aliveのまま、サーバー側のみリクエストごとに接続がクローズされます。
接続モード(すべてのセクション)
  • mode: リッスンするプロトコルを指定します
    • tcp: TCPモードは、クライアント-サーバー間の全二重接続を確立します。 レイヤー7のパケットの処理をしない、SSL, SSH, SMTPなどをプロキシする場合に利用できます。
    • http: HTTPモード。RFCに準拠しないリクエストは破棄されます。レイヤー7の情報に基づくサーバー振り分けなどが可能になります。
    • health: モニター用のモードですが、現在非推奨なので使用しません。
backendの選択(defaults, frontend, listen)
  • default_backup: マッチするuse_backendが無かった場合に使用されるbackendを指定します。
  • use_backend:
ロードバランス(backend)
  • balance: ロードバランスに使用するアルゴリズムを指定します。HAProxyは非常に強力なロードバランス機能を提供しています。指定しなかった場合のデフォルトはroundrobinです。
    • roundrobin: ラウンドロビン、重み付けの機能あり、動的調整あり(サーバーダウンから回復直後に大量リクエストを割が割り当てられないように調整します)、各backendについて最大4128のアクティブサーバーを指定可能。
    • static-rr: ラウンドロビン、重み付けの機能あり、動的調整なし、backendサーバーの制限無し。
    • leastconn: もっとも接続数が少ないサーバーが割り当てられます。
    • source: IP Addressのハッシュ値を元に振り分け。
    • uri: URIの'?'の前をハッシュし、その値を元に振り分け。つまり、サーバーが停止/起動されない限りは、URIの値が同じなら同じサーバーに振り分けられる。ハッシュするURIの深さや長さなど詳細な設定も可能。 balance uri [len ] [depth ]
    • url_param: URIのパラメータにより振り分け、POSTの場合もcheck_postを指定するとPOSTのボディを使用する。 balance url_param [check_post []]
    • hdr(name): HTTPヘッダーにより振り分け
    • rdp-cookie, rdp-cookie(name): RDPクッキーをハッシュした値により振り分け、nameを指定しない場合は"mstshash"が使用される。
  • option forwardfor: backendサーバーへ送られるリクエストに"X-Forwarded-For"ヘッダーを追加します。HAPorxyはリバースプロキシとして動作するため、各backendサーバーはHAProxyサーバーのIPアドレスをクライアントアドレスとして認識してしまいます。これを解決するために本オプションを利用します。
タイムアウト
  • timeout client: クライアントとの接続アイドルタイムアウト時間。複雑性を排除するためには"timeout server"と同じ値が推奨される。frontend側で設定

他に"timeout http-keep-alive"、"timeout http-request"などのパラメータもある

  • timeout queue: 接続数がmaxconnに達した場合、リクエストはキューに貯められます。その後、この値の時間が経過した場合503が返されます。指定しなかった場合は"timeout connect"と同じ値が使用されます。backend用
  • timeout connection: backendサーバーに対する接続が確立するまでのタイムアウト時間。指定しない場合は、無制限。backend用
  • timeout server: backendサーバーへの接続のアイドルタイムアウト時間。backend用

ACL

Access Control Lists (ACL)は、HAProxyの肝となる重要な機能で、これによって柔軟なサーバー構成が可能になります。

ACLの指定フォーマットは以下のようになります:

acl   [flags] [operator]  ...

今回の例では、"Upgrade"ヘッダーに"WebSocket"という値が含まれている場合にis_websocketをtrueにします。

acl is_websocket hdr(Upgrade) -i WebSocket

以下のブログも参考になります:
HAProxyのACLとCriteria

ログ出力

設定例から、ログ出力に関する箇所だけ抜き出しました。

global
    ...
    log 127.0.0.1 local1 debug

defaults
    ...
    log global
...

logパラメータの設定は以下のフォーマットで記述します。

log <address> <facility> <level>
  • address: ログを送信するIPアドレスを指定します。設定例ではローカルのrsyslogデーモンにログを送信します。IPv4アドレスを指定した場合は、UDPソケットでログが送信されるのでrsyslog側で受けられるように設定する必要があります。また、Unixソケットを使用することもできます。
  • facility: syslogのファシリティを指定します。
  • level: rsyslogdに送信するログレベル "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" を指定します。省略した場合は、すべてのログが送信されます。

プロキシセクションで"log global"と記述した場合は"global"セクションの設定が引き継がれます。

Ubuntu 10.04でのログ設定を以下のブログを参考にまとめて見ます。
HAProxy Logging in Ubuntu Lucid

$ sudo vi /etc/rsyslog.d/haproxy.conf
# .. otherwise consider putting these two in /etc/rsyslog.conf instead:
$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1
 
# ..and in any case, put these two in /etc/rsyslog.d/haproxy.conf:
local1.* -/var/log/haproxy_1.log
& ~  
# & ~ means not to put what matched in the above line anywhere else for the rest of the rules
# http://serverfault.com/questions/214312/how-to-keep-haproxy-log-messages-out-of-var-log-syslog
$ sudo service rsyslog restart

ポートを空ける必要があるので、reloadじゃなくてrestart

ログ出力の確認

$ tail -f /var/log/haproxy_1.log 
Apr 20 15:15:54 localhost.localdomain haproxy[11220]: Connect from 127.0.0.1:57720 to 127.0.0.1:80 (all/HTTP)
Apr 20 15:15:54 localhost.localdomain haproxy[11220]: Connect from 127.0.0.1:57721 to 127.0.0.1:80 (all/HTTP)
Apr 20 15:15:54 localhost.localdomain haproxy[11220]: Connect from 127.0.0.1:57722 to 127.0.0.1:80 (all/HTTP)
...

ローテーションも設定しておきます

$ sudo vi /etc/logrotate.d/haproxy
/var/log/haproxy*.log {
        weekly
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 640 root adm
        sharedscripts
        postrotate
                reload rsyslog >/dev/null 2>&1 || true
        endscript
}

以上、大まかにHAProxyの設定について説明しました。
次回はstunnelのセットアップについて説明します。