LVS & firewalld による DSR 方式ロードバランサー

&gt; to English Pages
https://kyowg.blogspot.com/2017/11/load-balancer-with-lvs-keepalived-dsr.html
## 1. 要約
この記事では、LVS (keepalived + IPVS) & firewalld による DSR 方式ロードバランサーの設計と構築例を投稿します。

## 2. はじめに
高トラフィックなプロジェクトでは、そのシステムパフォーマンス会議などにおいて、ロードバランサーが議題に上がる事もあると思います。
そして、ソフトウェアロードバランサーのパフォーマンスについて、ネガティブな意見が出る事もあるかもしれません。
そのような場合、BIG-IP のような物理ロードバランサーの名前が上がる事もあるでしょう。
しかし、LVS (keepalivee + IPVS) & firewalld を利用した DSR 方式のソフトウェアロードバランサーが、2 〜 4vCPU、4 〜 16RAM スペックで、日に 100 万アクセスある某プロジェクトにおいて、SLA 100%、負荷率 10% で稼働しているという事実を知ったらいかがでしょうか？
この事実は、この設計技術が、ロードバランサー製品のないホスティングサービスや、オンプレミス、あるいはクラウドにおけるローコスト設計において、有効なロードバランサーソリューションの一つである事を実証してるといってもいいでしょう。
このようなハイパフォーマンスな成果は、特に DSR という通信方式によってもたらされている側面が強いでしょう。
DSR 方式とは、Direct Server Return の略語で、“ノードからクライアントへのレスポンスを、ロードバランサーを経由させずにダイレクト返却にする”という通信方式の事をさし、この仕組みによってロードバランサーの劇的な負荷軽減を実現させています。
また、この方法のメリットは、コスト削減や高パフォーマンスだけでなく、ハードウェアロードバランサーにまつわる様々な課題（故障、劣化、サポート契約や体制、機種依存など）の影響を受けないところにもあります。

では、以降で LVS (keepalived + IPVS) & firewalld による DSR 方式ロードバランサーの設計と構築の一例を投稿してみます。
また、この記事では、DSR V.S. Not DSR などのベンチマークテストについては実施しません。

## 3. DSR 方式ロードバランサー環境
* RHEL 7 系
* Keepalived 1.2.13
* ipvsadm 1.27
* firewalld 0.3.9

