본문으로 건너뛰기

실전 고수 팁 — 카오스 엔지니어링과 장애 대응 런북

카오스 엔지니어링이란?

**카오스 엔지니어링(Chaos Engineering)**은 프로덕션 환경의 불확실성을 사전에 실험적으로 검증하는 실천 방법론입니다. Netflix가 2011년 AWS로 인프라를 이전하면서 개발한 Chaos Monkey가 대표적인 도구로, 프로덕션 시스템에서 무작위로 서버 인스턴스를 종료하여 시스템의 복원력을 테스트합니다.

핵심 원칙

  • 프로덕션에서 실험: 실제 트래픽이 있는 환경에서 검증해야 진정한 복원력을 확인할 수 있습니다
  • 점진적 확대: 소규모 실험부터 시작하여 영향 범위를 점진적으로 확대합니다
  • 자동 중단 조건(abort condition): 사전에 정의된 SLO(서비스 수준 목표)가 위반되면 즉시 실험을 중단합니다
  • 지속적 실행: 1회성 이벤트가 아닌 지속적인 검증 프로세스로 운영합니다

Netflix GameDays

GameDay는 팀 전체가 참여하는 계획된 장애 훈련입니다. 실제 프로덕션 환경에서 다양한 장애 시나리오를 의도적으로 발생시키고, 팀이 얼마나 빠르게 대응·복구할 수 있는지 측정합니다.

카오스 테스트 스크립트

랜덤 컨테이너 중지 테스트

#!/bin/bash
# chaos-container-kill.sh — 랜덤 컨테이너 중지로 HA 검증

NAMESPACE="production"
EXCLUDE_CONTAINERS="nginx|database|redis"
LOG_FILE="/var/log/chaos/chaos-$(date '+%Y%m%d_%H%M%S').log"

mkdir -p /var/log/chaos

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE; }

# 사전 조건 확인 (최소 서비스 상태 검증)
pre_check() {
local HEALTH_URL="http://localhost/health"
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL)
if [ "$HTTP_STATUS" != "200" ]; then
log "사전 조건 실패: 서비스가 이미 비정상 상태 (HTTP $HTTP_STATUS)"
log "카오스 실험 취소"
exit 1
fi
log "사전 조건 확인 완료: 서비스 정상 (HTTP $HTTP_STATUS)"
}

# 랜덤 컨테이너 선택 및 중지
chaos_kill_container() {
# 제외 목록을 제외한 실행 중인 컨테이너 목록
CONTAINERS=$(docker ps --format "{{.Names}}" | grep -vE "$EXCLUDE_CONTAINERS")
CONTAINER_COUNT=$(echo "$CONTAINERS" | wc -l)

if [ $CONTAINER_COUNT -eq 0 ]; then
log "중지할 컨테이너가 없습니다"
exit 0
fi

# 랜덤 선택
RANDOM_INDEX=$((RANDOM % CONTAINER_COUNT + 1))
TARGET_CONTAINER=$(echo "$CONTAINERS" | sed -n "${RANDOM_INDEX}p")

log "카오스 실험 시작: 컨테이너 '${TARGET_CONTAINER}' 중지"
docker stop $TARGET_CONTAINER

log "중지 완료. 서비스 복원 모니터링 시작..."
}

# 서비스 복원 모니터링
monitor_recovery() {
local START_TIME=$(date +%s)
local HEALTH_URL="http://localhost/health"
local MAX_WAIT=120

while true; do
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL || echo "000")
ELAPSED=$(($(date +%s) - START_TIME))

if [ "$HTTP_STATUS" == "200" ]; then
log "서비스 복원 완료! (${ELAPSED}초 소요)"
break
fi

if [ $ELAPSED -ge $MAX_WAIT ]; then
log "경고: ${MAX_WAIT}초 내 서비스가 복원되지 않음 (HTTP $HTTP_STATUS)"
log "즉시 수동 대응 필요!"
exit 1
fi

log "복원 대기 중... (${ELAPSED}초, HTTP $HTTP_STATUS)"
sleep 5
done
}

# 메인 실행
pre_check
chaos_kill_container
monitor_recovery
log "카오스 실험 완료. 로그: $LOG_FILE"

tc 명령으로 네트워크 지연 시뮬레이션

#!/bin/bash
# chaos-network-delay.sh — 네트워크 지연 및 패킷 손실 시뮬레이션

INTERFACE="eth0" # 대상 네트워크 인터페이스
TARGET_PORT="8080" # 대상 포트
DELAY_MS="200" # 지연 시간 (밀리초)
JITTER_MS="50" # 지터 (랜덤 변동)
PACKET_LOSS="5" # 패킷 손실률 (%)
DURATION="60" # 실험 지속 시간 (초)

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }

