ども、cloudpack の かっぱ (@inokara) です。
追記(2014/12/12)
HAProxy のドキュメントを眺めていたら mysql-check について以下のように書かれていました。
If you specify a username, the check consists of sending two MySQL packet,
one Client Authentication packet, and one QUIT packet, to correctly close
MySQL session. We then parse the MySQL Handshake Initialisation packet and/or
Error packet. It is a basic but useful test which does not produce error nor
aborted connect on the server.
MySQL の死活監視について RDS 側で max_connect_errors
を 999999999
に設定して接続エラーを回避するのではなくてチェック用のユーザーを作成する方が良いようです。
GRANT USAGE ON *.* TO monitor@'%';
上記のように最低限の権限で監視用のユーザーを作成し、haproxy.cfg では以下のように設定します。
mode tcp option mysql-check user monitor balance leastconn
mysql-check
オプションに user ${ユーザー名}
を付加します。
はじめに
Amazon RDS のリードレプリカが複数台存在する場合に HAProxy を RDS とアプリケーションの間においてアクセスを分散させる…というのは既に以下の記事で紹介されています。
HAProxy を利用することで RDS のヘルスチェックは HAProxy に任せることが出来るのでカジュアルに導入出来ると思いますが,,,では、その HAProxy をどうやって冗長化しましょうか…という疑問に基づいた答えを探したのが今回の記事です。
参考
- CONSUL DOCUMENTATION
- オーケストレーションツールとしてのConsulの使い方←とても参考になります!
- Docker + consul-template で HAProxy のバックエンド登録の自動化を試す
概要
環境
- アプリケーション・サーバー ✕ 2
- RDS(Master + Read Replica x 2)
- データベースの読みと書きの区別はアプリケーション側で実装する
やりたいこと
- Read Replica へのアクセスは一意の名前でアクセス出来るようにする(Route53 の Private DNS での出来そう)
- Read Replica へのアクセスは HAProxy で分散させる
- 一台の HAProxy が死んでも RDS への接続が保証される
ツール類の整理
ツール | 用途(実装) |
---|---|
HAProxy | Read Replica へのアクセスを分散 |
consul | HAProxy の死活を監視してクラスタを構成、管理する |
consul-temlate | Consul に登録された情報を利用して /etc/hosts ファイルを生成する |
構成図
実装(という程ではないけど)
Read Replica の分散
HAProxy の設定は以下のように。
global log 127.0.0.1 local0 log 127.0.0.1 local1 notice maxconn 4096 daemon defaults log global option dontlognull retries 3 maxconn 2000 timeout connect 5000ms timeout client 50000ms timeout server 50000ms listen mysql bind 0.0.0.0:3306 mode tcp option mysql-check user monitor balance leastconn server read01 hogehoge-replica.xxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:3306 check port 3306 inter 10000 fall 2 server read02 hogehoge-replica02.xxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:3306 check port 3306 inter 10000 fall 2 server master hogehoge.xxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:3306 check port 3306 backup listen haproxy_stats bind 127.0.0.1:1919 mode http stats enable stats uri /stats
ポイントは…
- RDS のマスターは backup に指定しており Read Replica が全死亡でも最悪は Master からデータを取得することが出来る
stats enable
を設定することで curl で HAProxy の死活をチェックすることが出来る
注意点としては…こちらの記事「HAProxyを用いたRead Replica(RDS)の振り分け」にも書かれているように RDS のパラメータグループで max_connect_errors
を 999999999
にしておく必要があります。これは HAProxy からのヘルスチェックで RDS から接続をブロックしてしまうことを回避しています。これの設定を怠ると以下のようなエラーが出て接続が出来なくなります…orz
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0
consul の起動
両方ともに Server モードにて consul を起動します。
まずは一台目を以下のように起動します。
consul agent -data-dir=/tmp/consul -server -bootstrap-expect 2 &
さらに二台目を以下のように起動します。尚、consul は 0.4.1 を利用しており /usr/local/bin/
以下に展開済みです。
consul agent -data-dir=/tmp/consul -server -join ${一台目の consul ノードの IP} &
-bootstrap-expect
で指定した数の台数が揃ったタイミングで Bootstrap が走ります。
Bootstrap が終了したら consul members
でクラスタの状態を確認します。
# consul members 2014/12/12 19:13:53 [INFO] agent.rpc: Accepted client: 127.0.0.1:37823 Node Address Status Type Build Protocol ip-xxx-xxx-xxx-1 xxx.xxx.xxx.1:8301 alive server 0.4.1 2 ip-xxx-xxx-xxx-2 xxx.xxx.xxx.2:8301 alive server 0.4.1 2
consul へのサーヴィス登録と HAProxy の監視
既に HAProxy は起動している状態で consul にサービスを以下のように登録します。
cat << EOT | curl -XPUT http://127.0.0.1:8500/v1/agent/service/register -d @- { "name": "rdsproxy", "port": 3306, "check": { "script": "/path/to/bin/check_haproxy.sh", "interval": "10s" } } EOT
この操作は両方のノードで行います。
サービスの登録が終わったら登録されているサービスを確認します。
# curl -s http://127.0.0.1:8500/v1/catalog/service/rdsproxy | jq . [ { "Node": "ip-xxx-xxx-xxx-2", "Address": "xxx.xxx.xxx.2", "ServiceID": "rdsproxy", "ServiceName": "rdsproxy", "ServiceTags": null, "ServicePort": 3306 }, { "Node": "ip-xxx-xxx-xxx-1", "Address": "xxx.xxx.xxx.1", "ServiceID": "rdsproxy", "ServiceName": "rdsproxy", "ServiceTags": null, "ServicePort": 3306 } ]
また、HAProxy の監視スクリプト(/path/to/bin/check_haproxy.sh
)は以下のような内容です。
#!/bin/sh r=`curl -LI 127.0.0.1:1919/stats -o /dev/null -w '%{http_code}n' -s` if [ ${r} = "200" ];then exit 0 else exit 1 fi
監視スクリプトはドキュメントによると sensu や Nagios と同様に…
- Exit code 0 – Check is passing
- Exit code 1 – Check is warning
- ny other code – Check is failing
正常終了は 0
で終わらせることが重要なようです。
consul-template の起動と設定
次に consul 上のデータを利用して /etc/hosts に HAProxy の IP アドレスを設定する為に consul-template を利用します。consul-template についてはこちら「consul-template/README.md at master · hashicorp/consul-template」を御覧ください。
まず hosts ファイルの元になるテンプレートファイルを以下のように作成します。
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 {{range service "rdsproxy"}} {{.Address}} rdsproxy {{.Node}}{{end}}
{{range service "rdsproxy"}}
から {{end}}
までの間は Go のテンプレートになっており consul のサービス定義の中から Address
や Node
の値が埋め込まれます。
consul-template はサービスとして起動させることで consul を監視して、consul に登録されているデータに変化があれば consul-template が実行されて該当のファイルを更新しますので以下のように consul-template を両方のノードで起動しておきます。
consul-template -consul 127.0.0.1:8500 -template /etc/hosts.ctmpl:/etc/hosts &
はい。これで、準備完了です。
デモ
2 ノードの状態
/etc/hosts を覗いてみます。
# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 xxx.xxx.xxx.1 rdsproxy ip-xxx-xxx-xxx-1 xxx.xxx.xxx.2 rdsproxy ip-xxx-xxx-xxx-2
おお…rdsproxy
として 2 つのノードが登録されています。rdsproxy
という名前を利用して RDS につないでみます。
# mysql -uhoge -h rdsproxy -P 3306 -p Enter password: Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 34826 Server version: 5.6.19-log MySQL Community Server (GPL) Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or 'h' for help. Type 'c' to clear the current input statement. mysql> mysql> show variables like 'hostname%'; +---------------+---------------+ | Variable_name | Value | +---------------+---------------+ | hostname | ip-10-7-1-205 | +---------------+---------------+ 1 row in set (0.01 sec) mysql> exit
おお。
HAProxy の一台に障害やー
# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 xxx.xxx.xxx.2 rdsproxy ip-xxx-xxx-xxx-2
おお。consul-tempalte が /etc/hosts を書き換えて rdsproxy
が一台になっています。
RDS につないでみます。
# mysql -uhoge -h rdsproxy -P 3306 -p Enter password: Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 5730 Server version: 5.6.19 MySQL Community Server (GPL) Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or 'h' for help. Type 'c' to clear the current input statement. mysql> mysql> show variables like 'hostname%'; +---------------+--------------+ | Variable_name | Value | +---------------+--------------+ | hostname | ip-10-7-1-56 | +---------------+--------------+ 1 row in set (0.00 sec) mysql>
難なく RDS には接続出来ました。
ということで…
HAProxy の冗長化を図ろうとすると Corosync + Pacemaker か Heartbeat を選択しがちですが、consul と consul-template を利用すれば consul 自体の監視やプロセス管理方法等については引続き詰める必要はあると思いますが、カジュアルとモダン(自分比)に構築することが出来たような気がします。
元記事はこちらです。
「RDS のリードレプリカへのアクセスを HAProxy で分散しつつ consul と consul-template で冗長化するメモ」