LinodeにおけるIP Sharingを用いたフェールオーバー構成の実現

# はじめに
構築するシステムのサーバーでIPアドレスベースのフェールオーバー構成を組む場合、いずれかの方法を用いてサーバー間でIPアドレスを引き継がせる必要があります。よくある構成としては以下のようなパターンがあります。（IPアドレスはすべて例です）
- サーバーAにIP-A（192.168.0.1）、サーバーBにIP-B（192.168.0.2）、共有用としてIP-C（192.168.0.3）を割り当て、IP-Cを持っているサーバーが応答し、障害時はIP-Cを切り替えます。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/f4a3de75-7367-2c63-ca2b-04e52465bf2f.png">



- サーバーAにIP-A（192.168.0.1）、サーバーBにIP-B（192.168.0.2）を割り当て、障害時はIP-Aを引き継ぎます。あまり多くないパターンです。IP-Bは管理用として利用されています。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/de78be0a-6472-b909-88f6-fc0b9632a0fe.png">


Linodeにおいても通常は１台のインスタンスにそれぞれIPアドレスが割り当てられますが、LinodeのIP Sharing機能を利用して複数のIPアドレスを割り当てて、そのIPを共有することで、フェールオーバー構成を組むことが可能です。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/341ceb87-7b2c-71a7-2b8a-5548c0119c98.png">


なお、冗長構成及び負荷分散を実現する方法として、LinodeのマネージドロードバランサーであるNodeBalancersを用いる方法もあります。本記事でのIPアドレス方式でのフェールオーバー構成は、Linodeインスタンスにてロードバランサーを構築する場合や、何らかの理由でクライアントと直接通信を行う必要があるときなどに用いられます。

https://www.linode.com/docs/products/networking/nodebalancers/



# フェールオーバーの方式
IP Sharingの概要についてはLinodeのドキュメントに記載されています。
注意点としては、フェールオーバーメソッドや利用するソフトウェアがデータセンターにより違いがあり、Linodeインスタンスが配置されるデータセンターに併せて構成する必要があります。寄稿時に東京リージョンでサポートしているフェールオーバーメソッドはARPベースでのKeepAlived方式です。（最新情報は以下URLにて確認してください）

https://www.linode.com/docs/products/compute/compute-instances/guides/failover/#ip-sharing-availability

主にインターネット用のルーティングプロトコルのBGPや、聞きなれないlelastic、FRRなどの文言が出てきましたが心配ありません。Linode環境にて手順化されているため、ドキュメントを見ながら設定を行うだけでフェールオーバー構成が実現できます。


グローバルIPアドレスであるPublic IPをフェールオーバー構成とする場合は、2つ目のIPアドレスを1台のインスタンスに追加する必要があります。グローバルIPアドレスの追加はIPv4アドレス枯渇の観点からLinodeカスタマーサポートへ申請が必要ですので、手順の理解についてはこの記事が役に立てば幸いです。



# 事前準備
同じリージョンにてLinodeインスタンスを2台作成します。今回は対応しているフェールオーバー方式に併せて作成しました
- ARPベースでのKeepAlived方式: 東京リージョン
- BGPベースでのlelastic方式: シンガポールリージョン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/0a956b44-dd1b-bb3d-480f-afac4dfd237d.png)

ディストリビューションは「Ubuntu 22.04 LTS」を選択していますので、その他のディストリビューションを用いる場合はLinodeドキュメントに記載のコマンドを置き換えてください。


まずはApacheをインストールします。sudo用のユーザーは任意のユーザー名で作成してください。
```
# adduser -a user01
# gpasswd -a user01 sudo
ユーザーにて再ログインし、以後はsudoで実行します。
$ sudo apt update
$ sudo apt install apache2 -y
$ sudo vi /var/www/html/server.html
```
サーバーAに接続されていることがわかるように、シンプルなHTMLファイルを作成します。
```bash:/var/www/html/server.html
<html><head></head><body><h1>
Server-A
</h1></body></html>
```
サーバーBも同様に作成します。
```bash:/var/www/html/server.html
<html><head></head><body><h1>
Server-B
</h1></body></html>
```

両方のサーバーでWebサーバーを起動します。
```
$ sudo systemctl start apache2
$ sudo systemctl enable apache2
```

