← 메인으로 돌아가기

🏗️ EKS 기반 사이트 HA 구성

Docker Compose → EKS Multi-AZ HA 구성 실무 가이드

📅 2025년 10월 4일 👥 인프라 엔지니어, DevOps 엔지니어 📊 중급 ⏱️ 60분

🎯 학습 목표

📋 목차

  1. 아키텍처 설계 진화 과정
  2. Kubernetes HA 구성 구현
  3. Docker to K8s 마이그레이션 회고
  4. 실무 팁과 함정

🏗️ 아키텍처 설계 진화 과정

🔹 초기 설계 (v06) - 기본 구조

EduStack 아키텍처 v06 - 초기 설계

v06: 기본 구조 설계 - ALB 배치 문제 발견

사용자 → CloudFront → ALB → EKS Pods
                  → S3 (정적 콘텐츠)

발견된 문제점

  • ALB가 퍼블릭 서브넷에 제대로 배치되지 않음
  • Pod 배치가 HA 구성이 아님
  • VPC 내부/외부 서비스 경계가 불분명

🔹 점진적 개선 (v07-v10)

v07: VPC 내부/외부 서비스 구분 - "핵심 깨달음"

EduStack 아키텍처 v07 - 네트워크 분석

v07: VPC 내부/외부 서비스 구분 - 핵심 깨달음

핵심 깨달음: VPC 내부/외부 서비스를 명확히 구분해야 함

🌐 VPC 외부 서비스 (글로벌/리전)
  • S3: 글로벌 오브젝트 스토리지
  • CloudFront: 글로벌 CDN
  • WAF: 글로벌 보안 서비스
🏠 VPC 내부 서비스 (VPC 종속)
  • ALB: 퍼블릭 서브넷에 Multi-AZ 배치
  • NAT Gateway: 아웃바운드 트래픽용
  • EKS 워커 노드: 프라이빗 서브넷에 배치

트래픽 라우팅 경로:

정적 콘텐츠: 사용자 → WAF → CloudFront → S3
동적 API: 사용자 → WAF → CloudFront → VPC → ALB → EKS Pods

v08: ALB 정확한 표현 - "논리/물리적 개념 구분"

EduStack 아키텍처 v08 - ALB 정확한 표현

v08: ALB 정확한 표현 - 논리/물리적 개념 구분

ALB 정확한 표현: 논리적 개념과 물리적 배치를 명확히 구분

v09: HA 구성 설계 - "단일 장애점 제거"

EduStack 아키텍처 v09 - HA 구성

v09: HA 구성 완성 - 단일 장애점 제거

❌ 기존 문제점
AZ-1: FastAPI + PostgreSQL
AZ-2: FastAPI + Redis

→ 각각 단일 장애점

✅ HA 구성 해결책
각 AZ: FastAPI + PostgreSQL + Redis

→ 완전한 분산

v10: 범례 크기 조정 - "가독성 개선"

EduStack 아키텍처 v10 - 최종 완성

v10: 범례 크기 조정 - 가독성 개선

HA 설계 원칙:

  1. Multi-AZ 배치: 모든 서비스를 여러 AZ에 분산
  2. 무상태 설계: FastAPI는 상태를 저장하지 않음
  3. 데이터 복제: PostgreSQL과 Redis 클러스터링
  4. 로드밸런싱: ALB가 트래픽을 균등 분산

🔹 최종 설계 (v10) - 완성된 구조

EduStack 아키텍처 v10 - 최종 완성

🎉 최종 완성! VPC 내부/외부 서비스가 명확히 구분된 HA 아키텍처

🌐 VPC 외부: S3, CloudFront, WAF
🏠 VPC 내부: ALB (Multi-AZ), NAT Gateway, EKS (HA 구성)

최종 아키텍처 특징

  • 완전한 HA: 모든 컴포넌트가 Multi-AZ 분산
  • 명확한 경계: VPC 내부/외부 서비스 구분
  • 확장 가능: Auto Scaling 지원
  • 보안 강화: 다중 보안 계층

