0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

30Wで実現する金融級自律型HomeLab SREインフラ:Keepalived・PG18物理レプリケーション・WoLオンデマンド・コンピュート設計

0
Posted at

概要:なぜ今、自宅SREインフラなのか?

クラウドサービスの利用料高騰やアカウント突然死リスクを背景に、オンプレミス・自前インフラへの回帰(Repatriation)が注目されています。

本稿では、一般家庭のリビングルームにおいて、 常時消費電力わずか30W を維持しながら、エンタープライズ金融システムと同等の可用性(RPO<1秒, RTO<10分)を実現する 「HomeLab SREアーキテクチャ」 の設計と運用基準をまとめます。


1. 物理アーキテクチャとハードウェア仕様

【前提:推奨購入構成ではなく、手元の遊休機材の利活用です】
ここに示すマシン型番(N100、TH50、Pi4B等)は、 購入を推奨する構成ではありません 。筆者の手元に余っていた旧機材を廃棄せず活用した結果であり、読者の皆様がお持ちの余剰PCや旧型ノートパソコンでも本設計(主系・従系・独立監視の役割分担)をそのまま再現可能です。

安定性と静音性、極限の省電力を両立するため、以下の3ノード構成(Golden Trio)を採用しています(実測パラメータはコードベースの inventory.ini に準拠)。

ノード名 搭載ハードウェア 常態消費電力 SRE責務と主要ミドルウェア
Primary Master (n100) NiPoGi N100 / 8GB RAM / 256GB NVMe + 1TB WD SSD (192.168.3.8) ~8.5W-10.5W Keepalived Master, PostgreSQL 18 (RW: 5432), Gitea, Redmine, MinIO, n8n
Secondary Compute (th50) Minisforum TH50 i5-11320H / 16GB RAM / 512GB NVMe (192.168.3.9) ~14W-16W Keepalived Standby, PostgreSQL 18 (RO Standby: 5432), Dify AI, CI Runner
Monitoring Sentinel (pi4b) Raspberry Pi 4B (ARM) / 4GB RAM / 64GB SD / 外付USB 3.0 RAID1 (東芝 2TB×2) (192.168.3.2) ~3W 独立監視(Uptime Kuma 3001&Slack通知), Kopia引取型バックアップ (/mnt/backup-hdd), WoL起動
合計常時電力 N100(~10W) + i5(~15W) + Pi4B(~3W) + 外付RAID(~2W) ~30W (実測27~31W) ※手元の遊休機材を徹底活用、月額電気代 670円前後に完全抑制

【SRE設計思想:家庭・小規模環境における現実的判断】
万能のアーキテクチャは存在しません。エンタープライズの完全自動フェイルオーバーは二重系統電源とハードウェアSTONITH電源遮断が前提です。これらを持たない家庭内環境で完全自動クラスタを盲信すると、ネットワークの瞬断でスプリットブレインやフラッピングを誘発します。「独立監視ノードからの即時Slackアラート+確定的な迅速スクリプト復旧」こそが最も堅牢です。※UPSやスマートPDU導入後のQuorum Witness自動仲裁化へのアップグレードパスは第2方向にて詳述します。

Golden Trio 核心3機物理&高可用ネットワーク構成図


2. ネットワーク設計と非対称ルーティング対策

Keepalivedによる仮想IP(VIP: 192.168.3.200)運用時、複数のNICを持つ物理マシンでは 「非対称ルーティングによるサイレントパケットドロップ」 が頻発します。

カーネルパラメータの必須設定 (/etc/sysctl.d/99-sre-ha.conf)

# 非ローカルIPバインドの許可 (Keepalived VIPが未割り当ての状態でもHAProxyを安全に起動するため)
net.ipv4.ip_nonlocal_bind = 1

# リバースパスフィルタリングの緩和 (非対称ルーティングによるパケット破棄を防止)
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2

# TCP SYN Cookie の有効化 (SYN Flood防御)
net.ipv4.tcp_syncookies = 1

上記を適用後、sudo sysctl -p /etc/sysctl.d/99-sre-ha.conf を実行して反映します。


3. PostgreSQL 18 物理ストリーミングレプリケーション構成

論理レプリケーションではなく、バイナリWALをそのまま同期する非同期物理ストリーミングを採用。

従系側(i5)では primary_conninfo を設定し、standby.signal を配置。主系ダウン時は pg_ctl promote または Keepalived のフェイルオーバースクリプトにより自動昇格します。


4. オンデマンド異種GPUノードのWoL自動起動

大容量LLM(Ollama)や画像処理など高負荷タスクが発生した場合のみ、 Xeon D-1581 または i7-9700F + RTX 4060 搭載の拡張ノードをWake-on-LANでオンデマンド起動します。

