”逸般の誤家庭”への一歩を進める自宅サーバUPS導入

UPS導入と監視環境整備で予期せぬ電源断を回避する。

”逸般の誤家庭”への一歩を進める自宅サーバUPS導入

概要

自宅サーバーを運用する上で、避けて通れないリスクが停電や瞬停。特にデータベースやストレージを抱えるRaspberry Piにとって、予期せぬ電源断はデータの致命傷になりかねない。

過去にたった数回のブレーカー落ちが原因だったのか、起動しなくなった苦い思い出もある。(MicroSDからHDDに変えた)

このリスクを根本解決するため、定番のUPSであるAPC RS 550S (BR550S-JP)を導入した。本記事では、UPSの使用感と監視環境の整備について取り上げる。

結論

今回の構築により、UPSの状態はすべてGrafana上で可視化され、単に電源を守るだけでなく、「今どれくらいの電力を使っているか」「あと何分耐えられるか」がリアルタイムで把握できる環境となった。

また、異常時には即座にDiscordへ通知が飛ぶ環境が完成した。電源断が長期にわたる場合はサーバのシャットダウンの対応が可能となる。

システム構成

今回UPSに接続し、バックアップ対象としたデバイスの詳細は以下の通り。

  • サーバー: Raspberry Pi 4 Model B (8GB)
  • 接続デバイス:
    • Raspberry Pi 4
    • USBハブ(セルフパワー)
    • ホームルーター (TP-Link Deco)
    • ONU (NTT)
    • AirMac TimeCapsule (HDD)
  • 負荷状況: 合計約30W
  • 期待稼働時間: フル充電状態で約46min

詳細

今回はDocker Composeによる管理とした。OSに直接パッケージをインストールするのではなく、設定ファイルごとDockerコンテナに封じ込めることで、環境の移行や再構築を容易にしている。

また、ホストから分離されているため環境を汚さず、設定ファイルがすべてこのディレクトリに集約されるのもメリット。

ディレクトリ構造

.
├── docker-compose.yaml
├── Dockerfile.apcupsd
└── Dockerfile.exporter

Dockerfile.apcupsd

apcupsdをAlpine Linuxベースで軽量に構築した。設定ファイルはprintfでコンテナビルド時に生成するスタイルを採っている。

FROM alpine:latest
RUN apk add --no-cache apcupsd

RUN printf "## apcupsd.conf v1.1 ##\nUPSTYPE usb\nDEVICE /dev/usb/hiddev0\nUPSNAME APC_RS_550S\nNISIP 0.0.0.0\n" > /etc/apcupsd/apcupsd.conf

EXPOSE 3551
ENTRYPOINT ["apcupsd", "-f", "/etc/apcupsd/apcupsd.conf", "-P", "/var/run/apcupsd.pid", "-b"]

Dockerfile.exporter

Prometheus形式でデータを出力するためのエクスポーターだ。マルチステージビルドにより、実行用イメージにはGoの実行環境を含めず、バイナリのみを配置している。

FROM golang:alpine AS builder
RUN go install github.com/mdlayher/apcupsd_exporter/cmd/apcupsd_exporter@latest

FROM alpine:latest
COPY --from=builder /go/bin/apcupsd_exporter /usr/local/bin/
ENTRYPOINT ["apcupsd_exporter"]

docker-compose.yaml

ホスト側のUSBデバイス(/dev/usb/hiddev0)をコンテナにマウントし、privileged: trueを与えることでUPSとの通信を可能にしている。

services:
  apcupsd:
    build:
      context: .
      dockerfile: Dockerfile.apcupsd
    container_name: apcupsd
    privileged: true
    devices:
      - "/dev/usb/hiddev0:/dev/usb/hiddev0"
    networks:
      - proxy-nw
    restart: always

  apcupsd-exporter:
    build:
      context: .
      dockerfile: Dockerfile.exporter
    container_name: apcupsd-exporter
    command: ["-apcupsd.addr=apcupsd:3551"]
    depends_on:
      - apcupsd
    networks:
      - proxy-nw
    restart: always
    expose:
      - 9162

networks:
  proxy-nw:
    external: true

別プロジェクトのDocker Composeで管理されているGrafanaやPrometheusを含む監視コンポーネントと同じネットワークに属するため、proxy-nw を定義している。

# prometheus.ymlサンプル
  - job_name: 'apcupsd'
    scrape_interval: 15s
    static_configs:
      - targets: ['apcupsd-exporter:9162']

運用と検証

可視化にはGrafanaの既存ダッシュボード(ID: 12285)を採用した。また、異常を検知するためにDiscord通知を設定している。

APC UPS Dashboard | Grafana Labs

Discord通知

GrafanaのAlerting機能を使用。UPSの状態がOnline以外になった瞬間にDiscordへ通知が飛ぶように設定した。これにより、外出先でも停電の発生を知ることができる。

実測検証:コンセントを抜いてみた

実際にUPSの電源プラグを抜き、挙動を確認した。

  • 検知速度: プラグを抜いた数秒後にはDiscordに通知が到達。
  • 稼働時間: 現在の構成(約30W負荷)において、ダッシュボード上の予測では約35分以上の維持が可能であることが判明した。これだけの時間があれば、安全なシャットダウンシーケンスを走らせるには十分すぎる。

復帰後、再度オンラインになりバッテリーが充電されていることがわかる。

まとめ

UPSを導入して監視環境を整えたことで、停電に対する漠然とした不安が解消された。

稀に瞬断も起きていてUPSがバッテリー稼働になっていることもあったようなので、UPS導入が有効だったと感じられる。