CentOS 6.5 で LVS (IPVS) を keepalived で冗長化してみる
前回は ipvsadm コマンドを直接使って LVS を冗長化せずに使ってみた。 今回は keepalived を通して LVS を冗長化して使ってみる。 構成は前回と同様、方式は DSR でバランスアルゴリズムは rr を使う。 ただし LVS の IP アドレスは 192.168.33.{11,12} で Web サーバの IP アドレスは 192.168.33.{101,102} に変更している。
$ sudo yum -y install keepalived ipvsadm
インストールできたら keepalived のコンフィグを編集する。 keepalived は LVS をラップする機能がデフォルトで組み込まれているので、コンフィグを変更するだけで動作する。
$ sudo cp /etc/keepalived/keepalived.conf{,.orig} $ cat << EOS | sudo tee /etc/keepalived/keepalived.conf > /dev/null ! Configuration File for keepalived global_defs { router_id LVS_SAMPLE } vrrp_instance VirtualInstance1 { state BACKUP interface eth1 virtual_router_id 1 priority 100 advert_int 5 nopreempt authentication { auth_type PASS auth_pass passwd } virtual_ipaddress { 192.168.33.254 } } virtual_server 192.168.33.254 80 { delay_loop 1 lb_algo rr lb_kind DR nat_mask 255.255.255.0 protocol TCP real_server 192.168.33.101 80 { weight 1 HTTP_GET { url { path / } connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } real_server 192.168.33.102 80 { weight 1 HTTP_GET { url { path / } connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } } EOS
$ sudo sysctl -w net.ipv4.ip_forward=1 $ sudo sed -i.bak -e "s:^net.ipv4.ip_forward = .*$:net.ipv4.ip_forward = 1:" /etc/sysctl.conf
サービス用のポートと、keepalived が冗長化に使う VRRP を iptables で通す。
$ sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT $ sudo iptables -I INPUT -p vrrp -j ACCEPT $ sudo service iptables save
keepalived のサービスを開始する。 ipvsadm コマンドでバランスの設定が有効なことを確認する。
$ sudo service keepalived start $ sudo chkconfig keepalived on $ sudo ipvsadm -L -n IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.33.254:80 rr -> 192.168.33.101:80 Route 1 0 0 -> 192.168.33.102:80 Route 1 0 0
VIP が NIC に付与されていることを確認する。
$ ip addr show eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 08:00:27:e2:5a:3d brd ff:ff:ff:ff:ff:ff inet 192.168.33.11/24 brd 192.168.33.255 scope global eth1 inet 192.168.33.254/32 scope global eth1 inet6 fe80::a00:27ff:fee2:5a3d/64 scope link valid_lft forever preferred_lft forever
tcpdump で VRRP のアドバタイズが流れていることを確認する。 VRRP のアドバタイズを出しているホストが MASTER なので、この状態では 192.168.33.11 がそれになる。 ちなみにホスト両方からアドバタイズが流れているときは split brain が起こり両方が MASTER になってしまっていることを示す。
$ sudo yum -y install tcpdump $ sudo tcpdump -i eth1 vrrp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes 18:03:21.809098 IP 192.168.33.11 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 5s, length 20 18:03:26.810393 IP 192.168.33.11 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 5s, length 20 18:03:31.811821 IP 192.168.33.11 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 5s, length 20 ^C 3 packets captured 3 packets received by filter 0 packets dropped by kernel
上記の設定を冗長化するもう一台の LVS 用ホストにも行う。 もう一台のホストは BACKUP として動作するため VIP はつかない。
Web サーバの内容は前回のものと全く同じ。 コンテンツの内容を、どちらのサーバに振り分けられたか分かるようなものにしておく。
$ sudo yum -y install httpd $ sudo service httpd start $ sudo chkconfig httpd on $ sudo service iptables save $ cat << EOS | sudo tee /var/www/html/index.html > /dev/null <!DOCTYPE html> Web1 EOS
Web サーバに必要な iptables の設定を投入する。
$ sudo iptables -t nat -I PREROUTING -d 192.168.33.254 -j REDIRECT $ sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT $ sudo service iptables save
動作を確認する。 VIP 宛に HTTP リクエストを送ってバランスされることを確認する。
$ curl http://192.168.33.254/ <!DOCTYPE html> Web1 $ curl http://192.168.33.254/ <!DOCTYPE html> Web2
次に LVS の MASTER 側を落として BACKUP 側に動作が引き継がれることを確認する。 今回の環境は Vagrant で組んでいるので例えば以下のような感じ。
改めて VRRP を観測する。 前回は .11 がアドバタイズを出していたが、今回は .12 になっている。
$ sudo tcpdump -i eth1 vrrp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes 18:07:52.327695 IP 192.168.33.12 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 5s, length 20 ^C 1 packets captured 1 packets received by filter 0 packets dropped by kernel
$ ip addr show eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 08:00:27:eb:c5:5e brd ff:ff:ff:ff:ff:ff inet 192.168.33.12/24 brd 192.168.33.255 scope global eth1 inet 192.168.33.254/32 scope global eth1 inet6 fe80::a00:27ff:feeb:c55e/64 scope link valid_lft forever preferred_lft forever
もう一度 VIP 宛に HTTP リクエストを送ってみる。
$ curl http://192.168.33.254/ <!DOCTYPE html> Web1 $ curl http://192.168.33.254/ <!DOCTYPE html> Web2
.11 が落ちても .12 に機能が引き継がれてサービスが継続できていることが分かる。
次に Web サーバも片側を落としてみる。
すると落ちたサーバがヘルスチェックに引っかかってバランス対象から除外されている。
$ sudo ipvsadm -L -n IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.33.254:80 rr -> 192.168.33.102:80 Route 1 0 0
HTTP リクエストを送ると生きているサーバだけが応答する。
$ curl http://192.168.33.254/ <!DOCTYPE html> Web2 $ curl http://192.168.33.254/ <!DOCTYPE html> Web2
めでたしめでたし。
今回も環境を作るのには Vagrant を使ったので Vagrantfile も置いておく。
$ cat Vagrantfile # -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.define :lvs1 do |lvs1| lvs1.vm.box = "centos65" lvs1.vm.network :private_network, ip: "192.168.33.11" lvs1.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "1024", "--cpus", "2"] end end config.vm.define :lvs2 do |lvs2| lvs2.vm.box = "centos65" lvs2.vm.network :private_network, ip: "192.168.33.12" lvs2.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "1024", "--cpus", "2"] end end config.vm.define :web1 do |web1| web1.vm.box = "centos65" web1.vm.network :private_network, ip: "192.168.33.101" web1.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "512", "--cpus", "2"] end end config.vm.define :web2 do |web2| web2.vm.box = "centos65" web2.vm.network :private_network, ip: "192.168.33.102" web2.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "512", "--cpus", "2"] end end end