# 실험 시작: 네트워크 지연 적용
apply_network_chaos() {
log "네트워크 지연 시뮬레이션 시작"
log "인터페이스: $INTERFACE, 지연: ${DELAY_MS}ms±${JITTER_MS}ms, 패킷 손실: ${PACKET_LOSS}%"

# qdisc 규칙 추가 (네트워크 지연 + 지터)
tc qdisc add dev $INTERFACE root netem \
delay ${DELAY_MS}ms ${JITTER_MS}ms \
loss ${PACKET_LOSS}%

log "네트워크 카오스 적용 완료"
}

# 현재 네트워크 상태 확인
check_network_status() {
log "현재 tc 규칙:"
tc qdisc show dev $INTERFACE
}

# 실험 종료: 네트워크 규칙 제거
cleanup_network_chaos() {
log "네트워크 카오스 제거 중..."
tc qdisc del dev $INTERFACE root 2>/dev/null || true
log "네트워크 정상화 완료"
}

# 실험 중 서비스 응답시간 측정
measure_response_time() {
local HEALTH_URL="http://localhost:${TARGET_PORT}/health"
local RESULT=$(curl -s -o /dev/null \
-w "HTTP: %{http_code} | 응답시간: %{time_total}s | DNS: %{time_namelookup}s | 연결: %{time_connect}s" \
$HEALTH_URL 2>/dev/null)
log "서비스 응답: $RESULT"
}

# 실험이 중단되어도 반드시 정리
trap cleanup_network_chaos EXIT

apply_network_chaos
check_network_status

log "실험 시작. ${DURATION}초 동안 서비스 응답시간 모니터링..."
END_TIME=$(($(date +%s) + DURATION))
while [ $(date +%s) -lt $END_TIME ]; do
measure_response_time
sleep 5
done

cleanup_network_chaos
log "네트워크 카오스 실험 완료"

장애 대응 런북(Runbook) 템플릿

런북은 장애 발생 시 담당자가 따라야 할 표준 절차서입니다. 잘 작성된 런북은 새벽 3시에 장애가 발생해도 신속·정확하게 대응할 수 있게 합니다.

런북 예시: Tomcat 응답 없음

# 런북: Tomcat 서버 응답 없음 (P1)

## 증상
- Nginx 502 Bad Gateway 다수 발생
- Tomcat 헬스체크 엔드포인트 무응답
- 알람: "tomcat_health_check CRITICAL"

## 즉각 조치 (5분 이내)

### Step 1: 상황 파악
[ ] 현재 영향받는 사용자 수 확인 (모니터링 대시보드)
[ ] 에러 발생 시각 확인 (최근 배포와 연관 있는가?)
[ ] Tomcat 프로세스 상태 확인: systemctl status tomcat
[ ] 포트 응답 확인: curl -v http://localhost:8080/health

### Step 2: 빠른 복구 시도
[ ] Tomcat 재시작: systemctl restart tomcat
[ ] 재시작 후 60초 대기
[ ] 헬스체크 재확인: curl http://localhost:8080/health

### Step 3: 재시작으로 해결 안 되는 경우
[ ] 로그 확인: tail -n 200 /opt/tomcat/logs/catalina.out
[ ] OOM(Out Of Memory) 여부 확인: grep -i "OutOfMemoryError" /opt/tomcat/logs/catalina.out
[ ] 디스크 공간 확인: df -h
[ ] JVM 힙 사용량 확인: jmap -heap $(pgrep -f catalina)

## 에스컬레이션
- 5분 내 복구 실패: 팀장 호출
- 15분 내 복구 실패: CTO 호출 + 공식 인시던트 선언
- 30분 이상 지속: 전체 장애 공지

배포 전 체크리스트 (Pre-deployment Checklist)

#!/bin/bash
# pre-deploy-check.sh — 배포 전 필수 점검 항목

PASS=0
FAIL=0

check() {
local DESCRIPTION="$1"
local COMMAND="$2"
local EXPECTED="$3"

ACTUAL=$(eval "$COMMAND" 2>/dev/null || echo "ERROR")
if [ "$ACTUAL" == "$EXPECTED" ]; then
echo "✅ PASS: $DESCRIPTION"
PASS=$((PASS + 1))
else
echo "❌ FAIL: $DESCRIPTION (기대값: $EXPECTED, 실제값: $ACTUAL)"
FAIL=$((FAIL + 1))
fi
}

echo "=== 배포 전 체크리스트 ==="

# 1. 서비스 상태 점검
check "Tomcat 서비스 실행 중" \
"systemctl is-active tomcat" "active"

check "Nginx 서비스 실행 중" \
"systemctl is-active nginx" "active"

