10 AWS 프로덕션 사고가 나에게 가르쳐 준 실전 SRE

발행: (2026년 1월 9일 오전 01:25 GMT+9)
17 min read
원문: Dev.to

I’m happy to translate the article for you, but I need the full text of the article (or the portion you’d like translated) in order to do so. Could you please paste the content you want translated here? Once I have the text, I’ll keep the source line exactly as you provided and translate the rest into Korean while preserving the original formatting.

1️⃣ 3 AM 경보 – 403 오류

증상

  • CloudWatch 알람: 4XX 오류 증가.
  • 트래픽은 정상으로 보였지만 **≈ 30 %**의 요청이 403을 반환함.

내가 생각한 원인

  • API Gateway 제한 또는 IAM 권한 문제.

실제 원인

  • 코드 배포가 JWT 검증 로직을 변경함.
  • 구버전 모바일 앱(사용자 약 30 %가 여전히 사용)에서 발급된 토큰이 거부되고 있었음.

해결 방법

# Roll back the problematic deployment
aws deploy rollback \
    --application-name my-app \
    --deployment-group-name prod

# Add backward‑compatible token validation
# (code change – omitted for brevity)

신속한 조치

  • 배포를 롤백함.
  • 구버전 토큰에 대한 호환성을 추가함.
  • 앱 버전 분포를 모니터링하는 CloudWatch 메트릭을 설정함.

2️⃣ 피크 트래픽 시 5XX 급증

증상

  • 5XX 오류가 급증하고, 로드밸런서 헬스 체크는 통과.
  • **≈ 15 %**의 요청이 실패.

내가 생각한 원인

  • 백엔드 서비스가 과부하.

실제 원인

  • 트래픽 급증 중 콜드 스타트 때문에 Lambda 함수가 타임아웃되어 API Gateway를 통해 504 Gateway Timeout 반환.

해결 방법

# Enable provisioned concurrency for the hot functions
aws lambda put-provisioned-concurrency-config \
    --function-name my-function \
    --qualifier $LATEST \
    --provisioned-concurrent-executions 100
  • API Gateway 통합에 지수 백오프를 구현.

빠른 조치

  • 트래픽에 민감한 Lambda에 프로비저닝된 동시 실행을 활성화.
  • 동시 실행이 한계에 가까워질 때를 위한 CloudWatch 알람 추가.

3️⃣ 준비되지 않은 보조 리전으로의 Route 53 장애 조치

증상

  • 오후 2시에 Route 53 장애 조치가 전체 트래픽을 보조 리전으로 라우팅했으며, 해당 리전은 곧 과부하가 발생했습니다.

내가 생각한 원인

  • 기본 리전에 장애가 발생한 것으로 생각했습니다.

실제 원인

  • 보안‑그룹 변경으로 Route 53 헬스‑체크 엔드포인트가 차단되었습니다.
  • 서비스는 정상였지만 Route 53이 이를 확인할 수 없었습니다.

해결 방법

# Allow Route 53 health‑checker IP ranges
curl https://ip-ranges.amazonaws.com/ip-ranges.json |
jq -r '.prefixes[] | select(.service=="ROUTE53") | .ip_prefix' |
while read cidr; do
    aws ec2 authorize-security-group-ingress \
        --group-id sg-xxxxxx \
        --protocol tcp \
        --port 443 \
        --cidr "$cidr"
done
# Quick health‑check test
curl -v https://api.example.com/health

신속한 조치

  • 보안 그룹에 Route 53 헬스‑체커 IP 범위를 추가했습니다.
  • 엔드포인트 접근성 실제 서비스 상태를 모두 검증하는 내부 헬스 체크를 구현했습니다.

4️⃣ “Connection Pool Exhausted” 오류 – RDS

증상

  • 애플리케이션 로그: “connection pool exhausted”.
  • RDS 지표: CPU ≈ 20 %, 연결 수가 max_connections보다 훨씬 낮음.

내가 생각한 원인

  • DB의 max_connections를 늘려야 함.

실제 원인

  • 예외 발생 후 애플리케이션이 연결을 해제하지 않아 풀에 좀비 연결이 남아 있었음.

