node.jsで待機サーバーBを自動 ifconfig up する

これの途中経過。https://www.facebook.com/javascripting/posts/398389730217171
もっと良い手は無いかなぁ。。。とりあえず、身近で気楽そうなping監視版。
ソケット使う版はまたあとで。あと、ARPどうするかとか、メインサーバー生き返った時どうするかとかもあとで。ただ、heartbeatでいんじゃないの？という強い誘惑に勝てるかどうかは不明な今日この頃。。。

##node.jsで待機サーバーBを自動 ifconfig up する
![node.jsで待機サーバーBを自動 ifconfig up する](https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-ash4/s320x320/471_370340356366919_533892798_n.jpg)

サーバーA eth0 192.168.1.11 (メイン)
サーバーB eth0 192.168.1.12 (待機)

ルーターからのNATの宛先LAN側サーバーアドレス 192.168.1.10で

サーバーA に仮想IP eth0:1 192.168.1.10 を立てている時、

サーバーB から ping打って192.168.1.10を監視し、
死んでいたら、サーバーBの eth0:1へ仮想IP 192.168.1.10 を起動してサーバーを切り替える。

```test:サーバー構成
                       Internet
                           | 
                  +--------+---------+
                  |     GateWay      |
                  +--------+---------+
                           | 192.168.1.1
                           |
                           | 192.168.1.0/24
  +-----------+------------+------------+-----------+
              |                         | //on err, 
              | 192.168.1.10(eth0:1)    | //ifconfig eth0:1 192.168.1.10 up
              | 192.168.1.11(eth0)      | 192.168.1.12(eth0)
     +--------+-------+        +--------+-------+
     | Main Server A  |        |  Sub Server B  |
     +--------+-------+        +--------+-------+
```

それぞれの機器のネットワークはたとえば下記な感じで設定しておく

```text:ネットワーク構成

【GateWay】example.comとか
WAN側 ***.***.***.***
LAN側 192.168.1.1

NATエントリ
LAN側ホスト	プロトコル	ポート番号	優先度
192.168.1.10	TCP	22-22	1
192.168.1.10	TCP	80-80	2
192.168.1.10	TCP	443-443	3
192.168.1.10	TCP	21-21	4

【Main Server A : OS Ubuntu 12.04 LTS】192.168.1.11

$sudo vi /etc/network/interfaces

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 192.168.1.11
netmask 255.255.255.0
network 192.168.1.0
gateway 192.168.1.1
dns-nameservers 8.8.8.8

【Sub Server B : OS Ubuntu 12.04 LTS】192.168.1.12

$sudo vi /etc/network/interfaces

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 192.168.1.12
netmask 255.255.255.0
network 192.168.1.0
gateway 192.168.1.1
dns-nameservers 8.8.8.8
```
ファイルの構成は次の通り
待機サーバーBへ設置してますが、両方に置いても可。

```text:ファイル構成 
Sub Server B
 home                          
  └hoge                       
     └kagemusya
        ├ping-ifup.js           ... 0705 サーバーAの監視・障害時に仮想IPを起動する
        ├kage-start.sh          ... 0705 サーバーB起動時用スクリプト rc.localへ設定
        └log                    ... 0606
           └kage-start-log.txt  ... (自動生成)サーバーB起動時のログ
```

##pingで監視して、障害時にはifconfig upで仮想IPを起動する

*node.jsは、nvmでインストール済みです。

```javascript:ping-ifup.js

#!/home/hoge/nvm/v0.8.3/bin/node

//Vars
var targetAddr = '192.168.1.10';
var cmdPing = '/bin/ping';
  var count = '3';      //pingパケット試行回数
  var interval = 10000; //監視間隔
  var aliveStr = '100% packet loss';
  var tid;
var cmdIfconfig = '/sbin/ifconfig';
  var nic = 'eth0:1';
  var ifcmd = 'up';

//Modules
var sys = require('util');
var spawn = require('child_process').spawn;

//Go!
main(targetAddr);

//ping process
function ping(targetAddr, callback) {
  clearInterval(tid);
  tid = setInterval(function(){
    var pingProcess = spawn(cmdPing, ['-c ' + count , targetAddr]);
    pingProcess.stdout.on('data', function (data) {
      var data = data.toString().split('\n').join();
      var isOk = 
        data.indexOf(aliveStr) === -1 ;
      var is = isOk ? ' is ok.' : ' is ng.';
      console.log('============================================');
      console.log('targetAddr ' + targetAddr + is);
      console.log('stdout: ' + data);
      if(isOk) {
        //targetAddr is alive. 正常 
      }
      else {
        clearInterval(tid);
        pingProcess.kill();
        callback(isOk);
      }
      
    });
    pingProcess.on('exit', function (code) {});
  },interval);
}

//ifconfig process
function ifconfigUpDown(targetAddr) {
  var ifup = spawn(cmdIfconfig, [nic, targetAddr, ifcmd]);
  ifup.stdout.on('data', function (data) {
    console.log('stdout: ' + data);
  });
  ifup.stderr.on('data', function (data) {
    console.log('stderr: ' + data);
  });
}

//do it!
function main(targetAddr) {
  ping(targetAddr, function (isOk) {
    ifconfigUpDown(targetAddr, isOk);
  });
}

```

