RHMS 이중화 구축 보고: Secondary(hb5u) 배포 + HF_HUB_OFFLINE 정책화 + 폴백 클라이언트
초록
EC2 단일 RHMS 구조의 단일 장애점을 해소하기 위해 hb5u(RTX 5060)에 Secondary RHMS를 구축하고, 양 노드에 HF_HUB_OFFLINE=1을 적용하여 DNS 장애 재발을 차단하였다. EC2→hb5u 자동 폴백 HTTP 클라이언트(rhms_http_client.py) 신설. 폴백 검증 완료(5초 이내 전환).
1. 배경
2026-07-21 RHMS 메모리 폭증 분석(논문 2026-07-21-eos-rhms-memory-bloat-analysis) 이후, 사령관 지시에 따라 다음 두 가지 조치를 병행 실시하였다:
- 재발 방지: EC2 RHMS에
HF_HUB_OFFLINE=1적용 — DNS 의존성 제거 - 단일 장애점 해소: hb5u(RTX 5060)에 Secondary RHMS 구축 + EC2 폴백 클라이언트 신설
2. 아키텍처
[에이전트]
│
▼
rhms_http_client.py
├─ Primary → EC2 127.0.0.1:8090 (응답 시 사용)
│ HF_HUB_OFFLINE=1 ✅
└─ Secondary → hb5u 100.125.27.70:8090 (Primary 실패 시 자동 폴백)
HF_HUB_OFFLINE=1 ✅
타임아웃 _TIMEOUT = 5s. Primary 응답 없으면 Secondary로 즉시 재시도. 두 엔드포인트 모두 동일한 RHMS_KEY_* 인증.
3. Secondary RHMS 구축 절차
3.1 환경 확인
| 항목 | hb5u 값 |
|---|---|
| OS | Ubuntu Linux 6.17.0 |
| Python | 3.13.7 |
| GPU | RTX 5060 |
| 가용 디스크 | 540 GB |
| Tailscale IP | 100.125.27.70 |
| EC2 → hb5u RTT | 6~78 ms |
기존 설치: fastapi 0.138.2, numpy 2.4.6, uvicorn 0.49.0 ✅
3.2 추가 설치
python3 -m pip install --break-system-packages \
sentence-transformers python-dotenv langdetect
# torch 2.13.0 의존성으로 함께 설치됨
3.3 소스 배포 및 경로 패치
EC2에서 rsync 대신 scp로 app.py 전송 후, hb5u 환경에 맞게 경로 수정:
scp ~/hyperbook/services/rhms/app.py moos@100.125.27.70:~/rhms/
ssh moos@100.125.27.70 "
sed -i 's|~/hyperbook/.env|~/rhms/.env|g' ~/rhms/app.py
sed -i 's|~/hyperbook/services/rhms/rhms.db|~/rhms/rhms.db|g' ~/rhms/app.py
"
3.4 .env 구성
EC2 .env의 RHMS_KEY_* 값을 hb5u ~/rhms/.env에 복사. HF_HUB_OFFLINE=1 포함:
RHMS_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
RHMS_MULTILINGUAL=true
HF_HUB_OFFLINE=1
RHMS_KEY_EOS=<EC2와 동일>
...
3.5 모델 캐시 생성 (최초 1회)
nohup python3 -c "
from sentence_transformers import SentenceTransformer
SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
" > ~/rhms/model_download.log 2>&1 &
완료 후 HF_HUB_OFFLINE=1 고정 — 이후 인터넷 불필요.
3.6 systemd user 서비스
EC2와 달리 hb5u에서 moos 계정은 sudo 없음 → user-level systemd 사용:
mkdir -p ~/.config/systemd/user/
cat > ~/.config/systemd/user/rhms.service << 'EOF'
[Unit]
Description=ROOPS RHMS Secondary — Hopfield Memory System (hb5u)
After=network.target
[Service]
WorkingDirectory=/home/moos/rhms
EnvironmentFile=/home/moos/rhms/.env
Environment=HF_HUB_OFFLINE=1
Environment=TRANSFORMERS_OFFLINE=1
ExecStart=/usr/bin/python3 -m uvicorn app:app --host 0.0.0.0 --port 8090 --workers 1
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
EOF
loginctl enable-linger moos # 로그인 없이 서비스 유지
systemctl --user daemon-reload
systemctl --user enable rhms
systemctl --user start rhms
4. EC2 HF_HUB_OFFLINE 적용
EC2 ~/hyperbook/.env에 두 줄 추가:
HF_HUB_OFFLINE=1
TRANSFORMERS_OFFLINE=1
rhms.service EnvironmentFile이 이 파일을 로드하므로 재시작만으로 즉시 적용. sudo systemctl restart rhms.service 후 헬스체크 ✅.
이로써 Tailscale MagicDNS 재활성화 시 발생하던 huggingface.co SERVFAIL → 무한 재시도 루프 → 메모리 폭증 트리거 체인이 구조적으로 차단된다.
5. 폴백 클라이언트 (rhms_http_client.py)
agents/eos/rhms_http_client.py (커밋 2a41178):
_PRIMARY = "http://127.0.0.1:8090"
_SECONDARY = "http://100.125.27.70:8090"
_TIMEOUT = 5
def _request(self, method, path, body=None):
for base in (_PRIMARY, _SECONDARY):
try:
...
return json.loads(resp.read())
except Exception as e:
if base == _SECONDARY:
raise RuntimeError(f"primary·secondary 모두 실패: {e}")
폴백 동작 검증 — _PRIMARY를 존재하지 않는 포트(9999)로 패치 후 테스트:
active (primary 다운 시뮬): http://100.125.27.70:8090
health via secondary: ok
6. 검증 결과
| 항목 | EC2 Primary | hb5u Secondary |
|---|---|---|
| health | ✅ ok | ✅ ok |
| store (한국어) | ✅ dim=384 | ✅ dim=384 |
| recall | ✅ | ✅ score 0.68 |
| HF_HUB_OFFLINE | ✅ 1 | ✅ 1 |
| RAM 사용 | ~514 MB | ~2.1 GB (GPU 적재) |
| 폴백 테스트 | — | ✅ primary 5s 내 전환 |
hb5u의 RAM 사용(2.1 GB)은 PyTorch가 RTX 5060 VRAM에도 모델을 적재하기 때문이며, 추론 속도는 CPU-only EC2보다 빠를 것으로 예상된다.
7. 결론
EC2 단일 RHMS에서 Primary(EC2) + Secondary(hb5u) 이중화 구조로 전환 완료. DNS 장애·메모리 폭증·인스턴스 재시작 등 EC2 이벤트 발생 시 5초 내 hb5u로 자동 전환된다. HF_HUB_OFFLINE=1 정책화로 Tailscale MagicDNS 연동 장애의 근본 원인을 양쪽 노드에서 동시에 차단하였다.
제출: EOS — EC2 상주 에이전트 / 2026-07-21
