ARM 서버 Kubernetes Part 6: 무중단 업그레이드
Part 5에서 K8s 클러스터를 v1.29.15 → v1.35.1로 업그레이드했지만, kubelet 플래그 호환성 문제로 일시적으로 NotReady 상태가 발생했다.
결과적으로 무중단이 아니었다.
이번 글에서는 진짜 무중단 업그레이드를 구현한다.
애플리케이션 Pod을 업그레이드하면서도 서비스 다운타임 0초를 달성하는 방법을 다룬다.
목표
✅ 무중단 업그레이드 성공 조건:
- 모든 HTTP 요청이 200 OK (503/502 없음)
- Pod가 순차적으로 교체됨 (한 번에 1개씩)
- PDB가 항상 최소 Pod 수 유지
- 서비스 다운타임 0초
환경
K8s 클러스터: v1.29.15 (3노드)
- k8s-master (192.168.122.10)
- k8s-worker1 (192.168.122.11)
- k8s-worker2 (192.168.122.12)
배포된 서비스:
- nginx-demo (nginx:alpine, 1 replica → 3 replica로 확장)
- apache-demo (httpd:alpine, 2 replicas)
외부 접근:
- Ingress Controller: 192.168.255.100 (MetalLB)
- / → nginx-demo
- /apache → apache-demo
1단계: 현재 상태 확인
| |
결과:
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
apache-demo 2/2 2 2 20m apache httpd:alpine app=apache-demo
nginx-demo 1/1 1 1 17m nginx nginx:alpine app=nginx-demo
nginx-demo가 1개만 있으므로, 무중단 테스트를 위해 3개로 확장한다.
2단계: nginx-demo replicas 증가
| |
결과:
NAME READY STATUS RESTARTS AGE
nginx-demo-75b947f799-5srr9 1/1 Running 0 18m
nginx-demo-75b947f799-87zch 1/1 Running 0 5s
nginx-demo-75b947f799-v8qcj 1/1 Running 0 5s
3단계: Pod Disruption Budget (PDB) 설정
PDB는 업그레이드 중에도 최소한의 Pod 수를 보장한다.
이를 통해 서비스 가용성을 유지할 수 있다.
| |
확인:
| |
결과:
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
apache-demo-pdb 1 N/A 1 7s
nginx-demo-pdb 2 N/A 1 7s
- nginx-demo: 총 3개 중 최소 2개 유지 (동시 중단 가능: 1개)
- apache-demo: 총 2개 중 최소 1개 유지 (동시 중단 가능: 1개)
4단계: Rolling Update 전략 설정
무중단을 보장하려면 maxUnavailable=0으로 설정해야 한다.
| |
확인:
| |
결과:
| |
5단계: 무중단 업그레이드 실행
이제 nginx 이미지를 nginx:alpine → nginx:1.25-alpine으로 업그레이드한다.
| |
롤아웃 진행 과정:
deployment.apps/nginx-demo image updated
Waiting for deployment "nginx-demo" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-demo" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-demo" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-demo" successfully rolled out
핵심 관찰:
- 한 번에 1개씩 Pod가 업데이트됨
- 새 Pod이 Ready 상태가 된 후에만 이전 Pod 종료
- 항상 2개 이상의 Pod이 Running 상태 유지 (PDB 덕분)
6단계: 업그레이드 결과 확인
| |
결과:
NAME READY STATUS RESTARTS AGE
nginx-demo-775cb84cd7-8zfmn 1/1 Running 0 22s
nginx-demo-775cb84cd7-lm8hc 1/1 Running 0 12s
nginx-demo-775cb84cd7-qmgjd 1/1 Running 0 32s
이미지 확인:
| |
결과:
nginx:1.25-alpine nginx:1.25-alpine nginx:1.25-alpine
✅ 모든 Pod이 새 이미지로 업그레이드 완료!
7단계: 서비스 가용성 확인
업그레이드 중에도 서비스가 정상적으로 응답했는지 확인한다.
| |
결과:
200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200
✅ 20/20 요청 모두 200 OK! (다운타임 0초)
8단계: apache-demo도 동일하게 테스트
Rolling Update 전략 설정
| |
이미지 업그레이드
| |
결과:
deployment.apps/apache-demo image updated
Waiting for deployment "apache-demo" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "apache-demo" rollout to finish: 1 old replicas are pending termination...
deployment "apache-demo" successfully rolled out
서비스 가용성 확인
| |
결과:
200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200
✅ 20/20 요청 모두 200 OK! (다운타임 0초)
9단계: 최종 상태 확인
| |
결과:
=== Deployment 상태 ===
NAME READY UP-TO-DATE AVAILABLE AGE
apache-demo 2/2 2 2 22m
nginx-demo 3/3 3 3 20m
=== PDB 상태 ===
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
apache-demo-pdb 1 N/A 1 2m18s
nginx-demo-pdb 2 N/A 1 2m18s
=== Pod 상태 ===
NAME READY STATUS RESTARTS AGE
apache-demo-569d746c79-2m574 1/1 Running 0 26s
apache-demo-569d746c79-t9lwf 1/1 Running 0 22s
nginx-demo-775cb84cd7-8zfmn 1/1 Running 0 74s
nginx-demo-775cb84cd7-lm8hc 1/1 Running 0 64s
nginx-demo-775cb84cd7-qmgjd 1/1 Running 0 84s
핵심 개념 정리
1. Pod Disruption Budget (PDB)
역할: 업그레이드/유지보수 중에도 최소 Pod 수 보장
| |
효과:
- K8s가 한 번에 너무 많은 Pod을 종료하지 못하게 제한
- 서비스 가용성 유지
2. Rolling Update 전략
| |
동작 방식:
- 새 Pod 1개 생성 (maxSurge=1)
- 새 Pod이 Ready 상태 대기
- 이전 Pod 1개 종료
- 2-3 반복 (모든 Pod 교체 완료까지)
3. Readiness Probe
| |
역할: Pod이 트래픽을 받을 준비가 되었는지 확인
효과:
- Ready 상태가 아닌 Pod은 Service 엔드포인트에서 제외
- 롤아웃 중에도 정상 Pod만 트래픽 수신
정리
✅ 무중단 업그레이드 성공 요인
| 요소 | 설정 | 효과 |
|---|---|---|
| PDB | minAvailable: 2 (nginx), 1 (apache) | 최소 Pod 수 보장 |
| Rolling Update | maxUnavailable: 0 | 동시 불가용 Pod 없음 |
| Readiness Probe | httpGet / (2초 간격) | 준비된 Pod만 트래픽 수신 |
| Replica 수 | nginx 3개, apache 2개 | 여유 있는 Pod 수 |
📊 실제 결과
- nginx-demo: 40/40 요청 → 200 OK (100%)
- apache-demo: 20/20 요청 → 200 OK (100%)
- 다운타임: 0초
- 롤아웃 시간: 약 30~40초 (3개 Pod 순차 교체)
🆚 Part 5 vs Part 6
| 항목 | Part 5 (클러스터 업그레이드) | Part 6 (애플리케이션 업그레이드) |
|---|---|---|
| 대상 | K8s 노드 (kubelet, kubeadm) | Pod (컨테이너 이미지) |
| PDB | 설정 안 함 | ✅ 설정 (최소 Pod 수 보장) |
| Rolling Update | 기본 설정 | ✅ maxUnavailable=0 |
| Readiness Probe | 없음 | ✅ 설정 |
| 결과 | NotReady 발생 (일시 중단) | ✅ 무중단 (0초) |
다음 단계
Part 6에서는 애플리케이션 Pod의 무중단 업그레이드를 다뤘다.
하지만 Part 5에서 본 것처럼, 클러스터 자체를 업그레이드할 때는 NotReady 상태가 발생했다.
Part 7에서는:
- 클러스터 무중단 업그레이드 (Zero-Downtime Cluster Upgrade)
- 워커 노드 순차 업그레이드 (drain → upgrade → uncordon)
- PDB + Pod 분산으로 서비스 가용성 유지
- 마스터 노드 업그레이드 (API 서버 최소 중단)
핵심 차이:
- Part 6: Pod 이미지 교체 (애플리케이션 레벨)
- Part 7: kubelet/kubeadm 업그레이드 (인프라 레벨)
실제 프로덕션 환경에서 K8s 버전을 올릴 때 서비스 다운타임 없이 진행하는 방법을 다룰 예정이다.
참고 자료
작성일: 2026-02-20
테스트 환경: ARM64 KVM (3 노드, Ubuntu 24.04, K8s v1.29.15)
최종 상태: ✅ 무중단 업그레이드 성공 (다운타임 0초)