多機能なロードバランサとして使える多機能プロクシサーバー「HAProxy」入門

複数台のサーバーを利用して負荷分散を行う場合、クライアントからの接続を各サーバーに振り分けるロードバランサが必要となる。また、環境によってはリバースプロクシなどを使ってリクエストを振り分けるケースもある。今回は、「クライアントからのアクセスを別のサーバーに転送する」機能を提供するソフトウェア「HAProxy」を紹介する。

トラフィック中継に付随するさまざまな作業を実行できるHAProxy

今回紹介するHAProxyは、「HAProxyを実行しているサーバーに向けたTCP接続を別のサーバーに転送する」という処理を行うソフトウェアだ(図1)。

図1 HAProxyの使い方概要
図1 HAProxyの使い方概要

一般的にプロクシというと、「1対1」の転送が想像されるかもしれないが、HAProxyでは複数の転送先サーバーを設定することで、ロードバランサとして利用することもできる。また、パケットの内容に応じて転送先を変えるような設定も可能だ。HAProxyはTCPレベルでの接続転送に加えて、HTTPに特化した設定項目も用意されており、いわゆるリバースプロクシとしても利用できる。

もちろんサーバーの監視(ヘルスチェック)機能も搭載しており、稼動していないサーバーには接続を振り分けないような構成も可能だ。

HAProxyの利用ケース

HAProxyではトラフィックを転送するためのさまざま機能が用意されており、色々な利用ケースが考えられる。たとえばWebサーバーとして組み合わせて利用すれば、バックエンド側にある複数のWebサーバーに対して接続を振り分けるロードバランサとして利用できる(図2)。

図2 ロードバランサとしてHAProxyを使用する例
図2 ロードバランサとしてHAProxyを使用する例

このとき、HAProxyを稼動させているサーバーにSSL証明書を用意して設定を行えば、バックエンド側に変更を加えることなしにサイトをSSL接続に対応させることも可能だ。

また、バックエンドサーバーがすべて停止した場合のみに別のサーバーに接続を振り分けるといった設定も可能だ。これは、サイトのメンテナンス時や障害発生時にその旨を表示させる、といった用途に利用できる(図3)。

図3 サービスを運用しているサーバーがすべて利用できない場合のみバックアップサーバーに接続を振り分ける例
図3 サービスを運用しているサーバーがすべて利用できない場合のみバックアップサーバーに接続を振り分ける例

HAProxyはWebサーバーだけでなく、たとえばデータベースサーバーなどと組み合わせて利用することもできる。実際によく使われている例としては、MySQLデータベースクラスタの負荷分散などがある(図4)。

図4 HAProxyを使ったMySQLクラスタの負荷分散
図4 HAProxyを使ったMySQLクラスタの負荷分散

ただしこの場合、複数のMySQLサーバー間でデータが正しく同期できるように配慮する必要がある。MySQLをMaster-Slave構成にし、Slaveに対する読み出しアクセスのみを負荷分散する、といった方法が一般的だ。

もちろんMySQLだけでなくPostgreSQLや、Key-ValueストアであるMemcachedやRedisの負荷分散にも利用可能だ。

HAProxyの基本的な使い方

それでは、HAProxyの実際の使い方、設定方法について解説していこう。

HAProxyのインストール

HAProxyはLinuxだけでなく、FreeBSDやOpenBSD、Solaris、AIXなどさまざまなUNIX系OSで動作するが、今回は現在広く使われていると思われるLinuxでの利用について紹介する。

主要なLinuxディストリビューションではHAProxyが標準パッケージとして提供されており、多くの環境においてはディストリビューション標準のパッケージマネージャ経由でインストールできる。ただし、現在提供されているHAProxyの最新版はバージョン1.7系(リリースは2017年2月28日)だが、ディストリビューションによってはこれよりも古いバージョンのものが提供されている場合があるので注意したい。

たとえばDebian LinuxやUbuntuでは「haproxy」というパッケージ名でHAProxyが提供されており、次のようにしてインストールできる。

# apt-get install haproxy

Debianの最新安定版であるDebian 8系(jessie)ではバージョン1.5.8が、jessie向けにより新しいバージョンのソフトウェアをバックポートする「jessie-backports」ではバージョン1.7.3が提供されている。また、Ubuntuでは16.10および16.04 LTSでバージョン1.6.3が提供されている。

Red Hat Enterprise 7では、同じく「haproxy」というパッケージ名でバージョン1.5.18が提供されており、yumコマンドでインストールが可能だ。同様にFedoraでもhaproxyパッケージが提供されている。

# yum install haproxy

