Kubernetes 클러스터의 업그레이드를 단순히 "버전을 올리는 작업"으로 이해하기 쉽지만, 그 이면에는 꽤 정교한 내부 동작과 절차가 숨어 있습니다. 이 글에서는 kubeadm 설치 환경에서 Kubernetes 업그레이드가 실제로 어떻게 작동하는지, 살펴보며 정리해보겠습니다.
Kubernetes는 선언적(Declarative) 시스템입니다. kubeadm upgrade는 단순히 새 바이너리를 덮어씌우는 것이 아니라, 클러스터의 현재 상태를 읽고, 목표 상태로 점진적이고 안전하게 조정해 나갑니다.
주요 업그레이드 단계와 내부 로직
1. 현재 상태 평가 (upgrade plan)
- kubeadm upgrade plan 실행 시, 다음을 수행합니다
- kubeadm-config ConfigMap을 읽어 현재 버전과 구성 확인
- kube-apiserver의 version API로 실제 버전 조회
- 대상 버전과의 호환성 검사
- CoreDNS, kube-proxy, CNI 등 애드온 호환성 체크
관련코드
- https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/cmd/upgrade/plan.go
kubernetes/cmd/kubeadm/app/cmd/upgrade/plan.go at master · kubernetes/kubernetes
Production-Grade Container Scheduling and Management - kubernetes/kubernetes
github.com
실제로 업그레이드 실행은 하지 않고, 업그레이드가 가능한지 점검하고 계획(plan)을 출력하는 역할을 합니다.
kubeadm upgrade plan
이 명령어를 실행하면 아래의 함수가 실행되며 plan로직이 시작되게 됩니다.
func NewCmdPlan(...)
이 함수는 CLI 커맨드인 kubeadm upgrade plan을 정의합니다.
Run: func(cmd *cobra.Command, args []string) {
err := runUpgradePlan(...)
// ...
}
해당 커맨드가 실행되면 내부적으로 runUpgradePlan() 함수를 호출합니다
func runUpgradePlan(out io.Writer, flags *apply.PlanFlags) error
이 함수가 실제 plan 로직을 처리합니다. 주요 흐름은 아래와 같습니다
- upgrade.GetAvailableUpgrades() 호출 → 업그레이드 가능한 버전 목록 조회
- 현재 상태와 비교하여 업그레이드가 필요한 구성 요소 확인 (API Server, Controller Manager, Scheduler, CoreDNS, kube-proxy 등)
- 업그레이드 시 어떤 작업이 수행될지 플랜 출력
실제로 업그레이드 가능 버전 계산하는 부분
plans, err := upgrade.GetAvailableUpgrades(...)
func GetAvailableUpgrades(...)
이 함수는
- 현재 클러스터 상태 확인 (ConfigMap, kubelet, kube-apiserver 등)
- 업그레이드 가능한 버전 목록을 kubeadm binary 기준으로 계산
- CoreDNS, kube-proxy, etcd 등 업그레이드 대상 포함 여부 결정
실제 업그레이드를 수행하는 코드는?
kubeadm upgrade apply <version>
이 명령어를 실행하면 아래의 함수가 실행되며 apply로직이 시작되게 됩니다.
업그레이드는 다음 커맨드에서 수행됩니다
- https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/cmd/upgrade/apply.go
kubernetes/cmd/kubeadm/app/cmd/upgrade/apply.go at master · kubernetes/kubernetes
Production-Grade Container Scheduling and Management - kubernetes/kubernetes
github.com
func NewCmdApply(...)
2. kube-apiserver → controller → scheduler 순으로 static pod 교체
업그레이드 실행 시, 다음과 같은 절차로 static pod를 교체합니다
- /etc/kubernetes/manifests 경로에 있는 manifest 파일 교체
- kubelet이 static pod 파일 감지 → 컨테이너 재생성
- 교체된 프로세스가 readiness 상태로 돌아오면 다음 컴포넌트로 진행
관련코드
- https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/phases/upgrade/staticpods.go
kubernetes/cmd/kubeadm/app/phases/upgrade/staticpods.go at master · kubernetes/kubernetes
Production-Grade Container Scheduling and Management - kubernetes/kubernetes
github.com
func PerformStaticPodUpgrade(...)
- /etc/kubernetes/manifests/ 경로에 존재하는 static pod manifest들을 새로운 버전으로 교체
- kubelet이 이 변경을 감지하고 기존 pod 종료 + 새 pod 생성
- 업그레이드가 정상적으로 완료되었는지 확인 (해당 컴포넌트의 health check 수행)
실제 업그레이드 수행 코드
PerformStaticPodUpgrade() 내부의 핵심은 이 부분입니다
err = upgrader.UpgradeComponent(component)
여기서 upgrader는 StaticPodPathManager 인터페이스를 구현한 객체이며, 각 control plane 컴포넌트(kube-apiserver, controller-manager, scheduler)를 업그레이드합니다.
UpgradeComponent() 내부에서 어떤 일이?
UpgradeComponent()는 다음과 같은 흐름으로 동작합니다:
- 현재 static pod manifest를 백업
- 새로운 버전에 맞는 manifest를 생성
- 해당 manifest 파일을 /etc/kubernetes/manifests/에 덮어쓰기
- kubelet이 감지하여 새 static pod를 실행
- 업그레이드 완료되었는지 확인 (health check 또는 readiness probe 등으로)
이 로직은 다음과 같은 함수들을 통해 구성됩니다
- CreateStaticPodManifestFiles(...)
- WaitForStaticPodControlPlaneHashChange(...)
- StaticPodControlPlaneWaiter.WaitForStaticPodSingleHash(...)
예를 들어 kube-apiserver 업그레이드 시
- /etc/kubernetes/manifests/kube-apiserver.yaml의 내용을 새 버전 기준으로 덮어씀
- kubelet이 이를 감지하고 기존 apiserver pod를 종료
- 새로 생성된 manifest로 새로운 버전의 kube-apiserver pod 실행
- WaitForStaticPodControlPlaneHashChange()로 업그레이드된 Pod의 hash 값이 변경되었는지 확인하여 성공 여부 판단
- 이 과정을 kube-controller-manager, kube-scheduler 등 모든 control plane 구성요소에 대해 반복합니다.
static pod 교체의 핵심 원리
Kubernetes control plane의 핵심 컴포넌트(apiserver, controller-manager, scheduler)는 static pod로 동작합니다. 업그레이드 시에는 이 static pod의 manifest 파일이 새 버전으로 덮어쓰기되며, kubelet이 이를 감지하여 자동으로 재시작합니다.
/etc/kubernetes/manifests/kube-apiserver.yaml → 변경
└──> kubelet이 detect → 기존 컨테이너 종료 + 새로운 버전으로 시작
이 덕분에 systemd나 외부 툴 없이도 자체적으로 control plane을 self-managed하게 유지할 수 있습니다.
3. CoreDNS, kube-proxy 등의 애드온 리소스 업그레이드
- CoreDNS: Deployment를 patch하거나 recreate
- kube-proxy: DaemonSet을 rolling update 방식으로 갱신
- ClusterConfiguration의 필드 비교 후, apply 방식으로 업데이트
관련코드
- https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/phases/addons/dns/dns.go
kubernetes/cmd/kubeadm/app/phases/addons/dns/dns.go at master · kubernetes/kubernetes
Production-Grade Container Scheduling and Management - kubernetes/kubernetes
github.com
- https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/phases/addons/proxy/proxy.go
kubernetes/cmd/kubeadm/app/phases/addons/proxy/proxy.go at master · kubernetes/kubernetes
Production-Grade Container Scheduling and Management - kubernetes/kubernetes
github.com
func EnsureDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) error {
// ...
return coreDNS.EnsureDNSAddon(cfg, client)
}
func EnsureProxyAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) error {
// ...
return proxyaddon.EnsureProxyAddon(cfg, client)
}
4. kubelet, kubectl은 별도 수동 업그레이드
- 바이너리 교체 및 systemctl restart kubelet 필요
- 노드별 kubeadm upgrade node 명령으로 kubelet 설정 업데이트
- 업그레이드된 kubelet은 새 control plane과 통신하면서 정상 상태 유지
etcd는 언제 업그레이드될까?
- etcd는 kubeadm-config에 명시된 버전에 따라 업그레이드됩니다.
- 단독 실행 중이면 외부 관리 필요 (kOps, kubeadm 외)
- embedded etcd일 경우 static pod로 관리되며 다른 컴포넌트와 동일 방식으로 교체
kubeadm 업그레이드 흐름
etcd snapshot
└──> 업그레이드 전 백업(선택사항)
kubeadm upgrade plan
└──> 현재 상태 + kubeadm-config + version API 분석
kubeadm upgrade apply v1.31.1
├── static pod 교체 (/etc/kubernetes/manifests)
├── CoreDNS, kube-proxy patch
└── kubeadm-config 업데이트
정리
Kubernetes의 업그레이드는 단순히 “버전 교체”가 아니라, 클러스터의 상태를 확인하고 각 구성요소를 안정적으로 교체하는 과정입니다. kubeadm은 이러한 과정을 단계적 절차를 거치는 메커니즘을 통해 안전하게 업그레이드를 수행하고 있다는 것을 알아보았습니다.
실제 운영환경에서는 주기적인 버전 업그레이드 작업이 필요하게 됩니다. 본 글을 통해 업그레이드 과정시에 조금이나마 도움이 되셨으면 좋겠습니다.
'Kubernetes' 카테고리의 다른 글
Kubernetes에서 어떻게 인증서가 교체되나요? (0) | 2025.04.05 |
---|---|
Pod의 OOMKilled 문제 원인 분석 및 해결 방법 (0) | 2025.03.03 |
Node가 Not Ready 상태일 때의 원인과 해결 방법 (0) | 2025.03.03 |
Pod가 Pending 상태일 때의 원인과 해결 방법 (0) | 2025.03.02 |
CrashLoopBackOff 원인 분석 및 해결 방법 (0) | 2025.03.01 |