【修正履歴】
2012/08/02
　　　　　'100% packet loss'じゃなければOK。に変更^^;微調整中。
2012/08/02
　　　　　clearTimeoutとclearInterval書き間違えてた(^^;
　　　　　'0% packet loss'は'1.0% packet loss'の場合もあるので' 0% packet loss'へ変更
2012/07/31
　　　　　少しでも早く障害把握したいと思って、'Unreachable'と'100% packet loss'で
　　　　　死亡確認してたけれど、pingの返事には'Request timed out'とか他にもいろいろ
　　　　　あるので、結局最初の'0% packet loss'がひとつも無いという確認に変更。
　　　　　早く確認したければ、pingパケット試行回数を減らす(精度とトレードオフ)とか他の手で。
　　　　　注：この生存確認文字列aliveStrは、マシンによっても異なると思います。
2012/07/30 
　　　　　pingによるUnreachable検出に数十秒かかってしまっていたので、
　　　　　ping -c 3 192.168.1.10 でループした方が検出速いんじゃないかな？ってことで
　　　　　少し書き換えたら少し速くなった。
2012/07/30 
　　　　　昨日は192.168.1.11をpingで監視したんだけれど、192.168.1.10を監視に変更。
　　　　　目的は192.168.1.10の監視なのに、192.168.1.10が死んで、192.168.1.11が
　　　　　生きてるというケースもあるので。


##マシン起動時用スクリプト

上記のping-ifup.js を下記のような起動時用スクリプトの中に書いて、待機サーバーB 192.168.1.12の /etc/rc.local に置くと起動時にrootとして実行してくれるのでifconfig upも実行できる。

kage-start.shのパーミッションは0705とか実行権限を与えておく。rc.localだけで動かすなら0700でも良いかもしれない。

log用ディレクトリは /var/log 以下でも良いけど、ここではプロジェクト内の /home/hoge/kagemusya/log へ書き込み可で用意しました。

```kage-start.sh
#!/bin/sh

#This is a Sub Server 192.168.1.12
#NAT IP eth0:1 192.168.1.10

#一応起動ログもとっとく
logFile=/home/hoge/kagemusya/log/kage-start-log.txt

echo "================================================" >> $logFile
date '+%Y/%m/%d %H:%M:%S.%N' >> $logFile
echo "${0}" >> $logFile
echo "i am $(whoami)" >> $logFile

#サーバーAの仮想IP 192.168.1.10を監視して、
#落ちてたら自分でeth0:1へ仮想IP 192.168.1.10を立ち上げるスクリプト
/home/hoge/nvm/v0.8.3/bin/node /home/hoge/nvm/v0.8.3/bin/forever start /home/hoge/kagemusya/ping-ifup.js
echo "start /home/hoge/kagemusya/ping-ifup.js" >> $logFile

#サーバーで動かしたいWebSocketアプリとか
/home/hoge/nvm/v0.8.3/bin/node /home/hoge/nvm/v0.8.3/bin/forever start /home/hoge/node/ws-app.js
echo "start /home/hoge/node/ws-app.js" >> $logFile
```


##rc.localへ設定する
これで、マシン起動時に上記のping監視が自動で立ち上がります。

```rc.local
#!/bin/sh -e

/home/hoge/kagemusya/kage-start.sh

exit 0
```

##今回作ったマシンのお会計です。

```text:マシン購入費
【サーバーA】
ベアボーンキット	Shuttle XH61V ¥16,800
メモリ	TRJ JM1333KSN-8GK(SODIMM DDR3 PC3-10600 4GBx2)　¥3,170
CPU	Intel Core i3-2120 BOX (1155/3.30/3M/C2/T4)　¥9,760
SSD OCZSSD2-1VTXPL60G　¥5,980
----------------------------------------------
合計 35,710円

【サーバーB】
ベアボーンキット	Shuttle XH61V ¥16,800
メモリ	TRJ JM1333KSN-8GK(SODIMM DDR3 PC3-10600 4GBx2)　¥3,170
CPU	Intel Celeron G540 BOX(1155/2.50/2M/C2/T2)　¥3,480
SSD SP060GBSSDV30S　¥4,870
----------------------------------------------
合計 28,320円

ちなみに、サーバーAとサーバーBをWebSocketのシンプルなレスポンスで比較すると同一
ネットワーク内でどちらも2～5ms、インターネット側の別のマシンからは、どちらも
10～15msということで、全然差はありませんでした。

まぁ、数千件の同時アクセスとかなら差が出るのかもしれませんが、そういう用途以外
ならサーバーBの構成でも十分という感じ。もし、お金をかけるなら回線側の方が効果あ
るかも？

ちなみに、私が現在業務で使ってるWebSocketマシンはAtomなので、サーバーBより安物
ですが、これも快適です^^。node.js & WebSocket 万歳!(^0^)/。
```