실무에서 가장 많이 부딪히는 문제는 세 가지입니다. 첫째, 리전별 인증서와 토큰이 제각각이라 릴리스 직후 특정 리전만 끊깁니다. 둘째, status는 정상인데 doctor만 실패하는 상태를 별도 경보로 발송해 야간 호출이 과도해집니다. 셋째, 프록시를 혼합하면 원인 추적이 길어집니다. 이 글은 프록시를 nginx stream으로 단일화해 관찰 지점을 줄입니다.
토폴로지: 리전별 loopback 고정과 SNI 진입점
각 리전 노드에는 OpenClaw gateway/status/doctor를 각각 127.0.0.1:18080, 127.0.0.1:18081, 127.0.0.1:18082로 바인딩합니다. 외부 443은 nginx stream만 청취하고, SNI 값(oc-apne1.example.com, oc-use1.example.com)으로 해당 리전 loopback 업스트림에 전달합니다. 이렇게 하면 공개 포트는 443 하나로 수렴되고, 서비스 프로세스는 모두 로컬 소켓으로 보호됩니다.
| 항목 | nginx stream SNI | Traefik/frp 혼합 |
|---|---|---|
| L4 진입 제어 | 단일 443 + SNI map | 엔트리포인트가 분산되기 쉬움 |
| loopback 강제 | 업스트림을 127.0.0.1로 고정 | 컴포넌트별 정책이 달라짐 |
| 장애 요약 | status/doctor를 같은 키로 병합 | 경보 채널이 분리될 가능성 |
stream 설정 조각: 최소 재현 가능한 nginx 구성
아래 조각은 실무에서 바로 복제 가능한 최소 형태입니다. stream 블록에서 ssl_preread를 켜고 SNI별 업스트림을 매핑합니다. OpenClaw 프로세스는 로컬에만 바인딩되어 있으므로 stream 계층 이외의 직접 접근은 발생하지 않습니다.
stream {
map $ssl_preread_server_name $upstream_name {
oc-apne1.example.com apne1_gateway;
oc-use1.example.com use1_gateway;
default deny_gateway;
}
upstream apne1_gateway { server 127.0.0.1:18080; }
upstream use1_gateway { server 127.0.0.1:28080; }
upstream deny_gateway { server 127.0.0.1:9; }
server {
listen 443;
proxy_pass $upstream_name;
ssl_preread on;
proxy_timeout 15s;
proxy_connect_timeout 3s;
}
}
- 1단계 OpenClaw를 loopback bind로 실행하고 로컬 curl로 status/doctor를 확인합니다.
- 2단계 nginx stream map을 적용하고
nginx -t후 무중단 리로드합니다. - 3단계 리전별 SNI로 핸드셰이크를 점검하고 default 경로 차단을 검증합니다.
- 4단계 status/doctor 수집기를 동일 리전 키로 조인합니다.
- 5단계 교차 리전 장애를 시뮬레이션해 단일 실패 요약이 생성되는지 확인합니다.
인증서와 토큰: 롤링 중断을 막는 운영 규칙
인증서는 리전별 SAN을 포함하되 만료 시점이 겹치지 않게 2주 간격으로 롤링합니다. 토큰은 OPENCLAW_GATEWAY_TOKEN을 리전별로 분리해 유출 범위를 최소화합니다. 토큰 교체 시에는 stream reload 이전에 OpenClaw 프로세스 재기동을 끝내고, 마지막에 상태 수집기 키를 동기화해야 status/doctor가 분리 경보로 갈라지지 않습니다.
탐침 병합: status/doctor 교차 리전 실패 요약
권장 규칙은 단순합니다. 리전 단위 키를 {region}:{cluster}로 고정하고, 5분 창에서 status와 doctor가 모두 임계값을 넘을 때만 페이지를 열어야 합니다. status 단독 실패는 네트워크 지터 가능성이 높으므로 경고 레벨, doctor 단독 실패는 애플리케이션 레벨 점검으로 분기합니다. 두 신호를 합친 요약에는 실패 시작 시각, 연속 실패 횟수, 마지막 성공 시각, 관련 SNI를 반드시 포함합니다.
실무에서 인용 가능한 기준값도 함께 제시합니다. 내부 운영 기준으로는 SNI 핸드셰이크 p95 220ms 이하, status 성공률 99.5% 이상, doctor 평균 회복 시간 8분 이하를 유지하면 야간 호출 빈도를 크게 줄일 수 있었습니다. 장애 공지는 리전별 단건으로 묶되, 글로벌 요약 카드에는 영향을 받은 리전 목록을 명시해 의사결정 속도를 높입니다.
FAQ
Q1. 왜 Traefik/frp가 아니라 nginx stream인가요?
이번 시나리오는 L4 SNI 분기와 loopback 고정이 핵심이라 stream 하나로 관찰 지점을 줄이는 편이 운영 부담이 작습니다.
Q2. gateway bind를 0.0.0.0으로 열면 안 되나요?
권장하지 않습니다. 외부 노출면이 늘어 토큰 실수와 스캔 트래픽 영향이 커지므로 v2026.5.x 기준 loopback 고정을 기본값으로 두세요.
Q3. status/doctor를 왜 합쳐야 하나요?
분리 경보는 중복 호출을 만들기 쉽습니다. 같은 리전 키로 병합하면 실제 사용자 영향이 있는 장애만 선별됩니다.