# 2. 디스크 공간 (80% 미만)
DISK_USAGE=$(df /opt/tomcat | awk 'NR==2{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -lt 80 ]; then
echo "✅ PASS: 디스크 사용량 정상 (${DISK_USAGE}%)"
PASS=$((PASS + 1))
else
echo "❌ FAIL: 디스크 사용량 과다 (${DISK_USAGE}%)"
FAIL=$((FAIL + 1))
fi

# 3. 메모리 사용량 (90% 미만)
MEM_USAGE=$(free | awk '/Mem/{printf "%.0f", $3/$2*100}')
if [ "$MEM_USAGE" -lt 90 ]; then
echo "✅ PASS: 메모리 사용량 정상 (${MEM_USAGE}%)"
PASS=$((PASS + 1))
else
echo "❌ FAIL: 메모리 사용량 과다 (${MEM_USAGE}%)"
FAIL=$((FAIL + 1))
fi

# 4. 헬스체크 응답
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health || echo "000")
check "헬스체크 응답 200" "echo $HTTP_STATUS" "200"

# 5. 최근 5분 에러율 (1% 미만) — Prometheus 쿼리 예시
# ERROR_RATE=$(curl -s "http://localhost:9090/api/v1/query?query=rate(http_requests_total{status=~'5..'}[5m])" | jq '.data.result[0].value[1]')

echo ""
echo "=== 결과: PASS $PASS / FAIL $FAIL ==="

if [ $FAIL -gt 0 ]; then
echo "❌ 배포 전 점검 실패. 배포를 진행하지 마세요."
exit 1
else
echo "✅ 모든 점검 통과. 배포를 진행해도 됩니다."
exit 0
fi

배포 후 모니터링 포인트 (Golden Signals)

Google SRE에서 정의한 Four Golden Signals를 기반으로 배포 후 모니터링합니다.

지표설명정상 임계값알람 임계값
Latency(지연시간)p99 응답시간< 500ms> 2000ms
Traffic(트래픽)초당 요청 수(RPS)배포 전 대비 ±20%±50% 이상 변동
Errors(에러율)5xx 에러 비율< 0.1%> 1%
Saturation(포화도)CPU/메모리/스레드 사용률< 70%> 90%

무중단 배포 Anti-패턴

다음 행동들은 무중단 배포를 실패하게 만드는 대표적인 안티패턴입니다.

절대 하지 말아야 할 것들:

  1. 헬스체크 없이 로드밸런서 복구: 서버가 아직 준비되지 않았는데 트래픽을 보내는 가장 흔한 실수
  2. 드레이닝 시간 0으로 설정: Nginx에서 노드 제거 즉시 Tomcat을 종료하면 진행 중인 요청이 끊김
  3. 배포 직후 바로 다음 배포: 이전 배포의 영향이 남아 있는 상태에서 연속 배포 시 원인 파악 불가
  4. 롤백 계획 없는 배포: 헬스체크 실패 시 어떻게 할지 정하지 않고 배포 진행
  5. 프로덕션과 다른 스테이징 환경: 스테이징에서는 OK인데 프로덕션에서 실패하는 근본 원인

페일오버 드릴(Failover Drill) 정기 실행 계획

주기실험 내용담당예상 영향
월 1회단일 Tomcat 인스턴스 강제 종료DevOps 팀트래픽 다른 인스턴스로 자동 전환
분기 1회Nginx 서버 재시작DevOps + 개발 팀< 5초 중단 허용
반기 1회DB 페일오버 시뮬레이션DBA + DevOps모니터링 강화
연 1회전체 데이터센터 장애 시뮬레이션전 팀 GameDay사전 공지 후 진행

고가용성 최종 점검 체크리스트

배포 완료 후 HA 구성이 올바른지 확인하는 최종 체크리스트입니다.

# HA 최종 점검 체크리스트

## 인프라 구성
[ ] Tomcat 인스턴스 2개 이상 운영 중
[ ] Nginx 업스트림에 모든 인스턴스 등록 확인
[ ] 헬스체크 엔드포인트 모든 인스턴스에서 200 응답
[ ] 로드밸런서 failover 테스트 완료

## 배포 파이프라인
[ ] Blue-Green 또는 Rolling 배포 구성 확인
[ ] 자동 롤백 스크립트 테스트 완료
[ ] 헬스체크 타임아웃 값 적절히 설정 (너무 짧지 않게)
[ ] CI/CD 파이프라인 스테이징 → 프로덕션 분기 확인

## 모니터링
[ ] Prometheus/Grafana 대시보드 정상 동작
[ ] 알람 규칙 P1/P2/P3 우선순위 분류 완료
[ ] Slack/PagerDuty 알림 수신 테스트 완료
[ ] 로그 집계 (ELK/Loki) 정상 동작 확인

## 장애 대응
[ ] 런북 최신 버전으로 업데이트 확인
[ ] 온콜(On-call) 로테이션 담당자 확인
[ ] 에스컬레이션 연락처 목록 최신 상태 유지
[ ] 최근 3개월 내 페일오버 드릴 실시 확인

카오스 엔지니어링과 런북은 단순한 도구가 아니라 팀의 회복탄력성(Resilience) 문화를 만드는 실천입니다. 장애가 발생했을 때가 아닌, 발생하기 전에 시스템의 약점을 찾아 개선하는 것이 진정한 고가용성 운영의 핵심입니다.