HAProxy の external-check で Read Replica から Master に昇格した Amazon Aurora に接続できないようにする
追記 2016/07/22 19:06
error: 'Lost connection to MySQL server at 'reading initial communication packet', system error: 0'
というエラーがでて接続できなくなる現象が発生するようなので、改良が必要なようです。
エラーの検証をしようとしたら再現できなかったので、とりあえず気にしなくても良さそうです。
追記 2016/07/19 18:48
ブックマークにもコメントいただきましたが、
SHOW GLOBAL VARIABLES LIKE 'innodb_read_only';
を実行して、
OFF なら master、ON なら Read Replica という判断方法もあります。
Amazon Aurora を使用する際のベストプラクティス に書いてあります。
なぜ、これを使わなかったかというと、検証したときは知らなかったからです…
検証した環境は以下の通りです。
$ haproxy -v HA-Proxy version 1.6.6 2016/06/26 Copyright 2000-2016 Willy Tarreau <willy@haproxy.org> $ cat /etc/system-release Amazon Linux AMI release 2016.03
RDS for MySQL は Multi-AZ 構成にした場合、スタンバイ用のインスタンスにアクセスできません。
Amazon Aurora(以降、Aurora) はスタンバイ用インスタンスが Read Replica となっているため参照することができます。
Aurora を HAProxy で参照分散するとき、Read Replica のいずれかが Master に昇格するため、
Read Replica のつもりで参照していたインスタンスが Master になっている可能性があります。
Slave 参照しない Read Replica を用意し、Master に昇格させるインスタンスの優先順位を調整することで、
HAProxy からは Master に参照することがないように調整することも可能ですが、
一台は余剰なインスタンスとなってしまいますし、3台構成の場合に Master と Read Replica 1台が同時にダウンした場合どうするかなど、
考慮しなくてはならないことがたくさんあります。
もちろん、Master を参照しても負荷的に問題なければ良いですが、Slave 参照する場合は Master への負荷を軽減したい場合でしょうから問題になるケースが多いのではないでしょうか。
以上のことから、HAProxy の mysql-check では意図しない挙動をしてしまいます。
そこで、HAProxy の external-check という、任意のコマンドでヘルスチェックを行う機能を利用できないか検証しました。
haproxy.conf
使用した設定ファイルです。
global external-check resolvers mydns nameserver dns1 10.2.0.2:53 nameserver dns2 8.8.8.8:53 resolve_retries 3 timeout retry 1s hold valid 60s defaults log 127.0.0.1 local1 debug retries 2 timeout connect 3000 timeout server 5000 timeout client 5000 frontend f_mysql_slave bind 127.0.0.1:3307 default_backend b_mysql_slave backend b_mysql_slave log 127.0.0.1 local1 debug mode tcp option external-check external-check path "/usr/bin:/bin:/usr/local/bin" external-check command /usr/local/bin/check_mysql_slave.sh balance roundrobin server slave test-db01.xxxxxx.ap-northeast-1.rds.amazonaws.com:3306 check resolvers mydns server master test-db.cluster-xxxxxx.ap-northeast-1.rds.amazonaws.com:3306 check resolvers mydns backup
external-check を使うためには、
global external-check backend b_mysql_slave option external-check external-check path "/usr/bin:/bin:/usr/local/bin" external-check command /usr/local/bin/check_mysql_slave.sh
が必要です。
server master ... backup
は Read Replica が全部ダウンしたときのみ Master を参照するための設定です。
external-check command
HAProxy がコマンドを実行する際に環境変数を設定するようになっています。
環境変数は以下のとおりです。
- HAPROXY_PROXY_ADDR
- backend の bind IP
- backend に定義した bind は無視されるので実質使用しない(はず)
- HAPROXY_PROXY_ID
- backend ID.
- HAPROXY_PROXY_NAME
- backend 名
- HAPROXY_PROXY_PORT
- backend の bind port
- backend に定義した bind は無視されるので実質使用しない(はず)
- HAPROXY_SERVER_ADDR
- server の IP
- HAPROXY_SERVER_CURCONN
- server のカレントコネクション数
- HAPROXY_SERVER_ID
- server の ID
- HAPROXY_SERVER_MAXCONN
- server の 最大コネクション数
- HAPROXY_SERVER_NAME
- server の設定名
- HAPROXY_SERVER_PORT
- server で設定したホストの port
- PATH
- external-check path で設定した PATH
詳細は external-check command を参照してください。 環境変数を上から順に出力した例は以下のとおりです。
2 b_mysql_slave 10.206.1.20 0 2 0 slave 3306 usr/bin:/bin:/usr/local/bin
check_mysql_slave.sh
ヘルスチェックを行うスクリプトです。
#!/bin/bash BACKUP="master" MASTER_IP=$(dig +short test-db.cluster-cnttkfxtxwao.ap-northeast-1.rds.amazonaws.com | tail -n 1) [ "${MASTER_IP}" = "" ] && exit 1 [ "${HAPROXY_SERVER_NAME}" = "${BACKUP}" ] && exit 0 [ "${HAPROXY_SERVER_ADDR}" != "${MASTER_IP}" ]
${MASTER_IP}
が取得できなければ fail${HAPROXY_SERVER_NAME} = ${BACKUP}
の場合、backup 以外のインスタンスはダウンしているので Master を参照するようにする${HAPROXY_SERVER_ADDR} != ${MASTER_IP}
の場合、Read Replica を参照しているのでヘルスチェックを success にする
追記: 2016-07-25 16:05
SHOW GLOBAL VARIABLES LIKE 'innodb_read_only';
を使う場合は以下のようになります。
#!/bin/bash BACKUP="master" READ_ONLY=$(mysql -u haproxy -h ${HAPROXY_SERVER_ADDR} -P ${HAPROXY_SERVER_PORT} -BN -e "SHOW GLOBAL VARIABLES LIKE 'innodb_read_only';" --connect-timeout=5 | awk '{print $2}') [ "${HAPROXY_SERVER_NAME}" = "${BACKUP}" ] && exit 0 [ "${READ_ONLY}" = "ON" ]
DNSを引く場合と違って、BACKUP
以外は環境ごとに変わる要素がなくなったので使いやすくなっています。
検証結果
Master と Read Replica それぞれ 1 台ずつで 2 種類の検証を行いました。
Read Replica が Master に昇格した場合
backup の設定は削除した状態です。
server slave = Master なのでヘルスチェックに失敗しています。
Read Replica が 1 台なので no server available になっていますが、Read Replica が 2台以上ある場合は、
Read Replica には接続できて、Master には接続できない状態になります。
Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1032ms, status: 2/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1032ms, status: 2/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1007ms, status: 1/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1007ms, status: 1/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1016ms, status: 0/2 DOWN. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1016ms, status: 0/2 DOWN. Server b_mysql_slave/slave is DOWN. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue. Server b_mysql_slave/slave is DOWN. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue. backend b_mysql_slave has no server available! backend b_mysql_slave has no server available!
backup を設定して、Read Replica が Master に昇格した場合
backup 以外の server のヘルスチェックに失敗して、backup のみヘルスチェックに成功した場合のログです。
Master 以外ダウンしているときは Master に接続できるようになっていることがわかります。
Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1014ms, status: 2/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1014ms, status: 2/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1007ms, status: 1/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1007ms, status: 1/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1014ms, status: 0/2 DOWN. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1014ms, status: 0/2 DOWN. Server b_mysql_slave/slave is DOWN. 0 active and 1 backup servers left. Running on backup. 0 sessions active, 0 requeued, 0 remaining in queue. Server b_mysql_slave/slave is DOWN. 0 active and 1 backup servers left. Running on backup. 0 sessions active, 0 requeued, 0 remaining in queue. b_mysql_slave/master changed its IP from 10.2.0.10 to 10.2.1.20 by mydns/dns1. b_mysql_slave/master changed its IP from 10.2.0.10 to 10.2.1.20 by mydns/dns1. Health check for server b_mysql_slave/slave succeeded, reason: External check passed, code: 0, check duration: 1015ms, status: 1/2 DOWN. Health check for server b_mysql_slave/slave succeeded, reason: External check passed, code: 0, check duration: 1015ms, status: 1/2 DOWN. Health check for server b_mysql_slave/slave succeeded, reason: External check passed, code: 0, check duration: 1007ms, status: 3/3 UP. Health check for server b_mysql_slave/slave succeeded, reason: External check passed, code: 0, check duration: 1007ms, status: 3/3 UP. Server b_mysql_slave/slave is UP. 1 active and 1 backup servers online. 0 sessions requeued, 0 total in queue. Server b_mysql_slave/slave is UP. 1 active and 1 backup servers online. 0 sessions requeued, 0 total in queue. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1014ms, status: 2/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1014ms, status: 2/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1007ms, status: 1/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1007ms, status: 1/3 UP. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1016ms, status: 0/2 DOWN. Health check for server b_mysql_slave/slave failed, reason: External check error, code: 1, check duration: 1016ms, status: 0/2 DOWN. Server b_mysql_slave/slave is DOWN. 0 active and 1 backup servers left. Running on backup. 0 sessions active, 0 requeued, 0 remaining in queue. Server b_mysql_slave/slave is DOWN. 0 active and 1 backup servers left. Running on backup. 0 sessions active, 0 requeued, 0 remaining in queue.
まとめ
HAProxy の external-check で、Aurora の Read Replica が Master に昇格したときに参照させないようにすることができました。
また、Read Replica が全台ダウンしたときだけ Master を参照できることも確認できました。
独自のヘルスチェックが行えるので、他にも応用ができそうですね。