多機能プロクシサーバー「HAProxy」のさまざまな設定例

多機能プロクシサーバー「HAProxy」のさまざまな設定例

ロードバランサやリバースプロクシとして利用できる多機能プロクシ「HAProxy」では、HTTPリクエストの内容やTCPパケットの内容に応じた挙動をさまざまに定義できる。本記事では、よく利用されると思われる設定例を中心に、HAProxyの持つ機能を紹介していく。

HAProxyのさまざまな設定例

記事前編ではHAProxyの設定ファイルについて解説したが、続いて後編となる今回は実際の設定例を見ていこう。なお、本記事で例示する設定例ではglobalセクションやdefaultsセクションについては省略しているので、これらについては環境に応じて適宜設定してほしい。

また、以下の設定例は特に注記しないかぎり、httpモードとtcpモードの両方で利用が可能だ。

基本的な負荷分散

HAProxyでは、backendセクションに複数のサーバーを記述することで、それらに対し接続を振り分ける負荷分散が行われる。たとえば「centos01」および「centos02」の2つのWebサーバーに接続を振り分けたい場合、次のようになる。

frontend web_proxy
    default_backend web_servers
    bind *:80

backend web_servers
    server web01 centos01:80
    server web02 centos02:80

接続をどのように指定したサーバーに振り分けるかは、backendセクションの「balance」キーワードで設定できる。

balance <アルゴリズム>

balanceキーワードでの設定が行われていない場合、各サーバーに順番に接続を振り分ける「roundrobin」アルゴリズムが選択される。これ以外にも表1のアルゴリズムが利用可能だ。

表1 選択できる負荷分散アルゴリズム
名称 説明
roundrobin 各サーバーに順番に接続を振り分ける
static-rr 基本的にはroundrobinと同じだが、サーバーダウンなどでサーバーの総数が変化しても振り分け割合は再計算されない
leastconn 各サーバーに振り分けられる接続数が最小になるよう接続を振り分ける
first 接続を受け付けられる最初のサーバーに必ず接続を振り分ける
source 接続元IPアドレスのハッシュを使って振り分け先を決定する
uri 接続先URIのハッシュを使って振り分け先を決定する
url_param GETリクエストのパラメータに応じて振り分け先を決定する
hdr(<ヘッダ名>) 指定したHTTPヘッダに応じて振り分け先を決定する
rdp-cookie、rdp-cookie(<cookie名>) RDP cookieの値によって振り分け先を決定する

特定のサーバーに対して多く接続を振り分けたい場合、serverキーワードの「weight」オプションで振り分けを行う際の重み付けを指定できる。次の例では「centos01」の重みを1、「centos02」の重みを2に設定している。

frontend web_proxy
    default_backend web_servers
    bind *:80

backend web_servers
    server web01 centos01:80 weight 1
    server web02 centos02:80 weight 2

roundrobinアルゴリズムの場合、指定した重みに比例してそのサーバーに振り分けられる接続数が増加する。この例の場合、centos02というサーバーにはcentos01の2倍の数の接続が割り振られるようになる。

サーバーの監視

HAProxyのデフォルト設定では、バックエンドで指定されているサーバーの一部が停止した状態でも、そのサーバーに接続が振り分けられるようになっている。その結果、クライアントには適切なレスポンスが返らないというケースが発生する。これを防ぐには、バックエンド側で「option redispatch」キーワードを指定して「redispatch」オプションを有効にするとともに、「retries」キーワードでリトライ数を指定すれば良い。

backend web_servers
    option redispatch
    retries 3
    server web01 centos01:80 weight 1
    server web02 centos02:80 weight 2

redispatchオプションが有効になっていると、サーバーへの接続がretriesキーワードで指定した回数失敗した場合、別のサーバーにその接続を振り分けるようになる。

ただしこの設定の場合、停止しているサーバーへの接続の振り分け自体は行われ、その結果によって別のサーバーへの振り分けが行われるという挙動となる。停止しているサーバーへの接続振り分け自体を止めたい場合は、サーバーの死活監視を行う設定を追加すれば良い。

サーバーの死活監視を有効にするには、serverキーワードに「check」オプションを追加する。

backend web_servers
    option redispatch
    retries 3
    server web01 centos01:80 weight 1 check
    server web02 centos02:80 weight 2 check

これによりサーバーの死活監視が行われるようになり、停止しているサーバーに対しては接続が振り分けられないようになる。

死活監視方法の指定

HAProxyでは、デフォルトではサーバーに対しTCP接続が行えるかどうかで死活監視を行うのだが、httpモードではこれ以外にも指定したURLにアクセスし、その結果からサーバーの死活判断を行うという設定が可能だ。これらは「httpchk」オプションで指定できる。