⚙️ Kubernetes HA 구성 구현

🤔 핵심 개념 이해

🔹 Pod Anti-Affinity란?

🤔 왜 Anti-Affinity가 필요한가?

현재 문제 상황:

# 현재 상태 확인
kubectl get pods -o wide
# 결과: 모든 Pod가 같은 노드에 몰려있음

장애 시나리오: AZ-2a 장애 발생 → 모든 PostgreSQL Pod 다운 → 서비스 전체 중단 💥

Anti-Affinity 해결책:
Pod Anti-Affinity: "다른 PostgreSQL Pod와는 떨어져 있고 싶어!"
결과: 각 AZ에 하나씩 분산 배치 → 일부 장애 시에도 서비스 유지

🔹 StatefulSet vs Deployment 심화 이해

용어 분해:

상태(State)란?
  • Stateless (무상태): 메모리에 아무것도 저장 안 함, 재시작해도 똑같음
    → 예시: 계산기 앱 (껐다 켜도 똑같이 작동)
  • Stateful (상태 보유): 데이터를 저장하고 기억함, 재시작 시 복구 필요
    → 예시: 데이터베이스 (데이터 파일이 있어야 함)
📱 FastAPI (웹 서버)     → Deployment (Stateless)
   "요청 처리만 하고 끝, 메모리에 저장 안 함"

💾 PostgreSQL (데이터베이스) → StatefulSet (Stateful)  
   "데이터 파일 저장, 순서대로 시작/종료 필요"

🗄️ Redis (캐시)         → StatefulSet (Stateful)
   "메모리 데이터 보존 필요"

🔹 Topology Spread Constraints 심화

Anti-Affinity vs Topology Spread 차이점
Anti-Affinity: "절대 같은 곳에 있으면 안 돼!" (엄격함)
Topology Spread: "가능하면 고르게 분산해줘" (유연함)
  • FastAPI 특성: Stateless라서 어디든 배치 가능 → 균등 분산이 최적
  • 예시: 2개 Pod를 2개 AZ에 → 각 AZ마다 1개씩 균등 배치

🔹 Ingress가 왜 필요한가?

Service (ClusterIP)

클러스터 내부에서만 접근 가능

postgres:5432 ← FastAPI에서만 접근
Ingress

외부 인터넷에서 접근 가능

