HAProxy + Ubuntu14.04


はじめに

Ubuntu14.04上でHAProxy1.5.9を構築してSSLアクセラレーターを試してみたので記載しておきます。

構成

HAProxy
├── Nginx1
└── Nginx2

環境情報

  • OS: Ubuntu14.04
  • HAProxy: 1.5.9

Step1: HAProxyのインストール

まずはHAProxyのmakeに必要なパッケージをインストールします。

$ sudo apt-get update
$ sudo apt-get install build-essential make g++ libssl-dev

次に最新のソースを持ってきます。
2014/12/8現在の最新の安定版は1.5.9です。

$ cd /usr/local/src
$ sudo wget http://www.haproxy.org/download/1.5/src/haproxy-1.5.9.tar.gz
$ 

取ってきたパッケージを展開してインストールします。

$ sudo tar zxvf haproxy-1.5.9.tar.gz
$ cd haproxy-1.5.9
$ sudo make TARGET=linux2628 USE_OPENSSL=yes 
$ sudo make install # /usr/local/sbin/haproxyがインストールされる

Step2: haproxyグループ、ユーザー作成

$ sudo groupadd -g 2001 haproxy # haproxy用グループ作成
$ sudo useradd -u 2001 -g 2001 -s /sbin/nologin haproxy # haproxy用ユーザー作成

Step3: 必要なディレクトリの作成

設定ファイルやログファイル、pidファイルの置き場所のディレクトリを作成します。

$ sudo mkdir -p /var/run/haproxy /etc/haproxy/ssl /var/log/haproxy /var/lock/haproxy
$ sudo chown -R haproxy:haproxy /var/run/haproxy /etc/haproxy /var/log/haproxy

Step4: 設定ファイルの編集

ここは全部を読み切れていませんが、大きく分けると以下二つで構成されているようです。

  • globalセクション
  • Proxies: defalutsセクション、frontendセクション、backendセクション、listenセクションのセット。

各セクションの大体の意味合いは以下となります。

セクション名 役割
global プロセス全体やOS特有の設定を行うセクション。プロセス管理、セキュリティ、パフォーマンス調整用のkeywordを記載するセクション。
defaults その他のセクション(frontend, backend, listen)のためのデフォルトの値を設定するセクション
frontend listenしているソケットの定義を記載するセクション
backend リクエストのあったコネクションの投げ先となるサーバー情報を記載するセクション
listen frontendとbackendの設定を紐付けるセクション。

こちらそれぞれで指定可能なkeywordはHAProxyマニュアルを参照してください。

/etc/haproxy/haproxy.cfg
global
    log 127.0.0.1   local0
    log 127.0.0.1   local1 notice
    maxconn 4096 # 一つのプロセスで張れる最大のコネクション数
    pidfile     /var/run/haproxy.pid
    user haproxy
    group haproxy
    daemon # background設定
    ssl-server-verify  none # オレオレ証明書のためnoneに設定
    tune.ssl.cachesize 20000 # ssl session cacheの容量。200B * 20000程度の容量
    ssl-default-bind-options no-sslv3
    tune.ssl.default-dh-param 2048 # こちら設定しないとWarning出ます。

# Proxies #
defaults
    log global # globalセクションの設定した方法でログ出力。
    mode    http # 起動時のモードを選択。tcp|http|heakthが選択可能。
    option  httplog # httpリクエストのログを出力。
    retries 3 # 接続失敗時にリトライする回数。 
    maxconn 2000 # frontendで同時に張れるコネクション数
    timeout connect 5000 # リトライまでの待ち時間。単位はmillisecond。
    timeout client  5000 # クライアントとの間のタイムアウト時間。単位はmillisecond
    timeout server  50000 # サーバーとの間のタイムアウト時間。単位はmillisecond

frontend  ssl_proxy
    bind *:443 ssl crt /etc/haproxy/ssl/server.pem # frontedでlisteningしているIPアドレス、ポートを指定。crtでは証明書とprivate keyが記載されたpemファイルを指定。
    use_backend      nginx # 転送するbackend名を指定する。

