[EKS Best Practices] Reliability - Applications
EKS Best Practices - 애플리케이션 신뢰성 (Reliability)
Kubernetes는 선언적(declarative) 관리를 통해 애플리케이션을 고가용성(HA)으로 운영할 수 있게 해줍니다. 이 문서는 AWS EKS 환경에서 애플리케이션의 신뢰성을 높이기 위한 핵심 권장사항을 정리합니다.
전체 구조 개요
mindmap
root((애플리케이션 신뢰성))
고가용성 구성
Pod Disruption Budget
다중 Replica 운영
노드/AZ 분산 배치
Metrics Server
오토스케일링
HPA - 수평 확장
VPA - 수직 확장
업데이트 전략
Rolling Update
Blue/Green
Canary
Rollback 메커니즘
Health Check
Liveness Probe
Startup Probe
Readiness Probe
Disruption 대응
PDB
Graceful Shutdown
Chaos Engineering
Service Mesh
Observability
Monitoring
Logging
Distributed Tracing1. 고가용성 애플리케이션 구성
1.1 핵심 권장사항 요약
| 권장사항 | 설명 | 중요도 |
|---|---|---|
| PDB 설정 | 동시 중단 Pod 수를 제한하여 가용성 보장 | 필수 |
| Singleton Pod 금지 | 단일 Pod 대신 반드시 Deployment 사용 | 필수 |
| 다중 Replica 운영 | 최소 2개 이상 Replica로 장애 대비 | 필수 |
| 노드/AZ 분산 배치 | Anti-Affinity 또는 Topology Spread 활용 | 권장 |
| Metrics Server 설치 | HPA/VPA 오토스케일링의 기반 | 필수 |
1.2 Pod Disruption Budget (PDB)
PDB는 자발적 중단(voluntary disruption) 시 동시에 중단되는 Pod 수를 제한합니다. EKS Auto Mode, Karpenter, Cluster Autoscaler 모두 PDB를 준수합니다.
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: my-svc-pdb
spec:
minAvailable: 3 # 최소 3개 Pod는 항상 유지
selector:
matchLabels:
app: my-svc
참고: EKS Managed Node Group 업그레이드 시 15분 timeout이 적용됩니다. 15분 내에 drain이 완료되지 않으면 강제 업데이트가 아닌 경우 업데이트가 실패합니다.
1.3 Replica 분산 배치 전략
Pod를 여러 노드와 AZ에 분산 배치해야 단일 노드 장애 시에도 서비스가 유지됩니다.
graph TB
subgraph AZ-a
Node1[Node 1]
Pod1[Pod replica 1]
Pod2[Pod replica 2]
Node1 --> Pod1
Node1 --> Pod2
end
subgraph AZ-b
Node2[Node 2]
Pod3[Pod replica 3]
Pod4[Pod replica 4]
Node2 --> Pod3
Node2 --> Pod4
end
subgraph AZ-c
Node3[Node 3]
Pod5[Pod replica 5]
Pod6[Pod replica 6]
Node3 --> Pod5
Node3 --> Pod6
end
style AZ-a fill:#e1f5fe
style AZ-b fill:#e8f5e9
style AZ-c fill:#fff3e0방법 1: Pod Anti-Affinity
노드와 AZ 기준으로 Pod를 분산시킵니다. preferredDuringSchedulingIgnoredDuringExecution을 사용하면 분산을 선호하되 불가능한 경우에도 스케줄링을 허용합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: spread-host-az
labels:
app: web-server
spec:
replicas: 4
selector:
matchLabels:
app: web-server
template:
metadata:
labels:
app: web-server
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-server
topologyKey: topology.kubernetes.io/zone # AZ 분산
weight: 100
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-server
topologyKey: kubernetes.io/hostname # 노드 분산
weight: 99
containers:
- name: web-app
image: nginx:1.16-alpine
Tip: Replica 수가 AZ 수와 같다면(예: 3 Replica, 3 AZ)
requiredDuringSchedulingIgnoredDuringExecution을 사용하여 각 AZ에 반드시 1개씩 배치할 수 있습니다.
방법 2: Pod Topology Spread Constraints
Anti-Affinity보다 세밀한 분산 제어가 가능합니다. 핵심 속성은 다음과 같습니다.
| 속성 | 설명 | 예시 |
|---|---|---|
maxSkew |
토폴로지 도메인 간 최대 불균형 허용치 | 1 → 10개 Pod가 3 AZ에 4,3,3으로 분산 |
topologyKey |
분산 기준이 되는 노드 레이블 키 | topology.kubernetes.io/zone |
whenUnsatisfiable |
제약 조건 미충족 시 동작 | ScheduleAnyway 또는 DoNotSchedule |
labelSelector |
분산 대상 Pod 선택 기준 | matchLabels: {app: web-server} |
apiVersion: apps/v1
kind: Deployment
metadata:
name: spread-host-az
spec:
replicas: 10
selector:
matchLabels:
app: web-server
template:
metadata:
labels:
app: web-server
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: "topology.kubernetes.io/zone"
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web-server
containers:
- name: web-app
image: nginx:1.16-alpine
Anti-Affinity vs Topology Spread Constraints 비교
| 항목 | Pod Anti-Affinity | Topology Spread Constraints |
|---|---|---|
| 분산 방식 | Pod 간 반발력(repelling) 기반 | 균등 분배(even distribution) 기반 |
| 불균형 제어 | 제한적 (1개씩만 배치되는 경향) | maxSkew로 세밀한 제어 가능 |
| Fault Tolerance | 도메인당 1개 Replica만 존재할 수 있음 | 도메인당 여러 Replica 유지 가능 |
| 리소스 효율 | 낮음 (단일 Replica 전용 노드 발생) | 높음 (균등 분배로 효율적) |
| 적합한 상황 | 단순한 분산 요구 | 복잡한 분산 요구, 대규모 Replica |
2. 오토스케일링
2.1 Horizontal Pod Autoscaler (HPA)
HPA는 트래픽 증가에 대응하여 Pod 수를 수평으로 자동 확장합니다. 주기적으로 메트릭을 조회하는 Control Loop로 구현됩니다.
flowchart LR
A[Metrics Source] --> B{HPA Controller}
B -->|Scale Up| C[Deployment]
B -->|Scale Down| C
C --> D[Pod Replicas]
subgraph "메트릭 API"
E["metrics.k8s.io\n(CPU/Memory)"]
F["custom.metrics.k8s.io\n(Prometheus 등)"]
G["external.metrics.k8s.io\n(SQS, ELB 등)"]
end
E --> A
F --> A
G --> AHPA 메트릭 소스
| API | 설명 | 사용 예시 |
|---|---|---|
metrics.k8s.io |
CPU/Memory 리소스 메트릭 | Pod CPU 사용률 70% 초과 시 확장 |
custom.metrics.k8s.io |
클러스터 내부 커스텀 메트릭 | Prometheus 메트릭 기반 확장 |
external.metrics.k8s.io |
클러스터 외부 메트릭 | SQS Queue 깊이, ELB 지연 시간 기반 확장 |
커스텀/외부 메트릭 활용
- Prometheus Adapter: Prometheus 메트릭을 Metrics API 형식으로 노출하여 HPA에서 사용
- KEDA (Kubernetes Event-driven Autoscaling): 다양한 이벤트 소스 기반 오토스케일링 지원
- Amazon Managed Prometheus: AWS 관리형 Prometheus를 활용한 워크로드 오토스케일링
# Custom Metrics 확인
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/
2.2 Vertical Pod Autoscaler (VPA)
VPA는 Pod의 CPU/Memory 리소스 요청(request)을 자동으로 조정하여 Right-sizing을 수행합니다.
| 항목 | HPA | VPA |
|---|---|---|
| 확장 방향 | 수평 (Pod 수 증감) | 수직 (Pod 리소스 증감) |
| In-place 업데이트 | 해당 없음 | 현재 미지원 (Pod 재생성 필요) |
| 가용성 영향 | 낮음 | 일시적 서비스 중단 가능 |
| 적합한 워크로드 | Stateless 웹 서비스 | 리소스 요구량이 가변적인 앱 |
주의: VPA는 현재 in-place 조정을 지원하지 않으므로, 스케일링 시 Pod가 재생성됩니다. 일시적인 서비스 중단이 발생할 수 있습니다.
Fairwinds Goldilocks를 사용하면 VPA 권장 값을 대시보드로 시각화하고, 자동 스케일링을 설정할 수 있습니다.
3. 애플리케이션 업데이트 전략
3.1 Rolling Update (기본 전략)
Kubernetes Deployment의 기본 업데이트 전략입니다. 전체 Pod를 한 번에 교체하지 않고, 일부씩 순차적으로 교체합니다.
| 속성 | 설명 | 기본값 | 권장 설정 |
|---|---|---|---|
maxUnavailable |
업데이트 중 동시에 중단 가능한 Pod 수/비율 | 25% | 워크로드에 맞게 조정 |
maxSurge |
원하는 Pod 수 이상으로 추가 생성 가능한 Pod 수/비율 | 25% | 워크로드에 맞게 조정 |
예시: 100개 Pod,
maxUnavailable=25%인 경우 업데이트 중 최소 75개 Pod만 가동됩니다. 최소 80개가 필요하다면maxUnavailable=20%로 설정해야 합니다.
Rollback 메커니즘
# 이미지 업데이트 (--record 플래그로 변경 이력 기록)
kubectl --record deployment.apps/nginx-deployment set image \
nginx-deployment nginx=nginx:1.16.1
# 변경 이력 확인
kubectl rollout history deployment nginx-deployment
# Rollback 수행
kubectl rollout undo deployment nginx-deployment
3.2 세 가지 배포 전략 비교
graph LR
subgraph Rolling Update
R1[v1 Pod] -->|순차 교체| R2[v2 Pod]
end
subgraph Blue/Green
B1[v1 전체] -->|트래픽 전환| B2[v2 전체]
end
subgraph Canary
C1[v1 90%] -->|점진적 전환| C2[v2 10%]
C2 -->|확인 후 확대| C3[v2 100%]
end| 전략 | 설명 | 장점 | 단점 | 도구 |
|---|---|---|---|---|
| Rolling Update | Pod를 순차적으로 교체 | 간단, 기본 제공 | 이전/이후 버전 혼재 기간 존재 | Kubernetes 내장 |
| Blue/Green | 새 환경을 완전히 구성 후 트래픽 전환 | 즉시 Rollback 가능 | 2배의 리소스 필요 | AWS LB Controller, Flux, Jenkins, Spinnaker |
| Canary | 소량의 트래픽만 새 버전으로 전환, 점진적 확대 | 리스크 최소화 | 구성 복잡 | Flagger + Istio, KEDA |
3.3 Blue/Green 배포 구현
Kubernetes에서 Blue/Green 배포를 구현하는 방법:
- 기존 Deployment와 동일한 새 Deployment를 생성
- 새 Pod가 정상 동작하는지 검증
- Service의
selector를 새 Deployment의 Pod로 변경하여 트래픽 전환 - 문제 발생 시
selector를 원래 Deployment로 되돌려 Rollback
4. Health Check 및 Self-Healing
Kubernetes는 3가지 유형의 Health Check를 제공하며, kubelet이 모든 체크를 수행합니다.
flowchart TD
A[kubelet] --> B{Probe 유형}
B -->|Startup Probe| C[앱 시작 완료 확인]
B -->|Liveness Probe| D[앱 정상 동작 확인]
B -->|Readiness Probe| E[트래픽 수신 가능 확인]
C -->|성공| F[Liveness/Readiness 활성화]
C -->|실패| G[Pod 재생성]
D -->|실패| H[Pod 재생성]
D -->|성공| I[정상 유지]
E -->|실패| J[Service에서 제외]
E -->|성공| K[트래픽 수신]
style G fill:#ffcdd2
style H fill:#ffcdd2
style J fill:#fff9c4
style K fill:#c8e6c94.1 Probe 유형별 비교
| Probe | 목적 | 실패 시 동작 | 사용 시점 |
|---|---|---|---|
| Liveness | Deadlock 등 앱 비정상 상태 감지 | Pod 재생성 | 앱이 실행 중이지만 응답 불가 상태 감지 |
| Startup | 앱 시작 완료 여부 확인 | Pod 재생성 | 시작 시간이 긴 앱 (예: 캐시 로딩 Java 앱) |
| Readiness | 일시적 서비스 불가 감지 | Service에서 제외 (트래픽 차단) | I/O 집중 작업 등 일시적 부하 |
4.2 Probe 체크 방식
kubelet은 3가지 방식으로 Pod 상태를 확인할 수 있습니다:
| 방식 | 설명 | 적합한 상황 |
|---|---|---|
| exec | 컨테이너 내부에서 Shell 명령 실행 | 커스텀 스크립트로 복잡한 상태 확인 |
| httpGet | HTTP GET 요청 (200-399 = 정상) | 웹 서비스의 Health Endpoint |
| tcpSocket | TCP 연결 가능 여부 확인 | 데이터베이스 등 TCP 기반 서비스 |
4.3 Probe 설정 시 주의사항
| 주의사항 | 설명 |
|---|---|
| 외부 의존성 제외 | Liveness/Readiness Probe에 외부 DB 등 의존성을 포함하지 말 것. DB 장애 시 모든 Pod가 동시에 실패하여 전체 서비스 중단 발생 |
| 동시 실패 방지 | 모든 Pod가 동시에 Liveness 실패하면 Kubernetes가 전부 재생성 시도 -- 서비스 완전 중단 + Control Plane 부하 |
| exec Probe timeout | exec 방식 사용 시 timeoutSeconds 내에 명령이 완료되어야 함. 초과 시 <defunct> 프로세스 발생 -- 노드 장애 |
| Startup Probe 사용 기준 | 시작 시간이 예측 불가한 경우 Startup Probe 사용. 고정된 시작 시간이라면 initialDelaySeconds로 충분 |
| Readiness와 업데이트 | Readiness Probe 사용 시 Deployment 업데이트가 느려질 수 있음 (새 Pod가 Ready 되어야 이전 Pod 제거) |
5. Disruption 대응
5.1 Graceful Shutdown 프로세스
Pod 종료 시 다음 순서로 진행됩니다:
sequenceDiagram
participant K as Kubernetes
participant C as Container (PID 1)
participant P as PreStop Hook
K->>P: 1. PreStop Hook 실행
P-->>K: Hook 완료
K->>C: 2. SIGTERM 전송
Note over C: Grace Period 대기
(기본 30초)
K->>C: 3. SIGKILL 전송
(Grace Period 초과 시)중요:
terminationGracePeriodSeconds는 PreStop Hook 실행 시점부터 카운트됩니다. SIGTERM 전송 시점이 아닙니다.
5.2 PID 1 문제와 해결
Shell script가 ENTRYPOINT인 경우, 실제 애플리케이션 프로세스가 PID 1이 아닐 수 있습니다. 이 경우 SIGTERM이 앱에 전달되지 않습니다.
| 문제 상황 | 해결 방법 |
|---|---|
| Shell script가 PID 1 | ENTRYPOINT를 앱 프로세스로 직접 변경 |
| 복잡한 초기화 스크립트 필요 | dumb-init 등 init 시스템 사용 |
| 종료 전 정리 작업 필요 | PreStop Hook 활용 |
# 문제 확인: PID 1이 shell script인 경우
$ kubectl exec python-app -it -- ps
PID USER TIME COMMAND
1 root 0:00 {script.sh} /bin/sh ./script.sh # <-- SIGTERM 수신
5 root 0:00 python app.py # <-- SIGTERM 미수신!
5.3 Chaos Engineering
Chaos Engineering은 운영 환경에서 예기치 않은 장애에 대한 시스템의 복원력을 검증하는 방법론입니다.
| 도구 | 설명 |
|---|---|
| Gremlin | 상용 Chaos Engineering 플랫폼 |
| AWS FIS | AWS 관리형 장애 주입 서비스 |
| Litmus | 오픈소스 Kubernetes Chaos Engineering |
5.4 Service Mesh 활용
Service Mesh는 마이크로서비스 간 통신에 Sidecar Proxy를 추가하여 다음 기능을 제공합니다:
| 기능 | 설명 |
|---|---|
| 자동 재시도 (Retry) | 실패한 요청을 자동으로 재시도 |
| Timeout | 응답 지연 시 요청 중단 |
| Circuit Breaking | 장애 전파 방지 |
| Rate Limiting | 과도한 트래픽 제한 |
| Observability | 네트워크 통계, Access Log, 분산 추적 헤더 자동 생성 |
주요 Service Mesh 옵션: Istio, LinkerD, Consul
6. Observability (관측 가능성)
6.1 세 가지 관측 축
graph TB
O[Observability] --> M[Monitoring
메트릭 수집/알림]
O --> L[Logging
로그 수집/보관]
O --> T[Tracing
분산 추적]
M --> M1[Prometheus]
M --> M2[CloudWatch Container Insights]
L --> L1[Fluent Bit / Fluentd]
L --> L2[CloudWatch Logs]
L --> L3[Elasticsearch]
T --> T1[AWS X-Ray]
T --> T2[Jaeger]
T --> T3[Service Mesh 기반]6.2 Monitoring 권장사항
주요 메트릭 방법론
| 방법론 | 의미 | 적용 대상 |
|---|---|---|
| RED | Requests, Errors, Duration | 서비스 중심 모니터링 |
| USE | Utilization, Saturation, Errors | 인프라/리소스 중심 모니터링 |
권장 도구 구성
| 영역 | 도구 | 용도 |
|---|---|---|
| 메트릭 수집 | Prometheus, Prometheus Client Library | 클러스터 및 앱 메트릭 수집, 커스텀 메트릭 노출 |
| 대시보드/알림 | Grafana, CloudWatch Container Insights | 시각화 및 경보 설정 |
| 로그 수집 | Fluent Bit, Fluentd | DaemonSet으로 컨테이너 로그 수집 |
| 로그 저장 | CloudWatch Logs, Elasticsearch, InfluxDB | 중앙 집중식 로그 저장 |
| 분산 추적 | AWS X-Ray, Jaeger | 서비스 간 요청 흐름 추적 |
6.3 EKS Control Plane 로그
EKS는 5가지 Control Plane 로그를 CloudWatch Logs로 전송할 수 있습니다 (기본값: 비활성화).
| 로그 유형 | 용도 |
|---|---|
| API Server | API 요청/응답 로그 |
| Audit | API 호출 감사 로그 |
| Authenticator | 인증 관련 로그 |
| Controller Manager | Control Plane 진단 (병목, 오류 식별) |
| Scheduler | 스케줄링 관련 진단 |
6.4 Distributed Tracing 구현 방식
| 방식 | 장점 | 단점 |
|---|---|---|
| 공유 라이브러리 (코드 레벨) | 세밀한 제어 가능 | 코드 수정 필요, 다중 언어 환경에서 유지보수 부담 |
| Service Mesh | 코드 수정 최소화, 표준화된 메트릭/로그/추적 | 추가 인프라 오버헤드 |
종합 체크리스트
| 카테고리 | 체크 항목 |
|---|---|
| 고가용성 | Singleton Pod 없이 Deployment 사용 |
| 최소 2개 이상 Replica 운영 | |
| PDB 설정 완료 | |
| Pod를 여러 노드/AZ에 분산 배치 | |
| 스케일링 | Metrics Server 설치 |
| HPA 설정 (CPU/Memory 또는 커스텀 메트릭) | |
| VPA 또는 Goldilocks로 Right-sizing | |
| 업데이트 | Rolling Update 파라미터 튜닝 |
| Rollback 절차 문서화/테스트 | |
| Blue/Green 또는 Canary 전략 검토 | |
| Health Check | Liveness Probe 설정 (외부 의존성 제외) |
| Readiness Probe 설정 (외부 의존성 제외) | |
| 시작이 느린 앱에 Startup Probe 적용 | |
| Disruption | Graceful Shutdown 구현 (PID 1 확인) |
| PreStop Hook 필요 시 설정 | |
| terminationGracePeriodSeconds 적정값 설정 | |
| Observability | 중앙 집중식 로그 수집 설정 |
| EKS Control Plane 로그 활성화 | |
| Prometheus 등 모니터링 도구 설치 | |
| 분산 추적 시스템 도입 검토 | |
| RED/USE 메트릭 기반 알림 설정 |