문제

요청이 502 Bad Gateway를 반환하고 nginx 에러 로그에 no live upstreams while connecting to upstream이 찍혀 있습니다. 이건 백엔드 하나가 연결에 실패한 것과 다릅니다. nginx가 업스트림 그룹 전체를 보고 그 안의 모든 서버가 그 순간 요청을 받을 자격이 없다고 판단해 — 프록시할 대상이 없어 — 연결을 시도하지도 않고 502를 냈다는 뜻입니다. 먼저 답할 질문은, nginx가 당신의 서버들을 축출한 것인지(수동 헬스체크가 전부 쳐냄) 아니면 설정이 애초에 라우팅할 대상을 안 남긴 것인지입니다. 이 둘은 완전히 다른 버그이기 때문입니다.

증상

  • 클라이언트는 밋밋한 502 Bad Gateway를 보고, nginx 에러 로그에 no live upstreams while connecting to upstream이 뜹니다.
  • curl로 직접 때리면 백엔드는 멀쩡히 응답합니다 — 문제는 앱이 아니라 nginx의 라우팅 판정입니다.
  • 백엔드가 여럿이면 간헐적이고 부하 스파이크와 맞물립니다 — 몇 초 튀었다가 저절로 풀립니다.
  • 백엔드가 하나면 설정 키워드(down·backup·max_conns)나 DNS/resolver 실패에 묶여 있고, 앱을 아무리 재시작해도 바뀌지 않습니다.

이 에러의 실제 의미

nginx는 upstream 블록의 각 서버에 대한 판정을 계속 유지합니다. 서버를 둘 이상 나열하면 수동(passive) 헬스체크를 돌립니다 — 실제 프록시된 요청을 지켜보다가, 한 서버가 fail_timeout 안에 max_fails회 실패하면 사용 불가로 표시하고 fail_timeout이 만료될 때까지 우회한 뒤 조심스럽게 다시 시도합니다. 기본값이 공격적입니다 — max_fails=1, fail_timeout=10s — 그래서 단 한 번의 실패로도 서버가 10초간 벤치됩니다.

no live upstreams는 그룹의 모든 서버가 동시에 벤치됐을 때 nginx가 남기는 로그입니다. 자격 있는 피어가 하나도 안 남아 연결을 시도조차 못 하고 502를 냅니다. 이게 미스터리처럼 보이는 이유가 여기 있습니다 — 당신이 백엔드에 curl을 날릴 즈음엔 fail_timeout이 지나 다 “멀쩡”하지만, 그 창 동안엔 나쁜 순간 하나가 풀 전체를 쓸어버린 것입니다.

부하 중 이게 연쇄되는 방식이 사람들이 놓치는 지점입니다. 트래픽 스파이크가 한 백엔드를 max_fails를 넘길 만큼 느리게 만들고, nginx가 그걸 벤치하면서 그 몫을 생존자에게 밀어넣고, 늘어난 부하가 다음 서버를 넘어뜨리고 — 1~2초 안에 모든 서버가 걸려 그룹 전체가 fail_timeout 동안 죽습니다. 실제로는 아무도 크래시하지 않았는데도요. 지나치게 예민한 기본값으로 스스로 만든 장애입니다.

단독 서버 케이스: max_fails가 아니다

사람을 엉뚱한 길로 보내는 함정이 여기 있습니다. 업스트림 블록에 서버가 정확히 하나면 max_fails·fail_timeout 튜닝은 아무 것도 안 합니다 — nginx가 무시하기 때문입니다. 업스트림 모듈 문서에서: “그룹에 서버가 하나뿐이면 max_fails, fail_timeout, slow_start 파라미터는 무시되며, 그런 서버는 절대 사용 불가로 간주되지 않는다.”

