EKS 클러스터를 구성하고 Cluster Autoscaler로 워커 노드 2개를 운영하는 환경을 구성합니다.
이후 EC2 콘솔에서 노드 1개를 강제 종료했을 때 로드밸런서에 연결된 웹 서비스에 장애가 발생하는지 직접 확인합니다.
EKS 클러스터 생성 및 Cluster Autoscaler 설정
워커 노드 2개 환경에서 웹 서버 배포 및 External IP 접속 확인
노드 1개 강제 종료 후 서비스 장애 여부 확인
파드 Replica 수에 따른 고가용성 차이 비교
Cluster Autoscaler는 클러스터의 리소스 사용량에 따라 워커 노드(EC2 인스턴스)를 자동으로 늘리거나 줄여주는 기능입니다.
minSize, maxSize, desiredCapacity 값을 설정하면 그 범위 안에서 노드 수가 자동으로 조절됩니다.
이번 실습에서는 desiredCapacity: 2로 워커 노드를 2개로 고정하여 시작합니다.
EC2 콘솔에서 인스턴스를 강제 종료(terminate)하면 쿠버네티스의 정상적인 노드 제거 절차(drain)를 거치지 않습니다.
이 경우 아래와 같은 순서로 이벤트가 발생합니다.
1. EC2 콘솔에서 인스턴스 terminate
↓
2. 쿠버네티스가 해당 노드를 NotReady 상태로 감지 (약 40초 후)
↓
3. 해당 노드의 파드를 Terminating 처리
↓
4. 남은 노드로 파드 재스케줄링 시작
↓
5. ASG(Auto Scaling Group)가 종료된 인스턴스 감지 → 새 인스턴스 자동 생성
↓
6. 새 노드가 클러스터에 합류 (약 2~3분 소요)
파드 재스케줄링이 완료되기 전까지의 시간 동안 서비스 순단이 발생할 수 있습니다.
아키텍처 구조는 아래와 같습니다.
[ 로드밸런서 ]
|
┌───┴────────────────────────┐
│ │
노드A [파드 ●] 노드B [ ]
노드A를 강제 종료하면 아래와 같이 진행됩니다.
노드A 강제 종료
↓
파드 소멸 → 노드B로 재스케줄링 시도
↓
재스케줄링 완료까지 약 1~3분 동안
External IP 접속 시 → 502 / 503 에러 발생 ❌
파드가 1개뿐이므로 해당 노드가 종료되는 순간 서비스가 중단됩니다.
아키텍처 구조는 아래와 같습니다.
[ 로드밸런서 ]
|
┌───┴────────────────────────┐
│ │
노드A [파드 ●] 노드B [파드 ●]
노드A를 강제 종료하면 아래와 같이 진행됩니다.
노드A 강제 종료
↓
노드B의 파드는 살아있음
로드밸런서 → 노드B로만 트래픽 전달
↓
External IP 접속 시 → 정상 200 OK ✅
파드가 서로 다른 노드에 분산되어 있으므로 노드 1개가 종료되어도 서비스가 유지됩니다.
아키텍처 구조는 아래와 같습니다.
[ 로드밸런서 ]
|
┌───┴────────────────────────┐
│ │
노드A [파드 ● 파드 ●] 노드B [ ]
노드A를 강제 종료하면 아래와 같이 진행됩니다.
노드A 강제 종료
↓
파드 2개 동시 소멸 → 장애 발생 ❌
Replica를 늘려도 같은 노드에 파드가 몰려 있으면 고가용성 효과가 없습니다.
topologySpreadConstraints 설정을 통해 파드를 서로 다른 노드에 강제로 분산 배치해야 합니다.
| 설정 | 노드 1개 종료 시 결과 | 이유 |
|---|---|---|
| Replica 1개 | ❌ 장애 발생 (약 1~3분) | 파드 재스케줄링 전까지 서비스 다운 |
| Replica 2개 + 분산 배치 | ✅ 정상 유지 | 나머지 노드의 파드가 트래픽 수신 |
| Replica 2개 + 동일 노드 배치 | ❌ 장애 발생 | 두 파드가 같은 노드에 있어 의미 없음 |
eksctl을 사용하여 워커 노드 2개짜리 클러스터를 생성합니다.
# cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ha-test-cluster
region: ap-northeast-2
managedNodeGroups:
- name: worker-ng
instanceType: t3.medium
minSize: 1
maxSize: 4
desiredCapacity: 2
availabilityZones:
- ap-northeast-2a
- ap-northeast-2b
eksctl create cluster -f cluster.yaml
Replica를 2개로 설정하고, topologySpreadConstraints로 각 노드에 파드가 1개씩 배치되도록 강제합니다.
또한 readinessProbe를 설정하여 로드밸런서가 비정상 파드로 트래픽을 전달하지 않도록 합니다.
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 2
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web-app
containers:
- name: web
image: nginx
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
type: LoadBalancer
selector:
app: web-app
ports:
- port: 80
targetPort: 80
kubectl apply -f deployment.yaml
# External IP 확인 (발급까지 1~2분 소요)
kubectl get svc web-svc
EXTERNAL_IP=$(kubectl get svc web-svc -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
curl http://$EXTERNAL_IP
정상적으로 nginx 응답이 오면 배포가 완료된 것입니다.
터미널 2개를 열어 동시에 진행합니다.
터미널 1 — 지속적으로 HTTP 요청 전송
while true; do
curl -s -o /dev/null -w "%{http_code}\n" http://$EXTERNAL_IP
sleep 1
done
터미널 2 — 파드 상태 실시간 모니터링
watch kubectl get pods -o wide
이 상태에서 AWS EC2 콘솔 → 인스턴스 선택 → 인스턴스 종료를 실행합니다.
# 노드 상태 확인
kubectl get nodes
# 파드 재배치 확인
kubectl get pods -o wide -w
# 클러스터 이벤트 로그 확인
kubectl get events --sort-by=.metadata.creationTimestamp
이번 실습을 통해 단순히 노드를 2개 운영하는 것만으로는 고가용성이 보장되지 않는다는 점을 확인할 수 있습니다.
서비스 무중단을 달성하기 위해서는 아래 4가지 조건이 갖춰져야 합니다.
1. Replica ≥ 2
파드가 1개이면 해당 노드 종료 시 단일 장애점(SPOF)이 됩니다.
2. 파드 분산 배치 (topologySpreadConstraints)
Replica를 늘리더라도 파드가 같은 노드에 배치되면 의미가 없습니다.
노드 또는 AZ별로 분산 배치되어야 합니다.
3. Readiness Probe
로드밸런서가 준비되지 않은 파드로 트래픽을 전달하지 않도록 헬스체크를 설정해야 합니다.
4. 다중 AZ 구성
단일 AZ만 사용하는 경우 AZ 자체 장애 시 대응이 불가능합니다.
이 조건들이 갖춰졌을 때 비로소 노드 1개가 종료되어도 서비스가 중단 없이 운영될 수 있습니다.