본문으로 건너뛰기

헬스체크 설정: 장애 노드 자동 감지와 제거

로드밸런싱 환경에서 헬스체크는 없어서는 안 될 핵심 기능입니다. 백엔드 서버가 장애를 겪을 때 로드밸런서가 이를 자동으로 감지해 해당 서버로의 트래픽을 차단하고, 복구되면 자동으로 다시 포함해야 합니다. 헬스체크 없이는 사용자가 오류 응답을 직접 받게 됩니다.


Passive vs Active 헬스체크

헬스체크에는 두 가지 방식이 있습니다.

Health Check Flow

Passive 헬스체크 (소극적)

실제 사용자 요청에 대한 응답을 모니터링합니다. 오류가 임계값 이상 발생하면 서버를 자동 배제합니다.

사용자 요청 → App1 (정상)   ✓
사용자 요청 → App2 (오류) ✗ ← 카운트
사용자 요청 → App2 (오류) ✗ ← 카운트
사용자 요청 → App2 (오류) ✗ ← 임계값 도달 → App2 배제
이후 요청 → App1, App3만 사용

장점: 별도 트래픽 없이 실제 요청으로 상태 감지. 단점: 실제 사용자가 오류를 받은 후에야 감지됨. 감지 지연 발생.

Active 헬스체크 (적극적)

로드밸런서가 주기적으로 전용 헬스체크 엔드포인트에 요청을 보내 사전에 서버 상태를 확인합니다.

[로드밸런서]
│ (30초마다)
├──▶ App1 /health → 200 OK ✓ (정상)
├──▶ App2 /health → 연결 거부 ✗ → 자동 배제
└──▶ App3 /health → 200 OK ✓ (정상)

장점: 사용자 영향 없이 사전 감지. 훨씬 빠른 장애 감지. 단점: 별도 트래픽 발생. Nginx Plus, HAProxy, AWS ALB 등 상용/고급 버전에서 지원.


Nginx Passive 헬스체크

오픈소스 Nginx는 Active 헬스체크를 지원하지 않습니다. 대신 Passive 헬스체크를 max_failsfail_timeout으로 구성합니다.

upstream backend {
server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 max_fails=3 fail_timeout=30s;
}

동작 원리

fail_timeout(30초) 기간 내에 max_fails(3회) 실패 발생
→ 해당 서버를 fail_timeout(30초) 동안 배제
→ 30초 후 자동 복구 시도 (요청 1건 시도)
→ 성공하면 복귀, 실패하면 다시 30초 배제

실패로 간주되는 상황

기본적으로 연결 오류와 타임아웃만 실패로 처리합니다. HTTP 오류 코드(502, 503 등)를 추가하려면 proxy_next_upstream을 설정해야 합니다.

upstream backend {
server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
}

server {
location / {
proxy_pass http://backend;

# 이 상태 코드도 실패로 간주
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
}

Passive 헬스체크 한계 극복: 헬스체크 엔드포인트 모니터링

순수 Nginx에서 Active 헬스체크와 유사한 효과를 내는 방법으로, 외부 모니터링 도구(Prometheus, Nagios 등)와 연계합니다.

# 간단한 셸 스크립트로 헬스체크 시뮬레이션
#!/bin/bash
SERVERS=("10.0.0.1:8080" "10.0.0.2:8080" "10.0.0.3:8080")

for server in "${SERVERS[@]}"; do
response=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time 5 "http://$server/health")
if [ "$response" != "200" ]; then
echo "ALERT: $server is down (HTTP $response)"
# 알림 발송 (Slack, PagerDuty 등)
fi
done

Nginx Plus Active 헬스체크

Nginx Plus (상용 버전)는 Active 헬스체크를 지원합니다.

upstream backend {
zone backend 64k; # 공유 메모리 존 (Active 헬스체크 필수)

server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}

server {
location / {
proxy_pass http://backend;

# Active 헬스체크 설정
health_check
interval=10s # 10초마다 체크
fails=3 # 3번 연속 실패 시 배제
passes=2 # 2번 연속 성공 시 복귀
uri=/health # 헬스체크 엔드포인트
match=server_ok; # 응답 검증 조건
}
}

# 응답 검증 조건 정의
match server_ok {
status 200;
header Content-Type ~ "application/json";
body ~ '"status":"UP"'; # 응답 body 패턴 매칭
}

Apache mod_proxy_hcheck (Active 헬스체크)

Apache 2.4.21+에서 mod_proxy_hcheck 모듈을 통해 Active 헬스체크를 지원합니다.

# 모듈 활성화
sudo a2enmod proxy_hcheck
sudo systemctl reload apache2
# /etc/apache2/sites-available/app-lb.conf

<Proxy "balancer://mycluster">
BalancerMember "http://10.0.0.1:8080"
hcmethod=GET # 헬스체크 HTTP 메서드
hcuri=/health # 헬스체크 엔드포인트 URI
hcinterval=10 # 체크 주기 (초)
hcpasses=2 # N번 성공 시 복귀
hcfails=3 # N번 실패 시 배제
hcexpr=hc200ok # 응답 검증 표현식