https://your-domain.com/api → FastAPI로 라우팅
왜 ALB Ingress를 사용하나?
  • 자동 ALB 생성: YAML로 ALB 설정 자동화
  • SSL 종료: HTTPS 인증서 자동 적용
  • 경로 라우팅: /api/* → FastAPI, /static/* → S3
  • 헬스체크: 자동 Pod 상태 확인

📁 infrastructure/k8s 파일 변경 전후

🔹 변경 전: 기본 구조 (3개 파일)

infrastructure/k8s/
├── namespace.yaml
├── postgres-deployment.yaml    # ❌ Deployment, replicas: 1
└── backend-deployment.yaml     # ❌ 분산 정책 없음

🔹 변경 후: HA 구성 (6개 파일)

infrastructure/k8s/
├── namespace.yaml                    # 네임스페이스 분리
├── postgres-deployment.yaml         # ✅ StatefulSet + Anti-Affinity
├── redis-statefulset.yaml           # ✅ 새로 생성 (HA 구성)
├── backend-deployment.yaml          # ✅ Topology Spread 추가
├── backend-service.yaml             # LoadBalancer Service
└── ingress.yaml                     # ✅ 새로 생성 (ALB Ingress)
🎯 주요 변경사항:
  • 파일 수: 3개 → 6개 (Redis, Ingress 추가)
  • PostgreSQL: Deployment → StatefulSet (영구 스토리지)
  • Redis: 없음 → StatefulSet (캐시 + 세션)
  • FastAPI: 기본 → Topology Spread (균등 분산)
  • 외부 접근: 없음 → ALB Ingress (HTTPS)

🔧 실제 구현 과정

🔹 PostgreSQL StatefulSet 변경

📁 파일 위치: infrastructure/k8s/postgres-deployment.yaml
변경 전 (Deployment)
kind: Deployment          # ❌ Stateless
spec:
  replicas: 1             # ❌ 단일 인스턴스
  template:
    spec:
      # ❌ Anti-Affinity 없음
      containers:
      - name: postgres
        # ❌ 영구 스토리지 없음
변경 후 (StatefulSet) - 상세 설명
apiVersion: apps/v1
kind: StatefulSet         # ✅ Stateful - 상태를 기억하는 Pod 집합
metadata:
  name: postgres
spec:
  serviceName: postgres   # ✅ StatefulSet 필수 - 안정적인 네트워크 ID
  replicas: 2             # ✅ HA 구성 - 2개 인스턴스로 가용성 확보
  template:
    spec:
      affinity:           # ✅ Anti-Affinity 추가
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  # 엄격한 규칙
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - postgres
            topologyKey: topology.kubernetes.io/zone  # AZ 단위로 분산
      containers:
      - name: postgres
        image: postgres:15
        volumeMounts:     # ✅ 영구 스토리지 - 데이터 보존
        - name: postgres-storage
          mountPath: /var/lib/postgresql/data  # PostgreSQL 데이터 디렉토리
  volumeClaimTemplates:   # ✅ 각 Pod마다 독립 스토리지
  - metadata:
      name: postgres-storage
    spec:
      accessModes: ["ReadWriteOnce"]  # 하나의 노드에서만 읽기/쓰기
      resources:
        requests:
          storage: 10Gi   # 10GB 영구 스토리지 할당
🔍 주요 설정 설명
  • serviceName: StatefulSet의 안정적인 네트워크 식별자
  • topologyKey: 분산 기준 (zone = AZ 단위)
  • volumeClaimTemplates: 각 Pod마다 독립적인 영구 스토리지
  • ReadWriteOnce: 하나의 노드에서만 마운트 가능

🔹 최종 Pod 구성 (총 6개)

AZ-2a: 3개 Pod
  • postgres-0
  • redis-0
  • edustack-backend-1
AZ-2c: 3개 Pod
  • postgres-1
  • redis-1
  • edustack-backend-2

📁 Docker to K8s 마이그레이션 회고

🔄 4단계 진화 과정

1단계: 기본 K8s 변환

Docker Compose → 기본 K8s 매니페스트 (3개 파일)

2단계: HA 구성 적용

단일 인스턴스 → Multi-AZ 분산 (Anti-Affinity)

3단계: 상태 관리 개선

Deployment → StatefulSet (영구 스토리지)

4단계: 외부 접근 설정

ClusterIP → ALB Ingress (외부 접근)

📊 복잡도 vs 안정성 트레이드오프

단계 파일 수 가용성 확장성 관리 난이도
Docker Compose 1개 낮음 ❌ 낮음 ❌ 쉬움 😊
기본 K8s 3개 중간 ⚠️ 중간 ⚠️ 보통 😐
HA K8s 6개 높음 ✅ 높음 ✅ 어려움 😅

💡 핵심 인사이트

  1. 복잡도 증가는 필연적: 1개 파일 → 6개 파일
  2. 하지만 각 파일의 역할이 명확: 관심사 분리
  3. 점진적 개선이 핵심: 한 번에 모든 걸 바꾸지 않음
  4. 실제 문제 해결 중심: 이론이 아닌 실무 요구사항

🚨 실무 팁과 함정

🔹 흔한 설계 실수들

❌ ALB 위치 오해

잘못된 인식: ALB는 VPC 외부 서비스

올바른 이해: ALB는 VPC 내부, 퍼블릭 서브넷에 배치

❌ 단일 AZ 배치

위험한 구성: 모든 Pod를 하나의 AZ에 배치

안전한 구성: Multi-AZ에 균등 분산

🔹 AWS 네트워킹 모범 사례

퍼블릭 서브넷: ALB, NAT Gateway
프라이빗 서브넷: EKS 워커 노드, 애플리케이션

ALB SG: 80, 443 포트만 인터넷에서 허용
Worker SG: ALB에서만 트래픽 허용

🔹 비용 최적화 고려사항

🧪 실습: 네트워크 테스트

🔹 1. 연결성 테스트

# ALB 헬스체크 확인
kubectl get ingress
kubectl describe ingress edustack-ingress

# Pod 간 통신 테스트
kubectl exec -it fastapi-pod -- curl postgres:5432
kubectl exec -it fastapi-pod -- redis-cli -h redis ping

# 서비스 DNS 해상도 테스트
kubectl exec -it fastapi-pod -- nslookup postgres
kubectl exec -it fastapi-pod -- nslookup redis

🔹 2. HA 테스트 - 장애 시뮬레이션

⚠️ 주의: 개발 환경에서만 실행하세요!
# 현재 Pod 분산 상태 확인
kubectl get pods -o wide --show-labels

# 하나의 AZ 장애 시뮬레이션
kubectl cordon node-in-az-2a  # 노드 스케줄링 중단
kubectl delete pod postgres-0  # Pod 강제 삭제

# 복구 과정 관찰
kubectl get pods -o wide -w  # 실시간 상태 변화 관찰
kubectl logs postgres-1      # 남은 Pod 로그 확인

# 복구 후 정리
kubectl uncordon node-in-az-2a  # 노드 스케줄링 재개

🔹 3. 로드밸런싱 테스트

# 트래픽 분산 확인
for i in {1..10}; do
  curl -s https://your-domain.com/api/edustack/health | jq .hostname
done

# ALB 타겟 그룹 상태 확인
aws elbv2 describe-target-health \
  --target-group-arn arn:aws:elasticloadbalancing:... \
  --profile shared-service

# Pod 리소스 사용량 모니터링
kubectl top pods
kubectl top nodes

🔹 4. 문제 해결 가이드

❌ 자주 발생하는 문제들
# Pod가 Pending 상태로 멈춤
kubectl describe pod postgres-0
# 원인: 리소스 부족, Anti-Affinity 조건 불만족

# Service 연결 안됨
kubectl get endpoints postgres
# 원인: 라벨 셀렉터 불일치

# Ingress 접근 안됨
kubectl describe ingress edustack-ingress
# 원인: ALB 생성 실패, 서브넷 설정 오류
✅ 해결 방법
  • 리소스 부족: 노드 추가 또는 리소스 요청량 조정
  • 라벨 불일치: Service selector와 Pod labels 확인
  • ALB 생성 실패: 서브넷 태그 및 IAM 권한 확인
  • DNS 해상도 실패: CoreDNS 상태 및 네트워크 정책 확인

🔹 5. 성능 모니터링

# CloudWatch 메트릭 확인
aws cloudwatch get-metric-statistics \
  --namespace AWS/ApplicationELB \
  --metric-name RequestCount \
  --dimensions Name=LoadBalancer,Value=app/edustack-alb/... \
  --start-time 2025-10-04T00:00:00Z \
  --end-time 2025-10-04T23:59:59Z \
  --period 3600 \
  --statistics Sum

# Kubernetes 메트릭
kubectl top pods --sort-by=cpu
kubectl top pods --sort-by=memory

# HPA 설정 (Auto Scaling)
kubectl autoscale deployment edustack-backend \
  --cpu-percent=70 --min=2 --max=10

🎯 핵심 요약

🔹 설계 원칙

  1. 명확한 경계: VPC 내부/외부 서비스 구분
  2. HA 구성: Multi-AZ 배치 필수
  3. 보안 계층: 다중 보안 장치 적용
  4. 확장성: Auto Scaling 고려

🔹 구현 체크리스트

🔹 다음 단계

학습 목표 아키텍처 진화 K8s HA 구현 마이그레이션 회고 실무 팁
🏠