본문으로 바로가기
TaeyoungKim.dev

AWS ALB 대상 그룹 unhealthy: EC2는 실행 중인데 헬스 체크가 실패하는 이유

클라우드작성 약 4분 읽기TaeyoungKim
LinkedInX

EC2 화면에는 ‘실행 중’이라고 나온다. 그런데 ALB 대상 그룹에서는 같은 인스턴스가 unhealthy다. 서버가 두 번 평가받는 기분이지만, 실제로는 서로 다른 질문을 받고 있다. EC2의 실행 상태는 인스턴스가 켜졌는지를 말하고, ALB 헬스 체크는 지정한 포트와 경로에서 기대한 HTTP 응답을 받았는지를 본다.

EC2가 실행 중인데 ALB 대상은 왜 unhealthy일까?

ALB의 리스너는 클라이언트 요청을 받는다. 리스너 규칙은 요청을 대상 그룹으로 보내고, 대상 그룹에는 실제 앱이 받는 대상 포트와 상태 검사 경로가 있다. 화면에 HTTP:80이 여러 번 보여도 각각 리스너, 대상, 상태 검사의 자리일 수 있다. 한쪽 숫자만 맞췄다고 나머지가 자동으로 맞는 것은 아니다.

예를 들어 앱은 8080 포트의 /health에서 200을 반환하지만, 대상 그룹이 기본 경로 /를 검사한다면 404를 받을 수 있다. 포트가 맞아도 경로가 다르면 실패한다. 반대로 /health가 200이어도 대상 그룹이 앱이 듣지 않는 포트로 요청하면 연결 단계에서 막힌다.

헬스 체크 경로가 404를 돌려주는 상황 재현

아래 서버는 앱의 준비 상태를 /health에서만 알린다. 다른 경로는 404다.

js
import { createServer } from "node:http";

createServer((request, response) => {
  if (request.url === "/health") {
    response.writeHead(200);
    response.end("ok");
    return;
  }
  response.writeHead(404);
  response.end("not found");
}).listen(8080);

node health.mjs로 실행하고 다른 터미널에서 확인하면 /는 404, /health는 200이다.

bash
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/
# 404
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/health
# 200

이는 HTTP 경로별 응답을 확인한 로컬 예제다. AWS의 대상 등록, 보안 그룹, 실제 ALB 연결까지 재현한 결과는 아니다. 다만 상태 검사 경로가 /인지 /health인지에 따라 판정이 달라질 수 있다는 원인을 분리해 볼 수 있다.

대상 그룹에서 포트·경로·성공 코드는 어떤 순서로 확인할까?

먼저 대상 그룹의 Targets에서 상태와 이유 코드를 확인한다. initial이라면 초기 검사가 진행 중인 것이고, unused라면 등록·리스너 규칙·가용 영역을 먼저 봐야 한다. 둘을 모두 unhealthy라는 말로 뭉개면 엉뚱한 설정을 고치기 쉽다.

unhealthy라면 다음 순서로 좁힌다.

  1. 대상 포트: 앱이 실제로 듣는 포트와 대상 그룹에 등록한 포트가 같은가? 리스너의 80과 앱의 8080은 달라도 된다. 그 사이 라우팅 설정이 맞아야 한다.
  2. 상태 검사 경로: /가 로그인 페이지로 이동하거나 404를 돌려주지는 않는가? 앱이 준비 상태를 제공하는 /health 같은 경로를 확인한다.
  3. 성공 코드: 검사 응답 코드가 대상 그룹의 성공 코드 설정에 포함되는가? HTTP/1.1·HTTP/2의 기본 성공 코드는 200이다.
  4. 연결 조건: 응답이 없거나 시간 초과라면 인스턴스 내부에서 앱이 떠 있는지, 대상 포트와 보안 그룹·네트워크 경로가 맞는지 살핀다. 로컬의 curl 성공만으로 ALB에서 접근 가능하다고 단정하지 않는다.

AWS가 표시하는 Target.ResponseCodeMismatch는 기대한 코드와 다른 응답, Target.Timeout은 응답 시간 초과를 가리킨다. AWS의 ALB 상태 검사 설정과 이유 코드를 보면서 현재 대상 그룹의 실제 설정값과 비교하면 원인을 더 빨리 좁힐 수 있다. ‘응답이 왔는가’와 ‘원하는 응답이 왔는가’는 다른 검사다.

healthy가 되면 서비스가 반드시 안전할까?

헬스 체크가 통과해도 DB 연결이나 중요한 내부 기능까지 정상이라고 자동으로 증명되지는 않는다. /health가 늘 200만 돌려주는 형식적인 경로라면, 장애가 난 앱도 healthy로 남을 수 있다. 그렇다고 매 검사마다 무거운 DB 쿼리를 보내면 검사 자체가 부담이 된다. 이 경로가 무엇을 보장하는지 정하고, 더 깊은 기능 검사는 별도로 운영하는 편이 판단하기 쉽다.

또 하나의 예외가 있다. ALB 대상 그룹에서 등록된 대상이 모두 unhealthy라면 AWS는 이 대상으로 요청을 보낼 수 있다(fail-open). 따라서 unhealthy가 보인다고 ‘요청이 절대 가지 않는다’고 단정하거나, 검사 실패를 방치한 채 트래픽 차단 수단으로 삼아서는 안 된다.

핵심 요약

EC2의 ‘실행 중’은 ALB의 ‘healthy’와 다르다. 대상 그룹에서 상태·이유 코드를 먼저 보고, 앱이 실제로 듣는 포트, 검사 경로, 성공 코드를 같은 요청 한 건으로 맞춰 본다. /가 404이고 /health가 200인 앱이라면 인스턴스 재시작보다 검사 경로 확인이 먼저다. 그다음에야 ALB에서 앱까지의 연결과 운영상 헬스 체크의 의미를 판단할 수 있다.

작성자

TaeyoungKim

기초 개념을 구현과 검증, 실제 운영 판단까지 연결해 기록합니다.

#AWS ALB#대상 그룹#헬스 체크#unhealthy#Target.ResponseCodeMismatch

함께 읽으면 좋은 글