backend nginx
    balance roundrobin # ロードバランスのアルゴリズムを指定。
    server nginx1 10.3.0.29:80 check inter 2000 # 転送先のサーバー定義。checkを付けることでhealthチェックをしてくれる。この場合は2000millisecond。
    server nginx2 10.3.0.93:80 check inter 2000 # 転送先のサーバー定義。checkを付けることでhealthチェックをしてくれる。この場合は2000millisecond。
/etc/init.d/haproxy
#!/bin/bash
#
# chkconfig: - 85 15
# description: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited \
#              for high availability environments.
# processname: haproxy
# config: /etc/haproxy/haproxy.cfg
# pidfile: /var/run/haproxy.pid

# Source function library.
if [ -f /etc/init.d/functions ]; then
  . /etc/init.d/functions
elif [ -f /etc/rc.d/init.d/functions ] ; then
  . /etc/rc.d/init.d/functions
elif [ -f /lib/lsb/init-functions ] ; then
  # Ubuntu用
  . /lib/lsb/init-functions
else
  exit 0
fi

# This is our service name
BASENAME=`basename $0`
if [ -L $0 ]; then
  BASENAME=`find $0 -name $BASENAME -printf %l`
  BASENAME=`basename $BASENAME`
fi

[ -f /etc/$BASENAME/$BASENAME.cfg ] || exit 1

RETVAL=0
LOCK_DIR='/var/lock/haproxy';
if [ -f /usr/sbin/$BASENAME ] ; then
  CMD=/usr/sbin/$BASENAME;
elif [ -f /usr/local/sbin/$BASENAME ] ; then
  CMD=/usr/local/sbin/$BASENAME;
fi
start() {
  ${CMD} -c -q -f /etc/$BASENAME/$BASENAME.cfg

  if [ $? -ne 0 ]; then
    echo "Errors found in configuration file, check it with '$BASENAME check'."
    return 1
  fi

  echo -n "Starting $BASENAME: "
#  daemon ${CMD} -D -f /etc/$BASENAME/$BASENAME.cfg -p /var/run/$BASENAME.pid
  start_daemon ${CMD} -D -f /etc/$BASENAME/$BASENAME.cfg -p /var/run/$BASENAME.pid
  RETVAL=$?
  echo
  [ $RETVAL -eq 0 ] && touch ${LOCK_DIR}/$BASENAME
  return $RETVAL
}

stop() {
  echo -n "Shutting down $BASENAME: "
  killproc $BASENAME -USR1
  RETVAL=$?
  echo
  [ $RETVAL -eq 0 ] && rm -f ${LOCK_DIR}/$BASENAME
  [ $RETVAL -eq 0 ] && rm -f /var/run/$BASENAME.pid
  return $RETVAL
}

restart() {
  ${CMD} -c -q -f /etc/$BASENAME/$BASENAME.cfg

  if [ $? -ne 0 ]; then
    echo "Errors found in configuration file, check it with '$BASENAME check'."
    return 1
  fi
  stop
  start
}

reload() {
  ${CMD} -c -q -f /etc/$BASENAME/$BASENAME.cfg
  if [ $? -ne 0 ]; then
    echo "Errors found in configuration file, check it with '$BASENAME check'."
    return 1
  fi
  ${CMD} -D -f /etc/$BASENAME/$BASENAME.cfg -p /var/run/$BASENAME.pid -sf $(cat /var/run/$BASENAME.pid)
}

check() {
  ${CMD} -c -q -V -f /etc/$BASENAME/$BASENAME.cfg
}

rhstatus() {
  status $BASENAME
}

condrestart() {
  [ -e ${LOCK_DIR}/$BASENAME ] && restart || :
}

# See how we were called.
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    restart
    ;;
  reload)
    reload
    ;;
  condrestart)
    condrestart
    ;;
  status)
    rhstatus
    ;;
  check)
    check
    ;;
  *)
    echo $"Usage: $BASENAME {start|stop|restart|reload|condrestart|status|check}"
    exit 1
