헬스체크 설정: 장애 노드 자동 감지와 제거
로드밸런싱 환경에서 헬스체크는 없어서는 안 될 핵심 기능입니다. 백엔드 서버가 장애를 겪을 때 로드밸런서가 이를 자동으로 감지해 해당 서버로의 트래픽을 차단하고, 복구되면 자동으로 다시 포함해야 합니다. 헬스체크 없이는 사용자가 오류 응답을 직접 받게 됩니다.
Passive vs Active 헬스체크
헬스체크에는 두 가지 방식이 있습니다.
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_fails와 fail_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 옵션
| 값 | 설명 |
|---|---|
GET | HTTP GET 요청으로 헬스체크 |
HEAD | HTTP HEAD (body 없음, 가벼움) |
OPTIONS | HTTP OPTIONS |
TCP | TCP 연결만 확인 (HTTP 불필요) |
CPING | AJP 프로토콜 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
헬스체크 설정 권장값 요약
| 환경 | interval | fails | passes | fail_timeout |
|---|---|---|---|---|
| 고가용성 서비스 | 5s | 2 | 3 | 10s |
| 일반 웹 서비스 | 10s | 3 | 2 | 30s |
| 배치성/내부 서비스 | 30s | 3 | 2 | 60s |
핵심 원칙:
fails를 너무 낮게 → 일시적 지연으로도 배제 (오탐)fails를 너무 높게 → 장애 감지 지연 (미탐)passes를 너무 낮게 → 불안정한 서버가 조기 복귀 (플래핑)
다음 페이지에서는 점진적 트래픽 이동(카나리 배포)과 장애 노드 복구 전략을 알아봅니다.