LabHub
배우기 러닝패스 코스

쿠버네티스 배포판 — 직접 세운다 · 업그레이드와 판 차이 · 이론

마이너 판은 한 칸씩 올린다

LabHub 에서 이어서 보기

한 줄 요약

쿠버네티스 업그레이드는 "최신판을 설치하는 일" 이 아니라 마이너 판을 한 칸씩 올리며, 구성요소 사이의 판 차이(skew)를 허용 범위 안에 두는 일입니다. k3s 도 이 규칙에서 예외가 아닙니다.

왜 이게 필요했나

k3s 는 설치 한 줄로 끝나기 때문에 업그레이드도 한 줄이라고 생각하기 쉽습니다. 설치 스크립트를 다시 돌리면 새 바이너리를 받고 서비스를 다시 띄워 주니까요. 그래서 흔히 이렇게 합니다. "지금 stable 채널이 뭐지? 그걸로 올리자."

여기에 함정이 있습니다. 채널은 그 순간의 최신 권장판을 가리킬 뿐, 여러분 클러스터가 지금 몇 판인지는 모릅니다. 1.34 에서 멈춰 있던 클러스터를 stable 채널로 올리면, stable 이 1.36 을 가리키는 날에는 마이너 판 두 칸을 한 번에 건너뜁니다. 설치 스크립트는 이것을 막지 않습니다.