option httpchk
option httpchk <URI>
option httpchk <メソッド> <URI>
option httpchk <メソッド> <URI> <HTTPバージョン>

httpchkオプションでURIやメソッド、HTTPバージョンを指定すると、指定された条件でサーバーにアクセスし、そのステータスコードが「2xx」か「3xx」以外の場合にサーバーに異常が発生したと判断して振り分け対象から外すようになる。

また、「http-check」キーワードで設定することで、レスポンスの内容によってサーバーの死活判断を行えるようになる。

http-check expect [!] <マッチ対象> <パターン>

ここでマッチ対象には「status」もしくは「rstatus」、「string」、「rstring」が指定可能だ。statusが指定された場合、ステータスコードが指定されたパターンと一致した場合にサーバーが正常だと判断する。また、stringが指定された場合はレスポンスボディが指定されたパターンと一致した場合にサーバーが正常だと判断する。rstatusおよびrstringはstatusおよびstringと基本的に同様の挙動を行うが、パターンを正規表現として解釈してマッチした場合にサーバーが正常だと判断する点が異なる。

ここで注意したいのが、HAProxyが死活監視を行う際に受け取れるレスポンスのサイズには上限がある点だ。デフォルトは16384バイトだが、tune.chksizeキーワードでこの値は変更可能だ。

利用できるサーバーがない場合待機系サーバーへの振り分けを行う

バックエンドのサーバー設定で「backup」オプションを指定することで、そのサーバーを障害発生時のバックアップサーバーとして指定できる。このオプションが指定されたサーバーは、 backupオプションが指定されていないサーバーがすべてダウンした場合にのみ接続が振り分けられる。次の例は、「centos03」というサーバーをバックアップサーバーとして指定している。

backend web_servers
    server web01 centos01:80 weight 1 check
    server web02 centos02:80 weight 2 check
    server web03 centos03:80 backup

また、複数のバックアップサーバーを利用したい場合、これに加えて「option allbackups」を指定する必要がある。この場合、「backup」オプションを指定したサーバーに対し負荷分散が行われるようになる。

SSLオフローダとして使う

HAProxyのhttpモードでは適切なサーバー証明書を用意することで、いわゆる「SSLオフロード」的な処理を行わせることも可能だ(図1)。

図1 HAProxyをSSLオフローダとして使う
図1 HAProxyをSSLオフローダとして使う

この場合、クライアントとHAProxyとの接続は暗号化されるが、HAProxyとバックエンドサーバーとの接続は暗号化されない。この場合バックエンド側でのSSL関連の設定や処理が不要になるため、バックエンドの負荷軽減やSSL対応の手間を省くために利用できる。

使用するSSL証明書は、フロントエンドのbind設定に「ssl crt <証明書ファイルのパス名>」オプションを追加すれば良い。たとえば証明書ファイルのパス名が「/etc/haproxy/foo.example.com.pem」の場合、次のようになる。

frontend ssl_proxy
    mode http
    default_backend web_servers
    bind *:443 ssl crt /etc/haproxy/foo.example.com.pem
    reqadd X-Forwarded-Proto:\ https

backend web_servers
    mode http
    server web01 centos01:80
    server web02 centos02:80

この設定を行うと、HAProxyはSSL接続をデコードした上でcentos01およびcentos02に振り分ける、といった挙動を行うようになる。また、ここでは「reqadd」キーワードを指定し、サーバーへのリクエストヘッダに「X-Forwarded-Proto: https」を追加することで、サーバー側に対しフロントエンドがSSL接続を使っていることを通知するようにしている(reqaddキーワードについては後述)。

なお、ここで指定する証明書ファイルでは、証明書(CERTIFICATE)と秘密鍵(PRIVATE KEY)の両方が格納されている必要がある。たとえばLet's Encryptの証明書を使う場合、証明書は「fullchain.pem」ファイル、秘密鍵は「privkey.pem」ファイルという別々のファイルに格納されている。こういった場合、この2つのファイルをcatコマンド等で結合したファイルを作成し、そのファイルを証明書ファイルとして指定すれば良い。

# cat fullchain.pem privkey.pem > foo.example.com.pem

コンテンツを圧縮する

httpモードでは、HAProxy内でコンテンツを圧縮してクライアントに送信する機能が利用できる。コンテンツ圧縮を有効にするには、まず「compression algo <アルゴリズム>」キーワードで圧縮に使用するアルゴリズムを指定し、続けて「compression type <MIMEタイプ>」キーワードで圧縮を行うコンテンツの種別を指定する。

たとえば「text/html」および「text/plain」形式のコンテンツをGZIP形式で圧縮するには、以下のように設定する。

