ARM 서버 Kubernetes Part 7: 클러스터 무중단 업그레이드
Part 6에서는 애플리케이션 Pod을 무중단으로 업그레이드했다.
하지만 클러스터 자체(kubelet, kubeadm)를 업그레이드할 때는 어떻게 해야 할까?
Part 5에서 v1.29 → v1.35 직접 점프를 시도했을 때 NotReady 상태가 발생했다.
이번 글에서는 v1.29 → v1.35까지 순차적으로 무중단 업그레이드하는 방법을 다룬다.
목표
✅ 클러스터 무중단 업그레이드 성공 조건:
- v1.29.15 → v1.35.1까지 순차적 업그레이드
- PDB로 서비스 최소 Pod 수 유지
- 서비스 다운타임 0초 (API 서버 짧은 중단 제외)
- 올바른 순서: 항상 master 먼저, worker 나중에
환경
K8s 클러스터: v1.29.15 (3노드)
- k8s-master (192.168.122.10)
- k8s-worker1 (192.168.122.11)
- k8s-worker2 (192.168.122.12)
업그레이드 경로: v1.29.15 → v1.30.14 → v1.31.14 → v1.32.12 → v1.33.8 → v1.34.4 → v1.35.1
배포된 서비스:
- nginx-demo (3 replicas, PDB: minAvailable 2)
- apache-demo (2 replicas, PDB: minAvailable 1)
⚠️ 중요: 실행 위치 구분
이 문서의 모든 명령어는 실행 위치가 명시되어 있습니다:
- 📍 master 노드 (
192.168.122.10): kubectl 명령어 실행 - 📍 worker 노드 (
192.168.122.11,192.168.122.12): apt, systemctl 등 노드 자체 작업
⚠️ 중요: 업그레이드 순서
반드시 지켜야 할 규칙:
❌ 잘못된 순서: worker 먼저 → master 나중
✅ 올바른 순서: master 먼저 → worker 나중
이유:
- K8s는 워커 노드가 마스터보다 높은 버전이면 호환성 문제 발생
- worker가 더 높으면 kubelet이 API 서버와 통신 불가 → NotReady 상태
교훈:
처음에 worker를 먼저 업그레이드했다가 클러스터 전체가 NotReady 상태가 되었다.
반드시 master 먼저, worker 나중에 업그레이드해야 한다!
1단계: 현재 상태 확인
📍 실행 위치: master 노드 (192.168.122.10)
| |
결과:
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 76m v1.29.15
k8s-worker1 Ready <none> 76m v1.29.15
k8s-worker2 Ready <none> 76m v1.29.15
2단계: v1.30 업그레이드
2.1. Master 업그레이드 (먼저!)
📍 실행 위치: master 노드 (192.168.122.10)
Step 1: kubeadm 업그레이드
| |
Step 2: 컨트롤 플레인 업그레이드
📍 실행 위치: master 노드 (192.168.122.10)
| |
결과:
[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.30.14". Enjoy!
Step 3: kubelet & kubectl 업그레이드
📍 실행 위치: master 노드 (192.168.122.10)
| |
2.2. Worker1 업그레이드
Step 1: drain (Pod 이동)
📍 실행 위치: master 노드 (192.168.122.10)
| |
PDB 작동 확인:
error when evicting pods/"nginx-demo-xxx" -n "default" (will retry after 5s):
Cannot evict pod as it would violate the pod's disruption budget.
✅ PDB가 순차적으로 Pod 제거!
Step 2: worker1 노드 업그레이드
📍 실행 위치: worker1 노드 (192.168.122.11)
| |
Step 3: uncordon (스케줄링 재개)
📍 실행 위치: master 노드 (192.168.122.10)
| |
상태 확인:
| |
결과:
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 78m v1.30.14
k8s-worker1 Ready <none> 78m v1.30.14
k8s-worker2 Ready <none> 78m v1.29.15
2.3. Worker2 업그레이드
Step 1: drain
📍 실행 위치: master 노드 (192.168.122.10)
| |
Step 2: worker2 노드 업그레이드
📍 실행 위치: worker2 노드 (192.168.122.12)
| |
Step 3: uncordon
📍 실행 위치: master 노드 (192.168.122.10)
| |
최종 상태:
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 83m v1.30.14
k8s-worker1 Ready <none> 82m v1.30.14
k8s-worker2 Ready <none> 82m v1.30.14
3단계: v1.31 업그레이드
v1.30과 동일한 순서로 진행:
3.1. Master v1.31 업그레이드
📍 실행 위치: master 노드 (192.168.122.10)
| |
ConfigMap 버전 이슈 해결:
v1.31 업그레이드 중 ConfigMap의 kubernetesVersion이 v1.30으로 남아있어서 kubeadm이 거부하는 문제가 발생할 수 있다.
📍 실행 위치: master 노드 (192.168.122.10)
| |
3.2. Worker1 v1.31 업그레이드
📍 Step 1: master에서 drain
| |
📍 Step 2: worker1 노드에서 업그레이드
| |
📍 Step 3: master에서 uncordon
| |
3.3. Worker2 v1.31 업그레이드
동일한 방식으로 진행.
최종 상태:
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 137m v1.31.14
k8s-worker1 Ready <none> 137m v1.31.14
k8s-worker2 Ready <none> 137m v1.31.14
4단계: v1.32 → v1.33 → v1.34 순차 업그레이드
각 버전마다 동일한 순서 반복:
각 버전 업그레이드 순서
1. 📍 master 노드: kubeadm 업그레이드
2. 📍 master 노드: kubeadm upgrade apply v1.XX.YY
3. 📍 master 노드: kubelet/kubectl 업그레이드
4. 📍 master 노드: kubectl drain k8s-worker1
5. 📍 worker1 노드: kubelet/kubeadm/kubectl 업그레이드
6. 📍 master 노드: kubectl uncordon k8s-worker1
7. 📍 master 노드: kubectl drain k8s-worker2
8. 📍 worker2 노드: kubelet/kubeadm/kubectl 업그레이드
9. 📍 master 노드: kubectl uncordon k8s-worker2
v1.32 예시:
📍 master 노드:
| |
📍 worker 노드들 (각각):
| |
v1.33, v1.34도 동일한 방식으로 진행.
5단계: v1.35 업그레이드 (중요!)
5.1. Master v1.35 업그레이드
📍 실행 위치: master 노드 (192.168.122.10)
| |
5.2. ⚠️ v1.35 주요 변경사항
kubelet 플래그 제거:
v1.35부터 --pod-infra-container-image 플래그가 완전히 제거되었다.
업그레이드 후 kubelet이 계속 재시작되면서 아래 에러 발생:
E0220 07:16:28.686442 92191 run.go:72] "command failed"
err="failed to parse kubelet flag: unknown flag: --pod-infra-container-image"
해결:
📍 실행 위치: master 노드 (192.168.122.10)
| |
상태 확인:
| |
정상이면 active (running) 상태여야 함.
5.3. Worker 노드 v1.35 업그레이드
Worker1 업그레이드
📍 Step 1: master에서 drain
| |
📍 Step 2: worker1 노드에서 업그레이드
| |
📍 Step 3: 플래그 제거 (v1.35 전용)
| |
📍 Step 4: master에서 uncordon
| |
Worker2 업그레이드
동일한 방식으로 진행.
6단계: 최종 상태 확인
📍 실행 위치: master 노드 (192.168.122.10)
| |
결과:
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 4h v1.35.1
k8s-worker1 Ready <none> 4h v1.35.1
k8s-worker2 Ready <none> 3h59m v1.35.1
Pod 상태:
| |
결과:
NAME READY STATUS RESTARTS AGE
apache-demo-569d746c79-bwj9r 1/1 Running 0 53m
apache-demo-569d746c79-tg6ft 1/1 Running 0 65m
nginx-demo-775cb84cd7-fc7mn 1/1 Running 0 53m
nginx-demo-775cb84cd7-k4l8q 1/1 Running 0 65m
nginx-demo-775cb84cd7-zm45l 1/1 Running 0 53m
서비스 가용성:
| |
결과:
200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200
✅ 20/20 요청 모두 200 OK!
핵심 교훈
✅ 성공 요인
| 요소 | 내용 |
|---|---|
| 올바른 순서 | 항상 master 먼저, worker 나중에 |
| 실행 위치 | kubectl은 master, apt/systemctl은 각 노드 |
| 1버전씩 | v1.29 → v1.30 → v1.31 → … → v1.35 순차 진행 |
| PDB | drain 시 최소 Pod 수 보장 |
| 버전별 확인 | 각 버전마다 서비스 가용성 확인 |
❌ 실수와 해결
| 문제 | 원인 | 해결 |
|---|---|---|
| worker NotReady | worker를 master보다 먼저 업그레이드 | master 먼저 업그레이드 |
| kubectl 실패 | worker 노드에서 kubectl 실행 | master 노드에서만 실행 |
| ConfigMap 불일치 | kubeadm upgrade apply 미완료 | ConfigMap 수동 업데이트 |
| kubelet 재시작 반복 | v1.35에서 플래그 제거 | kubeadm-flags.env 수정 |
📊 최종 결과
- 업그레이드 경로: v1.29.15 → v1.30.14 → v1.31.14 → v1.32.12 → v1.33.8 → v1.34.4 → v1.35.1
- 소요 시간: 약 3시간 (6개 버전, 수동 진행)
- 다운타임: 0초 (API 서버 짧은 중단 외)
- 서비스 가용성: 100%
업그레이드 체크리스트
각 버전마다 반복:
| # | 작업 | 위치 | 명령어 |
|---|---|---|---|
| 1 | kubeadm 업그레이드 | 📍 master | sudo apt-get install kubeadm |
| 2 | 컨트롤 플레인 업그레이드 | 📍 master | sudo kubeadm upgrade apply |
| 3 | kubelet/kubectl 업그레이드 | 📍 master | sudo apt-get install kubelet kubectl |
| 4 | master kubelet 재시작 | 📍 master | sudo systemctl restart kubelet |
| 5 | 노드 상태 확인 | 📍 master | kubectl get nodes |
| 6 | worker1 drain | 📍 master | kubectl drain k8s-worker1 |
| 7 | worker1 업그레이드 | 📍 worker1 | ssh → apt-get install |
| 8 | worker1 uncordon | 📍 master | kubectl uncordon k8s-worker1 |
| 9 | worker2 drain | 📍 master | kubectl drain k8s-worker2 |
| 10 | worker2 업그레이드 | 📍 worker2 | ssh → apt-get install |
| 11 | worker2 uncordon | 📍 master | kubectl uncordon k8s-worker2 |
| 12 | 전체 노드 상태 확인 | 📍 master | kubectl get nodes |
| 13 | Pod 상태 확인 | 📍 master | kubectl get pods |
| 14 | 서비스 가용성 테스트 | 📍 master | curl 192.168.255.100 |
v1.35 전용 추가 작업
| # | 작업 | 위치 | 명령어 |
|---|---|---|---|
| 15 | master 플래그 제거 | 📍 master | sed -i ... kubeadm-flags.env |
| 16 | worker1 플래그 제거 | 📍 worker1 | sed -i ... kubeadm-flags.env |
| 17 | worker2 플래그 제거 | 📍 worker2 | sed -i ... kubeadm-flags.env |
| 18 | 모든 노드 kubelet 재시작 | 📍 각 노드 | systemctl restart kubelet |
| 19 | kubelet 상태 확인 | 📍 각 노드 | systemctl status kubelet |
주의사항
1. 절대 건너뛰지 말 것
❌ v1.29 → v1.35 직접 점프
✅ v1.29 → v1.30 → v1.31 → ... → v1.35 순차 진행
2. 순서 엄수
❌ worker1 → worker2 → master
✅ master → worker1 → worker2
3. 실행 위치 확인
kubectl 명령어 → 📍 master 노드에서만
apt/systemctl → 📍 각 노드에서 직접
4. 버전별 변경사항 확인
각 버전마다 deprecated/removed 기능이 있을 수 있다.
- v1.35:
--pod-infra-container-image플래그 제거 - 공식 릴리스 노트 확인 필수
5. PDB 필수
PDB 없이 drain하면 모든 Pod이 동시 제거 → 서비스 중단
📍 master 노드:
| |
다음 단계
v1.35까지 업그레이드했으니, 이제 최신 기능을 활용할 수 있다!
Part 8에서는:
- Auto-scaling (HPA): 부하에 따라 자동으로 Pod 수 조절
- Cluster Autoscaler: 노드 수 자동 조절
- Vertical Pod Autoscaler (VPA): Pod 리소스 자동 조절
실제 운영 환경에서 필요한 자동화 기능을 다룰 예정이다.
참고 자료
작성일: 2026-02-20
테스트 환경: ARM64 KVM (3 노드, Ubuntu 24.04)
업그레이드 경로: v1.29.15 → v1.35.1 (6단계 순차)
최종 상태: ✅ 클러스터 무중단 업그레이드 성공 (다운타임 0초)