2018年4月26日

ブロードバンドタワーで Scality RING を担当しています、ポールです。
今回は Scality RING の1機能として提供されている S3 コネクタについて、負荷を効率よく分散させる方法を調査しましたので説明します。

負荷分散を調査した背景

S3 コネクタは Amazon S3 互換の API を処理するためのコネクタです。RING を専用のオブジェクト格納先とし、 サーバ 5台単位で構築する必要があります(5台で1セットのコネクタです)。RING の最小構成が6台なので、S3 コネクタを用いる RING 環境の最小構成は、RING が動作する 6台のうち5台で S3 コネクタを同居させたものになります。

過去に何度か 6台のサーバでRINGとS3コネクタを同居させて構成し、性能検証も行っているのですが、そのときに決まって悩むことがありました。5台の S3 コネクタに対してどうやって均等に負荷をかけるのか、という点です。S3 コネクタ側には負荷分散の仕組みが用意されていないため、クライアント側で負荷のかけかたを工夫する(もしくは妥協する)必要がありました。

負荷分散が大変の図
負荷分散が大変の図

世の中のソフトウェアでは、クライアント側で負荷分散をする仕組みを予め用意しているようなものもあるとは思いますが、すべての Amazon S3 互換クライアントにそのような仕組みがあるわけではありません。なので、クライアントとサーバの間にロードバランサをはさめば解決するのだろう、と頭では理解していました。

ロードバランサの図
ロードバランサの図

とはいえ、単純な負荷分散のためだけに箱モノのロードバランサを用意するのは芸がありません。また、性能検証のときにしか使わないロードバランサの設定を極めても使いどころが少ないので、世の中で普通に使われているソフトウェアベースのロードバランサを試して、実運用でも求められる設定例を考えることにしました。

インターネットを検索したところ、HAProxy というロードバランサが見つかりました。設定ファイルの例を載せている日本語サイトも多数見つかりますし、何より CentOS の base リポジトリに RPM が入っていますので導入が簡単です。。。というような流れで、今回は HAProxy を試してみることにしました。動作環境は、組み合わせる RING/S3 コネクタと同じ CentOS 7.4 になります。

HAProxy の導入

CentOS の base リポジトリに入っていますので、導入はとても簡単です。

yum install -y haproxy

導入されたのは haproxy-1.5.18-6.el7.x86_64 というバージョンで、少し古めのものです。この記事を書いている段階で最新の安定バージョンは 1.8系になりますが、HAProxy のベーシックな機能を試すだけなら 1.5 でも問題はないと思います。

今回目指した設定の方向性と、最初の設定

今回目指した方向性は、次のものです。

  • 普段は、S3コネクタ 5台全体で均一に負荷分散する
  • S3 コネクタの障害時には、死活監視により障害ホストをアクセス先から自動で除外する
  • S3 コネクタのメンテナンス時には、対象のコネクタをアクセス制御して、負荷を与えない

最初は 5台で均一に負荷分散するような設定を行いました。TCPモードでラウンドロビンです。末尾の backend s3_connectors に記述された stor87~stor91 の5台が Storage Server に同居している S3 コネクタになります。(ホスト名とIPアドレスのルールが少々わかりにくいですが、ご容赦ください)

#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    mode                    tcp
    log                     global
    option                  tcplog
    timeout connect         10s
    timeout client          1m
    timeout server          1m

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend  s3_proxy
    default_backend             s3_connectors
    bind                        *:80

#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend s3_connectors
    balance     roundrobin
    server  stor87 192.168.18.17:80
    server  stor88 192.168.18.18:80
    server  stor89 192.168.18.19:80
    server  stor90 192.168.18.20:80
    server  stor91 192.168.18.21:80

HAProxy のログを書き出すときの注意点

