※1年以上前の旧い記事なのでご注意ください。※
こんにちは。プラットフォームの小宮です。
今回は、ELBは高いのでスモールスタートだからLVSを構築しましょうという趣旨ですすめた記録です。 最初に書くのもなんですが、LVSでWEBもDBも負荷分散しちゃおうぜということだったんですが、結果的にはWEB側はやっぱELBにという話になりました。 (公私混同フラットNWであることを顧客に説明するのが大変だという話とSSLとかスケールとか運用の利便性等で。)
普段よりは気をつける点: ・環境がAWS(インスタンスを気軽に作り直してChange Source/Dest CheckをDisableにし忘れる等に要注意) ・クライアントがグローバル越しだとDSRできない(エッジルータでENIとIPが合わないパケットが破棄される) ・フラットなNW構成でないとDSRできない(これは普段通りだけど気をつけるポイント) ・複数NWでNATインスタンスが必要になる等の特殊事情(だからNWは1つだけにしました) ・DBはDSR、WEBはNATで分散する ・VIPは使えるけどAWS的な管理上APIサーバと通信して切り替える必要がある ・AWS的な管理上splitbrainは実質起こらないがどっちについてるか確認必要 ・AWS的な制約でマルチキャストは使えないのでHA通信はunicastを用いる ・証明書は負荷分散ライセンスとかでWEBサーバ側で管理する
目次: ・基本構成 ・分散対象側の基本的な設定 ・ipvsadmで負荷分散の単体テスト ・heartbeatの導入設定 ・ldirectordの導入設定 ・EIP・PrivateIP移動シェル作成 ・リソース設定 ・インスタンスコピーの際の作業 ・切替テスト ・ハマったところ
・基本構成
LVS01(主系) LVS02(待機系) web2台、配信サーバ2台、db2台に対しそれぞれ80と443、3306readを分散
lvs01:172.18.1.23 lvs02:172.18.1.25 db-r-vip:172.18.1.200 web-vip:172.18.1.199 db-client:web(local) web-client:any(global) real-db:172.18.1.220,221 real-web:172.18.1.33,34 webの分散方式:NAT dbの分散方式:DSR
設計書ではフロントセグメントとバックセグメントに分かれていたのですが、AWS的な仕様上LVSと組み合わせる為にはフラットにせざるをえませんでした。 ⇒クライアントがグローバル越しのWEBサーバはエッジルータでENIとIPが一致しないパケットが破棄される為分散方式はDSRは無理でNATしか利用できない ※こちらのスライドの17枚目が論拠です。 ⇒分散方式がNATだとリアルサーバは負荷分散サーバをデフォルトGWとして指定する必要がある ⇒NATインスタンスはNICを一つしか持つことができない仕様だがデフォルトGWは同一セグメントでないと指定できない ⇒少なくともWEBはセグメント1つにしないとLVS+VPCでは負荷分散できないがDBとWEBの分散は同じLVSで一緒にやりたいしもうNWセグメントは1つでいいや ⇒バックセグメントが無くてもVPCだしSecurityGroupの設定さえまともなら大丈夫 というような事情によりまして設計書の仕様変更をしないとどうにもなりませんでした。 なんでHAproxyじゃないのかというと、運用上不慣れだからとLVSのがパフォーマンスがいいらしいから。DBだけでもDSRできるのは嬉しいです。
・分散対象側の基本的な設定
WEB側の設定
vi /etc/sysconfig/network-script/ifcfg-eth0
※webサーバは以下を追記
GATEWAY=172.18.1.199
※配信サーバは以下を追記
GATEWAY=172.18.1.206
service network restart
netstat -rn
※もしAWSのAPIと通信してメタデータでインスタンスIDとかとる需要がある場合、 GW変えてしまうととれなくなるので注意。
マネジメントコンソールから「Change Source/Dest Check」をDisableにする
チェックファイルを置く
echo balance `uname -n` > /var/www/html/chk.html
vi /etc/rc.local
echo balance `uname -n` > /var/www/html/chk.html
セキュリティグループの設定を正しく行い、ブラウザから動作確認をする
DB側の設定 DBサーバ側でarpを無効にしてlo:0インタフェースをUPする(ループバックIFのエイリアスを作る) (iptablesでルーティングする方法もあるけどオーバヘッドが気になるのでlo:0を使う)
vi /etc/sysctl.conf
# LVS(DSR)参照分散対象の場合にarpを返さない設定
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.eth0.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.eth0.arp_announce = 2
sysctl -p
vi /etc/sysconfig/network-scripts/ifcfg-lo:0
------------------------------------------------
DEVICE=lo:0
TYPE=Ethernet
BOOTPROTO=none
BROADCAST=172.18.1.200
IPADDR=172.18.1.200
NETMASK=255.255.255.255
NETWORK=172.18.1.200
NAME=loopback
ONPARENT=yes
PEERDNS=no
インタフェース有効化
ifup lo:0
ifconfig -a lo:0
マネジメントコンソールから「Change Source/Dest Check」をDisableにする
WEBからDSR構成のDB参照分散のテスト
mysql -u root -p -h 172.18.1.200 -e show global variables like 'hostname';
・ipvsadmで負荷分散の単体テスト
まずAWSコンソール画面から、LVSとWEBとDBインスタンス全てのChange Source/Dest.CheckをDisableにします。
ipvsadmをインストール
yum install ipvsadm
カーネルパラメータ調整(パケットの転送を許可しルータの機能を持たせる
vi /etc/sysctl.conf
net.ipv4.ip_forward = 1
vm.swappiness = 30
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 8192
net.ipv4.tcp_keepalive_intvl = 3
net.ipv4.tcp_keepalive_probes = 2
net.ipv4.tcp_keepalive_time = 10
sysctl -p
仮想IPを追加する
ip addr add 172.18.1.200/24 brd 172.18.1.255 dev eth0
ip addr add 172.18.1.199/24 brd 172.18.1.255 dev eth0
ip addr show eth0
設定のクリア
ipvsadm -C
FATAL: Error inserting ip_vs (/lib/modules/2.6.32-358.18.1.el6.x86_64/kernel/net/netfilter/ipvs/ip_vs.ko): Unknown symbol in module, or unknown parameter (see dmesg)
Can't initialize ipvs: Protocol not available
Are you sure that IP Virtual Server is built in the kernel or as module?
上記のようなエラーが出る場合、参考リンクのとおりの方法でipv6を無効にする(install ipv6 /bin/trueは削除) http://pocketstudio.jp/log3/2013/04/18/howto_disable_ipv6_on_centos/
vi /etc/modprobe.d/ipv6.conf
options ipv6 disable=1
手動で設定 DB側の分散設定を追加
ipvsadm -A -t 172.18.1.200:3306
ipvsadm -a -t 172.18.1.200:3306 -r 172.18.1.220:3306 -g -w 10
WEB側の分散設定を追加
ipvsadm -A -t 172.18.1.199:80
ipvsadm -a -t 172.18.1.199:80 -r 172.18.1.33:80 -m -w 10
ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.18.1.199:http wlc
-> 172.18.1.33:http Masq 10 0 0
TCP db-r-vip:mysql wlc
-> 172.18.1.220:mysql Route 10 0 0
設定を保存する場合は以下(ipvsadmサービスを起動する場合)
service ipvsadm save
今回はldirectord使うのでipvsadmは無効にしておかないと 2つ起動して設定が書き換えられて混乱に陥るので注意。
・heartbeatの導入設定
Linux-HA Japanで確認 http://linux-ha.sourceforge.jp/wp/dl/packages
cd /usr/local/src
wget http://ftp.iij.ad.jp/pub/sourceforge.jp/linux-ha/58548/pacemaker-1.0.13-1.1.el6.x86_64.repo.tar.gz
cp -a /usr/local/src/pacemaker*.tar.gz /tmp/
cd /tmp
VIRSION=1.0.13-1.1.el6
tar zxvf pacemaker-$VIRSION.x86_64.repo.tar.gz
cd pacemaker-$VIRSION.x86_64.repo
yum install perl-TimeDate PyXML libtool-ltdl OpenIPMI-libs lm_sensors-libs libesmtp
yum -y -c pacemaker.repo install corosync.x86_64 heartbeat.x86_64 pacemaker.x86_64 \
pm_diskd pm_extras pm_logconv-hb
vi /etc/yum.conf
exclude=kernel* pacemaker pacemaker-libs corosync cluster-glue heartbeat resource-agents
vi /etc/ha.d/ha.cf
========================================
crm on
logfile /var/log/ha-log
#debugfile /var/log/ha-debug
logfacility none
debug 0
udpport 34694
keepalive 2
warntime 15
deadtime 30
initdead 60
ucast eth0 172.18.1.23
auto_failback off
node lvs01
node lvs02
uuidfrom nodename
watchdog /dev/watchdog
respawn root /usr/lib64/heartbeat/pingd -m 100 -a default_ping_set
ping 172.18.1.1
respawn root /usr/lib64/heartbeat/diskd -N /dev/xvde1 -a diskcheck_status_internal -i 10
#respawn root /etc/ha.d/monitoring/heartbeat_logmoni.sh monitor
========================================
vi /etc/ha.d/authkeys
========================================
auth 1
1 crc
========================================
chown root:root /etc/ha.d/ha.cf
chown root:root /etc/ha.d/authkeys
chmod 600 /etc/ha.d/authkeys
scp /etc/ha.d/ha.cf lvs02:/etc/ha.d/.
scp /etc/ha.d/authkeys lvs02:/etc/ha.d/.
/etc/init.d/heartbeat start
crm_mon -1
・ldirectordの導入設定
yum -y install ipvsadm perl-Socket6 perl-MailTools \
perl-Net-SSLeay perl-libwww-perl perl-IO-Socket-INET6 ldirectord
vi /etc/ha.d/ldirectord.cf
========================================
#Global Directives
checktimeout=15
checkinterval=30
autoreload=yes
logfile=/var/log/ldirectord.log
quiescent=no
# Virtual for HTTP
virtual = 172.18.1.199:80
real=172.18.1.33:80 masq 5 # web01
real=172.18.1.34:80 masq 5 # web02
fallback=172.18.1.71:80 masq # sorry
checktype=negotiate
service=http
request=chk.html
receive=balance
scheduler=wrr
protocol=tcp
virtual = 172.18.1.206:80
real=172.18.1.44:80 masq 5 # mov01
real=172.18.1.45:80 masq 5 # mov02
fallback=172.18.1.71:80 masq # sorry
checktype=negotiate
service=http
request=chk.html
receive=balance
scheduler=wrr
protocol=tcp
# Virtual for HTTPS
virtual = 172.18.1.199:443
real=172.18.1.33:443 masq 5 # web01
real=172.18.1.34:443 masq 5 # web02
fallback=172.18.1.71:443 masq # sorry
checktype=negotiate
service=https
request=chk.html
receive=balance
scheduler=wrr
persistent=600
protocol=tcp
# MySQL virtual service.
virtual = 172.18.1.200:3306
real=172.18.1.220:3306 gate 4 # db01
real=172.18.1.221:3306 gate 2 # db01
service=mysql
scheduler=wlc
netmask=255.255.255.255
protocol=tcp
checktype=connect
========================================
scp /etc/ha.d/ldirectord.cf lvs02:/etc/ha.d/.
chkconfig ldirectord off
chkconfig --list ldirectord
・EIP・PrivateIP移動シェル作成 最近でたpythonのツールのほうが軽くていいらしいので、pythonのセットアップから行う。
wget http://peak.telecommunity.com/dist/ez_setup.py
python ez_setup.py
easy_install pip
pip install awscli
complete -C aws_completer aws
vi /root/.ec2/aws.config
-----
[default]
aws_access_key_id=AKIAIBLHWHEQNV*****
aws_secret_access_key=OyvqwLtDiB9HZQO44fJryv86t1vjvvmf*****
region=ap-northeast-1
-----
echo export AWS_CONFIG_FILE=/root/.ec2/aws.config ~/.bash_profile
echo complete -C aws_completer aws ~/.bash_profile
export AWS_CONFIG_FILE=/root/.ec2/aws.config
コマンドの調査をしながら作成する
aws ec2 associate-address help
サブコマンドの作成
mkdir /opt/{bin,log}
vi /opt/bin/aws_eip_ctl.sh
cat /opt/bin/aws_eip_ctl.sh
-------------------
#!/bin/bash
#
# aws_eip_ctl.sh : ErasticIPとPrivateIPを取り外してローカルインスタンスに付与
# 依存関係:awsコマンド
# 更新履歴:20131001 - create komiyay
#
if [ $# -lt 2 ];then
echo 引数が足りません。
echo usage: $0 erastic_ip private_ip1,private_ip2
exit 1
else
:
fi
export PATH=$PATH:/usr/local/bin
export AWS_CONFIG_FILE=/root/.ec2/aws.config
## variables
EIP=$1
PIP=`echo $2|sed -e 's/,/ /g'`
PIP1=`echo $2|awk -F, '{print $1}'`
PIP2=`echo $2|awk -F, '{print $2}'`
MYinstanceID=`/usr/bin/curl -s http://169.254.169.254/latest/meta-data/instance-id`
MY_ENI=`aws ec2 describe-network-interfaces --filters Name=attachment.status,Values=attached,Name=attachment.instance-id,Values=$MYinstanceID|grep NetworkInterfaceId|awk '{print $2}'|sed -e 's/[,]//g'`
## disassociate erastic-ip.
aws ec2 disassociate-address --public-ip ${EIP}
## unassign and assign private-ip.
for i in $PIP;do
attached_ENI=`aws ec2 describe-network-interfaces --filters Name=addresses.private-ip-address,Values=${i} |grep NetworkInterfaceId|awk '{print $2}'|sed -e 's/[,]//g'`
if [ -n ${attached_ENI} ];then
if [ ${attached_ENI} != ${MY_ENI} ];then
sleep 2
aws ec2 unassign-private-ip-addresses \
--network-interface-id ${attached_ENI} \
--private-ip-addresses ${i}
## sleep for aws api server.
sleep 18
aws ec2 assign-private-ip-addresses \
--network-interface-id ${MY_ENI} \
--private-ip-addresses ${i} --allow-reassignment
else
echo PrivateIP ${i} is already attached on this instance.
fi
else
echo PrivateIP ${i} is not bind instance.
aws ec2 assign-private-ip-addresses \
--network-interface-id ${MY_ENI} \
--private-ip-addresses ${i} --allow-reassignment
fi
done
sleep 2
## associate
aws ec2 associate-address --instance-id $MYinstanceID \
--public-ip ${EIP} --private-ip-address ${PIP1} --allow-reassociation
exit $?
-------------------
chmod 700 /opt/bin/aws_eip_ctl.sh
ここでbash -xで単体動作テストする。
LSB準拠のリソーススクリプトを作成する
vi /etc/init.d/eip_web
-------
#!/bin/bash
#
# eip Associate EIP.
#
# chkconfig: 2345 99 10
# description: Associate EIP
# Source function library.
. /etc/init.d/functions
ErasticIP=xxx.xxx.131.136
PrivateIP=172.18.1.199,172.18.1.200
prog=eip_web
lock=/var/lock/subsys/$prog
log=/opt/log/aws_error.log
# Source config
if [ -f /etc/sysconfig/$prog ] ; then
. /etc/sysconfig/$prog
fi
case $1 in
start)
echo `date +%Y-%m-%d %T` begin associate-eip >> $log
touch $lock
/opt/bin/aws_eip_ctl.sh ${ErasticIP} ${PrivateIP} >> $log 2>&1
exit $?
;;
stop)
echo `date +%Y-%m-%d %T` end associate-eip >> $log
rm -f $lock
exit $?
;;
status)
if [ -f $lock ]
then
exit 0
else
exit 3
fi
;;
*)
echo $Usage: $0 {start|stop}
exit 1
esac
-------
vi /etc/init.d/eip_mob
-------
#!/bin/bash
#
# eip Associate EIP.
#
# chkconfig: 2345 99 10
# description: Associate EIP
# Source function library.
. /etc/init.d/functions
ErasticIP=xxx.xxx.155.40
PrivateIP=172.18.1.206
prog=eip_mob
lock=/var/lock/subsys/$prog
log=/opt/log/aws_error.log
# Source config
if [ -f /etc/sysconfig/$prog ] ; then
. /etc/sysconfig/$prog
fi
case $1 in
start)
echo `date +%Y-%m-%d %T` begin associate-eip >> $log
touch $lock
/opt/bin/aws_eip_ctl.sh ${ErasticIP} ${PrivateIP} >> $log 2>&1
exit $?
;;
stop)
echo `date +%Y-%m-%d %T` end associate-eip >> $log
rm -f $lock
exit $?
;;
status)
if [ -f $lock ]
then
exit 0
else
exit 3
fi
;;
*)
echo $Usage: $0 {start|stop}
exit 1
esac
-------
chmod +x /etc/init.d/eip*
セカンダリにも上記スクリプトをコピーしておく
・リソース設定
crm
configure
primitive eip_web lsb:eip_web
primitive eip_mob lsb:eip_mob
edit
ここでリソースグループを以下のように編集
group rg_LVS res_Mailto eip_mob eip_web res_VIPcheck_WEB res_VIP_WEB res_VIPcheck_MOB res_VIP_MOB res_VIPcheck_DB res_VIP_DB res_ldirectord
verify
commit
show
node $id=54ce2c1a-7647-851f-2146-e7f2bde2fce4 lvs02
node $id=b6592742-bfd3-dcdf-63cc-8db63f4ea265 lvs01
primitive eip_mob lsb:eip_mob \
op start interval=0s timeout=120s on-fail=restart \
op stop interval=0 timeout=120s on_fail=block \
op monitor interval=30s timeout=120s on_fail=restart
primitive eip_web lsb:eip_web \
op start interval=0s timeout=120s on-fail=restart \
op stop interval=0 timeout=120s on_fail=block \
op monitor interval=30s timeout=120s on_fail=restart
primitive res_Mailto ocf:heartbeat:MailTo \
params email=unyou-all@isao.net subject=(xxxx) \
op start interval=0 timeout=60s on_fail=restart \
op monitor interval=10s timeout=60s on_fail=restart \
op stop interval=0 timeout=60s on_fail=block
primitive res_VIP_DB ocf:heartbeat:IPaddr2 \
params ip=172.18.1.200 nic=eth0 cidr_netmask=24 \
op start interval=0 timeout=60s on_fail=restart \
op stop interval=0 timeout=60s on_fail=block \
op monitor interval=30s timeout=60s on_fail=restart
primitive res_VIP_MOB ocf:heartbeat:IPaddr2 \
params ip=172.18.1.206 nic=eth0 cidr_netmask=24 \
op start interval=0 timeout=60s on_fail=restart \
op stop interval=0 timeout=60s on_fail=block \
op monitor interval=30s timeout=60s on_fail=restart
primitive res_VIP_WEB ocf:heartbeat:IPaddr2 \
params ip=172.18.1.199 nic=eth0 cidr_netmask=24 \
op start interval=0 timeout=60s on_fail=restart \
op stop interval=0 timeout=60s on_fail=block \
op monitor interval=30s timeout=60s on_fail=restart
primitive res_VIPcheck_DB ocf:heartbeat:VIPcheck \
params target_ip=172.18.1.200 count=1 wait=10 \
op start interval=0 timeout=90s on_fail=block \
op stop interval=0 timeout=90s on_fail=block
primitive res_VIPcheck_MOB ocf:heartbeat:VIPcheck \
params target_ip=172.18.1.206 count=1 wait=10 \
op start interval=0 timeout=90s on_fail=block \
op stop interval=0 timeout=90s on_fail=block
primitive res_VIPcheck_WEB ocf:heartbeat:VIPcheck \
params target_ip=172.18.1.199 count=1 wait=10 \
op start interval=0 timeout=90s on_fail=block \
op stop interval=0 timeout=90s on_fail=block
primitive res_ldirectord ocf:heartbeat:ldirectord \
params configfile=/etc/ha.d/ldirectord.cf \
op start interval=0 timeout=60s on_fail=restart \
op monitor interval=30s timeout=60s on_fail=restart \
op stop interval=0
group rg_LVS res_Mailto eip_mob eip_web res_VIPcheck_WEB res_VIP_WEB res_VIPcheck_MOB res_VIP_MOB res_VIPcheck_DB res_VIP_DB res_ldirectord
location group_lvs-location rg_LVS \
rule $id=group_lvs-location-rule 200: #uname eq lvs01 \
rule $id=group_lvs-location-rule-0 100: #uname eq lvs02 \
rule $id=group_lvs-location-rule-1 -inf: defined default_ping_set and default_ping_set lt 100
property $id=cib-bootstrap-options \
dc-version=1.0.13-30bb726 \
cluster-infrastructure=Heartbeat \
expected-quorum-votes=2 \
no-quorum-policy=ignore \
stonith-enabled=false \
startup-fencing=false \
dc-deadtime=20s \
last-lrm-refresh=1380613160
rsc_defaults $id=rsc-options \
resource-stickiness=INFINITY \
migration-threshold=1
※AWSのAPIサーバへの応答待ち時間があり、結構時間がかかってtimeoutしたためtimeout値を伸ばしました
これで設定はおわりで次は動作確認を行う。
crm_mon -i1 -Af
・インスタンスコピーの際の作業
vi /etc/ha.d/ha.cf
echo lvs02 > /proc/sys/kernel/hostname;bash
sed -i 's/lvs01/lvs02/g' /etc/sysconfig/network
service rsyslog restart;tail -f /var/log/messages
service postfix restart;tail -f /var/log/maillog
ucast eth0 172.18.1.23
↓
ucast eth0 172.18.1.25
service heartbeat start
※masterから先に実施してリソースを持たせます
tail -f /var/log/ha-log
・切替テスト
確認コマンドで確認しつつheartbeatを落とすなりして切替動作を確認します
ip addr show eth0
ipvsadm -Ln
crm_mon -i1 -Af
代表IPに外部からpingしておく
ping 172.18.1.199
ping 172.18.1.200
ping 172.18.1.206
・ハマったところ
ipvsadmはipv6無効にしないと動かなかった。 AWS(VPC)においてDSRはフラットな構成でクライアントがローカルNW内にいないと無理だった。 インスタンスを気軽に作り直して「Change Source/Dest Check」をDisableにするのを忘れてたりした。 lvsに浮動EIP以外に固定のEIPつけておかないと、グローバルに出れずAPIサーバと通信できないので注意。 固定EIPでないと初回起動時のグローバル通信が間に合わずリソースがスタートしない。 smallインスタンスだとひとつのIFに付与できるPrivateIPは3つまでなので余計なのがついてるとLimitエラーが出るので注意。 デフォルトGWを初期値以外に変更するとAPIから帰るメタデータがGWのものになってしまいローカルのがとれなくなる。 AWSのAPIサーバと通信する等の事情でタイムアウト値を伸ばさないと上手く切り替わらない。
・参考サイト いっぱいありますけど特に参考になったところを。 LVS参考 Linux Virtual Server Tutorial Stray Penguin - Linux Memo (Ultra Monkey) heartbeat参考: suz-lab - blog: “High Availability NAT"の作成(CentOS6) AWS上のAsteriskサーバをHeartbeat+Pacemakerで冗長化 - サーバーワークス エンジニアブログ HeartBeatとElastic IPを使用した冗長化構成をテストする - サーバーワークス エンジニアブログ IPとGWの制限等の情報 Amazon EC2 Instance Comparison [Fedora] routeコマンドを実行すると「SIOCADDRT: そのようなプロセスはありません」というエラーが出る - Life with IT Linux - NICのオフロード機能を無効にする - Qiita [キータ] DELLのサーバでCentOS6でLVS+keepalivedなロードバランサを構築したらハマったりした話 - インフラエンジニアway - Powered by HEARTBEATS 2011-11-14-AWS_System_Design SSLがらみ LVS DSR の環境で、複数の SSL 証明書を設定する方法 | Carpe Diem Persistence Handling in LVS
さて、vrrpのが軽いとかzoneまたぎで構築しなくてzone障害にどう対応するのかとか色々突っ込みどころがあるかもしれません。 vrrpは調べたけど運用経験的な問題で却下になりました。 zone障害は、AMIとsnapshotで手動で頑張る感じになるかと。お金がないので一か所に固めて通信速度優先みたいな感じでしょうか。
WEB側をELBに分離したときどういう構成にしたかという話は↓こちらに載せましたのでよかったらどうぞ。 Lvsをvpc上に構築してみた話
以上、見ていただいてありがとうございました。