그런데 쿠버네티스 프로젝트 정책은 분명합니다. [버전 skew 정책](https://kubernetes.io/releases/version-skew-policy/) 은 kube-apiserver 가 업그레이드할 때 마이너 판을 건너뛰면 안 되고, 인스턴스가 하나뿐인 클러스터도 마찬가지 라고 적고 있습니다. k3s 의 [수동 업그레이드 문서](https://docs.k3s.io/upgrades/manual) 도 같은 정책이 적용된다며 중간 마이너 판을 건너뛰지 말라고 합니다. 이유는 저장소에 들어 있는 오브젝트의 변환, 사용 중단된 API 의 제거, 컨트롤러의 동작 변화가 한 판 단위로만 시험되기 때문입니다. 두 칸을 건너뛰면 아무도 시험하지 않은 경로를 여러분이 처음 걷게 됩니다.

어떻게 동작하나

판 차이의 허용 범위

skew 정책은 구성요소마다 "API 서버보다 몇 판까지 달라도 되는가" 를 정해 둡니다. 핵심은 어떤 구성요소도 API 서버보다 새로우면 안 된다는 것입니다.

구성요소                  API 서버가 1.35 일 때 허용되는 판kube-apiserver (HA)       서로 한 마이너 이내controller-manager·scheduler  1.35, 1.34 (한 판 낮게까지)kubelet·kube-proxy        1.35, 1.34, 1.33, 1.32 (세 판 낮게까지)kubectl                   1.36, 1.35, 1.34 (위아래 한 판)

그래서 올리는 순서도 정해집니다. 컨트롤 플레인(API 서버)이 먼저, kubelet 은 나중입니다. 거꾸로 하면 kubelet 이 API 서버보다 새로워지는 순간이 생깁니다. k3s 문서가 서버 노드를 한 대씩 먼저, 그다음 에이전트 노드 순서를 요구하는 이유가 이것입니다. 또 kubelet 이 세 판까지 뒤처져도 되는 덕분에, 컨트롤 플레인을 한 칸씩 여러 번 올리는 동안 워커를 매번 따라 올리지 않아도 됩니다.

k3s 에서 실제로 일어나는 일

k3s 는 API 서버·kubelet·컨트롤러가 한 바이너리 입니다. 설치 스크립트를 INSTALL_K3S_VERSION 으로 판을 고정해 다시 돌리면 그 판의 바이너리를 받고 서비스를 재시작합니다. 한 노드에서는 컨트롤 플레인과 kubelet 이 함께 올라가므로 skew 는 노드 사이에서만 생깁니다.

주의할 점이 둘 있습니다.

첫째, 설치 때 환경변수나 인자로 준 설정은 다시 돌릴 때 또 주지 않으면 사라집니다. k3s 문서가 명시한 동작입니다. 그래서 설정은 /etc/rancher/k3s/config.yaml 에 두는 편이 안전합니다. 설치 스크립트와 무관하게 남기 때문입니다.

둘째, k3s 를 멈춰도 파드의 컨테이너는 계속 돕니다. 그래서 API 가 잠시 끊겨도 서비스는 대개 살아 있지만, API 가 끊기는 동안 문제가 되는 워크로드라면 먼저 drain 하라고 문서가 권합니다.

실측으로 본 모습도 적어 둡니다. 이 실습과 같은 VM 에서 v1.34.11+k3s1 을 v1.35.8+k3s1 로 설치 스크립트로 올리는 데 11초가 걸렸고, 미리 띄워 둔 nginx 파드 둘은 파드 UID·컨테이너 ID 가 그대로이고 재시작 횟수도 0 이었습니다. 반면 [kubeadm 업그레이드 문서](https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/) 는 컨테이너 스펙 해시가 바뀌어 업그레이드 뒤 모든 컨테이너가 재시작된다고 적고, 마이너 판 kubelet 업그레이드 전에는 반드시 drain 하라고 합니다. 같은 "쿠버네티스 업그레이드" 라도 배포판과 판 조합에 따라 워크로드가 겪는 일이 다르니, 문서의 한 줄로 가정하지 말고 재야 합니다.

drain 이 하는 일과 못 하는 일

[kubectl drain](https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/) 은 노드를 cordon(새 파드 배치 금지)한 뒤 파드를 eviction API 로 내보냅니다. eviction 은 PodDisruptionBudget 을 지킵니다. 예산을 깨는 eviction 은 거절되고, drain 은 거절된 파드를 계속 다시 시도합니다.

이 말은 갈 곳이 없으면 drain 이 끝나지 않는다는 뜻입니다. 노드가 한 대뿐이면 쫓겨난 파드의 대체 파드가 cordon 된 그 노드에 못 올라가 Pending 이 되고, 준비된 파드 수가 예산 아래로 떨어져 다음 eviction 이 막힙니다. 실측에서 drain 은 Cannot evict pod as it would violate the pod's disruption budget 을 5초 간격으로 여덟 번 되풀이하다 --timeout=40s 에 걸려 끝났습니다. 이 실습에서 그 장면을 직접 봅니다.

백업과 사용 중단 API 점검

k3s 의 기본 저장소는 SQLite 입니다. [백업·복구 문서](https://docs.k3s.io/datastore/backup-restore) 는 /var/lib/rancher/k3s/server/db/ 와 함께 토큰 파일(/var/lib/rancher/k3s/server/token)을 반드시 챙기라고 합니다. 토큰이 저장소 안의 기밀 데이터를 암호화하는 데 쓰이기 때문에, DB 만 있고 토큰이 없으면 복구가 되지 않습니다.

사용 중단 API 는 [API 이전 안내](https://kubernetes.io/docs/reference/using-api/deprecation-guide/) 에서 판별로 무엇이 사라지는지 확인하고, API 서버의 apiserver_requested_deprecated_apis 지표로 실제로 누가 그 API 를 부르고 있는지 확인합니다. 문서만 보면 "우리는 안 쓴다" 고 믿게 되고, 지표를 보면 오래된 헬름 차트나 스크립트가 여전히 옛 판을 부르는 것이 드러납니다.

현장에서 만나는 모습

엣지에 흩어진 k3s 수십 대를 한동안 방치했다가 보안 공지 때문에 급히 올리는 경우가 흔합니다. 이때 "어차피 최신으로 갈 거니까" 하고 채널을 stable 로 걸면, 노드마다 출발 판이 달라 어떤 노드는 한 칸, 어떤 노드는 세 칸을 건너뜁니다. 올라가는 데는 성공해도 옛 API 로 만든 오브젝트나 제거된 기능에 기대던 워크로드가 그 뒤에 조용히 깨집니다.

자동화할 때는 Rancher 의 [system-upgrade-controller](https://docs.k3s.io/upgrades/automated) 를 씁니다. Plan 오브젝트에 목표 판(version) 또는 채널(channel)을 적고, concurrency·cordon·nodeSelector 로 한 번에 몇 대를 어떻게 올릴지 정합니다. 여기서도 채널을 걸면 같은 함정이 생깁니다. 게다가 실측해 보니 Plan CRD(v0.20.1)는 versionchannel 도 없는 Plan 을 서버 검증에서 그대로 받아 주었습니다. 적용이 성공했다는 것은 판이 옳다는 뜻이 아닙니다. 판을 고정해 한 칸씩 Plan 을 바꿔 가는 편이 skew 정책과 맞습니다.

실무에서 진짜 중요한 것

다음 실습에서 할 것

VM 안의 k3s 1.34 한 대에서 출발합니다. 출발 판을 기록하고, PDB 가 걸린 워크로드를 올린 뒤, 사용 중단 API 지표와 SQLite 백업으로 준비를 합니다. drain 이 한 대짜리 노드에서 왜 멈추는지 보고, 설치 스크립트로 1.35 로 한 칸 올린 다음 워크로드와 노드가 같은 오브젝트로 살아 있는지 대조합니다. 마지막으로 다음 한 칸을 위한 system-upgrade-controller Plan 을 쓰고, stable 채널을 따라갔다면 몇 칸을 건너뛰었을지 계산합니다.