上記では削除していますが、オリジナルの /etc/haproxy/haproxy.cfg 内のコメントには syslog (CentOS7 だと rsyslog) を用いてログを書き出すには、、、といった主旨のコメントがあります。しかしその通りに HAProxy 用の設定を /etc/rsyslog.d/haproxy.conf として作成するだけではログが書き出されません。 /etc/rsyslog.conf の UDP に関する記述について、コメントを外して有効にする必要がありました。(私はこの設定箇所にたどり着くまでに、かなりの時間を要しました)

# Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514

また、今回はログを細かく出すことで負荷分散の様子を追いかける必要があったので、/etc/rsyslog.d/haproxy.conf ではあらゆるものを出すようにしました。

local2.*                       /var/log/haproxy.log

ただし、負荷が相応にかかる環境では出力する内容の検討が必要だと思います。例えば性能検証を行う環境であれば、正常動作を確認したらログは 1行も書き出させないほうがよいでしょう。一方、運用環境では1行も書き出させないわけにもいかないでしょうから、問題が発生した時だけ書き出させるような設定が望ましいと思います。

これらの設定を行ったうえで S3 クライアント(s3cmd)を用いて HAProxy 経由でアクセスさせると、ログファイルにより無難にラウンドロビンで接続できていることがわかりました。

# tail /var/log/haproxy.log
Apr 18 15:24:48 localhost haproxy[12450]: Proxy s3_proxy started.
Apr 18 15:24:48 localhost haproxy[12450]: Proxy s3_connectors started.
Apr 18 15:59:14 localhost haproxy[12451]: 192.168.18.72:46271 [18/Apr/2018:15:59:13.925] s3_proxy s3_connectors/stor87 1/0/251 520 -- 0/0/0/0/0 0/0
Apr 18 15:59:25 localhost haproxy[12451]: 192.168.18.72:46305 [18/Apr/2018:15:59:24.914] s3_proxy s3_connectors/stor88 1/0/646 222 -- 0/0/0/0/0 0/0
Apr 18 15:59:38 localhost haproxy[12451]: 192.168.18.72:46393 [18/Apr/2018:15:59:38.419] s3_proxy s3_connectors/stor89 1/0/513 610 -- 0/0/0/0/0 0/0
Apr 18 16:07:07 localhost haproxy[12451]: 192.168.18.72:48745 [18/Apr/2018:16:07:07.511] s3_proxy s3_connectors/stor90 1/0/357 479 -- 0/0/0/0/0 0/0
Apr 18 16:07:13 localhost haproxy[12451]: 192.168.18.72:48843 [18/Apr/2018:16:07:12.725] s3_proxy s3_connectors/stor91 1/0/487 479 -- 0/0/0/0/0 0/0
Apr 18 16:07:16 localhost haproxy[12451]: 192.168.18.72:48853 [18/Apr/2018:16:07:16.945] s3_proxy s3_connectors/stor87 1/0/40 479 -- 0/0/0/0/0 0/0
Apr 18 16:07:19 localhost haproxy[12451]: 192.168.18.72:48863 [18/Apr/2018:16:07:19.014] s3_proxy s3_connectors/stor88 1/0/45 479 -- 0/0/0/0/0 0/0

S3 コネクタが停止するとどうなる?

正常時には無難にラウンドロビンで負荷分散できましたので、次はS3コネクタの障害発生時です。難しいことを考えずに stor90(192.168.18.20) をシャットダウンしてから、s3cmd の動作を見てみます。

# s3cmd -c /root/s3cfg-testaccount ls s3://bucket1

WARNING: Retrying failed request: /?delimiter=%2F ('')
WARNING: Waiting 3 sec...
2018-04-18 07:34       277   s3://bucket1/hosts
#

確かにバケットのリスト結果が返ってきたのですが、数秒待たされました。これが最初の1回だけならよいのですが、ラウンドロビンで stor90 が割り振られるたびに同じだけ待たされる挙動になっていました。

数秒待たされる部分については、次のようにログが出ていました。