## 4. DSR 方式ロードバランサー設計
![dsr.png](https://qiita-image-store.s3.amazonaws.com/0/260161/fb866179-ca26-1229-6578-93a0641fb849.png)

## 5. Web サーバー側 DSR 用設定
### 5-1. firewalld 設定
@ Web サーバー 1
`$ sudo systemctl start firewalld`
`$ sudo systemctl enable firewalld`
`$ sudo systemctl status firewalld`
`$ sudo firewall-cmd --set-default-zone=internal`
`$ sudo firewall-cmd --add-port=22/tcp --zone=internal`
`$ sudo firewall-cmd --add-port=22/tcp --zone=internal --permanent`
`$ sudo firewall-cmd --add-port=80/tcp --zone=internal`
`$ sudo firewall-cmd --add-port=80/tcp --zone=internal --permanent`
物理 IP アドレス用
`$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.3 -j REDIRECT`
`$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.3 -j REDIRECT`
仮想 IP アドレス用
`$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT`
`$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT`
`$ sudo firewall-cmd --reload`
`$ sudo firewall-cmd --list-all-zone`
`$ sudo firewall-cmd --direct --get-rule ipv4 nat PREROUTING`

@ Web サーバー 2
`$ sudo systemctl start firewalld`
`$ sudo systemctl enable firewalld`
`$ sudo systemctl status firewalld`
`$ sudo firewall-cmd --set-default-zone=internal`
`$ sudo firewall-cmd --add-port=22/tcp --zone=internal`
`$ sudo firewall-cmd --add-port=22/tcp --zone=internal --permanent`
`$ sudo firewall-cmd --add-port=80/tcp --zone=internal`
`$ sudo firewall-cmd --add-port=80/tcp --zone=internal --permanent`
物理 IP アドレス用
`$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.4 -j REDIRECT`
`$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.4 -j REDIRECT`
仮想 IP アドレス用
`$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT`
`$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT`
`$ sudo firewall-cmd --reload`
`$ sudo firewall-cmd --list-all-zone`
`$ sudo firewall-cmd --direct --get-rule ipv4 nat PREROUTING`

## 6. LVS (keepalived + IPVS) ロードバランサー構築
### 6-1. Web サーバー疎通確認
@ ロードバランサー 1, 2
`$ sudo telnet 10.0.0.3 80`
`$ sudo telnet 10.0.0.4 80`

### 6-2. Linux カーネル設定
@ Web サーバー 1, 2 
`$ sudo vim /etc/sysctl.conf`

```conf:/etc/sysctl.conf
# Enable Packet Transfer between Interfaces
net.ipv4.ip_forward = 1

# Do not discard packets from networks that do not belong to the interface.
net.ipv4.conf.all.rp_filter = 0
```

`$ sudo sysctl -p`

```
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
```

### 6-3. keepalived 設定
@ ロードバランサー 1, 2
`$ sudo yum -y install keepalived`
`$ sudo yum -y install ipvsadm`
`$ sudo cp -a /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.org`

@ ロードバランサー 1
`$ sudo vim /etc/keepalived/keepalived.conf`

```conf:/etc/keepalived/keepalived.conf
; Common Configuration Block
global_defs {
    notification_email {
        alert@example.com
    }
    notification_email_from lb1@example.com
    smtp_server mail.example.com
    smtp_connect_timeout 30
    router_id lb1.example.com
}

; Master Configureation Block
vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 1
    priority 101
        nopreempt
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass foo
    }
    virtual_ipaddress {
        10.0.0.5/24 dev eth0
    }
}

; Virtual Server Configureation Block
virtusl_server 10.0.0.5 80 {
    delay_loop 6
    lvs_sched rr
    lvs_method DR
    persistence_timeout 50
    protocol TCP
    sorry_server 10.0.0.254 80
    real_server 10.0.0.3 80 {
        weight 1
		inhibit_on_failure
        HTTP_GET {
            url {
                path /
                status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server 10.0.0.4 80 {
        weight 1
	    inhibit_on_failure
        HTTP_GET {
            url {
                path /
                status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}
```

`$ sudo systemctl start keepalived`
`$ sudo systemctl enable keepalived`
`$ sudo systemctl status keepalived`
VIP を取得していることを確認
`$ sudo ip addr`

ロードバランサー 2
`$ sudo vim /etc/keepalived/keepalived.conf`

```conf:/etc/keepalived/keepalived.conf
; Common Configuration Block
global_defs {
    notification_email {
        admin@example.com
    }
    notification_email_from lb2@example.com
    smtp_server mail.example.com
    smtp_connect_timeout 30
    router_id lb2.example.com
}

; Backup Configureation Block
vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 1
    priority 100
        nopreempt
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass foo
    }
    virtual_ipaddress {
        10.0.0.5/24 dev eth0
    }
}

; Virtual Server Configureation Block
virtusl_server 10.0.0.5 80 {
    delay_loop 6
    lvs_sched rr
    lvs_method DR
    persistence_timeout 50
    protocol TCP
    sorry_server 10.0.0.254 80
    real_server 10.0.0.3 80 {
        weight 1
	    inhibit_on_failure
        HTTP_GET {
            url {
                path /
                status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server 10.0.0.4 80 {
        weight 1
	    inhibit_on_failure
        HTTP_GET {
            url {
                path /
                status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}
```

`$ sudo systemctl start keepalived`
`$ sudo systemctl enable keepalived`
`$ sudo systemctl status keepalived`
VIP を取得してない事を確認<br>
`$ sudo ip addr`

### 6-4. IPVS (IP Virtual Server) 設定
@ ロードバランサー 1, 2
`$ sudo yum -y install ipvsadm`
`$ sudo touch /etc/sysconfig/ipvsadm`
`$ sudo systemctl start ipvsadm`
`$ sudo systemctl enable ipvsadm`
`$ sudo systemctl status ipvsadm`
`$ sudo ipvsadm -Ln`

## 7. テスト
### 7-1. 疎通テスト
ブラウザまたは、違うホストから VIP 宛てへ http アクセス

### 7-2. フェイルオーバーテスト
@ ロードバランサー 1
`$ systemctl stop keepalived`
`$ ip addr`
`$ ipvsadm -Ln`

@ ロードバランサー 2
`$ ip addr`
`$ ipvsadm -Ln`
7-1 疎通テストと同様

## 8. まとめ
この記事では、LVS (keepalived + IPVS) & firewalld による DSR 方式ロードバランサーの設計と構築の一例を投稿してみました。
こうすることで、ソフトウェアロードバランサーの弱点である高負荷に対するパフォーマンスの低下を DSR ソリューションによって改善する事ができるようになります。
また、この方法のメリットは、コスト削減や高パフォーマンスだけでなく、ハードウェアロードバランサーにまつわる様々な課題（故障、劣化、サポート契約や体制、機種依存など）の影響を受けないところにもあります。