サーバーAのグローバルIPにアクセスすると、「サーバーA」が表示されます。
http://[LinodeのグローバルIP]/server.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/0dcba87d-d1ed-2008-bba4-cec717591298.png)

同様にサーバーBでは、「サーバーB」が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/fa21ced8-5c96-c2ae-712a-3717983dbd82.png)



# ARPベースでのKeepAlived方式

東京リージョン等でサポートしている方式です。ここで紹介する手順は以下のURLをベースにしていますが、コンフィグファイルに多少の変更を加えています。

https://www.linode.com/docs/products/compute/compute-instances/guides/failover-legacy-keepalived/

### Public IPアドレスの追加

Linodeインスタンスのネットワーク設定より、サーバーAに対して、IPアドレスの追加を行います。なお追加の課金（2023/4現在、IP毎に月$2）が発生します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/38c29bfc-a7cc-3a4e-f2c5-b626b79cb40d.png)


PublicであるグローバルIPアドレスを追加する場合は、カスタマーサポートへ申請が必要です。申請をしていない場合は以下のメッセージが表示され追加できません。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/bdcd840b-36f1-48f6-c924-2f6aee5f1744.png">

今回はグローバルIPアドレスを冗長化するため、Publicを選択し「Allocate」をクリックします。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/92ee1907-4072-9ef7-5cbf-613b33772532.png">

2つめの「IPv4 - Public」が割り当てられました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/c93a26ff-d40a-72d2-2e61-854e265dca4d.png)



## IP Sharingの設定
先ほど割り当てたPublic IPに対して、IP Sharingの設定を行います。
サーバーBのネットワーク設定にて「IP Sharing」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/23047694-805b-b397-b68b-534600e9389d.png)

先ほど割り当てたサーバーAの**2つめのPublic IP**を選択し、「Save」をクリックします。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/82cf3c09-4367-57ce-91da-fc60f40a62e3.png">

サーバーAの共有IPが「IPv4 - Shared」として割り当てられました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/39145a0a-65ff-a47c-6378-e137d5376424.png)


### keepalivedのインストールと設定

両方のサーバーにkeepalivedをインストールします。
```
$ sudo apt update && sudo apt upgrade
$ sudo apt install keepalived
$ sudo vi /etc/keepalived/keepalived.conf
```

サーバーAにてkeepalived.confを以下のように作成します。
```bash:/etc/keepalived/keepalived.conf
global_defs {
}

vrrp_script check_apache2 {
    script "/etc/keepalived/apache2-check.sh"
    interval 2
    fall 2
    rise 2
}

vrrp_instance Instance1 {
    state MASTER
    interface eth0
    virtual_router_id 10
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass fowebpass    #fowebpassは例。共通のパスワードを記載
    }
    unicast_src_ip 198.51.100.1 #左記は例。サーバーAの1つ目のPublic IPを記載
    unicast_peer {
       198.51.100.2   #左記は例。サーバーBのPublic IPを記載
    }
    virtual_ipaddress {
       198.51.100.3   #左記は例。IP Sharingで割り当てたIPを記載
    }
    track_script {
      check_apache2
    }
}
```

サーバーBにも同じようにkeepalived.confを以下のように作成します。

```bash:/etc/keepalived/keepalived.conf
global_defs {
}

vrrp_script check_apache2 {
    script "/etc/keepalived/apache2-check.sh"
    interval 2
    fall 2
    rise 2
}

vrrp_instance Instance1 {
    state BACKUP    # サーバーBはBACKUPに変更
    interface eth0
    virtual_router_id 10
    priority 99    # サーバーBは99に変更
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass fowebpass    #fowebpassは例。共通のパスワードを記載
    }
    unicast_src_ip 198.51.100.2 #左記は例。サーバーBのPublic IPを記載
    unicast_peer {
       198.51.100.1   #左記は例。サーバーAの1つ目のPublic IPを記載
    }
    virtual_ipaddress {
       198.51.100.3   #左記は例。IP Sharingで割り当てたIPを記載
    }
    track_script {
      check_apache2
    }
}
```



両方のサーバーにapache2のプロセス正常性を確認するための簡単なスクリプトを追加します。
```
$ sudo vi /etc/keepalived/apache2-check.sh
```

```bash:/etc/keepalived/apache2-check.sh
#!/bin/bash
systemctl is-active apache2
```

