# nginx-nlu-lab — nginx `resolve` "no live upstreams" 재현·검증 실험 nginx 1.28.0 `server ... resolve` + GSLB 단일 A 레코드 교번 환경에서 "no live upstreams" 502의 실제 발생 경로를 실증하는 실험 환경. 결과 판정은 `REPORT.md`, 원본 데이터는 `artifacts/` 참조. ## 요구사항 - Docker + compose v2, 인터넷 (이미지 pull + nginx 소스 tarball) - GPU 불필요. 전체 소요 약 60–90분 (P1/P6이 각 10분 부하) ## 토폴로지 ``` network: repro-net 172.20.0.0/24 +-----------+ +-----------+ +--------------------+ | loadgen | --> | nginx | --> | backend1 (.20:8080)| | (vegeta) | | (.10:80) | | backend2 (.30:8080)| | (.100) | +-----------+ +--------------------+ +-----------+ | v resolver (valid=5s) +-----------+ | dns (.53) | <-- state/mode 파일로 응답 제어 +-----------+ ``` - `dns`: 커스텀 UDP 응답기 (`dns/server.py`). 매 질의마다 `state/mode`를 읽어 `static:` / `alternate:,` / `multi:` / `nxdomain` / `timeout` 동작. 모든 질의를 `state/dns.log`에 기록 (` `), TTL=1. `dc1.test.local→172.20.0.20`, `dc2.test.local→172.20.0.30`은 모드 무관 고정. - `backend1/2`: asyncio HTTP 서버, 200 + 자기 hostname 응답. NET_ADMIN + iptables (RST 주입: `iptables -A INPUT -p tcp --dport 8080 -j REJECT --reject-with tcp-reset`) - `nginx`: 공식 `nginx:1.28.0`. config는 `conf/active.conf`(= 변형 파일 복사본). **변형 적용은 reload가 아니라 restart** (shared zone peer 상태 초기화 보장) - `loadgen`: vegeta. 결과는 `artifacts//results.bin` + `vegeta-report.txt` ## config 변형 (`conf/`) | 파일 | 내용 | |---|---| | `base.conf` | `valid=5s`, `server app.test.local:8080 resolve;` 1줄 | | `v-valid30.conf` | base + `valid=30s` | | `v-dual.conf` | 동일 resolve server 라인 2개 (peer≥2 → single=0 → max_fails 무장) | | `v-dual-mf0.conf` | v-dual + 두 라인 모두 `max_fails=0` | | `v-perdc.conf` | `dc1/dc2.test.local` 2-hostname + `max_fails=2 fail_timeout=5s` | | `v-shim.conf` | base + `error_page 502 = @retry_once` (재시도 shim 반증용) | ## 실행 순서 (전체 재현) ```bash cd nginx-nlu-lab cp conf/base.conf conf/active.conf docker compose build && docker compose up -d curl http://172.20.0.10/ # backend1 응답 확인 bash scripts/p1.sh # baseline: alternate 500rps x 10min -> 502_nlu=0 기대 bash scripts/p2.sh # NXDOMAIN 6s 주입 x3 (base / valid30-same / valid30-hit) bash scripts/p3.sh # resolver timeout 10s -> 502=0 기대 bash scripts/p4.sh # RST 2s x3 (v-dual / v-dual-mf0 / base) -> max_fails tail 검증 bash scripts/p5.sh # failover RTO 3회 x (valid5 / valid30) bash scripts/p6.sh # per-DC 2-hostname: 10min 부하 + RST 5s failover bash scripts/p7.sh # (선택) slab churn 프로브 + retry shim 반증 ``` 각 스크립트는 종료 시 `artifacts//analysis.txt`를 출력함. 페이즈별 산출물: `access.log`, `error.log`, `dns.log`, `results.bin`, `vegeta-report.txt`, `window.txt`(측정 구간 epoch), `analysis.txt`, `verdict.md` ## 계측 규약 - 모든 조작은 `artifacts/events.log`에 epoch 기록 (`MODE`/`CONF`/`RST_ON`/`RST_OFF`/`PHASE_*`) - nginx access log: `$msec $status $upstream_addr $upstream_status $request_time` - 분류: `502_nlu` = 502이고 `$upstream_addr`에 그룹명 `be`가 등장 (no live upstreams), `502_conn` = 502이고 IP:port만 등장 (connect 실패형) - `scripts/analyze.py`: access.log + dns.log + events.log를 epoch 기준 join, 502 분류·최장 연속 502 구간·DNS 스왑 횟수·재질의 주기 실측 출력 ## 주의 - 서브넷 172.20.0.0/24가 기존 docker 네트워크와 겹치면 compose가 실패함 → 겹치면 수정 - 정리: `docker compose down` (이미지까지: `docker compose down --rmi local`)