ソースコードから自前でビルドして利用することも可能だが、HAProxyのほとんどの基本的な機能はバージョン1.5系でも利用可能なので、より新しいバージョンで提供されている機能が必要という場合でなければあまりおすすめしない。

HAproxyの起動

HAProxyでは、起動時のコマンドラインオプションで設定を記述したファイルを指定する必要がある。多くのLinuxディストリビューションでは「/etc/haproxy/haproxy.cfg」がこの設定ファイルとなっており、各システム標準のサービス管理ツール経由でHAProxyの起動/停止を管理できるようになっている。たとえばCentOS 7では、以下のようにしてHAProxyを起動できる。

# systemctl start haproxy

また、次のようにしてコマンドラインから直接HAProxyを起動することもできる。これは設定のテストやデバッグの際に便利だ。

# haproxy -f <設定ファイル> -db -V

ここで、「-db」オプションはフォアグラウンドでHAProxyを実行することを指定するオプション、「-V」オプションは詳細なメッセージ出力を有効にするオプションだ。「-db」オプションを指定しないと、バックグラウンドでデーモンとしてHAProxyが起動するので注意したい。その場合、psコマンドなどでプロセスID(PID)を確認したうえでkillコマンドでデーモンを終了させる必要がある。

# haproxy -f /etc/haproxy/haproxy.cfg -V
Available polling systems :
      epoll : pref=300,  test result OK
       poll : pref=200,  test result OK
     select : pref=150,  test result FAILED
Total: 3 (2 usable), will use epoll.
Using epoll() as the polling mechanism.
# ps aux | grep haproxy
↓haproxyのPIDは24166
haproxy  24166  0.0  0.1  47824  1072 ?        Ss   20:22   0:00 haproxy -f /etc/haproxy/haproxy.cfg -V
root     24178  0.0  0.0 112664   968 pts/0    R+   20:23   0:00 grep --color=auto haproxy
# kill 24166

また、「-c」オプションで設定ファイルが正しいかどうかのチェックを実行できる。たとえば設定ファイルが/etc/haproxy/haproxy.cfgの場合、以下のように実行することで設定ファイル内に記述ミスなどがないかを確認できる。

$ haproxy -f /etc/haproxy/haproxy.cfg -c
Configuration file is valid  ←問題がない場合、validと表示される

HAProxyの設定ファイル

HAProxyでは、その挙動のすべてを設定ファイルで管理するようになっている。この設定ファイルは、設定内容を以下のような複数の「セクション」に分割して記述する。

↓「global」セクション(HAProxy全体に関連する設定を記述する)
global
  <設定項目1> <設定値1>
  <設定項目2> <設定値2>
  :
  :

↓「defaults」セクション(各セクションにおけるパラメータのデフォルト値を記述する)
defaults
  <設定項目1> <設定値1>
  <設定項目2> <設定値2>
  :
  :

↓以下、プロクシの動作に関連する設定を記述していく
<listen/frontend/backendセクション1> <名前>
  <設定項目1> <設定値1>
  <設定項目2> <設定値2>
  :
  :

<listen/frontend/backendセクション2> <名前>
  <設定項目1> <設定値1>
  <設定項目2> <設定値2>
  :
  :

設定項目と設定値は1つ以上の空白(ホワイトスペース、水平タブなど)で区切る形になっている。また、「#」から行末まではコメントとして無視される。文字列中で「#」を使いたい場合は「\#」のようにバックスラッシュでエスケープを行えばよい。空白を含む文字列を指定したい場合は「\ 」のようにエスケープするか、もしくは「"」(ダブルクォーテーション)や「'」(シングルクォーテーション)で文字列を囲めば良い。

設定値には、「$<環境変数名>」や「${環境変数名}」といった形式で環境変数を使用することも可能だ。そのほか、時間の設定が必要な設定項目では単位としてus(マイクロ秒)/ms(ミリ秒)/s(秒)/m(分)/h(時間)/d(日)が利用できる。

また、設定ファイル内で設定等に「名前」を付けることが必要となるケースも多い。このとき、名前には英大文字/小文字、数字、「-」(ハイフン)、「_」(アンダースコア)、「.」(ピリオド)、「:」(コロン)が利用可能だ。このとき英大文字と子文字は区別される。

バックエンドとフロントエンド

HAProxyでは、「バックエンド(backend)」と「フロントエンド(frontend)」という単位でプロクシとしての動作に関する各種設定を行うようになっている。フロントエンドはHAProxyによる待ち受けなどの処理を行う単位で、バックエンドは転送先のサーバーを指定する単位となる(図5)。

図5 HAProxyの「フロントエンド」と「バックエンド」
図5 HAProxyの「フロントエンド」と「バックエンド」