즉 단독 백엔드는 수동 헬스체크로 축출되지 않습니다. 서버가 하나인데 no live upstreams가 뜬다면 원인은 다른 데 있고, 거의 항상 이 중 하나입니다.

  • 서버가 down으로 표시됨. server 줄에 남은 down(또는 그걸 붙인 설정 템플릿)이 있으면 nginx는 영구 사용 불가로 봅니다. down 서버 1개 = 살아 있는 서버 0개.
  • max_conns 포화. 서버의 동시 연결 수에 상한을 걸었고 그게 다 찼다면, nginx는 새 연결을 열 슬롯이 없어 그 서버를 not live로 셉니다.
  • 전부 backup. 모든 항목이 backup인데 primary가 없으면 서비스할 살아 있는 primary가 없습니다 — backup은 primary가 죽을 때만 활성화되는데, 그 primary가 없는 것입니다.
  • resolver/변수 업스트림 해석 실패. 업스트림 주소가 요청 시점에 해석되는 변수인데 DNS 조회가 실패하면, nginx는 연결할 주소가 없어 no live upstream으로 보고합니다.

각각은 헬스체크 튜닝 문제가 아니라 설정·DNS 문제입니다. 여기서 max_fails=3을 만지는 건 아무 것도 못 고칩니다.

DechoNet으로 진단하기

  • HTTP 점검은 바깥 세계가 실제로 무엇을 받는지 — 최종 상태가 nginx발 502인지 — 확인하고, 실패하는 경로와 되는 경로를 비교하게 해줍니다. 502가 간헐적이면 반복 점검으로 부하와 상관 있는지(연쇄)인지 상시 발생인지(설정 함정) 볼 수 있습니다. nginx 에러 로그의 정확한 문구와 짝지어 보세요 — 그게 진짜 진단 열쇠입니다.

해결 체크리스트

  • 정확한 로그 문구를 먼저 읽으세요. no live upstreams(그룹 전체 사용 불가)는 connect() failed·upstream timed out(한 번의 시도 실패)와 다른 버그입니다. 엉뚱한 걸 고치지 마세요.
  • upstream 블록의 서버 개수를 세세요. 하나 → max_fails가 아닙니다. down·backup·max_conns·resolver 실패를 확인. 여럿 → 수동 헬스체크 축출을 의심.
  • 다중 서버 연쇄라면 예민한 기본값을 완화하세요 — max_fails(예: 3)와/또는 fail_timeout을 올려 한 번 느린 순간이 멀쩡한 서버를 벤치하지 않게 하고, 백엔드가 느렸던 근본 원인(포화·타임아웃·GC 멈춤)을 고치세요.
  • 설정에서 남은 down 하나, 그리고 모든 항목이 backup이고 primary가 없는 블록을 grep하세요. 남은 down은 제거하고, 그룹에 실제 primary를 주세요.
  • 업스트림이 변수+resolver를 쓴다면 resolver가 도달 가능한지, 이름이 해석되는지 확인하세요 — 여기서의 DNS 실패가 “no live upstreams”로 읽힙니다.
  • HTTP 점검을 다시 돌려 해당 경로가 더 이상 502를 반환하지 않는지 확인하세요.

언제 에스컬레이션할까

  • 502가 트래픽 스파이크를 따라 fail_timeout 안에 풀린다면, 진짜 문제는 nginx가 아니라 업스트림 용량·지연입니다 — max_fails만 넓히지 말고 포화·느린 쿼리·커넥션 풀 한도·백엔드 타임아웃을 파세요.
  • 관리형 로드밸런서나 인그레스 컨트롤러 뒤에서 nginx를 돌린다면, 자체 임계값을 가진 헬스체크 계층이 하나 더 있을 수 있습니다 — 한 계층엔 “정상”인 백엔드가 다른 계층엔 벤치돼 있을 수 있습니다.
  • 백엔드 하나가 응답 헤더가 nginx 버퍼를 넘쳐 502가 나는 밀접한 케이스는 upstream sent too big header (nginx 502)를, 502 일반 결정 트리는 502 Bad Gateway 체크리스트를 보세요.

내 도메인에서 바로 확인

무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.