해결 방법

# Example context manager to ensure proper cleanup
from contextlib import contextmanager

@contextmanager
def db_cursor(conn):
    cur = conn.cursor()
    try:
        yield cur
    finally:
        cur.close()
        conn.commit()
  • 연결 타임아웃 설정, 서킷 브레이커 로직, 풀 상태를 추적하는 CloudWatch 대시보드 추가.

빠른 조치

  • 위의 컨텍스트 매니저를 구현함.
  • 풀 사용률이 **70 %**에 도달하면 알람을 설정(기존 95 % 대신).

5️⃣ Lambda “Rate Exceeded” Errors During a Batch Job

증상

  • 배치 작업을 처리하는 동안 Lambda 함수가 Rate exceeded 오류로 실패했습니다.
  • 작업이 완전히 중단되었습니다.

내가 생각한 원인

  • AWS 서비스 한도에 도달했다고 생각했습니다.

실제 원인

  • 배치 작업이 10 000개의 동시 DynamoDB 쓰기를 백오프 없이 수행하여 몇 초 만에 테이블의 쓰기 용량을 소진했습니다.

해결 방법

import time, random
from botocore.config import Config

def exponential_backoff_retry(func, max_retries=5):
    for attempt in range(max_retries):
        try:
            return func()
        except Exception as e:
            backoff = (2 ** attempt) + random.random()
            time.sleep(backoff)
            if attempt == max_retries - 1:
                raise e
# Use the built‑in retry config for the AWS SDK
config = Config(
    retries={
        'max_attempts': 10,
        'mode': 'standard'
    }
)

빠른 조치

  • DynamoDB 쓰기를 재시도 헬퍼로 감쌌습니다.
  • 쓰기 용량에 대해 DynamoDB 자동 스케일링을 활성화했습니다.

6️⃣ ALB 정상 인스턴스를 비정상으로 표시

증상

  • ALB가 간헐적으로 인스턴스를 비정상으로 표시 → 일부 요청에서 502 오류 발생.

내가 생각한 원인

  • 인스턴스가 실제로 부하 하에서 실패하고 있었다.

실제 원인

  • 헬스‑체크 간격이 5 s이고 2 s 타임아웃이 설정되어 있었다.
  • 짧은 CPU 스파이크가 헬스‑체크 응답을 방해해 잘못된 부정(negative) 헬스 보고가 발생했다.

해결 방법

# Adjust target‑group health‑check settings
aws elbv2 modify-target-group \
    --target-group-arn arn:aws:elasticloadbalancing:... \
    --health-check-interval-seconds 30 \
    --health-check-timeout-seconds 5 \
    --healthy-threshold-count 3 \
    --unhealthy-threshold-count 3
  • 헬스‑체크 엔드포인트를 경량화 (DB 쿼리 없음)했다.

모범 사례

✅ 할 일❌ 하지 말아야 할 일
프로세스가 살아 있는지만 확인하는 헬스 체크 (예: /ping).비용이 많이 드는 작업을 수행하는 헬스 체크 (예: 전체 DB 쿼리).

신속한 조치

  • 헬스‑체크 구성을 업데이트했다.
  • 경량 /ping 엔드포인트를 배포했다.

7️⃣ 낮은 트래픽 시 P99 지연이 8 초로 급증

증상

  • P99 지연이 8 초까지 급증했으며, P50은 조용한 기간 동안 200 ms를 유지했습니다.

내가 생각한 원인

  • 백엔드 데이터베이스 성능 저하.

실제 원인

  • Lambda 콜드 스타트. 함수가 유휴 기간 동안 종료되어 다음 요청 시 긴 시작 시간이 발생했습니다.

해결 방법

# 지연에 민감한 함수에 프로비저닝된 동시성 활성화
aws lambda put-provisioned-concurrency-config \
    --function-name api-handler \
    --qualifier $LATEST \
    --provisioned-concurrent-executions 200
# EventBridge 일정(5분마다 실행)으로 함수를 따뜻하게 유지
aws events put-rule \
    --name WarmLambdaRule \
    --schedule-expression "rate(5 minutes)"
  • 배포 패키지 크기를 ≈60 % 감소시켰습니다(사용하지 않는 라이브러리 제거).