BalancerMember "http://10.0.0.2:8080"
hcmethod=GET
hcuri=/health
hcinterval=10
hcpasses=2
hcfails=3
hcexpr=hc200ok

ProxySet lbmethod=bybusyness
</Proxy>

# 응답 검증 조건: 200 OK만 정상으로 간주
<IfDefine hc200ok>
# hcexpr 이름 정의
</IfDefine>

# 정규식으로 헬스체크 응답 검증
ProxyHCExpr hc200ok {%{REQUEST_STATUS} =~ /^[23]/} # 2xx, 3xx 정상
ProxyHCExpr hcjsonok {%{body} =~ /"status"\s*:\s*"UP"/} # JSON body 검증

<VirtualHost *:80>
ServerName example.com

ProxyPass "/" "balancer://mycluster/"
ProxyPassReverse "/" "balancer://mycluster/"
</VirtualHost>

hcmethod 옵션

설명
GETHTTP GET 요청으로 헬스체크
HEADHTTP HEAD (body 없음, 가벼움)
OPTIONSHTTP OPTIONS
TCPTCP 연결만 확인 (HTTP 불필요)
CPINGAJP 프로토콜 CPING (Tomcat 전용)

애플리케이션 헬스체크 엔드포인트 구현

로드밸런서의 헬스체크가 의미있으려면, 백엔드 애플리케이션이 올바른 헬스체크 응답을 반환해야 합니다.

Spring Boot Actuator (Java)

// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'
# application.yml
management:
endpoints:
web:
exposure:
include: health
endpoint:
health:
show-details: always

Spring Boot Actuator가 /actuator/health 엔드포인트를 자동 생성합니다:

{
"status": "UP",
"components": {
"db": {"status": "UP"},
"diskSpace": {"status": "UP"},
"ping": {"status": "UP"}
}
}

DB 연결이 끊기면 자동으로 "status": "DOWN" + HTTP 503을 반환합니다.

커스텀 헬스체크 엔드포인트 (Node.js 예시)

// Express.js 헬스체크 라우터
const express = require('express');
const app = express();

// 데이터베이스 연결 상태 확인
async function checkDatabase() {
try {
await db.query('SELECT 1');
return { status: 'UP' };
} catch (e) {
return { status: 'DOWN', error: e.message };
}
}

app.get('/health', async (req, res) => {
const dbStatus = await checkDatabase();

const health = {
status: dbStatus.status === 'UP' ? 'UP' : 'DOWN',
timestamp: new Date().toISOString(),
components: {
database: dbStatus,
}
};

const statusCode = health.status === 'UP' ? 200 : 503;
res.status(statusCode).json(health);
});

헬스체크 엔드포인트 설계 원칙

✅ 올바른 헬스체크 설계:
- GET /health → 200 OK (서비스 정상)
- GET /health → 503 Service Unavailable (서비스 불가)
- DB 연결, 외부 서비스 의존성 확인 포함
- 응답 시간 < 5초 (타임아웃 이전 응답)
- 인증 불필요 (로드밸런서 접근 용이)

❌ 잘못된 헬스체크:
- 항상 200 OK 반환 (의미 없음)
- DB 포함 모든 의존성 체크 (너무 엄격, 오탐 발생)
- 응답 시간이 긴 로직 포함 (타임아웃 오류)
- IP 제한 또는 인증 요구 (로드밸런서 접근 불가)

실전 헬스체크 전략

계층별 헬스체크 구성

[로드밸런서]

├── Shallow Check (빠름, 30초마다)
│ GET /health/ping → 200 OK
│ (애플리케이션 프로세스가 살아있는지만 확인)

└── Deep Check (느림, 5분마다 외부 모니터링)
GET /health/deep → 200 OK
(DB, Redis, 외부 API 연결 모두 확인)

로드밸런서는 Shallow Check만 수행합니다. Deep Check는 Prometheus, Nagios 등 외부 모니터링 도구가 담당해 알림만 발생시킵니다.

헬스체크 로그 관리

헬스체크 요청이 액세스 로그에 쌓이면 노이즈가 됩니다. 필터링합니다:

# Nginx: /health 요청 로그 제외
map $request_uri $loggable {
~^/health 0;
default 1;
}

access_log /var/log/nginx/access.log combined if=$loggable;
# Apache: /health 요청 로그 제외
SetEnvIf Request_URI "^/health$" dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog

헬스체크 설정 권장값 요약

환경intervalfailspassesfail_timeout
고가용성 서비스5s2310s
일반 웹 서비스10s3230s
배치성/내부 서비스30s3260s

핵심 원칙:

  • fails를 너무 낮게 → 일시적 지연으로도 배제 (오탐)
  • fails를 너무 높게 → 장애 감지 지연 (미탐)
  • passes를 너무 낮게 → 불안정한 서버가 조기 복귀 (플래핑)

다음 페이지에서는 점진적 트래픽 이동(카나리 배포)과 장애 노드 복구 전략을 알아봅니다.