apcupsdで冗長構成を作る話


#背景
サーバー屋さんで最も数がでていると思われる2ソケットサーバーは大抵PSUが２個、ストレージサーバー等では３個の冗長電源構成になっていて、余程ハイスペックCPUやGPUを搭載してたりしない限り片肺での稼働も可能になっている。

サーバー搭載のPSU（電源ユニット）の故障までの冗長性を確保するのであれば、その上流のPDUやUPSは１系統でいいかもしれないが、できればブレーカから分けてUPSの故障などにも対応できるようにできないか？と考えるのは自然な要求だと思う。世の中には発電所や電力会社から分けたいなんて需要もあるようだけど、ここでは考えない。

![Diagram1.png](https://qiita-image-store.s3.amazonaws.com/0/53893/20933ab7-e3f3-62c7-d3d2-363192bbcd14.png)

フリーの[apcupsd](http://www.apcupsd.org/)を用いて、それを実現してみる。

#用意
<dl>
 <dt>OS 環境
 <dd>CentOS7.3.1611
 <dt>UPS
 <dd>Smart-UPS 3000 RM XL
 <dt>apcupsd
 <dd>apcupsd-3.14.12 (epel 由来）
 <dt>接続
 <dd>USBx2 (USB/シリアル併用も可能だが、最近のサーバーはシリアルを持ってないことが増えた)
</dl>

#実装

apcupsdは単独では複数のUPSを管理できないため、接続するUPSの数だけ設定を分けて作成し、それぞれapcupsdを立ちあげ、その上で停電時の処理スクリプト内でand条件やor条件を付ける必要がある。

###複数USBへの対応
USBケーブルを繋ぐとudevがhidとして認識してデバイスを作り、apcupsdは勝手にそれを認識するため、通常の１台のみ接続の場合`apcupsd.conf`では**DEVICE**欄を空欄にすることになっている。しかし、厳密に２台を区別したければ、また他のデバイスが同様な認識をする場合にも、*別名*を割り当てる必要がある。


ケーブルを刺した状態で`lsusb`コマンドを叩き、バスIDとデバイスナンバーを取得する。

```
[root@server ~]# lsusb
Bus 002 Device 002: ID 8087:8002 Intel Corp. 
......
Bus 003 Device 046: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
Bus 003 Device 047: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
```

これは接続されるたびに異なる番号になるので、接続した全てのUPSシリアル番号を取得してudevルールを作成する。

```
[root@server ~]# lsusb -v -s 003:046

Bus 003 Device 046: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x051d American Power Conversion   ←これと
  idProduct          0x0002 Uninterruptible Power Supply　　←これと
  bcdDevice            0.06
  iManufacturer           3 American Power Conversion
  iProduct                1 Smart-UPS 3000 RM XL FW:691.01.M USB FW:7.4
  iSerial                 2 AS170XXXXXXX   ←これを使う
  bNumConfigurations      1
```

取得した情報をもとに、`/etc/udev/rules.d/`に`30-apcups.conf`を作成し、以下のように記述する。

```
KERNEL=="hiddev*", ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0002", ,ATTRS{serial}=="AS170XXXXXXX", SYMLINK+="apcups2"
KERNEL=="hiddev*", ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0002", ,ATTRS{serial}=="AS170YYYYYYY", SYMLINK+="apcups1"

```

このファイルを作成後、ケーブルを抜き差しすると以下のようにリンクが作成される。

```
[root@server ~] ls -l /dev/apcups*
lrwxrwxrwx 1 root root 11  3月 17 19:26 /dev/apcups1 -> usb/hiddev1
lrwxrwxrwx 1 root root 11  3月 17 19:26 /dev/apcups2 -> usb/hiddev0
```

###監視設定
`/etc/apcupsd/apcupsd.conf`を`apcupsd-1.conf`, `apcupsd-2.conf` としてコピーして、**DEVICE**には作成した`/dev/apcups1`, `/dev/apcups2`をそれぞれ指定する。**UPSNAME**や**EVENTFILE**は分けておくこと。同様に**STATFILE**, **NISPORT**も必要であれば変更しておく。

`/usr/lib/systemd/system/apcupsd.service`を設定と同様 -1, -2 の２つにわけ、それぞれの設定ファイルを読み込むよう**ExecStart**を書き換える。

```
[Service]
ExecStartPre=-/bin/rm -f /etc/apcupsd/powerfail
ExecStart=/sbin/apcupsd -b -f /etc/apcupsd/apcupsd-1.conf
```
設定を読み込んで enable & start。

```
[root@server ~] systemctl daemon-reload
[root@server ~] systemctl enable apcupsd-1; systemctl enable apcupsd-2
[root@server ~] systemctl start apcupsd-1; systemctl start apcupsd-2
```

#ToDo
今はどっちが落ちてもシャットダウンする「OR条件」で動作することになる。これを、シャットダウンスクリプト内で互いの生死を確認して、一方が生きていればシャットダウンしない「AND条件」でシャットダウンするようにする。