HAProxyでヘルスチェック、URL rewriteする


Apache Sparkを構築し、管理サーバ経由でSparkの管理画面を参照するため、HAProxyをReverse Proxyとして構築します。

## 要件/仕様

1. Apache Spark masterは3台で構成する。
2. 上記3台のうちSparkの状況がわかるのはLeaderのみ。管理画面上でStatus: ALIVEとなっているものを参照できるようにする（Leader以外のstandby機はWorker等のステータスが表示されないため）
3. Sparkの管理画面は http://管理サーバIP/spark/ でアクセスできるものとする（leaderに直接アクセスすれば http://Leader_IP:8080/ で状況を確認できる）


![スクリーンショット_2015-07-30_16_57_48.png](https://qiita-image-store.s3.amazonaws.com/0/28210/d17343f5-0f0a-4d6a-f74d-425fa61b0511.png "スクリーンショット_2015-07-30_16_57_48.png")

## 実現構成

上記要件より下図のような構成とし、HAProxyの設定を行います。

![名称未設定.png](https://qiita-image-store.s3.amazonaws.com/0/28210/283156c9-88b3-e4de-e5e1-865ae82a47f3.png "名称未設定.png")

#### 仕様2の対応

ヘルスチェック機能の戻り値チェックを利用し、レスポンスの中身でStatus: ALIVEが含まれるものを正常とする。

#### 仕様3の対応

HTTPリクエストのリプレース機能を利用し、 /spark/ にマッチする場合、そのマッチしたものを取り除いたリクエストを転送する。

## インストール

パッケージが利用可能です。

```bash
sudo yum install -y haproxy
```

ただし、上記でインストールすると、古いバージョンがインストールされてしまいます。もし本番環境に導入される場合は、脆弱性対応がされた最新版のソースからビルドしたほうがいいです。


## 設定内容

今後別なミドルウェアの管理画面も管理サーバ経由で参照することを考慮し、frontendとbackendにわけて設定しています（frontendでURLマッチング判定し、適切なbackendへ転送するため）。すでに転送先が決まっている場合は、listen でまとめてしまってもいいと思います。設定については[HAProxyのドキュメント](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html)を確認ください。

#### 仕様2の設定
```text
  option  httpchk GET /
  http-check expect rstring .+Status.+ALIVE.+
```

```option httpchk GET /```で各Spark masterの / にアクセスします。単にこの設定だけだと、Responseが2xxか3xxかしかチェックしません。```http-check expect```を使用することで、Responseの中身までチェックできるようになります。```rstring```は戻り値のチェックを正規表現で行いたい場合に使用します。

#### 仕様3の設定
```text
  reqrep  ^([^\ :]*)\ /spark\/(.*)  \1\ /\2
```

```reqrep```がURL rewriteする場合の設定です。URLだけでなく、リクエスト全体を見ているため、上記のような正規表現が必要になります。
（補足）http のリクエストは一般的に下記のようになります。

```text
user% curl -v http://管理サーバIP/spark/
* Hostname was NOT found in DNS cache
*   Trying xx.xx.xx.xx...
* Connected to xx.xx.xx.xx (xx.xx.xx.xx) port 80 (#0)
> GET /spark/ HTTP/1.1
> User-Agent: curl/7.37.1
> Host: xx.xx.xx.xx
> Accept: */*
```

最終的な設定は以下になります。

```text
global
  log      127.0.0.1  local4 info
  user     root
  group    root
  daemon

defaults
  log      global
  mode     http
  option   dontlognull
  option   redispatch
  balance  leastconn
  retries  3
  maxconn  100
  timeout  connect  5000
  timeout  client  50000
  timeout  server  50000
  stats    enable

frontend MGMTSRV
  bind    :80
  mode    http
  option  httplog  clf
  acl     spark    path_reg  /spark/
  acl     static   path_reg  /static/
  use_backend SRV_SPARK  if spark
  use_backend SRV_SPARK  if static

backend  SRV_SPARK
  mode    http
  option  httplog  clf
  option  httpchk GET /
  http-check expect rstring .+Status.+ALIVE.+
  reqrep  ^([^\ :]*)\ /spark\/(.*)  \1\ /\2
  server  spark1 SparkMaster1_IP:8080 check inter 10s rise 2 fall 2
  server  spark2 SparkMaster2_IP:8080 check inter 10s rise 2 fall 2
  server  spark3 SparkMaster3_IP:8080 check inter 10s rise 2 fall 2

```

※ SparkのCSSが/static/配下に保存されているため、その場合もSpark masterに転送する設定を入れてあります。

## 結果（haproxy?stats）

http://管理サーバIP/haproxy?stats にアクセスするとどのサーバが正常に起動しているか確認できます。

![スクリーンショット 2015-07-30 17.34.50.png](https://qiita-image-store.s3.amazonaws.com/0/28210/dc86e361-b8d1-56c9-5112-3a9c8670f8a3.png "スクリーンショット 2015-07-30 17.34.50.png")

## 所感

HAProxyは便利ですね。動作も軽快で、CPUへのインパクトがほとんどありません。たまに発生する管理画面へのアクセスであれば、t2.microで動かしてもまったく問題ないです。素晴らしいです。

## 注意

動作は確認していますが、保証するものではありません。実際お試しになる場合は正しく動作しない可能性もありますので、ご留意ください。
内容で間違っている部分等ありましたらご指摘いただけるとうれしいです。