Apr 18 16:34:35 localhost haproxy[12451]: 192.168.18.72:60245 [18/Apr/2018:16:34:16.706] s3_proxy s3_connectors/stor90 1/-1/19018 0 sC 0/0/0/0/3 0/0
Apr 18 16:34:38 localhost haproxy[12451]: 192.168.18.72:60335 [18/Apr/2018:16:34:38.727] s3_proxy s3_connectors/stor91 1/0/44 806 -- 0/0/0/0/0 0/0

5台のうち1台ですから、5回に1回待たされることになります。これではさすがによろしくありませんので、HAProxy 側に死活監視の設定(check)を追加します。 backend s3_connectors の部分を次のように書き換えました。

backend s3_connectors
    balance     roundrobin
    option redispatch
    retries 3
    server  stor87 192.168.18.17:80 check
    server  stor88 192.168.18.18:80 check
    server  stor89 192.168.18.19:80 check
    server  stor90 192.168.18.20:80 check
    server  stor91 192.168.18.21:80 check

同様に s3cmd を用いてアクセスさせると、特に待たされることはなくなりました。HAProxy のログには次のように出力されていました。

# tail -f /var/log/haproxy.log
Apr 18 16:42:18 localhost haproxy[12451]: 192.168.18.72:63433 [18/Apr/2018:16:42:06.681] s3_proxy s3_connectors/stor90 1/-1/12023 0 SC 0/0/0/0/3 0/0
Apr 18 16:42:21 localhost haproxy[12451]: 192.168.18.72:63563 [18/Apr/2018:16:42:21.709] s3_proxy s3_connectors/stor91 1/0/27 806 -- 0/0/0/0/0 0/0
Apr 18 16:42:24 localhost haproxy[12451]: 192.168.18.72:63575 [18/Apr/2018:16:42:24.832] s3_proxy s3_connectors/stor87 1/0/150 806 -- 0/0/0/0/0 0/0
Apr 18 16:54:50 localhost haproxy[13643]: Proxy s3_proxy started.
Apr 18 16:54:50 localhost haproxy[13643]: Proxy s3_connectors started.
Apr 18 16:54:50 localhost haproxy[12451]: Stopping frontend s3_proxy in 0 ms.
Apr 18 16:54:50 localhost haproxy[12451]: Stopping backend s3_connectors in 0 ms.
Apr 18 16:54:50 localhost haproxy[12451]: Proxy s3_proxy stopped (FE: 16 conns, BE: 0 conns).
Apr 18 16:54:50 localhost haproxy[12451]: Proxy s3_connectors stopped (FE: 0 conns, BE: 16 conns).
Apr 18 16:54:53 localhost haproxy[13645]: Server s3_connectors/stor90 is DOWN, reason: Layer4 timeout, check duration: 2001ms. 4 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
Apr 18 16:56:30 localhost haproxy[13645]: 192.168.18.72:24884 [18/Apr/2018:16:56:30.764] s3_proxy s3_connectors/stor87 1/0/34 806 -- 0/0/0/0/0 0/0
Apr 18 16:56:34 localhost haproxy[13645]: 192.168.18.72:24904 [18/Apr/2018:16:56:34.223] s3_proxy s3_connectors/stor88 1/0/198 806 -- 0/0/0/0/0 0/0
Apr 18 16:56:36 localhost haproxy[13645]: 192.168.18.72:24910 [18/Apr/2018:16:56:36.180] s3_proxy s3_connectors/stor89 1/0/41 806 -- 0/0/0/0/0 0/0
Apr 18 16:56:38 localhost haproxy[13645]: 192.168.18.72:24922 [18/Apr/2018:16:56:38.028] s3_proxy s3_connectors/stor91 1/0/188 806 -- 0/0/0/0/0 0/0
Apr 18 16:56:39 localhost haproxy[13645]: 192.168.18.72:24968 [18/Apr/2018:16:56:39.804] s3_proxy s3_connectors/stor87 1/0/27 806 -- 0/0/0/0/0 0/0
Apr 18 16:56:41 localhost haproxy[13645]: 192.168.18.72:25006 [18/Apr/2018:16:56:41.571] s3_proxy s3_connectors/stor88 1/0/34 806 -- 0/0/0/0/0 0/0
Apr 18 16:56:43 localhost haproxy[13645]: 192.168.18.72:25018 [18/Apr/2018:16:56:43.174] s3_proxy s3_connectors/stor89 1/0/29 806 -- 0/0/0/0/0 0/0
Apr 18 16:56:55 localhost haproxy[13645]: 192.168.18.72:25066 [18/Apr/2018:16:56:55.305] s3_proxy s3_connectors/stor91 1/0/33 806 -- 0/0/0/0/0 0/0