esac

exit $?
/etc/haproxy/ssl/server.pem
-----BEGIN PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3fOpcu73aVjofSld6lcAyj3cVd2qfKro8BDsXAtyZGmTDS2VgzqEk+LNUZJtVfwE
6RzFDjsh2e31hX9RAgZpR1uk9534Hw1YcSBMbgAnW2hTqcV0/mESVYn1+1zPNlfy
2sHgs2dzAdujp0Cvm6T14SmHKqOgDt6XuexuQRZNonJ4dB04eSVyJVj47CRo6NqX
ZOBBfHN9R6dIRz1BnjUeRttdj13qljhJ0E+u4MnCRNbghpGrzwxLMvWwp320f3AZ
qrtPEQ8gH97PEwx7baU7Z+wltRzSXGxqEdvNILSdjMnGjFIVwX2iKsXcrKz9A2SK
RbksYX/hAgMBAAECggEAPvBecxhKMjp+zXuzlVALLxgXJpxD9hUntAwljulci59u
JZ3LtZ5vQhv1ks36w+34d17APtrkxYBgMHUSB8A3lFSyo/6PWyzTsJ0PRuO53D2p
08SyfnbrqaVD3Erl4GHRDVcJIzevZqUnMaNSw9XCAKd8YXoiD5wcFkAJ/ZODUVX0
unsQVSRLVpjP3LxbkLA1behgUbYfe6IgQY3PdgU6ddZgFKMYo901AOAc6GT43c0B
33ehGO+rZohMPUKLME0LbfsLscPztL2ZNXf6K3iA67bTrOfK+L4LiVnJllf4WWDU
YyQaE+BP2jqfPBtzidHqtDo5uSowpQx9j6wpupum7QKBgQDeezBt49CZWTyWWfF7
XkbNC6DIavbtJCtDmxlAiPw+6de8O9UJpe64jXBX1n3C7T2MvZh2hYN+hOxOUS0M
YMncyxQ8AJpeUn/qxjvdZ82iR96YxZrtw1A9EL66hQQAY1MfLPnlgBRzj9S1KZf7
BtDkwZIbaY2aNounQSUjo9msswKBgQDDrqVD5c2l2BQZglJ1TRstSLQOwQl/6EbK
Cohi2sOZ+6znL37ta8UCN4OGnV8q+hNnegGc+UUZcPXsx6Omou0qkjy0IKEqepZC
1On9OXcoyXBn7i5K2kIycJ+Ztw0OBewD8BJzSbKuRB9k1JHOiGAZpszurjpGPJJJ
w8CudHkTGwKBgFfEhWB0pRA58frmVKt0j73IPo+SEwOOZzrubtHUuJ4hJiht+icq
PLi1o4Ijbc1+Re/Fw+XN2ggJbCehQoJFlJzzyHG7UyqupSP7PBamIqtIavodynbN
vFO6zWNLoj588Ie1+IefNXI5ZZJqbDhtge8cpgUGaQRTTFiZDIu40eCdAoGADVNk
X8ZThdq8K6yLTv+sPR45XNPagMJhp7ql5Q1+yoqwEKJUY3i2VttO8qje5T9YePB/
EaCMCxp9NN23kE6CFicVSTL7prfoaa60LJFwQu/M3tcCgghw/5xZ0g78LZtLok0E
BKPdLi7ncGdlcooo+TY5uHtW7peLYsiY44h9AkcCgYArW6zrNr3ASfJ/xjG0FItv
ltzQUa/vBm+H2q0QKq4LWw0u542rymEXpRIyV/67EWxfUBo8xkik7IvNrnzzt/jC
4mUarfHuRHrXXcvU8joSalaj7/B7h/ps1y43mjOfwgXPtoi2tE8yx2fL4kETvQBP
TbHkZz8FCRsVN7cEfVUAcg==
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
SlAxDjAMBgNVBAgMBVRva3lvMRIwEAYDVQQHDAlTaGluanl1a3UxGjAYBgNVBAoM
EUlEQyBGcm9udGllcixpbmMuMSkwJwYDVQQLDCBUZWNobmljYWwgRGV2ZWxlcG1l
bnQgRGVwYXJ0bWVudDETMBEGA1UEAwwKVWVubyBBa2l0bzEcMBoGCSqGSIb3DQEJ
ARYNYXVlbm9AaWRjZi5qcDAeFw0xNDA4MjQwNTQwMTdaFw0xNDA5MjMwNTQwMTda
MIGrMQswCQYDVQQGEwJKUDEOMAwGA1UECAwFVG9reW8xEjAQBgNVBAcMCVNoaW5q
eXVrdTEaMBgGA1UECgwRSURDIEZyb250aWVyLGluYy4xKTAnBgNVBAsMIFRlY2hu
aWNhbCBEZXZlbGVwbWVudCBEZXBhcnRtZW50MRMwEQYDVQQDDApVZW5vIEFraXRv
MRwwGgYJKoZIhvcNAQkBFg1hdWVub0BpZGNmLmpwMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAqg+dPwdkKEiKpN3zqXLu92lY6H0pXepXAMo93FXdqnyq
6PAQ7FwLcmRpkw0tlYM6hJPizVGSbVX8BOkcxQ47Idnt9YV/UQIGaUdbpPed+B8N
WHEgTG4AJ1toU6nFdP5hElWJ9ftczzZX8trB4LNncwHbo6dAr5uk9eEphyqjoA7e
l7nsbkEWTaJyeHQdOHklciVY+OwkaOjal2TgQXxzfUenSEc9QZ41HkbbXY9d6pY4
SdBPruDJwkTW4IaRq88MSzL1sKd9tH9wGaq7TxEPIB/ezxMMe22lO2fsJbUc0lxs
ahHbzSC0nYzJxoxSFcF9oirF3Kys/QNkikW5LGF/4QIDAQABMA0GCSqGSIb3DQEB
BQUAA4IBAQA+C5SzaN+cRfaMxjWuWlxMLDSQ2jVg9oyS9XY4CfME6GWyU4DWMyhe
NJ+H4r+MxSJb9nJs1N5weR7hObvZCM/K2qh7P+d+UGa2qfZeq1PJq6HZqVPgzKBc
rsuFEfDgnx2OS0MsfxWZc4kamZb6W5d46tYaFI0aCutcRQg2wF5+VQvfPYtPG27T
h7lYMA8Rw4lys55hOiMx1BxJgZX6o1RhV8UJp/+hIAQ9pQ64uU/UzJRm6jvHxAo2
4iBMw8qoZV42/Oh/ZsKJcS1FEYqCHa9nnr1ogsYijsQxDG2jP/dmoOAXcXt/LCqP
vPPyDdcONycVadjAWZgVZ0HsquCrHMtb
-----END CERTIFICATE-----

Step5: HAProxyを起動する

$ sudo chmod +x /etc/init.d/haproxy
$ sudo /etc/init.d/haproxy start

Step6: アクセスしてみる

せっかくなのでtcpdumpをとり、HTTP通信になっているかどうかを確認してみる。

$ sudo tcpdump -i eth0 -s 2048K -w /tmp/tcpdump port 80
$ curl -k https://${haproxy_server}
$ curl -k https://${haproxy_server}
$ curl -k https://${haproxy_server}
$ curl -k https://${haproxy_server}
$ curl -k https://${haproxy_server}

Step7: 確認してみる

取得したtcpdumpをWiresharkで見てみる。
10.3.0.9310.3.0.20に交互にリクエストが投げられていることが分かる。

おわりに

HAProxyを利用すれば、結構簡単にSSLアクセラレーションのLBを作成できることがわかりました。
ただ設定できるkeywordが多く本格的に利用するにはまずはドキュメントの読み込みが必要そうです^^;

おしまい。