frontend web_proxy
    default_backend web_servers
    bind *:80
    compression algo gzip
    compression type text/html text/plain

バックエンドのサーバー側が圧縮されたコンテンツを返した場合、HAProxyでは何も処理を行わず、それがそのままクライアントに送信される。

HTTPヘッダの追加や操作

HAProxyからサーバーに対してリクエストを送信する際に指定したリクエストヘッダを追加/削除/変更したり、HAProxyからクライアントにレスポンスを返す際にレスポンスヘッダを追加/削除/変更する、といった処理も可能だ。たとえば先のSSL関連設定で使用した「reqadd」キーワードは、サーバーに対して指定したリクエストヘッダを追加するものだ。

reqadd <ヘッダ文字列>

また、指定した正規表現にマッチするリクエストヘッダを削除する「reqdel」キーワードや、指定した正規表現にマッチした部分を指定した文字列に置換する「reqrep」といったキーワードも用意されている。

reqdel <正規表現>
reqrep <正規表現> <文字列>

同様にHAProxyがサーバーに転送するレスポンスヘッダについても、ヘッダの追加/削除/変換を行う「rspadd」「rspdel」「rsprep」キーワードが用意されており、同様の処理が実行できる。

rspadd <ヘッダ文字列>
rspdel <正規表現>
rsprep <正規表現> <文字列>

これらと次節で説明するACLを組み合わせることで、特定の条件でのみヘッダの操作を行わせることも可能だ。

「ACL」を使った条件分岐

HAProxyでは、一部のキーワードやオプションにおいて「ACL」と呼ばれる機構を使った条件分岐が利用できる。これを利用することで、条件にマッチした接続のみを特定のサーバーに振り分ける、といった操作が可能だ。

HAProxyでは、ACLによる条件分岐を定義するための「acl」キーワードが用意されている。

acl <ACL名> <対象> [<フラグ>] [<オペレータ>] [<値>]

ここで「ACL名」ではそのACLの名前を、「対象」では条件分岐を決定するためにどの情報を参照するかを指定する。指定できる対象は多岐に渡るので詳しくはドキュメントを参照して欲しいが、たとえばhttpモードでは表1のようなものが利用できる。

表1 httpモードでよく使われる対象
名前 説明
method HTTPリクエストメソッド
path リクエストされたパス
query URL中のクエリ文字列
cookie(<cookie名>) 指定したcookie
hdr(<ヘッダ名>) 指定したリクエスト/レスポンスヘッダ
src クライアントのIPアドレス

また、ACLでは基本的に指定した対象が指定した値にマッチしているかどうかをチェックするが、「フラグ」ではそのマッチ方法を指定できる。指定できるフラグは表2のとおりだ。

表2 aclキーワードで指定できるフラグ
フラグ 説明
-i 大文字/小文字を区別しない
-f <ファイル名> 指定したファイルからマッチに使用するパターンを読み出す
-m <マッチ方法> マッチ方法を指定する
-n DNSを用いた名前解決を使用しない
-M <ファイル名> 指定したファイルに記述されているマップパターンを使用する
-u ACLのユニークIDを利用する

なお、「-m」フラグで選択できる主なマッチ方法としては次の表3がある。

表3 -mフラグで選択できる主なマッチ方法
マッチ方法 説明
bool 対象が0かfalse以外の場合にマッチする
int 対象を数値として値と比較する
ip 対象をIPアドレスとして値と比較する
len 対象の長さが値と一致した場合にマッチする
str 対象を文字列として値と比較する
sub 対象の文字列に値が含まれていた場合にマッチする
reg 正規表現を使ってマッチする
beg 対象が値で指定した文字列で始まっていればマッチする
end 対象が値で指定した文字列で終わっていればマッチする

たとえば次の例は、Hostヘッダが「www.example.com」という文字列にマッチする、という条件を「host_www_example.com」という名前で定義している。

acl host_www_exmaple_com hdr(host) -m str www.example.com

また、次の例はリクエストされたパスが「/foo/」で始まる、という条件を「path_foo」という名前で定義するものだ。

acl path_foo path -m beg /foo/

なお、HAProxyではよく使われる条件についてはあらかじめ定義済みACLとして提供している。たとえば「GETリクエスト」という条件は、「METH_GET」というACL名で参照できる。定義済みのACL一覧についてはドキュメントの「Pre-defined ACLs」ページを参照して欲しい。

ACLを利用した各種設定

HAProxyの多くの設定キーワードでは、後ろに「if」もしくは「unless」とともにACLを指定することで、そのパラメータを有効にするための条件を設定できる。ifを指定した場合、そのACLで定義された条件が達成された場合のみそのパラメータが有効になる。unlessを指定した場合はその逆で、条件が達成されなかった場合のみパラメータが有効になる。