HAProxyの設定では、まずフロントエンドの設定で待ち受けを行う条件と、その条件を満たす接続をどのバックエンドに転送するかの設定を行い、続いてバックエンドの設定で使用するサーバーを指定することで、どの接続をどのサーバーに転送するのかというルールを設定する仕組みになっている。

「http」モードと「tcp」モード

HAProxyでは、「http」モードと「tcp」モードというモードが用意されている。どちらのモードもクライアントからの接続を別のサーバーに転送するという動作に変わりはないが、httpモードではHTTPヘッダやリクエスト/レスポンス内容をチェックし、それに応じて挙動を変えるといった設定が可能だ。

これらモードは、フロントエンドおよびバックエンド単位で設定できる。通常はHTTPに関する処理を行う場合はhttpモード、それ以外の場合はtcpモードを選択するのが一般的だ。

HAProxy全体の動作を指定する「global」セクション

それでは、この設定ファイルの各セクションについて細かく見ていこう。

まず「global」セクションでは、ログの設定やプロセスの実行に使用するユーザー/グループ、PIDファイルのパスなど、HAProxyのプロセス全体で共有する設定パラメータを記述する。また、動作のチューニングに関わるパラメータも用意されている。

ここで指定できる全パラメータはドキュメントのGlobal parametersに記載されているので、詳しくはそちらを参照して欲しい。Linuxディストリビューションのパッケージでインストールした場合は、あらかじめデフォルト値が記述された設定ファイルが用意されていることが多いので、まずはそれをそのまま使うのも良いだろう。

また、注意したいのがログに関する設定だ。HAProxyでは自前でのログ出力機能を持たないが、その代わりとしてsyslogへのログ出力機能を搭載している。ただし、これは設定ファイルで明示的に設定しないと有効にならないため、ログが必要な場合は注意したい。syslogに対するログ出力は、「log」キーワードで設定できる。

log <出力先syslogのアドレスもしくはUNIXドメインソケットのパス> <ファシリティ>

ファシリティでは、syslogに通知するファシリティ(ログの分類)を指定する。指定できるのは以下のどれか1つだ。

kern   user   mail   daemon auth   syslog lpr    news
uucp   cron   auth2  ftp    ntp    audit  alert  cron2
local0 local1 local2 local3 local4 local5 local6 local7 

また、出力するログの1行の最大長や、出力フォーマットなども設定可能になっている。これらについて詳しくはドキュメントを確認して欲しい。

パラメータのデフォルト値を指定する「defaults」セクション

次の「defaults」セクションは、globalセクション以外のセクションにおけるパラメータの「デフォルト値」を指定できるセクションで、複数のセクションで共通するパラメータ設定を行いたい場合などに便利だ。特にデフォルト値の設定が不要な場合は省略しても問題ない。

まとめて設定しておきたいパラメータとしては、タイムアウトやリトライ数、ログ設定などのパラメータがある。たとえばHAProxyに接続してくるクライアントに対するタイムアウト時間は「timeout client」キーワードで、接続転送先サーバーのタイムアウト時間は「timeout server」キーワードで、転送先サーバーへの接続に失敗した際のリトライ試行数は「retries」キーワードで指定できる。

defaults
  timeout client <時間>
  timeout server <時間>
  retries <回数>

ちなみに、これらは「何も応答が無くなった際に切断を行うまでの時間」となる。接続時に何も応答がない、といったケースのタイムアウト時間は「timeout connect」という別のキーワードで指定する。

  timeout connect <時間>

また、HTTPモードでしか利用しない場合は「mode」キーワードを指定し、デフォルトのモードとして「http」を指定しておくと良い。

  mode http

HAProxyによるクライアントからの待ち受け動作の挙動を指定する「frontend」セクション

続くlisten/frontend/backendセクションは、HAProxyのプロクシとしての動作について記述していくセクションだ。ちなみにドキュメントではこれらとdefaultsセクションをまとめて「proxies」セクションと呼んでいる。defaultsセクション以外のproxiesセクションでは、セクションの定義開始時にその名前も同時に指定する必要がある。

たとえば「web_proxy」という名称のフロントエンドを定義する場合、以下のようになる。

frontend web_proxy
  (以下、設定を記述する)
  :
  :

さて、frontendセクションではHAProxyによる待ち受けやサーバーへの振り分けといったフロントエンドとしての挙動を指定する必要がある。ここでまず重要なのが、待ち受けに使用するIPアドレスやポート番号を指定する「bind」キーワードだ。

bind <待ち受けアドレス>:<ポート番号>

たとえば、そのホストに割り当てられているすべてのIPアドレスに対し、80番ポートで待ち受けを行うには次のように記述する。

bind *:80

