TT Lab
블로그 시작하기
배우기 러닝패스 코스

쿠버네티스 전문가 — 장애를 진단하고 클러스터를 지킨다

업그레이드는 분기 업무다 — 순서와 증거

TT Lab 에서 이어서 보기

한 줄 요약

kubeadm 업그레이드는 「저장소 → 컨트롤 플레인(kubeadm upgrade apply) → 노드마다 비우고·올리고·되돌리기」 순서이고, 그 순서는 버전 차이 규칙이 정합니다. 무중단은 업그레이드 도구가 아니라 PDB·파드 배치·탐침이 만듭니다.

왜 이게 필요했나

쿠버네티스 1.34·1.35·1.36·1.37 이 2025년 8월부터 2026년 8월 사이에 나왔고, 1.34 는 2026년 10월 27일에 지원이 끝났습니다. 마이너 하나가 약 14개월만 지원되니, 클러스터를 지원 범위 안에 두려면 1년에 서너 번 올려야 합니다. 그 사이 사라지는 것도 있습니다 — 1.35 의 cgroup v1 제거 공지, 1.36 부터의 containerd 1.x 지원 종료, ipvs 지원 중단. 업그레이드를 미루면 한 번에 여러 마이너를 건너야 하는데, API 서버는 마이너를 건너뛸 수 없어서 미룬 만큼 업그레이드를 연달아 해야 합니다. 공개 사례 중에는 업그레이드에서 노드 라벨 하나가 바뀐 것을 네트워크 구성요소가 쓰고 있어 몇 시간 장애가 난 것도 있습니다(2차 정리 글로만 확인).

어떻게 동작하나

버전 차이 규칙. API 서버끼리는 한 마이너 안, 컨트롤러 매니저·스케줄러는 API 서버보다 새로울 수 없고 한 마이너까지 뒤처져도 되며, kubelet·kube-proxy 는 API 서버보다 세 마이너까지 뒤처져도 됩니다(1.25 미만은 둘). kubectl 은 위아래 한 마이너. 그래서 순서는 언제나 API 서버 → 컨트롤 플레인의 나머지 → kubelet 입니다. 반대로 하면 kubelet 이 API 서버보다 새로운, 지원하지 않는 상태가 됩니다.

저장소. pkgs.k8s.io 는 마이너마다 저장소가 따로이고(옛 apt.kubernetes.io 는 2023년 9월에 동결), 서명 키도 그 주소에서 받습니다. 저장소 주소를 바꾸지 않으면 apt 는 새 판을 아예 모릅니다. 패키지는 apt-mark hold 로 묶어 두는 것이 kubeadm 문서의 권장이라, 올릴 때는 풀고 다시 묶습니다.

kubeadm upgrade apply. 업그레이드할 수 있는지 검사하고(API 서버 응답, 노드 Ready, 버전 차이), 새 이미지를 받고, etcd·API 서버·컨트롤러 매니저·스케줄러의 정적 파드 매니페스트를 하나씩 바꾸고(하나라도 안 뜨면 되돌림), CoreDNS·kube-proxy 를 새 판으로 바꾸고, kubeadm 이 관리하는 인증서를 갱신합니다. 공식 문서는 파드 스펙 해시가 바뀌어 컨트롤 플레인 컨테이너가 모두 다시 시작한다고 미리 알립니다. 실패하면 같은 명령을 다시 돌리면 되고(멱등), /etc/kubernetes/tmp/ 에 이번 업그레이드의 백업이 남습니다(자동으로 지우지 않음).

노드. kubelet 의 마이너 업그레이드는 반드시 노드를 비운 뒤에 합니다. 워커는 kubeadm upgrade node 로 kubelet 설정을 새 판에 맞추고, 패키지를 바꾸고, kubelet 을 다시 띄우고, uncordon 합니다. 비우는 동안 서비스를 지키는 것은 PDB(한 번에 몇 개까지 내려가도 되는가)와 배치(같은 노드에 몰려 있지 않은가)입니다.

탐침. 이 실습은 서비스 주소로 0.2초마다 요청을 보내 기록합니다. API 서버가 바뀌는 동안에도 이미 놓인 파드와 kube-proxy 의 규칙은 그대로 돌기 때문에, 컨트롤 플레인 업그레이드 자체는 데이터 경로를 끊지 않는 것이 원칙입니다. 끊긴다면 대개 노드를 비울 때(파드가 하나뿐이거나 한 노드에 몰렸을 때)이거나 kube-proxy 가 바뀔 때입니다.

이 클러스터에서 잰 것. 정답지 그대로 1.35.8 → 1.36.4 를 한 번 올리는 동안 탐침은 5분 6초에 1,457번 두드렸고 실패는 2번, 가장 긴 공백은 1.4초였습니다. kubeadm upgrade apply 는 214초 안에 끝났고(이미지 받기 포함), 그 사이 컨트롤 플레인이 통째로 바뀌는 동안의 실패는 0번이었습니다. 실패 둘은 모두 controlplane 을 비우고 kubelet 을 다시 띄우던 몇 초 사이였습니다 — 쫓겨나는 파드가 아직 서비스 엔드포인트에서 빠지기 전에 요청을 받은 것입니다. PDB 는 「동시에 몇 개」 를 지킬 뿐 이 짧은 틈은 막지 못합니다. 그 틈을 줄이는 것은 애플리케이션 쪽의 종료 처리(preStop 에서 잠깐 기다리기, SIGTERM 뒤 새 연결 거절)입니다 — CKAD 코스의 「파드 퇴근 실험실」 이 그 실습입니다. 첫 회귀에서는 controlplane 의 루트 디스크(2.4GiB)가 apt 목록·캐시와 kubeadm 의 업그레이드 백업(/etc/kubernetes/tmp, 124MB)으로 가득 차 kubelet 패키지 설치가 실패했습니다. 업그레이드 전 점검에 「루트에 몇 백 MB 가 남는가」 를 넣으세요.

현장에서 만나는 모습

플랫폼 엔지니어 채용 공고는 「런북 기반의 무중단 롤링 업그레이드」 와 「변경 전 etcd 스냅숏」 을 함께 요구합니다. 런북의 뼈대는 이 실습의 일곱 단계와 같습니다 — 판과 저장소, 지키는 장치와 보는 눈, 되돌릴 길, 컨트롤 플레인, 노드 하나씩, 기록. 업그레이드 전에는 없어질 API 를 쓰는 매니페스트가 없는지도 봐야 합니다(이 실습의 범위 밖이지만, 실제로는 그쪽이 더 자주 사고를 냅니다).

다음 실습에서 할 것

저장소를 v1.36 으로 바꾸고, PDB 와 탐침을 켜고, 스냅숏과 설정 사본을 남긴 뒤, 컨트롤 플레인과 두 노드의 kubelet 을 차례로 올립니다. 마지막에 탐침 기록에서 실패 수와 가장 긴 실패 구간을 셉니다.