IDCFクラウドでkeepalivedを使ったLVS構築(2)

こんにちは、ソリューションアーキテクト部の金杉です！ :smiley_cat: 

IDCフロンティアでは、主に中のエンジニアさんたちが執筆する[エンジニアブログ](http://www.idcf.jp/blog/)があります！そして、私も、先月やっと自分のエンジニアブログデビューを果たしました！ :joy: そして今回の記事は、前回の記事の第二部になります〜

#テーマ
前回のエンジニアブログでは、[IDCFクラウド](http://www.idcf.jp/cloud/)CentOS6の仮想マシンでkeepalivedを使ったLVSを構築しました。LVSの主な二つの機能を振り返ってみると、
>　1. バランシングしているサーバーのヘルスチェック :heavy_check_mark: 
　2. VRRPを用いたロードバランサーの冗長化 :dancers: 

などがありますね。機能１は[前回のブログ](http://www.idcf.jp/blog/cloud/keepalived/)で紹介させて頂いたので、今回は機能２の部分、VRRPを用いてLVSを冗長構成にしてみたいと思います。そしてバランシングするサーバーの特徴に基づいた、２種類の構築方法を紹介します！

![キャプチャ１.PNG](https://qiita-image-store.s3.amazonaws.com/0/103356/39776668-5d2e-19c8-f9fd-b0b62f2cc8b7.png)
　　　 :point_up:今回構築する構成

#仮想マシンの準備

CentOS 6.6の仮想マシンで構築をします。バランシングするリアルサーバー（WebやDBなど）2台とバーチャルサーバー（LVS）2台、計４台の仮想マシンを以下のスペックで作成します。「台数」選 択の部分で、:four:を選択すると一気に作れてしまいます :notes: 

 　マシンタイプ　　　　　　　　　　　　　Light.S1
 　イメージ　　　　　　　　　　　　　　　CentOS 6.6 64-bit
 　ボリューム　　　　　　　　　　　　　　そのまま
 　SSH Key　　　　　　　　　　　　　　　 作成
 　仮想マシン台数　　　　　　　　　　　　4
 　ネットワークインターフェース　　　　　そのまま
 　詳細情報・仮想マシン名　　　　　　　　LB, LB-backup, Real1, Real2
 　詳細情報・プライベートIPアドレス　　　自動設定

仮想マシンは、それぞれSSHできるように「IPアドレス」の設定をしておいてください。
もしリアルサーバーが公開用であれば、80番ポートのファイアウォールだけあけておいて、ポートフォワードルールは記入しないでください。後ほど新しいルールを記入します。

詳しくは、[めちゃ楽ガイド](http://www.idcf.jp/cloud/pdf/IDCFCloud_installation_guide.pdf)をご参照ください！

# 今回の構成

今回は、Figure 1のような構成になります。仮想ルーターの下にLB（マスターロードバランサー）、LB-backup（バックアップロードバランサー）、Real1、Real2（リアルサーバー）が同じセグメントに設置されています。仮想ルーターのIPアドレスが公開用のIPアドレスになります。

一方、LBがバランシングする時の様子がFigure 2になります。LVSを冗長化することが今回の目的なので、LVSはLBとLB-backupの二台になります。LB-backupは、LBが万が一落ちてしまった場合バックアップとして動作する役割を持っています。中身はLBとほぼ同じです。

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/103356/c20afb44-7c42-b11a-ea94-0ecd09dfc7c0.png) 　　　![キャプチャ9.PNG](https://qiita-image-store.s3.amazonaws.com/0/103356/4e569861-f948-0e11-beb5-5ed9ad916fbb.png)

普段、マスターロードバランサーとバックアップロードバランサーはセットになっていて、仮想的に1台のロードバランサーとして動作します。マスターに問題が発生した時、バックアップがマスターに昇格し、処理を代行します。このように、同じLANで複数台のルーターを１つに束ねる仕組みをVRRP(Virtual Router Redundancy Protocol)と呼び、自動的な切り替えをフェイルオーバーと言います。

Figure 2で見ると、LBとLB-backupは仮想的に１台のロードバランサーとして扱われます。なので、リクエストを一括で受けとるため、VIP(Virtual IP)と言う複数台のLVSを代表する仮想的なIPアドレスが必要となります。VIPを使うことによって、サービスへのリクエストはVIP宛となり、SSHなどのLVS単体へのリクエストは、サーバーの実IP宛のままで大丈夫です。イメージしやすくするには、VRRPによって束ねられているロードバランサー達を１つの仮想ロードバランサーと見て、そしてVIPはその仮想ロードバランサーのIPアドレスと想像すると良いかもしれません。

この肝心なVRRPですが、インフラの環境によってはサポートされていない場合が多いです。ですがIDCFクラウドではVRRPが使用できて、そしてVIPを取得する方法が、なんと二つもあります :star: 共通の手順が多いのですが、構築される環境の特徴に応じて「Step1. VIPの取得」の部分を**手順１**と**手順２**に分けて紹介します。

> バランシング対象がWeb等といった、トラフィックが仮想ルーター経由でくる場合→手順１
> バランシング対象がDB等といった、トラフィックが内部サーバー経由でくる場合→手順２

手順がわかれる主な理由は、IDCFクラウドの仕様にあります。とても簡単な話ですが、仮想マシンが直接外部と通信する場合、仮想ルーターを経由するので、ポートフォワードを設定する必要があります。ポートフォワードをする場合はAPIでIPアドレスを取得する必要があり、具体的な方法は手順１で紹介されています。一方、リアルサーバーが内部サーバーのみと通信する場合、ポートフォワードは必要ありません。この場合、LVSの構築は比較的簡単で、その方法が手順２で紹介されています。

##Step1. VIPの取得
###共通手順
**使用できるIPアドレスの確認**
VIPとして使うIPアドレスの確認をします。IDCFクラウドのポータル画面より、左メニューバー「ネットワーク」、ターゲットのネットワーク名、「IPアドレス一覧」をクリックします。

![ip.png](https://qiita-image-store.s3.amazonaws.com/0/103356/83e0d657-e062-c218-fce0-ed8fa0128271.png)
　　　　　　　　　　　　　 :point_up:IPアドレス一覧の画面

**手順１の場合**
ここに表示されている仮想マシンやルーターに割当済みのIPアドレスと、「割当範囲外」のIPアドレスを除いたすべてのIPアドレスがVIPとして割当可能です。VIPはDHCPで取得することも可能ですが、今回は10.5.0.100を指定して使おうと思います。【手順１】の部分へお進みください。

**手順２の場合**
「割当範囲外」のIPアドレスがすべてVIPとして指定できるのですが、「利用不可」と書かれている最後の10個のIPアドレスだけはIDCFクラウド側で使用するため割当はできません。なので、この例では「利用不可」の10.5.7.235〜10.5.7.254を除いた範囲で選びましょう！特に決まりはないですが、今回は10.5.4.100を使います。では、【手順２】の部分へお進みください。
###【手順１】仮想ルーター経由トラフィック用のLVS

トラフィックが仮想ルーターからくる場合は、下の図のような構成になります。Webなどの公開用サーバーをバランシングする時は、基本このような構成を取ります。リクエストは仮想ルーターを通過したら、直接ロードバランサーのVIP宛に届きます。そしてバランシング処理が済んだあと、再度このVIPよりリアルサーバーへ転送されます。

![キャプチャ2.PNG](https://qiita-image-store.s3.amazonaws.com/0/103356/0d8b0ac2-d6aa-8e23-f9c0-6699cb0ab1bc.png)
　 :point_up:仮想ルーター経由トラフィック用のLVS

VIPの取得はAPI経由で行います。APIはハードルが高そうに見えますが、何回か使ううちにAPIのファンになっていく方が多いです（笑）。実際私もそうでした！ :neckbeard: IDCFクラウドではCloudStackのAPIがそのまま使えるので、なじみがある方もいらっしゃるかもしれませんね！IPアドレスの値は、先ほど確認した10.5.0.100をスタティックで付与します。APIに関しては、[API References](http://docs.idcf.jp/cloud/api/)をご参照ください！

**コマンドラインツールのインストール**
APIを利用するにあたって、コマンドラインツールのインストールが必要になります。インストールするサーバーは、同じアカウント内のどのサーバーでもかまいません。APIの[Getting Started ガイド](http://docs.idcf.jp/cloud/introduction/)に沿って「簡単な使い方：Step4」までを実行して頂いても良いですし、以前上野が説明した[「IDCFクラウド　APIを利用した仮想マシンの作成からSSH接続まで」](http://www.idcf.jp/blog/cloud/api_autoscale2/)の「Step3」まで実行することでも実装できます。

**APIでVIPを取得**
いよいよ準備はOKです。VIPをマスターロードバランサーLBのNICに追加する作業を行います。

```
# 仮想マシンのid一覧を表示
[root@LB ~]# cloudstack-api listVirtualMachines -t displayname,id
+-------------+--------------------------------------+
| displayname |                  id                  |
+-------------+--------------------------------------+
| LB          | a2fa6425-aacc-4aba-bb84-e486a41e1c9e |
| LB-backup   | bb4095f1-c6b1-4646-8e89-762cd26a53d2 |
| Web1        | 785020c1-4d3b-4bb4-84c4-c9abe8da9ccb |
| Web2        | ea2f2045-64b5-4f70-9201-e0a6aa9c81c5 |
+-------------+--------------------------------------+

# 仮想マシンLBのNIC idを表示
[root@LB ~]# cloudstack-api listNics --virtualmachineid a2fa6425-aacc-4aba-bb84-e486a41e1c9e -t ipaddress,id
+------------+--------------------------------------+
| ipaddress  |                  id                  |
+------------+--------------------------------------+
| 10.5.0.133 | d555c213-3ede-4bb4-96bd-d1ce6c475852 |
+------------+--------------------------------------+
 
# 仮想マシンLBのNICにセカンダリIPアドレスを指定して割り当てる
[root@LB ~]# cloudstack-api addIpToNic --nicid d555c213-3ede-4bb4-96bd-d1ce6c475852 --ipaddress 10.5.0.100

# もう一度仮想マシンLBのNICの情報を表示
[root@LB ~]# cloudstack-api listNics --nicid d555c213-3ede-4bb4-96bd-d1ce6c475852 --virtualmachineid a2fa6425-aacc-4aba-bb84-e486a41e1c9e
{
  "listnicsresponse": {
    "count": 1,
    "nic": [
    {
      "gateway": "10.5.0.1",
      "id": "d555c213-3ede-4bb4-96bd-d1ce6c475852",
      "ipaddress": "10.5.0.133",
      "isdefault": true,
      "macaddress": "02:00:56:df:00:0f",
      "netmask": "255.255.252.0",
      "networkid": "15616765-d713-4ff3-bba3-a1c71a46d9bf",
      "secondaryip":
      {
        "id": "f2ff97fe-ecfd-4e01-ab34-9b17cbce16f9",
        "ipaddress": "10.5.0.100"
      }
    }
    ]
  }
}
```

**APIでポートフォワードルールを記入**

```
# 仮想ルーターのソースIPのidを表示
[root@LB ~]# cloudstack-api listPublicIpAddresses -t ipaddress,id
+----------------+--------------------------------------+
|   ipaddress    |                  id                  |
+----------------+--------------------------------------+
| 210.140.68.220 | 0c5cc05a-26d5-4539-9c7a-0f935291f71d |
+----------------+--------------------------------------+
 
# VIPを指定してポートフォワードルールを記入
[root@LB ~]# cloudstack-api createPortForwardingRule --ipaddressid 0c5cc05a-26d5-4539-9c7a-0f935291f71d --privateport 80 --protocol tcp --publicport 80 --virtualmachineid a2fa6425-aacc-4aba-bb84-e486a41e1c9e --vmguestip 10.5.0.100
{
  "createportforwardingruleresponse": {
    "jobid": "dfc3d84e-c9e6-47d3-9f50-4fc4b2c25493",
    "id": "dfc3d84e-c9e6-47d3-9f50-4fc4b2c25493"
  }
}
```

**注意点**
API経由で取得したsecondary IPの扱いには、２つ注意点がございます。
※東日本リージョンのみの注意点となります。

Secondary IPはポータルからの管理ができないので、ご自身での管理となります。なので誤って仮想マシンを削除してしまわないよう気を付けましょう！ :scream: 
Secondary IPをグローバルIPアドレスとスタティックNATした場合、APIでスタティックNATを解除してから仮想マシンを削除するようにしてください。スタティックNATの設定解除せず、そのまま仮想マシンを削除してしまうとシステム側からスタティックNATが削除できなくなってしまいます。 :scream: 
###【手順２】内部サーバー経由トラフィック用のLVS

トラフィックが内部サーバーからくる場合、下の図のような構成になります。DBなどの非公開用サーバーをバランシングするとき、よく取られる構成です。リクエストはWebサーバーなどの内部サーバーからロードバランサーのVIP宛に届きます。そしてバランシング処理が済んだあと、再度このVIPよりリアルサーバー1かリアルサーバー2へ転送されます。
![キャプチャ３.PNG](https://qiita-image-store.s3.amazonaws.com/0/103356/6a68a7e5-2cc2-39cb-032d-3ebfb1ddbbcb.png)
　 :point_up:内部サーバー経由トラフィック用のLVS

このような構成の場合、VIPの取得はとても簡単です。VIPをあらかじめ決めておく事以外、特に設定の必要もありません！IPアドレスの値は、先ほど確認した10.5.4.100を使います。

**注意点**
VIPとして指定されたIPアドレスは、ポータルから管理ができないので、ご自身の管理となってしまいます。なのでIPアドレスが重複しないよう気を付けてください。 :scream: 
#Step2. keepalivedの準備

LVSの設定をしていきます。もし前回の記事に沿って設定されているかたは「LVSの設定」部分から続けて下さい。

**必要なパッケージをダウンロード**

```
[root@LB ~]# yum -y install ipvsadm keepalived
```
**自動起動の設定**

```
[root@LB ~]# chkconfig keepalived on
[root@LB ~]# chkconfig --list keepalived
keepalived  0:off  1:off  2:on  3:on  4:on  5:on  6:off
```
**パケット転送の設定**

```
[root@LB ~]# vim /etc/sysctl.conf
net.ipv4.ip_forward = 1 (0から1へ変更)
[root@LB ~]# sysctl -p
net.ipv4.ip_forward = 1 (変更されていればOK)
```
**LVSの設定**
keepalivedはデフォルトでipvsadmをラップしているので、LVSの設定はkeepalivedのコンフィグを編集して行います。マスターとバックアップ両方のコンフィグを記入する必要がありますが、priorityの値:exclamation:だけは異なるように設定してください。 

```
[root@LB ~]# vim /etc/keepalived/keepalived.conf
```

```keepalived.conf
# vrrp_instance [お好きな名前]で複数設定が可能です
vrrp_instance VI_1 {
  # マスター、バックアップのいずれか
  state MASTER
  # 監視するインターフェース
  interface eth0
  # VRIDは任意の数値で良いが
  # 同じセグメントの他のVRIDとかぶらないように設定する
  # 例：仮想ルーターのVRIDや追加ネットワークのVRID
  virtual_router_id 1
  # マスターとバックアップで必ず違う値にする
  # 例：マスター：300、バックアップ１：200、バックアップ２：100
  priority 100
  # VRRP信号を送る間隔
  advert_int 1
  # VIPを指定、手順2の場合は10.5.4.100
  virtual_ipaddress {
    10.5.0.100 dev eth0
  }
}
# LVSの設定
virtual_server 10.5.0.100 80 {
  # ヘルスチェック間隔
  delay_loop 3
  # 分散方式はround robinを使う
  lb_algo rr
  # パケット転送の方法はdirect routing (DSR)
  lb_kind DR
  protocol TCP
  # リアルサーバのIPアドレスおよびヘルスチェックのポート番号
  # Webは80番や443番、MySQLは3306番など
  real_server 10.5.0.36 80 {
    weight 1
    # ヘルスチェック失敗時はweightをさげる
    inhibit_on_failure
    # ヘルスチェック方式
    # WebはHTTP_GETやSSL_GET、MySQLはTCP_CHECKなど
    HTTP_GET {
    # Webはurl、MySQLではconnect_port 3306など
    url {
    # ヘルスチェック用のページ
      path /healthchk.html
      status_code 200
    }
    connect_timeout 3
    # ヘルスチェック失敗時の間隔
    delay_before_retry 7
  }
}
# リアルサーバー2台目以降も同じ設定にする
  real_server 10.5.0.229 80 {
    weight 1
    inhibit_on_failure
    HTTP_GET {
      url {
        path /healthchk.html
        status_code 200
      }
      connect_timeout 3
      delay_before_retry 7
    }
  }
}
```

#Step3. リアルサーバーの準備

手順１の場合はWebサーバー、手順２の場合はDBサーバーを例として準備します。ApacheやMySQLをインストールして、サービスをスタートすれば動作確認はとれます。Webサーバーに関しては、ヘルスチェック用のページも忘れず用意しておいてくださいね！ :smiley_cat: 

**iptablesの記入**
重要です。リアルサーバーがLVSのVIP宛のパケットを受け取ったとき、自分宛のパケットとして処理できるようにさせてあげる必要があります。

```
[root@Real1 ~]# iptables -t nat -A PREROUTING -d 10.5.0.100 -j REDIRECT
# -d 10.5.0.100 の部分でご自身のLVSのVIPを指定する
[root@Real1 ~]# service iptables save
```

#Step4. keepalivedの起動

LVS冗長構成の設定とVIPの準備ができたので、keepalivedを起動して、様子を見てみましょう！:eyes: 

**keepalivedの起動**

```
[root@LB ~]# service keepalived start
```
**VIPの確認**
マスター（LB）のほうでeth0に割り当てられているIPアドレスを見てみましょう。

```
[root@LB ~]# ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:00:56:df:00:0f brd ff:ff:ff:ff:ff:ff
inet 10.5.0.133/21 brd 10.5.7.255 scope global eth0
inet 10.5.0.100/32 scope global eth0
inet6 fe80::56ff:fedf:f/64 scope link
valid_lft forever preferred_lft forever
```
マスターのほうにVIPがついています。VIPは、実際にLVSとして動いているサーバーに付きます。なのでこの時点で同じコマンドをバックアップのほうで入力しても、VIPは表示されません。マスターが正常にLVSとして稼働しているからです。:v: 

**動作確認**
動作確認の前に、LVS上でipvsadmコマンドを使ってバランシングの状態をマスター、バックアップ両方で確認してみましょう。

```
[root@LB ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.5.0.100:80 rr
-> 10.5.0.36:80                 Route   1      0          0
-> 10.5.0.229:80                Route   1      0          0
LVSのIPアドレスが見事VIPの10.5.0.100（手順２の場合10.5.4.100）になっています。ここでマスターを落としてみて、バックアップへフェイルオーバーするか確認してみます。

[root@LB ~]# service keepalived stop
keepalived を停止中:                                       [  OK  ]
[root@LB ~]# ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:00:56:df:00:0f brd ff:ff:ff:ff:ff:ff
inet 10.5.0.133/21 brd 10.5.7.255 scope global eth0
inet6 fe80::56ff:fedf:f/64 scope link
valid_lft forever preferred_lft forever
```
マスターでもう一度eth0に割り当てられているIPアドレスを見てみると、VIPが消えています。では、バックアップのほうを見てみましょう。

```
[root@LB-backup ~]# ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:00:0a:30:00:10 brd ff:ff:ff:ff:ff:ff
inet 10.5.0.195/21 brd 10.5.7.255 scope global eth0
inet 10.5.0.100/32 scope global eth0
inet6 fe80::aff:fe30:10/64 scope link
valid_lft forever preferred_lft forever
```
VIPが自動的にバックアップのNICに切り替わりました。冗長化は保たれていますね。:v: 

```
[root@LB-backup ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.5.0.100:80 rr
-> 10.5.0.36:80                 Route   1      0          0
-> 10.5.0.229:80                Route   1      0          0
```
バランシング状態も大丈夫のようです。これでLVSが冗長化されていることがわかりました。ばっちりですね！:+1: 

#終わりに

みなさん、LVSの冗長化はいかがでしたか？コンフィグの部分はさておき、VIPの取得が少し手間かもしれませんが、高価なロードバランサー２台分のお値段を考えると、全然お得ですよね！

もっとも魅力的なのはやはりクラウド環境で、VRRPを用いたLVSの冗長化が可能な仕様だと思っています。例の通り、IDCFクラウドはAPIさえクリアしてしまえば簡単にそれを実現できてしまいます。インフラのポイント、安定性と柔軟性においては文句なしです！:ok_woman: 

もちろん、もし内部トラフィックのバランシングのためでしたら、特にAPIを使う必要もないです。IPアドレスを指定してkeepalived.confに記述するだけで大丈夫です。なので、ぜひ構築したい環境の必要に応じて、楽できるインフラ構築を目指しましょう！ありがとうございましたー:smile_cat: 

**＜関連記事＞**
[IDCFクラウドでkeepalivedを使ったLVS構築(1)] (http://www.idcf.jp/blog/cloud/keepalived/)