たとえば、「user-server」キーワードでは、指定した条件に合致した/しなかった場合に接続振り分け先として使用するサーバーを指定できる。

use-server <サーバー名> if <ACL>
use-server <サーバー名> unless <ACL>

たとえば次の例は、「192.0.2.0/24」というネットワークからの接続のみを別のサーバーに振り分けるものだ。

frontend web_proxy
    default_backend web_servers
    bind *:80

backend web_servers
    server web01 centos01:80 check
    acl forbidden_ip src -m ip 192.0.2.0/24
    use-server web10 if forbidden_ip
    server web10 centos02:80 weight 0 check

ここでは、発信元IPアドレスが192.0.2.0/24にマッチするという条件を「forbidden_ip」という名前のACLで定義し、さらにuse-serverキーワードを使ってその条件に合致した接続を「web10」という名前で定義したサーバーに転送するよう設定している。また、このサーバーの設定で重みを0に設定することで、条件に合致しない接続は振り分けられないようにしている。

ちなみにACLを事前に定義せず、次のように「{}」を使って直接ifキーワードの後に記述することも可能だ。

    use-server web10 if { src -m ip 192.0.2.0/24 }

また、先の設定は次のように記述することもできる。

frontend web_proxy
    default_backend web_servers
    use_backend forbidden_servers if { src -m ip 192.0.2.0/24 }
    bind *:80

backend web_servers
    server web01 centos01:80

backend forbidden_servers
    server web10 centos02:80

こちらでは、バックエンドではなくフロントエンド側で「use_backend」キーワードを使い、接続元IPアドレスが指定したものに合致した場合に別のバックエンドを使用するよう指定している。

ACLは非常に強力な機能であり、これを利用することで柔軟にサーバーの振り分けルールを設定することができる。また、リダイレクトを行う「redirect location」キーワードや、ヘッダの追加/削除/編集を行うreqadd/reqdel/reqrepなどと組み合わせることでアクセス制御を行ったり、サーバーの挙動を変更させることも可能だ。

HTTP以外で活用する

さて、ここまでHAProxyの設定例をいくつか挙げてきたが、これら設定の多くはhttpモードでもtcpモードでも共通で利用できるものだった。HAProxyではこれ以外に、tcpモードでのみ利用できる設定パラメータも用意されている。その1つが、死活監視設定用の設定項目だ。

前述のとおり、HAProxyのデフォルト設定では単にTCPでの接続が可能かどうかだけでサーバーの死活監視を行うようになっている。また、httpモードでは、特定のパスにリクエストを送信したり、そのレスポンスをチェックしてサーバーの状態を判断することが可能であった。HAProxyではこれ以外のいくつかのサーバーについても、表4のような死活監視設定が提供されている。

表4 HAProxyが備える死活監視設定
監視対象 指定するキーワード オプション引数
LDAP option ldap-check (なし)
MySQL option mysql-check user <ユーザー名>
PostgreSQL option pgsql-check user <ユーザー名>
Redis option redis-check (なし)
SMTP option smtpchk <使用するhelloコマンド> <ドメイン>
SPOP option spop-check (なし)
SSLv3 ssl-hello-chk (なし)

たとえばMySQLサーバーを監視するための「option mysql-check」設定では、バックエンドで以下のように指定することで、指定したユーザー名でログインできるかをチェックし、ログインできた場合は正常に稼働していると判断するようになる。

option mysql-check user <ユーザー名>

また、特定のデータを送信して死活判定を行う「option tcp-check」オプションや、外部コマンドを実行してその結果から死活判定を行う「option external-check」といったオプションも用意されている。詳しくはドキュメントを参照して欲しいが、これらを利用することで、HAProxyが標準で監視手段を提供していないサーバーについても詳細な死活判定が行えるようになる。

さまざまなケースで活用できるHAProxy

今回は紹介していないが、HAProxyでは指定した条件の接続をブロックしたり、統計情報を収集したり、特定の条件の接続があった場合にログに出力する、といった機能なども用意されており、監視やセキュリティ保護といった用途にも活用できる。

昨今ではリバースプロクシやロードバランサとしてさまざまなソフトウェアの選択肢があるが、HAProxyでは堅牢性や性能についても高いと謳われている。ほかのソフトウェアと比較しても十分に性能は高く、導入や設定も比較的容易だ。バージョン1.0のリリースが2001年と、かなり枯れていながら、メンテナンスや機能強化も続いているという点も注目したい。リバースプロクシやロードバランサが必要な状況となった場合、HAProxyの利用を検討してみてはいかがだろうか。