スクリプトの権限を変更し、keepalivedを起動します。
```
$ sudo chmod 744 /etc/keepalived/apache2-check.sh
$ sudo systemctl enable keepalived
$ sudo systemctl start keepalived
```

状態確認を行います。
サーバーAでのログは以下の通りです。マルチマスターにならないように一旦BACKUP STATEで起動し、すぐにMASTER STATEに選出されていることがわかります。
```
$ sudo tail -20 /var/log/syslog  | grep Keepalive
Mar 27 01:26:09 localhost systemd[1]: Started Keepalive Daemon (LVS and VRRP).
Mar 27 01:26:09 localhost Keepalived_vrrp[10352]: VRRP_Script(check_apache2) succeeded
Mar 27 01:26:09 localhost Keepalived_vrrp[10352]: (Instance1) Entering BACKUP STATE
Mar 27 01:26:12 localhost Keepalived_vrrp[10352]: (Instance1) Entering MASTER STATE
```

eth0に共有IPが適用されています。
```
$ sudo ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet サーバーAのIP/24 brd BROADCAST scope global eth0
       valid_lft forever preferred_lft forever
    inet 共有IP/32 scope global eth0
       valid_lft forever preferred_lft forever
```

サーバーBでのログは以下の通りです。BACKUP STATEになっていることがわかります。
```
$ sudo tail -20 /var/log/syslog  | grep Keepalive
Mar 27 01:26:13 localhost systemd[1]: Started Keepalive Daemon (LVS and VRRP).
Mar 27 01:26:13 localhost Keepalived[48819]: Startup complete
Mar 27 01:26:13 localhost Keepalived_vrrp[48820]: VRRP_Script(check_apache2) succeeded
Mar 27 01:26:13 localhost Keepalived_vrrp[48820]: (Instance1) Entering BACKUP STATE
```
共有IPは適用されていません。
```
$ sudo ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet サーバーBのIP/24 brd BROADCAST scope global eth0
       valid_lft forever preferred_lft forever
```


パソコンのブラウザから共有IPにアクセスすると、サーバーAが応答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/826e603b-96a5-54f7-35de-c24d52b5c369.png)



### サーバーの切り替え

切り替えを行います。ここではapacheを停止します。
```
$ sudo systemctl stop apache2
```

パソコンのブラウザから共有IPにアクセスすると、サーバーBが応答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/f9d26536-4ad4-028d-665c-34f3fdf62ea0.png)

同時にパソコンからpingにてサーバーの切り替えを確認した結果です。
```
> ping 共有IP -t
共有IP からの応答: バイト数 =32 時間 =30ms TTL=49
共有IP からの応答: バイト数 =32 時間 =31ms TTL=49
要求がタイムアウトしました。　 # ここで切り替わっています。
共有IP からの応答: バイト数 =32 時間 =29ms TTL=49
共有IP からの応答: バイト数 =32 時間 =32ms TTL=49
```


状態確認を行います。
サーバーAでのログは以下の通りです。FAULT STATEになっていることがわかります。
```
$ sudo tail -20 /var/log/syslog  | grep Keepalive
root@localhost:~# tail -50 /var/log/syslog  | grep Keepalive
Mar 27 02:02:21 localhost Keepalived_vrrp[10352]: Script `check_apache2` now returning 3
Mar 27 02:02:23 localhost Keepalived_vrrp[10352]: VRRP_Script(check_apache2) failed (exited with status 3)
Mar 27 02:02:23 localhost Keepalived_vrrp[10352]: (Instance1) Entering FAULT STATE
```

共有IPは適用されていません。
```
$ sudo ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet サーバーAのIP/24 brd BROADCAST scope global eth0
       valid_lft forever preferred_lft forever
```

サーバーBでのログは以下の通りです。MATER STATEに切り替わっていることがわかります。
```
$ sudo tail -20 /var/log/syslog  | grep Keepalive
Mar 27 01:35:07 localhost Keepalived_vrrp[48820]: (Instance1) Entering BACKUP STATE
Mar 27 02:02:24 localhost Keepalived_vrrp[48820]: (Instance1) Entering MASTER STATE
```

サーバーBのeth0に対して共有IPが適用されています。
```
$ sudo ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet サーバーBのIP/24 brd BROADCAST scope global eth0
       valid_lft forever preferred_lft forever
    inet 共有IP/32 scope global eth0
       valid_lft forever preferred_lft forever
```

