3.4.2 キープアライブを用いたLVSのHA構成

（2016年時点での内容をアーカイブとして掲載しているため、一部の掲載内容が最新情報とは異なる場合がありますので、ご了承ください。最新のIBM Cloudのアップデート情報は[IBM Cloud アップデート情報](https://www.ibm.com/cloud-computing/jp/ja/cloud-update/) や [柔らか層本](https://ibm.ent.box.com/v/ibmcloud-yawaraka)をご参照ください。）

概要
キープアライブ(Keepalived)をつかってLVSの高可用性(High Availability)構成をご紹介します。 LVSはウェブサーバー、KVS(Key-Value Store)、メモリキャッシュの負荷分散と高可用性のために、たいへん良く利用されています。 特にLVSは、データベースのアクセス負荷を軽減してレスポンスを改善するためにAPサーバーとDBサーバーの間で多く利用されています。 さらに、今回はMySQLのリード・レプリカを構成例を通してご紹介していきます。
 

LVSの高可用性構成
LVS (Linux Virtual Server)は、安価に構築できるのに高性能なロードバランサーとして、たいへん重宝されています。 このLVSをアクティブ・スタンバイ方式の高可用性の構成するために、キープアライブが利用されます。 SoftLayerでは、APサーバーとDBサーバー間など、プライベート・ネットワークの通信に対して、ローカル・ロードバランサーや高可用性の提供がありません。 しかし、LVSを利用することで、この課題を解決することができます。 LVSの概要説明は3.4 アクティブ・スタンバイのサーバーを構成するには？、シングル構成の設定方法は3.3 ロードバランサーを利用するには？をご参照お願いします。

次の図のLVSの左側のクライアントは、VIP(仮想IPアドレス)に向かってアクセスします。 そして、右側のサーバー群へ分散してアクセスします。 障害や保守のために、アクティブのLVSが停止した場合、スタンバイのLVSヘVIPが移動することで、クライアントは継続してVIPにアクセスして処理を継続することができます。 一方、LVSの右側にはMySQL, KVS, memcachedを配置して、応答を返せないサーバーへのアクセスを止めるために利用します。 次の図の中では、MySQLサーバーのリードレプリカに対する負荷分散の例です。

ウェブのよるカタログ販売の場合、そのほとんどが、カタログを閲覧しながら欲しい商品を探し求めるユーザーのアクセスとなり、商品データベースへの接続の殆どが、読取りアクセスとなります。 ここで快適な応答速度で表示ができなければ、ユーザーは去って行ってしまいます。 このため、MySQLの負荷分散は重要なテーマです。 KVSを使ってSQL文をキーとして結果をバリューに保管することで、MySQLの負荷を下げ、検索速度を劇的に改善する手法もありますが、ここでは、MySQLの読取り専用レプリカを複数準備してLVSで負荷分散する方法を例に挙げます。 この方法では、アプリケーションで、KVSをアクセスするためのコードを書かなくても良いため、実装が簡単になるメリットがあります。


VIPは、仮想サーバーや物理サーバーの起動時に付与されるIPアドレス以外のものを利用する必要があります。 クラウドの環境では、同じサブネットの空いているIPアドレスを適当にVIPとして使うと、IPアドレスは、クラウドの管理システムが割当を行なうため、新しく起動するサーバーのIPアドレスと同じになるといった障害になる恐れがあります。 そこで、SoftLayerでは、ポータブルIPという機能を活用します。 この機能の利用方法は、4.2 サーバーが変わっても同じIPアドレスを利用するには？をご参照お願いします。
 

構成に必要な材料の注文
具体的な構成では、次の図のようにLVSを一つのVLAN内だけで利用します。 LVSのVIPは、プライベート・アドレスを設定して、同じプライベートVLAN内のサーバーへ負荷分散することを実装していきます。

上記の構成を作るために、次の3点をカスタマーポータル ( https://control.softlayer.com/ )から注文します。

1. 仮想サーバー CentOS 6.x - Minimal Install (64 bit) x 2 LVSのアクティブとスタンバイ
2. 仮想サーバー CentOS 6.x - LAMP Install (64 bit) x 4 MySQLマスタ・サーバー x1, MySQLリード・レプリカ x2, アプリケーション・サーバー x1
3. ポータブルIPアドレス（8 Portable Private IP Addresses）× 1 VIP用

LVS用として「仮想サーバー CentOS 6.x - Minimal Install (64 bit)」を注文します。 最初のサーバーの設定後に取得したイメージ・テンプレートから複製サーバーを起動して、アクティブ・スタンバイの設定を追加していきます。 MySQLのマスター、リード・レプリカ、ウェブサーバー用に「仮想サーバー CentOS 6.x - LAMP Install (64 bit)」を注文します。 「LAMP Install」は、Apache HTTP server、MySQL、PHPが最初から稼働できる状態でサーバーが起動してきます。 一方「Minimal Install」は、Linuxディストリビューションの最小限の導入になっています。

VIPのためのプライベートのポータブルIPアドレスは、無料ですので少し多めに、8個を確保しておきます。 この8個の内、3つはゲートウェイ、ネットワークアドレス、ブロードキャストアドレスとして、予約されていますので、5個を使うことができます。 ただし、プライベートの場合、SoftLayerのHSRPの予約アドレスが含まれることがあり、3個しか使えないことがあります。

これらの注文の方法は、仮想サーバーは1.2 仮想サーバーを起動するには？、ポータブルIPアドレスは4.2 サーバーが変わっても同じIPアドレスを利用するには？を参照してください。 そして、2台目のサーバーを作るには、「3.2.サーバーを複製するには？」の方法を利用していきます。 ここでは、判り易くするために、最初にまとめて注文するようにしていますが、仮想サーバーの費用を節約するには、必要な都度、注文する方法もあります。
 

LVSサーバーの追加ソフトウェアのインストール
仮想サーバーが起動したら、sshでログインして、KVSを設定を進めていきます。 仮想サーバーにログインする方法は、1.4 サーバーへログインするには？を参照してください。

仮想サーバーの起動直後は、OSの導入イメージ作成時点のソフトウェア更新レベルのため、現在までのアップデートを適用します。 これは以降の作業にとって重要なので必ず実施します。
 
```
# yum update
```

LVSのソフトウェアの導入を以下のコマンドで実行します。
 
```
# yum install ipvsadm keepalived
```

導入したサービスが、自動起動するように設定します。
 
```
# chkconfig ipvsadm on
# chkconfig keepalived on
```

複数のethインタフェース間でパケットの転送を許可するため /etc/sysctl.conf に、net.ipv4.ip_forward = 1 を設定します。

```
# vi /etc/sysctl.conf
# cat /etc/sysctl.conf
...省略
# Controls IP packet forwarding                        
net.ipv4.ip_forward = 1           <--  1 へ変更する
```

編集した設定をカーネルに反映させるため、以下のコマンドを実行します。
 
```
# sysctl -p
```

LVSのアクティブ側設定
アクティブ・スタンバイの構成のため、キープアライブの /etc/keepalived/keepalived.conf を編集します。 変更箇所は「<--」で注釈しています。
 
```
[root@lvs1 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

vrrp_instance vip_1 {
    state MASTER     <--  アクティブは「MASTER」、スタンバイは「BACKUP」と設定
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        10.66.136.58/32 dev eth0     <-- VIP と ネットワークインターフェース
    }
}

virtual_server 10.66.136.58 3306 {
    delay_loop 6
    lb_algo wlc     <-- 負荷分散アルゴリズムは、コネクション数と重み付けによる負荷分散
    lb_kind DR      <-- 転送方式は、ダイレクト・ルーティング
    nat_mask 255.255.255.0
    persistence_timeout 50
    protocol TCP

    real_server 10.67.24.196 3306 {  <-- MySQL リードレプリカ #1
        weight 1
        TCP_CHECK {
          connect_ip 10.67.24.196
          connect_port 3306
        }
    }

    real_server 10.67.24.198 3306 {  <-- MySQL リードレプリカ #2
        weight 1
        TCP_CHECK {
          connect_ip 10.67.24.198
          connect_port 3306
        }
    }
}
```

これで、LVSのアクティブ側のキープアライブの設定が完了しました。 サービスを起動しておきます。
 
```
# service ipvsadm start
# service keepalived start
```

ここでスタンバイ側の設定は完了していませんが、以下のように、VIPが有効になっていることがわかります。
 
```
# ip addr show dev eth0
2: eth0:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 06:ac:64:68:be:bf brd ff:ff:ff:ff:ff:ff
    inet 10.67.24.200/26 brd 10.67.24.255 scope global eth0     <-- 仮想サーバーのI/Fアドレス
    inet 10.66.136.58/32 scope global eth0                      <-- VIP
    inet6 fe80::4ac:64ff:fe68:bebf/64 scope link
       valid_lft forever preferred_lft forever
```

さらに、次のコマンドで、キープアライブの設定ファイルの内容が、IPVS(IP Virtual Server: ロードバランサ機能を持つLVSの構成要素)に反映されていることが確認できます。
 
```
[root@lvs1 ~]# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.66.136.58:3306 wlc persistent 50
  -> 10.67.24.196:3306            Route   1      0          0        
  -> 10.67.24.198:3306            Route   1      0          0      
```

LVS用の仮想サーバーの複製
ここまで作成してきた仮想サーバーをマスターのイメージにして、スタンバイになるサーバーを作成します。 この作成方法は、3.2 サーバーのクローンを作成するには？を参照してください。 イメージから新たなサーバーを起動する場合、元の仮想サーバーと同じVLANに作成する必要があります。 この理由は、VIPを利用するポータブルIPはVLANに割当られるためです。 新たに起動される仮想サーバーのIPアドレスは、異なるアドレスが付与され、ホスト名は起動時に指定したものが付与されます。

複製されたサーバーのIPアドレスのサブネットは、元になったサーバーと異なるサブネットに割り当てられる可能性がありますが、VLANが同じであれば、ポータブルIPを利用することに問題はありません。
 

LVSのスタンバイ側設定
イメージ・テンプレートから起動した仮想サーバーの /etc/keepalived/keepalived.conf の次の部分を「BACKUP」に値を変更します。
 
```
# vi /etc/keepalived/keepalived.conf

...省略
vrrp_instance vip_1 {
    state MASTER     <--  スタンバイは「BACKUP」と設定
    interface eth0
...省略
```

これで、LVSのスタンバイの設定が完了しましたので、サービスを起動します。
 
```
# service ipvsadm start
# service keepalived start
```

これで、LVSのアクティブ・スタンバイの設定が完了しました。 次に、LVSが負荷分散するMySQLのリードレプリカの設定に移っていきます。 ここで、次に説明する設定を行なわないで、VIPをアクセスして振り分けの確認をしてはいけません。 少しだけ我慢をお願いします。
 

MySQLのリードレプリカの設定 (LVSのリアルサーバー設定)
LVSがリクエストを振分け先は、同じプライベートIP側のMySQLリードレプリカのサーバーに転送するために、少し特別な設定が必要になります。 LVSのVIPに届いたパケットをダイレクトに転送するため、転送先のサーバーがパケットを自分のパケットとして受け取るように、ifcfg-lo:0ファイルを作成して、VIPアドレスを設定します。
 
```
[root@db2 ~]# cat /etc/sysconfig/network-scripts/ifcfg-lo:1
DEVICE=lo:1
IPADDR=10.66.136.58        <-- VIPのアドレス
NETMASK=255.255.255.255
ONBOOT=yes
NAME=loopback
```

次の設定は、大変重要な設定で省略するとLVSが期待通りに動作しません。 イーサーネットでは、ARPによって、IPパケットの次の転送先を記憶する機能があります。 もし間違った値を周辺のルーターが記憶していまうと、キャッシュがリフレッシュされるまで、正しく通信できない状態になります。 次の設定は、この問題を回避するために、LVSの振り分け先のサーバーでARPを無視、また、通知しないというものです。 もしこの設定をしないで動かしてしまうと、LVSから来たパケットを処理する過程で、SoftLayer側のルーターへ、MySQLレプリカのサーバーである自己のMACアドレスが、VIPの代表アドレスとしてARPで通知します。 そうすると、それ以降は、VIPの転送先は、LVSではなくMySQLサーバー側に固定され、LVSをバイパスされ負荷分散が働かなくなります。

/etc/sysctl.confのファイルに次の4行を追加します。
 
```
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
```

カーネルへ設定を反映させるためsysctlコマンドを実行します。
 
```
# sysctl -p
```

ループバック・インターフェースを有効にします。
 
```
# ifup lo:1
```

MySQLのマスターサーバーの設定
リードレプリカへ更新ログを提供するためのMySQLサーバーを設定した後、リードレプリカのMySQLサーバーを設定します。
 
```
# cat /etc/my.cnf
[mysqld]
server-id=100      <-- 複数のMySQLサーバーを連携させるため、識別子を設定  
log-bin=log-bin    <-- バイナリ形式のログを出力するように設定
character-set-server = utf8
collation-server = utf8_general_ci
...省略
```

設定を保存してMySQLサービスを開始します。 また、サーバーが再起動しても自動起動するように設定しておきます。
 
```
# service mysqld start
# chkconfig mysqld on
```

次のコマンドで、MySQLサーバーにログインして、レプリカサーバーの登録、レプリカへのデータ転送を進めていきます。
 
```
# mysql
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 38
Server version: 5.1.73-log Source distribution
```

リード・レプリカのユーザー、IPアドレス、パスワードを登録します。 負荷分散するリード・レプリカのサーバー数分だけ登録します。
 
```
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@10.67.24.196 IDENTIFIED BY 'Passw0rd';
Query OK, 0 rows affected (0.00 sec)
```

データを転送する前に、バッファを書き出してロックします。
 
```
mysql> FLUSH TABLES WITH READ LOCK;
Query OK, 0 rows affected (0.00 sec)
```

ログファイルの名前とポジションは、レプリカ側で開始前に設定する必要があるので、メモしておきます。
 
```
mysql> SHOW MASTER STATUS;
+----------------+----------+--------------+------------------+
| File           | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+----------------+----------+--------------+------------------+
| log-bin.000003 |      257 |              |                  |
+----------------+----------+--------------+------------------+
1 row in set (0.00 sec)
```

MySQLのデータ領域を丸ごとレプリカ側へ転送します。 そして、他のすべてのレプリカ・サーバーへもコピーを実行します。
 
```
# cd /var/lib/mysql
# scp -r * 10.67.24.196:/var/lib/mysql
```

次のコマンドで、ロックを解除して、マスター側の設定は完了です。
 
```
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
```

MySQLのリードレプリカの設定
設定ファイルの変更します。 レプリカでは、バイナリーログの出力設定は不要です。
 
```
# cat /etc/my.cnf
[mysqld]
server-id=101      <-- 他のサーバーと異なる一意の識別子を設定  
character-set-server = utf8
collation-server = utf8_general_ci
...省略
```

コピーしたファイルは、オーナーを変更して、MySQLサーバーを起動します。 次回から自動起動にします。
 
```
# chown -R mysql:mysql /var/lib/mysql/
# service mysqld start
Starting mysqld:                                           [  OK  ]
# chkconfig mysqld on
```

マスター側で、コピー前にロックしていたので、解除します。
 
```
mysql> UNLOCK TABLES;
Query OK, 0 rows affected (0.00 sec)
```

マスター側で取得したファイル名とポジションを設定します。
 
```
mysql > CHANGE MASTER TO MASTER_HOST='10.67.24.201',MASTER_USER='repl',MASTER_PASSWORD='Passw0rd',
MASTER_LOG_FILE='log-bin.000003',MASTER_LOG_POS=257;
```

次のコマンドで、レプリカを開始します。
 
```
mysql> START SLAVE;
```

マスターとリードレプリカの連携状態を確認します。 ここで問題が発見された場合、最初から手順を見直します。
 
```
mysql> show slave status G

...省略
             Slave_IO_Running: Yes  <-- YESであれば成功
            Slave_SQL_Running: Yes   <-- 同上
...省略
                Last_IO_Errno: 0
                Last_IO_Error:        <-- 上記がNOになる原因のメッセージが表示される
               Last_SQL_Errno: 0
               Last_SQL_Error:        <-- 同上
1 row in set (0.00 sec)
```

おめでどうございます！ これでマスターとレプリケーションの設定が完了です。

動作確認
アプリケーションサーバーからVIPをアクセスして、MySQLのリードレプリカをアクセスして、動作確認を行います。
最初にマスターのMySQLで、テーブルを作ってデータを登録しておきます。 ここで登録したデータがリード・レプリカのMySQLサーバーから読み取ることで、レプリカ設定を確認します。
 
```
mysql> use test;
Database changed
mysql> create table person (id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into person (id,name) values (1, 'takara');
Query OK, 1 row affected (0.00 sec)

mysql> insert into person (id,name) values (2, 'suzuki');
Query OK, 1 row affected (0.00 sec)
```

マスター側とレプリカ側で、それぞれ SELECT を実行して、同じ結果が得られることを確認します。

```
# mysql
...省略
mysql> select id,name from person;
+------+--------+
| id   | name   |
+------+--------+
|    1 | takara |
|    2 | suzuki |
+------+--------+
2 rows in set (0.00 sec)
```

次に、アプリケーション・サーバーから、VIPを指定して mysql コマンドを実行して、接続を確認します。 その前に、外部のサーバーからアクセスできるようにするための、設定を行ないます。 次のコマンドは、アプリケーション・サーバーのIPアドレスから、root ユーザーが指定のパスワードで、アクセスを許可するものです。 VIPを介してアクセスするアプリケーション・サーバーについて、アクセスできるように設定しておく必要あります。
 
```
mysql> grant all on *.* to 'root'@'10.67.24.194' IDENTIFIED BY 'Passw0rd';
```

この設定により、アプリケーション・サーバーから mysql コマンドを実行できるようになったので、確認します。
 
```
[root@web1 html]#  mysql -u root -h 10.66.136.58  test -p
Enter password:
...省略
mysql> 
```

LVSのアクティブ側のサーバーにログインして、次のコマンドを実行することで、VIPから転送されている状況を確認することができます。
 
```
[root@lvs1 ~]# ipvsadm -Lnc
IPVS connection entries
pro expire state       source             virtual            destination
TCP 00:12  NONE        10.67.24.194:0     10.66.136.58:3306  10.67.24.198:3306
TCP 14:22  ESTABLISHED 10.67.24.194:45782 10.66.136.58:3306  10.67.24.198:3306
```

それでは、PHPのアプリケーションからの動作確認です。 次のテスト用コードを /var/httpd/html において、ブラウザからアクセスします。
 
```
# cat index.php



接続に成功しました。

');

$db_selected = mysql_select_db('test', $link);
if (!$db_selected){
  die('データベース選択失敗です。'.mysql_error());
}

print('

testデータベースを選択しました。
');

mysql_set_charset('utf8');

$result = mysql_query('SELECT id,name FROM person ORDER BY id');
if (!$result) {
  die('クエリーが失敗しました。'.mysql_error());
}

while ($row = mysql_fetch_assoc($result)) {
  print('id='.$row['id']);
  print(',name='.$row['name']);
  print('
');
}

$close_flag = mysql_close($link);

if ($close_flag){
  print('

切断に成功しました。
');
}
？>
```

次のブラウザ画面のように表示されたら成功です。 マスターMySQLでデータを適当に追加して、レプリカ側へ反映されていることが確認できます。

これで、HA構成の動作確認として、MySQLサーバーのリード・レプリカの一つを停止、また、LVSのプライマリを停止させても、ブラウザからのアクセスが継続できることが確認できます。 いろいろ試してみて構成の理解を深めて頂けると幸いです。
 

まとめ
本章では、プライベート・ネットワークに負荷分散サーバーとして LVS をHA構成で立ち上げ、ウェブ・アプリケーションサーバーが、VIPを経由して、MySQLのリード・レプリカサーバーへアクセスする方法を見てきました。 この方法は、KVSにも適用でき、たいへん応用価値の高い構成です。 設定の手間はかかりますが、システム内部の連携に関して、最小の仮想サーバーでも構成できるためコストパフォーマンスが良く、高可用性の構成が可能であることを、ご理解頂けたのはないかと思います。