stor90 への割り振りがなくなり、残った4台だけで処理を続けられています。
では今度は、落とした stor90 を起動したらどうなるか、HAProxy のログを見てみます。

Apr 18 17:25:11 localhost haproxy[13645]: Server s3_connectors/stor90 is UP, reason: Layer4 check passed, check duration: 0ms. 5 active and 0 backup servers online. 0 sessions requeued, 0 total in queue.
Apr 18 17:26:13 localhost haproxy[13645]: 192.168.18.72:37826 [18/Apr/2018:17:26:13.327] s3_proxy s3_connectors/stor87 1/0/40 806 -- 0/0/0/0/0 0/0
Apr 18 17:26:15 localhost haproxy[13645]: 192.168.18.72:37836 [18/Apr/2018:17:26:15.018] s3_proxy s3_connectors/stor88 1/0/32 806 -- 0/0/0/0/0 0/0
Apr 18 17:26:16 localhost haproxy[13645]: 192.168.18.72:37846 [18/Apr/2018:17:26:16.258] s3_proxy s3_connectors/stor89 1/0/39 806 -- 0/0/0/0/0 0/0
Apr 18 17:26:17 localhost haproxy[13645]: 192.168.18.72:37848 [18/Apr/2018:17:26:17.454] s3_proxy s3_connectors/stor91 1/0/40 806 -- 0/0/0/0/0 0/0
Apr 18 17:26:18 localhost haproxy[13645]: 192.168.18.72:37858 [18/Apr/2018:17:26:18.645] s3_proxy s3_connectors/stor90 1/0/151 806 -- 0/0/0/0/0 0/0

stor90 の起動を自動で検出して、再び負荷を割り振るようになりました。ということで、S3 コネクタの障害に関する HAProxy の動作は一旦合格として、先に進みます。

S3 コネクタのメンテナンス時はどうする?

ここまでの HAProxy の設定変更と動作確認は、 /etc/haproxy/haproxy.cfg を編集した後に systemctl reload haproxy コマンドで反映させて、ログファイルを見る、ということをやっていました。ただ、メンテナンス停止の際に設定ファイルを書き換えて再読み込みさせる、というのは少々イマイチかなと思いました。メンテナンスが終われば元の状態に戻るわけなので、可能ならば設定ファイルを変更することなく、メンテナンス動作を動的に反映させられるほうがよいだろうと考えました。

と同時に、ログファイルを見るのではなく、何か Web コンソールのようなものはないのかな?と思いました。私も(ロボットではなく)人間なので、メンテナンスの状態変化をパッと見てわかりやすいのは CLI の表示より GUI です。

インターネット検索をしたところ、どちらも簡単に見つかりました。動作の動的な変更については、 socat というプログラムを使って unix socket 経由で対処できます(必要に応じて yum install -y socat でインストールしてください)。Web コンソールも、設定を追加することで利用可能です。

まず Web コンソールですが、次の設定を /etc/haproxy/haproxy.cfg の末尾に追加しました。

listen hastats 0.0.0.0:8008
    mode http
    maxconn 64
    timeout connect 5000
    timeout client 10000
    timeout server 10000
    stats enable
    stats show-legends
    stats uri /haproxy?hastats
    stats auth admin:password

8008番ポートを指定して URI にアクセスすると認証を求められますので、設定した admin/password で認証します。すると、カラフルな画面が出てきます。

HAProxy の Web UI
HAProxy の Web UI