신속한 조치

  • 사용자‑대면 API에 프로비저닝된 동시성을 적용했습니다.
  • 주기적인 “핑” 호출을 예약했습니다.
  • 패키지 크기를 최적화했습니다.

8️⃣ DynamoDB ProvisionedThroughputExceededException 발생 보고서 생성 중

증상

  • 쓰기는 성공했지만 일일 보고서 생성 중 ProvisionedThroughputExceededException 로 읽기가 실패했습니다.

내가 생각한 원인

  • 읽기 용량 단위를 늘려야 한다.

실제 원인

  • 보고서에서 페이지네이션 없이 Scan 작업을 사용해 핫 파티션이 발생했고, 몇 초 만에 모든 읽기 용량을 소모했습니다.

해결 방법

def paginated_query(table, key_condition):
    items = []
    last_evaluated_key = None

    while True:
        if last_evaluated_key:
            response = table.query(
                KeyConditionExpression=key_condition,
                ExclusiveStartKey=last_evaluated_key
            )
        else:
            response = table.query(
                KeyConditionExpression=key_condition
            )

        items.extend(response["Items"])
        last_evaluated_key = response.get("LastEvaluatedKey")
        if not last_evaluated_key:
            break

    return items
  • 가능한 경우 Query 로 전환했습니다.
  • 페이지네이션과 지수 백오프를 구현했습니다.
  • DynamoDB 자동 스케일링을 활성화하고 효율적인 접근 패턴을 위해 복합 정렬 키를 추가했습니다.

빠른 조치

  • 위의 페이지네이션 쿼리를 사용하도록 보고서 작업을 리팩터링했습니다.
  • 테이블에 자동 스케일링을 켰습니다.

9️⃣ 과도한 헬스‑체크로 인한 오탐

  • 지나치게 공격적인 헬스‑체크 간격은 정상인 인스턴스를 비정상으로 표시할 수 있습니다.
  • 균형: 실제 장애를 포착할 만큼 충분히 자주 수행하되, 일시적인 스파이크가 오경보를 일으키지 않을 정도로 조절합니다.

🔟 일반 교훈 및 요점

사건핵심 교훈
1 – JWT validation버전 호환성이 중요합니다; 롤아웃 시 항상 구버전 클라이언트를 지원하세요.
2 – Lambda cold starts프로비저닝된 동시성 + 워밍‑업 스케줄이 지연 스파이크를 완화합니다.
3 – Route 53 health checks보안‑그룹 규칙이 헬스‑체커 IP 범위를 허용해야 합니다.
4 – DB connection pools적절한 정리를 강제하고, 포화 상태에 도달하기 전에 풀 사용량을 모니터링하세요.
5 – DynamoDB rate limits첫 날부터 지수 백오프와 재시도를 구현하세요.
6 – ALB health checks헬스‑체크 엔드포인트를 가볍게 유지하고, 라우팅 체크와 깊은 체크를 분리하세요.
7 – P99 latency콜드 스타트가 꼬리 지연을 지배하므로, 프로비저닝된 동시성이 해답입니다.
8 – DynamoDB hot partitionsScan보다 Query를 선호하고, 페이지네이션을 사용하며, 키 분포를 고르게 설계하세요.
9 – Aggressive health checks너무 짧은 간격은 오탐을 유발하므로, 임계값과 타임아웃을 조정하세요.
10 – (Overall)관측성 우선 – 인스트루멘테이션, 알람, 가정을 테스트하여 사고가 되기 전에 대비하세요.

각 사건을 학습 기회로 삼고, 체계적이고 관측 가능한 해결책을 적용하면 혼란스러운 운영 대응을 예측 가능하고 회복력 있는 프로세스로 전환할 수 있습니다.

Incident 1 – Blue‑Green 배포 중 502 오류

증상

  • **5 %**의 요청이 매 배포 시 502 Bad Gateway 오류와 함께 실패했습니다. Blue‑Green 배포 전략을 사용했음에도 불구하고 발생했습니다.