# Raspberry Pi 4B監視ノードからGPUノードを起動するコマンド
wakeonlan 00:11:22:33:44:55

# HAProxy ランタイムソケットによる重み付け更新 (GPUノード活性化)
echo "set server backend_ai/gpu_node weight 100" | sudo socat stdio /var/run/haproxy.sock

タスク完了後は自律監視スクリプトがCPU/GPU負荷を監視し、15分間アイドルが続いた場合にS3ステート(2〜3W待機)へ降下させます。


まとめ

本設計により、月額数百ドル〜千ドル以上のクラウド請求を発生させることなく、堅牢かつ自律的なSRE運用を家庭内で完結させることが可能です。

全125章にわたる連載の次章では、 Intel N100とRaspberry Pi 4Bの物理インターフェース選定と電源ノイズ対策 を深掘りします。


付録:ノード基盤ヘルスチェック完全ソースコード(コピー&ペーストで即実行)

付録 A:ノード健全性&SRE不変条件検証スクリプト (cluster-health-check.sh)

実行手順 :以下のコードをコピーして cluster-health-check.sh として保存し、chmod +x cluster-health-check.sh && ./cluster-health-check.sh --verbose を実行します。

#!/usr/bin/env bash
# ==============================================================================
# HomeLab SRE Cookbook - Node Baseline SRE Health Check (Read-Only)
# Target: Golden Trio Hardware Nodes (NiPoGi N100, Minisforum TH50, Raspberry Pi 4B)
# Usage: ./cluster-health-check.sh [--verbose] [--strict]
# Principle: Zero Silent Failures — Non-zero exit code on critical invariant breaches.
# ==============================================================================
set -euo pipefail

VERBOSE=0
STRICT=0
for arg in "$@"; do
  case "${arg}" in
    --verbose) VERBOSE=1 ;;
    --strict)  STRICT=1 ;;
  esac
done

ERRORS_COUNT=0
WARNINGS_COUNT=0

echo "========================================================================"
echo " [HomeLab SRE] Golden Trio Node Baseline Health Check"
echo " Time: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo "========================================================================"

# 1. Check Local CPU, Memory, and Disk Pressure
printf "%s" "[1/4] Checking Local Host Resources... "
LOAD_1M=$(cat /proc/loadavg 2>/dev/null | awk '{print $1}' || true)
[[ -z "${LOAD_1M}" ]] && LOAD_1M="N/A"

MEM_FREE_MB=$(free -m 2>/dev/null | awk '/^Mem:/{print $7}' || true)
[[ -z "${MEM_FREE_MB}" ]] && MEM_FREE_MB="N/A"

# Portable POSIX df parsing robust against spaces in mount points or LVM line wrapping
DISK_USAGE_PCT=$(df -Pk / 2>/dev/null | awk 'NR>1 {for(i=1;i<=NF;i++) if($i ~ /^[0-9]+%$/) {sub(/%/,"",$i); print $i; exit}}' || true)
if [[ -z "${DISK_USAGE_PCT}" || ! "${DISK_USAGE_PCT}" =~ ^[0-9]+$ ]]; then
  DISK_USAGE_PCT=0
fi

if [[ "${DISK_USAGE_PCT}" -ge 95 ]]; then
  echo "CRITICAL (Root disk usage ${DISK_USAGE_PCT}% >= 95% - Outage Imminent)"
  ERRORS_COUNT=$((ERRORS_COUNT + 1))
elif [[ "${DISK_USAGE_PCT}" -ge 90 ]]; then
  echo "WARNING (Root disk usage ${DISK_USAGE_PCT}% >= 90%)"
  WARNINGS_COUNT=$((WARNINGS_COUNT + 1))
else
  echo "OK (Load: ${LOAD_1M}, Free Mem: ${MEM_FREE_MB}MB, Root Disk: ${DISK_USAGE_PCT}%)"
fi

# 2. Check Key Network & Kernel Tunables (rp_filter, ip_nonlocal_bind)
printf "%s" "[2/4] Checking Essential SRE Kernel Parameters... "
RP_FILTER_ALL=$(sysctl -n net.ipv4.conf.all.rp_filter 2>/dev/null || true)
[[ -z "${RP_FILTER_ALL}" ]] && RP_FILTER_ALL="N/A"

RP_FILTER_DEF=$(sysctl -n net.ipv4.conf.default.rp_filter 2>/dev/null || true)
[[ -z "${RP_FILTER_DEF}" ]] && RP_FILTER_DEF="N/A"

NONLOCAL_BIND=$(sysctl -n net.ipv4.ip_nonlocal_bind 2>/dev/null || true)
[[ -z "${NONLOCAL_BIND}" ]] && NONLOCAL_BIND="N/A"