このIPアドレス/ポートに接続があった場合のデフォルトの転送先は、「default_backend」キーワードで指定する。

default_backend <バックエンド>

「バックエンド」には、backendセクションで指定したバックエンド名を指定する。

転送先サーバーを指定する「backend」セクション

backendセクションでは、HAProxyが接続を転送する先となるサーバーの設定を行う。具体的なサーバーの情報は「server」キーワードで指定する。

server <名前> <ホスト名/IPアドレス>:<ポート>

ここで、「名前」はHAProxy内でサーバーを参照するために付ける名前で、任意のものが設定できる。たとえば、「centos01」というホストに接続を転送する場合の設定は次のようになる。

backend web_servers
  server web01 centos01:80

この場合、HAProxyの設定ファイル内ではここで指定した「web01」という名前でこのサーバーを参照できるようになる。

また、サーバーはホスト名でもIPアドレスでも指定できるが、ホストを指定した場合はHAProxyの起動時にそのIPアドレスが解決される。状況に応じて毎回IPアドレスを取得するのではない点には注意したい。

シンプルなプロクシの設定例

次の設定例は、以上で説明した設定例を踏まえたシンプルなプロクシの設定例だ。HAProxyが稼動しているマシンの80番ポートへのHTTPリクエストを「centos01」というホストの80番ポートに転送する、という挙動を行うものとなる。

global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy  ←このパスにchrootしてからサーバープロセスを稼動させる
    pidfile     /var/run/haproxy.pid  ←プロセスのIDを格納するファイルのパス名
    maxconn     4000  ←最大同時接続数
    user        haproxy  ←プロセスを実行するユーザー
    group       haproxy  ←プロセスを実行するグループ
    daemon  ←デーモンとしてHAProxyを実行
    stats socket /var/lib/haproxy/stats  ←指定したUNIXソケット経由で統計情報を取得できるよう指定

defaults
    mode                    http
    log                     global  ←globalセクションで指定したログ設定を使用する
    option                  httplog  ←ログにHTTPリクエストに関する情報を出力
    option                  dontlognull  ←空のコネクションに関する情報はログに出力しない
    option http-server-close  ←サーバー側でのコネクションクローズを有効にする
    option forwardfor       except 127.0.0.0/8  ←指定したネットワーク以外からの接続に対してはX-Forwarded-Forヘッダを追加する
    option                  redispatch  ←振り分け先のサーバーに接続できない場合、別のサーバーにその接続を振り分ける
    retries                 3
    timeout http-request    10s  ←HTTPリクエストのタイムアウト時間を指定
    timeout queue           1m  ←接続待ちキューにおけるタイムアウト時間を指定
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s  ←keep-alive接続の場合で、新たなリクエスト待ちをしている際のタイムアウト時間を指定
    timeout check           10s  ←接続確立後のタイムアウト時間を設定
    maxconn                 3000  ←最大同時接続数を指定

frontend web_proxy
    default_backend web_servers
    bind *:80

backend web_servers
    server web01 centos01:80

フロントエンドとバックエンドをまとめて設定できる「listen」セクション

上記の設定ファイルにおけるfrontendセクションおよびbackendセクションに記述されている設定は、「listen」セクションを使って次のようにも記述できる(globalセクションおよびdefaultsセクションの内容は共通)。

listen web_proxy
    bind *:80
    server web01 centos01:80

listenセクションを利用することで設定をコンパクトにまとめることが可能となるが、設定する項目数が多くなった場合に分かりにくくなる場合もあるので、フロントエンドの設定とバックエンドの設定を分離して書くことをおすすめする。

複数のfrontend/backendセクションを記述する

HAProxyでは、一つの設定ファイル内に複数のfrontendセクションやbackendセクションを記述することが可能だ。たとえば次の例は、HAProxyの80番ポートへの接続をcentos01というホストの80番ポートに、HAProxyの8080番ポートへの接続をcentos02というホストの80番ポートに転送するという設定を行うものだ。

frontend web_proxy_main
    default_backend web_servers_main
    bind *:80

frontend web_proxy_images
    default_backend web_servers_images
    bind *:8080

backend web_servers_main
    server web01 centos01:80

backend web_servers_images
    server dev01 centos02:80

多機能だが基本的な設定はシンプル

HAProxyは非常に多くの機能を備えているため、設定ファイル内で使用できるキーワードも多い。そのため難解な印象を受けるかもしれないが、基本はここままで説明したとおりシンプルだ。フロントエンドとバックエンド、という概念さえ理解できれば、あとはドキュメントを参照しながらさまざまな応用が行えるだろう。

本記事後編では、実際によく利用されると思われる設定例を中心に、HAProxyが備えるさまざまな機能を紹介していく。