초기 가설

  • 인스턴스가 너무 빨리 종료되고 있었습니다.

근본 원인

  • ALB의 연결‑드레인 타임아웃(등록 해제 지연)이 30 초로 설정되어 있었지만, 일부 API 호출은 60 초까지 걸렸습니다.
  • ALB가 요청 중에 해당 연결을 종료하면서 502 오류가 발생했습니다.

해결 방법

# 연결‑드레인 타임아웃(등록 해제 지연) 증가
aws elbv2 modify-target-group-attributes \
    --target-group-arn arn:aws:elasticloadbalancing:region:account-id:targetgroup/name/xxxxxxxxxxxx \
    --attributes Key=deregistration_delay.timeout_seconds,Value=120
  • 배포 중에 진행 중인 요청이 삭제되지 않았는지 확인하는 배포 건강‑검사를 추가했습니다.

신속히 수행한 조치

  1. 등록 해제 지연 시간을 늘렸습니다.
  2. 애플리케이션에 우아한 종료 루틴을 구현했습니다(새 요청 수락 중단, 기존 요청 완료).
  3. 배포 전 검증 단계를 추가했습니다.

교훈

  • 연결‑드레인 타임아웃은 가장 긴 요청 지연 시간보다 길어야 합니다.
  • P99 지연 시간을 정기적으로 모니터링하고 그에 맞게 타임아웃을 설정하세요.

사건 2 – 배포 스크립트가 보안 그룹을 일관되지 않게 남김

증상

  • 배포 스크립트가 중간에 실패하여 보안 그룹이 일관되지 않은 상태로 남음.
  • 인스턴스에 SSH 접속이 불가능하고 배포를 롤백할 수 없음.

내가 생각한 것

  • 보안 그룹을 수동으로 수정한다.

실제 원인

  • 자동화 스크립트에 롤백 메커니즘이 없었으며 테스트 없이 프로덕션 보안 그룹을 변경함.

해결 방법

# Open a Session Manager session to the affected instance
aws ssm start-session --target i-1234567890abcdef0
  1. 현재 보안 그룹을 확인 (참고용).

  2. 원자적으로 변경 – 예: 필요한 규칙을 추가:

    aws ec2 authorize-security-group-ingress \
        --group-id sg-12345 \
        --ip-permissions IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges='[{CidrIp=0.0.0.0/0}]'
  3. 변경이 정상적으로 적용됐는지 검증.

  4. 검증이 성공한 후에만 기존 규칙을 제거.

더 나은 접근법

  • AWS CloudFormation(또는 기타 IaC 도구)으로 보안 그룹을 관리하여 원자적이고 버전‑관리된 업데이트를 보장한다.

신속히 취한 조치

  • 모든 인스턴스에 Systems Manager Session Manager를 활성화.
  • 보안 그룹 관리를 CloudFormation으로 전환.
  • 변경 승인 워크플로우를 구현.

교훈

  • 프로덕션에서 보안 그룹을 수동으로 수정하면 안 된다. 한 번의 실수로 접근이 차단될 수 있다.
  • Infrastructure‑as‑Code와 Session Manager를 안전망으로 활용하라.

이를 쉽게 만드는 도구들

When incidents happen, speed matters. I built an Incident Helper script that automates the repetitive parts of incident response:

  • Collects relevant CloudWatch logs.
  • Checks service health status.
  • Identifies common AWS misconfigurations.

실제 교훈

  • Document 모든 사고를 기록합니다.
  • runbooks 를 작성하고 유지합니다.
  • 정기적으로 fail‑over 절차를 테스트합니다.
  • 팀과 함께 주간 사후 검토 회의를 진행합니다.

다음 사고는 이미 예정되어 있습니다; 언제 일어날지는 모를 뿐입니다.
준비가 되어 있으면 모든 차이를 만들 수 있습니다.

Back to Blog

관련 글

더 보기 »

제로에서 SQS Lambda까지 15분 안에

내 AWS 여정 나는 현재 조직에 입사했을 때 AWS 여정을 시작했습니다. 그 이전에는 VPS와 bare‑metal 서버를 사용하며 완전히 만족했습니다. 긴 이야기…