RP_STATUS="COMPLIANT"
if [[ "${RP_FILTER_ALL}" != "2" || "${RP_FILTER_DEF}" != "2" ]]; then
  RP_STATUS="WARNING (all=${RP_FILTER_ALL}, default=${RP_FILTER_DEF}; recommend 2 for dual-NIC asymmetric routing)"
  WARNINGS_COUNT=$((WARNINGS_COUNT + 1))
fi

BIND_STATUS="COMPLIANT"
if [[ "${NONLOCAL_BIND}" != "1" ]]; then
  BIND_STATUS="WARNING (ip_nonlocal_bind=${NONLOCAL_BIND}; required 1 for Keepalived VIP dynamic socket binding)"
  WARNINGS_COUNT=$((WARNINGS_COUNT + 1))
fi

echo "DONE"
echo "      -> Reverse Path Filtering: ${RP_STATUS}"
echo "      -> Non-local Socket Binding: ${BIND_STATUS}"

# 3. Check Core High Availability Port Availability
printf "%s" "[3/4] Checking Core Ingress & Database Listening Sockets... "
SS_OUTPUT=$(ss -tuln 2>/dev/null || netstat -tuln 2>/dev/null || true)
# Monitored ports: PG(5432), Web(80,443), Grafana(3000), UptimeKuma(3001), ChromaDB(8000), Dify(8080), Traefik(8443), MinIO(9000), Ollama(11434)
PORTS_TO_CHECK=(5432 80 443 3000 3001 8000 8080 8443 9000 11434)
ACTIVE_PORTS=()

for port in "${PORTS_TO_CHECK[@]}"; do
  # Avoid pipeline pipefail / SIGPIPE with pre-captured SS_OUTPUT
  if echo "${SS_OUTPUT}" | grep -E -q "[:.]${port}\b"; then
    ACTIVE_PORTS+=("${port}")
    if [[ ${VERBOSE} -eq 1 ]]; then
      echo "      -> Socket [:${port}]: ACTIVE"
    fi
  fi
done

LISTENING_COUNT=${#ACTIVE_PORTS[@]}
echo "DONE (${LISTENING_COUNT}/${#PORTS_TO_CHECK[@]} monitored sockets active: ${ACTIVE_PORTS[*]:-none})"

# In production, at least one core SRE socket (DB, Web, or Container) must be listening
if [[ ${LISTENING_COUNT} -eq 0 ]]; then
  echo "      [NOTICE] No core service sockets currently listening on this local host."
  WARNINGS_COUNT=$((WARNINGS_COUNT + 1))
fi

# In strict mode, promote any parameter warnings to critical errors (Zero Silent Failures)
if [[ ${STRICT} -eq 1 && ${WARNINGS_COUNT} -gt 0 ]]; then
  echo " [STRICT MODE] Promoting ${WARNINGS_COUNT} warnings to critical failures."
  ERRORS_COUNT=$((ERRORS_COUNT + WARNINGS_COUNT))
fi

# 4. Overall Node Readiness Verdict (Anti-Silent-Failure Enforcement)
echo "========================================================================"
if [[ ${ERRORS_COUNT} -gt 0 ]]; then
  echo " [VERDICT: FAILED] ${ERRORS_COUNT} critical errors detected. Node NOT ready for production."
  echo "========================================================================"
  exit 1
elif [[ ${WARNINGS_COUNT} -gt 0 ]]; then
  echo " [VERDICT: PASSED WITH WARNINGS] ${WARNINGS_COUNT} warnings detected. Review kernel/socket tunables."
  echo "========================================================================"
  exit 0
else
  echo " [VERDICT: PASSED] All node baseline invariants satisfied. Ready for SRE state machine."
  echo "========================================================================"
  exit 0
fi

付録 B:最小限の検証用 Docker Compose 構成 (docker-compose.infra.yml)

実行手順 :以下のコードをコピーして docker-compose.infra.yml として保存し、docker compose -f docker-compose.infra.yml up -d で起動します。

services:
  # HomeLab SRE Baseline Verification Stack
  # Lightweight node exporter for baseline CPU/Mem/Disk telemetry (approx 15MB RAM)
  node-exporter:
    image: prom/node-exporter:v1.8.2
    container_name: homelab-node-exporter
    restart: unless-stopped
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--path.rootfs=/rootfs'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    network_mode: host
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro

  # Ingress verification dummy service (mapped to non-conflicting port 8088 to avoid Dify 8080)
  ingress-probe:
    image: nginx:1.27-alpine
    container_name: homelab-ingress-probe
    restart: unless-stopped
    ports:
      - "127.0.0.1:8088:80"
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1/ || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?