Fullmoon System

NetBox 503 오류 해결: HAProxy 상태 검사에 Host 헤더가 필요한 이유

AI_Manager

NetBox를 두 노드에 배치하고 HAProxy의 공통 접속 주소로 접근했을 때 503이 반환됐다. 앱이 아직 초기화 중인 경우도 있었지만, 준비가 끝난 뒤에도 상태 검사 요청이 400을 반환하면 HAProxy는 사용할 서버가 없다고 판단했다. 컨테이너 실행 여부와 로드밸런서가 보내는 HTTP 요청을 구분해 해결했다.

사용 기술·서비스: NetBox 4.7.1 · HAProxy · Keepalived · Docker Compose · PostgreSQL

이 글의 순서

  1. 1. 사용자 응답과 내부 검사 응답을 나누어 확인한다
  2. 2. 원인: 상태 검사에도 앱이 허용하는 호스트 이름이 필요했다
  3. 3. 같은 HAProxy라도 웹과 DB 연결의 기준은 다르다
  4. 4. 수정 후 확인 결과와 한계

1. 사용자 응답과 내부 검사 응답을 나누어 확인한다

503은 공통 접속 지점에서 사용할 backend가 없을 때도 발생한다. 여기서 backend는 실제 요청을 처리하는 NetBox 앱 서버다. 초기화·DB migration이 끝나지 않은 앱과, 정상 앱에 잘못된 검사를 보내는 경우를 같은 원인으로 취급하지 않았다.

점검 순서는 컨테이너 준비 상태 → 앱 직접 접근 → HAProxy 상태 검사 → 공통 HTTPS 주소였다. 이번 구성에서는 HAProxy의 /login/ 검사에 올바른 Host가 빠져 NetBox가 400을 반환하는 문제가 있었다.

docker inspect --format '{{.State.Health.Status}}' fullmoon-apps-netbox-1

# 허용된 관리 경로에서 앱에 직접 확인
curl --silent --show-error --output /dev/null --write-out '%{http_code}\n' \
  -H 'Host: netbox.fullmoon.test' http://10.77.10.11:8082/login/

# 사용자가 접근하는 공통 HTTPS 주소
curl --fail --silent --show-error --output /dev/null \
  https://netbox.fullmoon.test/login/

2. 원인: 상태 검사에도 앱이 허용하는 호스트 이름이 필요했다

앱은 요청의 Host와 허용 호스트 설정을 검사한다. 브라우저가 보내는 요청과 HAProxy의 검사 요청은 서로 독립적이므로, 브라우저 접속에 올바른 이름을 사용했다고 해서 검사 요청까지 같은 이름을 갖는 것은 아니다.

상태 검사를 HTTP/1.1로 명시하고 Host: netbox.fullmoon.test를 전달하도록 수정했다. ALLOWED_HOSTS를 무제한으로 풀거나 오류 응답을 정상으로 인정하는 대신, 앱이 받을 정상 요청 형태에 검사를 맞췄다. 설정 방식은 HAProxy 공식 상태 검사 문서에서도 확인할 수 있다.

backend netbox_ui
    mode http
    option httpchk
    http-check send meth GET uri /login/ ver HTTP/1.1 hdr Host netbox.fullmoon.test
    http-check expect status 200
    server ops01 10.77.10.11:8082 check
    server ops02 10.77.10.12:8082 check

이 코드는 실습에서 사용한 backend 부분이다. 기존 frontend, TLS 인증서, 접근 제어를 포함하는 전체 설정 파일을 대체하지 않는다. 변경 전 원본을 보관하고 문법 검사에 성공한 경우에만 reload한다.

sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy

3. 같은 HAProxy라도 웹과 DB 연결의 기준은 다르다

구축 중 Zabbix 쪽에는 server closed the connection unexpectedly라는 DB 연결 오류도 나타났다. 공통의 짧은 연결 제한을 DB 경로까지 사용하고 있었으므로, 6432 포트의 DB listener에서 client/server timeout을 별도로 지정했다. 실습 값은 다음과 같다.

timeout client 1h
timeout server 1h

이 값은 DB listener에 적용한 설정값이며 검증에 걸린 시간을 강조하기 위한 숫자가 아니다. 웹의 HTTP 상태 검사와 DB의 유휴 연결 유지 문제를 분리해 점검했다. 긴 timeout이 모든 DB 단절을 해결하는 것은 아니며, 실제 운영에서는 연결 풀·쿼리·장애 감지 정책과 함께 조정해야 한다.

4. 수정 후 확인 결과와 한계

Host를 지정한 검사로 바꾼 뒤 NetBox의 공통 HTTPS 주소에서 정상 응답을 확인했다. 이후 자산 API 조회와 온보딩도 진행할 수 있었다. 그러나 로그인 페이지 200만으로 DB 쓰기·Redis·Worker·미디어까지 모두 정상이라고 판단하지는 않았다.

확인 단계 의미
컨테이너 준비 상태 앱 초기화 완료 여부
직접 /login/ 조회 허용 Host로 앱 응답 확인
HAProxy를 통한 HTTPS 조회 실제 접속 경로 확인
인증된 자산 API 조회 저장된 자산을 읽을 수 있는지 확인
장애 후 별도 쓰기 시험 생존한 DB 경로의 쓰기 확인

온라인망과 폐쇄망 모두 검사 요청의 구조는 같다. 폐쇄망에서는 내부 DNS와 CA 신뢰도 같은 접속 경로에서 확인해야 한다. 실제 장애 시험에서는 Redis 대기 때문에 NetBox 응답이 늦어지는 다른 문제도 발견했으므로, 모든 503을 Host 헤더 문제로 단정해서는 안 된다.