次に socat を用いた動作変更ですが、設定ファイルにあるこの部分を使います。

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

ただしこのままだと動作状況を見ることはできても動作を変更することができません。次のように設定変更する必要がありました。root ユーザと wheel グループに対して、admin レベルの操作を unix socket 経由で可能にする、といった意味になります。

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats user root group wheel level admin

実際に socat を次のように起動することで、unix socket 経由での設定変更が可能になります。最初に prompt コマンドを使うことで対話式の操作を可能にし、次の disable server コマンドで stor90 をメンテナンス停止させました。最後に quit コマンドで socat を終了しました。

# socat readline /var/lib/haproxy/stats
prompt

> disable server s3_connectors/stor90

> quit
#

すると、Web UI 上では stor90 の表示が茶色に変わりました。UI 上の Legend 表記より「DOWN for maintenance (MAINT)」になっていることがわかります。

メンテナンス停止を実施
メンテナンス停止を実施

これまでと同様に s3cmd でアクセスしてログを見てみると、stor90 を外してくれていることがわかります。

Apr 19 11:21:24 localhost haproxy[20503]: Server s3_connectors/stor90 is going DOWN for maintenance. 4 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
Apr 19 11:21:33 localhost haproxy[20503]: 10.3.0.101:60757 [19/Apr/2018:11:21:33.001] hastats hastats/<STATS> 0/0/0 19516 LR 2/2/0/0/0 0/0
Apr 19 11:21:43 localhost haproxy[20503]: 10.3.0.101:60758 [19/Apr/2018:11:21:33.002] hastats hastats/<NOSRV> -1/-1/10000 212 cR 0/0/0/0/3 0/0
Apr 19 11:25:30 localhost haproxy[20503]: 192.168.18.72:40487 [19/Apr/2018:11:25:30.484] s3_proxy s3_connectors/stor87 1/0/58 806 -- 0/0/0/0/0 0/0
Apr 19 11:25:35 localhost haproxy[20503]: 192.168.18.72:40513 [19/Apr/2018:11:25:35.867] s3_proxy s3_connectors/stor88 1/0/46 806 -- 0/0/0/0/0 0/0
Apr 19 11:25:37 localhost haproxy[20503]: 192.168.18.72:40515 [19/Apr/2018:11:25:37.377] s3_proxy s3_connectors/stor89 1/0/49 806 -- 0/0/0/0/0 0/0
Apr 19 11:25:38 localhost haproxy[20503]: 192.168.18.72:40525 [19/Apr/2018:11:25:38.598] s3_proxy s3_connectors/stor91 1/0/55 806 -- 0/0/0/0/0 0/0

メンテナンスが終了したら、再度 socat を使って動作を変更すればよいです。

# socat readline /var/lib/haproxy/stats
prompt

> enable server s3_connectors/stor90

> quit
#

enable server コマンドによって、Web UI 上では stor90 が茶色から緑に変更されました。同時にダウンタイムの積算もそこで止まるようです。メンテナンスに要した時間を計算せずに済みますから、それなりに便利だと思います。

メンテナンス停止から復活
メンテナンス停止から復活

まとめ

今回のブログ記事では HAProxy を S3 コネクタと組み合わせる場合の設定例についてまとめてみました。HAProxy のベーシックな機能を用いて、S3 コネクタの障害時やメンテナンス停止を考慮した動作確認を行いましたが、事前に考えていたよりもはるかに容易に実現できたと思います。

一方、実際の運用で HAProxy を使うには HAProxy そのものの冗長性の確保も必要だと思います。今回はそこまで踏み込んではいませんが、機会があればその辺りの調査結果もブログ記事にできればと思います。

本ブログの情報につきましては、自社の検証に基づいた結果からの情報提供であり、
品質保証を目的としたものではございません。

投稿者: ポール

ブロードバンドタワーで Scality RING を主に担当。ストレージ業界の知り合いからはポールと呼ばれているが、本人も由来はよくわからない。得意領域はデータベースとストレージ。