サーバーAのapacheが再度立ち上がるとフェールバックされて、サーバーAが応答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/826e603b-96a5-54f7-35de-c24d52b5c369.png)


# BGPベースでのlelastic方式

シンガポールリージョンなどでサポートしているlelasticによる方式を記載します。
ドキュメントでは以下URLにて記載されています。

https://www.linode.com/docs/products/compute/compute-instances/guides/failover/#configure-failover


apacheのインストールなど基本的なサーバーの準備は「事前準備」と同じですが、リージョンはシンガポールを選択しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/d8c44f90-d3e9-852b-6cf5-52fb7e9f4d00.png)


### Public IPアドレスの追加及び、IP Sharingの設定

こちらもkeepalivedの場合と同様に、Public IPの追加及びIP Sharingの設定を行います。


### 共有IPをLinuxのネットワーク構成に追加


両方のサーバーのloインターフェースに共有IPを適用します。
```
$ sudo vi /etc/netplan/01-netcfg.yaml
```
```
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: yes
    lo:   #ここから追加
      match:
        name: lo
      addresses:
        - 共有IP/32  #/32を指定してください。

```

設定を適用します。loインターフェースに共有IPが割り当たっていることが確認できます。
```
$ sudo netplan apply
$ ip addr

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 共有IP/32 scope global lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 一つ目のPublic IP/24 brd BROADCAST scope global eth0
       valid_lft forever preferred_lft forever
    inet 共有IP/24 brd BROADCAST scope global eth0

```


### lelasticのインストールと設定
両方のサーバーにlelasticをインストールします。以下のURLにてversionを確認し、次のコマンドの引数に適用してください。
https://github.com/linode/lelastic/releases

確認したversionに書き換えてインストールを行います。
```
version=v0.0.6
curl -LO https://github.com/linode/lelastic/releases/download/$version/lelastic.gz
gunzip lelastic.gz
chmod 755 lelastic
sudo mv lelastic /usr/local/bin/
```

起動スクリプトを作成します。
- [id]はフェールオーバー方式一覧からリージョンのIDを取得して適用します。
https://www.linode.com/docs/products/compute/compute-instances/guides/failover/#ip-sharing-availability

- [role]は、サーバーAはprimary、サーバーBはsecondaryを選択します。


[id]、[role]を修正して作成します。
両方のサーバーで設定内容は同じです。
```
$ sudo vi /etc/systemd/system/lelastic.service
```
```bash:/etc/systemd/system/lelastic.service
[Unit]
Description= Lelastic
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/lelastic -dcid [id] -[role] &
ExecReload=/bin/kill -s HUP $MAINPID

[Install]
WantedBy=multi-user.target
```

lelasticを起動します。
```
$ sudo chmod 644 /etc/systemd/system/lelastic.service
$ sudo systemctl start lelastic
$ sudo systemctl enable lelastic
```

パソコンのブラウザから共有IPにアクセスすると、サーバーAが応答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/826e603b-96a5-54f7-35de-c24d52b5c369.png)



### サーバーの切り替え

切り替えを行います。ここではサーバーAを停止します。
```
$ sudo shutdown -h now
```

パソコンのブラウザから共有IPにアクセスすると、サーバーBが応答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/f9d26536-4ad4-028d-665c-34f3fdf62ea0.png)

同時にパソコンからpingにてサーバーの切り替えを確認した結果です。
```
> ping 共有IP -t
共有IP からの応答: バイト数 =32 時間 =210ms TTL=44
共有IP からの応答: バイト数 =32 時間 =203ms TTL=44
要求がタイムアウトしました。　 # ここで切り替わっています。
共有IP からの応答: バイト数 =32 時間 =204ms TTL=44
共有IP からの応答: バイト数 =32 時間 =209ms TTL=44
```

サーバーAのOSが再度立ち上がるとフェールバックされて、サーバーAが応答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821330/826e603b-96a5-54f7-35de-c24d52b5c369.png)



# まとめ
LinodeでIPアドレスベースのフェールオーバー構成を実現する方法を紹介しました。NodeBalancersを用いないIPアドレスベースのサーバー冗長化を行う際に、公式のドキュメントと併せてぜひ参考にしてください。