[Kubernetes] Cluster: RKE2를 이용해 클러스터 구성하기 - 4.2. 자동 업그레이드

11 분 소요

서종호(가시다)님의 On-Premise K8s Hands-on Study 7주차 학습 내용을 기반으로 합니다.


TL;DR

이번 글의 목표는 system-upgrade-controller를 이용한 RKE2 자동 업그레이드 방식을 이해하고 실습으로 확인하는 것이다.

  • 자동 업그레이드: Rancher의 system-upgrade-controller가 Plan CRD를 감시하고 선언된 버전으로 노드를 자동 업그레이드한다.
  • Kubernetes-native: Operator 패턴을 따르며, kubectl apply로 Plan을 선언하면 컨트롤러가 알아서 처리한다.
  • Plan 구성: 서버(컨트롤 플레인)용 Plan과 에이전트(워커)용 Plan을 최소 두 개 생성해야 한다.
  • 업그레이드 순서 보장: 에이전트 Plan의 prepare 블록이 서버 Plan 완료를 대기하여 순서를 보장한다.
  • 다운그레이드 방지 장치 없음: rke2-upgrade 이미지에는 다운그레이드를 막는 검증 로직이 없으므로 주의해야 한다.


들어가며

이전 글에서 Installation Script 방식의 수동 업그레이드를 다뤘다. 수동 업그레이드는 노드마다 SSH 접속해서 스크립트를 실행하고 서비스를 재시작해야 하는 imperative한 방식이다.

이번 글에서는 Kubernetes-native한 방식인 자동 업그레이드를 다룬다. Rancher의 system-upgrade-controller를 클러스터에 설치하고, Plan Custom Resource를 선언하면 컨트롤러가 알아서 업그레이드를 수행한다.


자동 업그레이드

개념

자동 업그레이드는 선언형 업그레이드다. 사용자가 Plan Custom Resource를 통해 업그레이드할 노드목표 버전을 선언하면, 별도로 설치한 system-upgrade-controller가 이를 감시하고 실제 업그레이드를 자동으로 수행한다. 수동 업그레이드처럼 노드에 SSH 접속해서 스크립트를 실행할 필요가 없다.

절차

자동 업그레이드는 두 단계로 진행한다.

  1. system-upgrade-controller 설치: 클러스터에 컨트롤러를 배포한다.
  2. Plan 생성: 업그레이드할 노드 그룹과 업그레이드 방법을 선언하는 Plan CR을 생성한다.

Plan이 생성되면 컨트롤러가 이를 인식하고 클러스터 업그레이드를 시작한다.


System Upgrade Controller

개념

system-upgrade-controller는 Rancher에서 개발한 Kubernetes-native 클러스터 업그레이드 컨트롤러다. Plan Custom Resource를 정의하면, 컨트롤러가 이를 감시하고 Plan에 선언된 대로 노드를 업그레이드한다.

전형적인 Kubernetes Operator 패턴을 따른다.

Plan CR (desired state)
  ├── 대상 노드: label selector
  ├── 목표 버전: v1.35.x
  └── 업그레이드 방식: cordon → upgrade → uncordon

system-upgrade-controller (operator)
  └── watch Plan → diff → Job 생성 → 노드별 순차 업그레이드

Kubernetes의 핵심 원리인 declarative + reconciliation loop를 그대로 활용한다. Plan CR이 곧 desired state이고, controller가 current state → desired state로 수렴시키는 구조이다.

동작 방식은 다음과 같다.

  1. CRD 정의: Plan이라는 Custom Resource를 등록한다.
  2. Controller가 watch: Plan CR의 생성/변경을 감시한다.
  3. Reconcile loop: Plan에 선언된 desired state(어떤 노드를, 어떤 버전으로)와 현재 상태를 비교하고, 차이가 있으면 업그레이드 Job을 생성해 실행한다.

수동 업그레이드와의 차이는 이 점에 있다.

방식 성격
curl -sfL https://get.rke2.io \| sh imperative — 노드에 SSH 접속해서 직접 실행
system-upgrade-controller + Plan CR declarative — CR 선언하면 controller가 알아서 처리

kubectl apply -f plan.yaml 한 번이면 끝이다. 클러스터 외부 도구가 필요 없고, SSH 접속해서 스크립트를 돌릴 필요도 없다.

설치

system-upgrade-controller는 공식 GitHub 릴리스에서 제공하는 매니페스트로 설치한다. CRD와 컨트롤러를 한 번에 적용한다.

kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/crd.yaml \
  -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/system-upgrade-controller.yaml

설치 시 생성되는 리소스는 다음과 같다.

리소스 설명
CRD (plans.upgrade.cattle.io) Plan Custom Resource 정의
Namespace (system-upgrade) 컨트롤러와 Plan이 배포되는 전용 네임스페이스
Deployment system-upgrade-controller Pod
ServiceAccount 컨트롤러가 사용하는 서비스 계정
ClusterRole / ClusterRoleBinding 노드 cordon/drain, Job 생성 등에 필요한 RBAC 권한
ConfigMap (default-controller-env) 컨트롤러 동작 설정 (환경변수)

Rancher가 설치된 환경이라면 system-upgrade-controller가 이미 포함되어 있을 수 있으므로, 별도 설치가 필요 없을 수 있다.

설정

컨트롤러의 동작은 default-controller-env ConfigMap으로 설정한다. 이 ConfigMap에는 컨트롤러가 참조하는 환경변수들이 정의되어 있다.

ConfigMap을 수정한 후에는 컨트롤러 Pod를 삭제해야 변경 사항이 반영된다. Deployment가 Pod를 재생성하면서 새 설정을 적용한다.

# ConfigMap 수정 후 Pod 재생성
kubectl delete pod -n system-upgrade -l app.kubernetes.io/name=system-upgrade-controller


Plan

개념

Plan은 업그레이드 정책과 요구 사항을 정의하는 Custom Resource다. 어떤 노드를 어떤 버전으로 업그레이드할지를 레이블 셀렉터와 버전 정보로 선언한다.

전체 동작 흐름은 다음과 같다.

  1. 사용자가 Plan CR을 생성한다 (kubectl apply -f plan.yaml).
  2. system-upgrade-controller가 Plan을 감시하다가 대상 버전을 확인한다.
  3. 컨트롤러가 대상 노드를 선택하고 업그레이드 Job을 생성한다.
  4. Job이 성공적으로 완료되면, 컨트롤러가 해당 노드에 완료 레이블을 부여한다.

Plan의 상세 스펙은 공식 문서에서 확인할 수 있다.

RKE2 클러스터가 Rancher로 관리되는 경우에는 Rancher UI를 사용하여 업그레이드를 관리해야 한다. Rancher는 클러스터 버전, 노드 상태 등을 자체 DB에 추적하고 있기 때문에, Rancher가 모르는 사이에 직접 업그레이드하면 Rancher가 인식하는 상태와 실제 상태가 불일치할 수 있다.

참고로 RKE2는 Kubernetes 배포판(클러스터 자체)이고, Rancher는 Kubernetes 관리 플랫폼(웹 UI + API)이다. 같은 회사(SUSE/Rancher Labs)에서 만들었지만 역할이 다르다. 독립 실행(standalone) RKE2는 system-upgrade-controller 또는 스크립트로 업그레이드하고, Rancher가 관리하는 RKE2는 Rancher UI로 업그레이드하는 것이 원칙이다.

구성

서버 노드는 항상 에이전트 노드보다 먼저 업그레이드해야 한다. 따라서 최소 두 개의 Plan을 생성하는 것이 권장된다.

  • 서버 Plan: 컨트롤 플레인 노드 업그레이드용
  • 에이전트 Plan: 워커 노드 업그레이드용

필요에 따라 추가 Plan을 생성하여 노드 간 업그레이드 배포를 더 세밀하게 제어할 수도 있다.

주요 필드

필드 설명
concurrency 동시에 업그레이드할 노드 수
cordon 업그레이드 전 노드 스케줄링 차단 여부
drain 업그레이드 전 노드 drain 수행 여부 및 옵션
nodeSelector 대상 노드 선택 조건 (레이블 셀렉터)
channel 릴리스 채널 URL (컨트롤러가 주기적으로 폴링해 최신 버전 확인)
version 특정 버전 직접 지정 (channel 대신 사용 가능)
prepare 실제 업그레이드 전 실행할 준비 단계
serviceAccountName 업그레이드 Job이 사용할 ServiceAccount
upgrade.image 업그레이드를 수행하는 컨테이너 이미지
secrets 업그레이드 스크립트 등을 담은 Secret 참조
window 업그레이드 실행 시간 범위

Plan은 컨트롤러가 배포된 것과 동일한 네임스페이스(system-upgrade)에 생성해야 한다.

에이전트 Plan의 prepare 블록이 핵심이다. prepare에 서버 Plan 이름을 지정하면, 서버 Plan이 완료될 때까지 에이전트 업그레이드가 대기한다. 이렇게 CP 먼저, 워커 나중이라는 순서가 보장된다.

RKE2 Plan 예시

공식 문서에서 제공하는 RKE2 업그레이드 Plan 예시다. stable 릴리스 채널을 대상으로 클러스터를 최신 안정 릴리스로 지속 업그레이드한다.

# Server plan
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: server-plan
  namespace: system-upgrade
spec:
  concurrency: 1
  cordon: true
  nodeSelector:
    matchExpressions:
    - key: node-role.kubernetes.io/control-plane
      operator: In
      values:
      - "true"
  serviceAccountName: system-upgrade
  upgrade:
    image: rancher/rke2-upgrade
  channel: https://update.rke2.io/v1-release/channels/stable
---
# Agent plan
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: agent-plan
  namespace: system-upgrade
spec:
  concurrency: 1
  cordon: true
  nodeSelector:
    matchExpressions:
    - key: node-role.kubernetes.io/control-plane
      operator: DoesNotExist
  prepare:
    args:
    - prepare
    - server-plan
    image: rancher/rke2-upgrade
  serviceAccountName: system-upgrade
  upgrade:
    image: rancher/rke2-upgrade
  channel: https://update.rke2.io/v1-release/channels/stable

주의할 점을 정리하면 다음과 같다.

  1. concurrency: 1은 동시에 업그레이드할 노드 수다. 한 번에 1대씩 순차 진행한다.
  2. 서버 Plan은 node-role.kubernetes.io/control-plane: "true" 레이블이 있는 노드를 대상으로 한다. 에이전트 Plan은 해당 레이블이 없는 노드(DoesNotExist)를 대상으로 한다.
  3. 에이전트 Plan의 prepare 단계가 server-plan의 완료를 기다린 후 실행된다. 이 로직은 rancher/rke2-upgrade 이미지에 내장되어 있으며, system-upgrade-controller 자체의 기능은 아니다.
  4. 두 Plan 모두 channel이 stable 릴리스 채널 URL로 설정되어 있어, 새 릴리스가 확인될 때마다 자동으로 업그레이드한다.

이 외에도 system-upgrade-controller GitHub에서 다양한 Plan 예시를 확인할 수 있다.

  • k3s-upgrade.yaml: K3s 자체를 업그레이드하는 예시
  • ubuntu/bionic.yaml: apt-get 방식으로 임의의 패키지를 고정된 버전으로 업그레이드하는 예시

버전 지정: 채널 vs. 직접 지정

버전을 지정하는 방법은 두 가지다.

채널 방식

channel 필드에 릴리스 채널 URL을 설정한다. 컨트롤러가 해당 URL을 주기적으로 폴링하여 새 릴리스가 확인되면 자동으로 업그레이드한다.

spec:
  channel: https://update.rke2.io/v1-release/channels/stable

직접 지정 방식

channel 대신 version 필드로 특정 버전을 명시한다.

spec:
  version: v1.33.4+rke2r1

채널 방식을 사용하면 최신 안정 릴리스로 지속적으로 자동 업그레이드된다. 운영 환경에서 통제된 업그레이드가 필요하다면 version 직접 지정이 더 적합하다.

컨트롤러는 version 필드 또는 채널 서버 폴링을 통해 Plan의 대상 버전이 확정되면 즉시 업그레이드를 시작한다. Plan을 수정하면 컨트롤러가 해당 Plan을 재평가하여 추가 업그레이드가 필요한지 판단한다. 채널이 구성된 경우 해당 URL도 주기적으로 폴링된다.

업그레이드 진행 상황은 kubectl 명령어로 모니터링할 수 있다.

kubectl -n system-upgrade get plans -o wide
kubectl -n system-upgrade get jobs

스케줄링

Plan의 window 필드로 업그레이드 실행 시간을 제한할 수 있다.

spec:
  window:
    days:
      - monday
      - tuesday
      - wednesday
      - thursday
      - friday
    startTime: 19:00
    endTime: 21:00
    timeZone: UTC

시간 범위 바깥에서는 업그레이드 Job이 생성되지 않는다. 단, 이미 생성된 Job은 시간 범위가 종료되어도 계속 실행될 수 있다.

다운그레이드 방지

주의할 점이 있다. Kubernetes는 컨트롤 플레인 컴포넌트의 다운그레이드를 지원하지 않는다. 그런데 rke2-upgrade 이미지에는 다운그레이드를 막는 검증 로직이 포함되어 있지 않다. Plan에 낮은 버전을 지정하면 그냥 실행된다.

다운그레이드가 실행되면 etcd 스키마, API 리소스 버전 등이 상위 버전 기준으로 이미 마이그레이션된 상태이므로 데이터 호환성이 깨질 수 있다. 롤백이 필요하다면 반드시 etcd 스냅샷 복원과 함께 수행해야 한다. 바이너리만 내리면 데이터 불일치로 클러스터가 깨진다.

업그레이드 Job의 권한

업그레이드 Job이 생성하는 Pod는 기본 노드에 변경을 적용하기 위해 높은 권한이 필요하다. 기본적으로 다음과 같이 구성된다.

  • Host IPC, NET, PID namespaces
  • CAP_SYS_BOOT capability
  • 호스트 루트(/)가 /host에 read/write로 마운트


실습

수동 업그레이드 실습에 이어, v1.34에서 v1.35로 자동 업그레이드를 진행한다.

system-upgrade-controller 설치

system-upgrade-controller를 설치한다. CRD와 컨트롤러 매니페스트를 한 번에 적용한다.

[root@week07-k8s-node1 ~]# kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/crd.yaml -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/system-upgrade-controller.yaml
customresourcedefinition.apiextensions.k8s.io/plans.upgrade.cattle.io created
namespace/system-upgrade created
serviceaccount/system-upgrade created
role.rbac.authorization.k8s.io/system-upgrade-controller created
clusterrole.rbac.authorization.k8s.io/system-upgrade-controller created
clusterrole.rbac.authorization.k8s.io/system-upgrade-controller-drainer created
rolebinding.rbac.authorization.k8s.io/system-upgrade created
clusterrolebinding.rbac.authorization.k8s.io/system-upgrade created
clusterrolebinding.rbac.authorization.k8s.io/system-upgrade-drainer created
configmap/default-controller-env created
deployment.apps/system-upgrade-controller created

설치되는 리소스는 CRD, Deployment, ServiceAccount, ClusterRoleBinding, ConfigMap 등이다.

[root@week07-k8s-node1 ~]# kubectl get deploy,pod,cm -n system-upgrade
NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/system-upgrade-controller   1/1     1            1           58s

NAME                                             READY   STATUS    RESTARTS   AGE
pod/system-upgrade-controller-5f667989c7-6hbq4   1/1     Running   0          58s

NAME                               DATA   AGE
configmap/default-controller-env   11     58s
configmap/kube-root-ca.crt         1      58s

[root@week07-k8s-node1 ~]# kubectl get crd | grep upgrade
plans.upgrade.cattle.io                                 2026-02-19T16:03:00Z

컨트롤러 Pod가 Running 상태이고, plans.upgrade.cattle.io CRD가 등록된 것을 확인할 수 있다.

컨트롤러 로그를 확인하면 리더 선출 후 각 리소스에 대한 컨트롤러가 시작된 것을 볼 수 있다.

[root@week07-k8s-node1 ~]# kubectl logs -n system-upgrade -l app.kubernetes.io/name=system-upgrade-controller -f
W0219 16:03:09.380712       1 client_config.go:682] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
time="2026-02-19T16:03:09Z" level=info msg="Updating embedded CRD plans.upgrade.cattle.io"
I0219 16:03:09.439463       1 leaderelection.go:258] "Attempting to acquire leader lease..." lock="system-upgrade/system-upgrade-controller"
I0219 16:03:09.445895       1 leaderelection.go:272] "Successfully acquired lease" lock="system-upgrade/system-upgrade-controller"
time="2026-02-19T16:03:10Z" level=info msg="Starting /v1, Kind=Node controller"
time="2026-02-19T16:03:10Z" level=info msg="Starting /v1, Kind=Secret controller"
time="2026-02-19T16:03:10Z" level=info msg="Starting batch/v1, Kind=Job controller"
time="2026-02-19T16:03:10Z" level=info msg="Starting upgrade.cattle.io/v1, Kind=Plan controller"

모니터링 준비

업그레이드 진행 상황을 여러 터미널에서 관찰한다.

# 어플리케이션 통신 모니터링
while true; do curl -s http://192.168.10.12:30000 | grep Hostname; date; sleep 1; done

# Plan 및 Job 상태 모니터링
watch -d 'kubectl -n system-upgrade get plans -o wide; echo ; kubectl -n system-upgrade get jobs,pods'

# 노드 상태 모니터링
watch -d "kubectl get node"

# kube-system 파드 모니터링
watch -d "kubectl get pod -n kube-system -owide --sort-by=.metadata.creationTimestamp | tac"

Plan 생성 및 실행

서버 Plan과 에이전트 Plan을 생성한다.

[root@week07-k8s-node1 ~]# cat << EOF | kubectl apply -f -
# 서버 노드 업그레이드 플랜
apiVersion: upgrade.cattle.io/v1          # system-upgrade-controller의 CRD API 그룹
kind: Plan
metadata:
  name: server-plan
  namespace: system-upgrade               # SUC(system-upgrade-controller)가 watch하는 네임스페이스
spec:
  concurrency: 1                          # 동시에 업그레이드할 노드 수 (1 = 한 번에 1대씩 순차 진행)
  cordon: true                            # 업그레이드 전 노드를 cordon (스케줄링 차단) → drain은 별도 설정
  nodeSelector:
    matchExpressions:
    - key: node-role.kubernetes.io/control-plane   # RKE2 서버 노드에 자동 부여되는 레이블
      operator: In
      values:
      - "true"                            # 이 레이블 값이 "true"인 노드만 대상 → 즉, 컨트롤 플레인 노드만
  serviceAccountName: system-upgrade      # SUC가 사용할 ServiceAccount (RBAC 권한 필요)
  upgrade:
    image: rancher/rke2-upgrade           # 업그레이드를 수행하는 컨테이너 이미지 (rke2 바이너리 교체 + 서비스 재시작)
  channel: https://update.rke2.io/v1-release/channels/latest
  # ↑ 채널 URL: SUC가 이 URL에서 최신 버전을 조회하여 자동으로 대상 버전 결정
  # version 필드로 직접 지정도 가능 (예: version: v1.35.0+rke2r3)

---
# 에이전트(워커) 노드 업그레이드 플랜 
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: agent-plan
  namespace: system-upgrade
spec:
  concurrency: 1                          # 워커도 한 번에 1대씩 순차 업그레이드
  cordon: true
  nodeSelector:
    matchExpressions:
    - key: node-role.kubernetes.io/control-plane
      operator: DoesNotExist              # 이 레이블이 "없는" 노드 = 워커 노드만 대상
  prepare:                                # ★ 핵심: 실제 업그레이드 전에 실행되는 "준비" 컨테이너
    args:
    - prepare                             # rke2-upgrade 이미지의 prepare 모드 실행
    - server-plan                         # server-plan이 완료될 때까지 대기 → CP 먼저, 워커 나중 순서 보장
    image: rancher/rke2-upgrade
  serviceAccountName: system-upgrade
  upgrade:
    image: rancher/rke2-upgrade
  channel: https://update.rke2.io/v1-release/channels/latest
EOF
plan.upgrade.cattle.io/server-plan created
plan.upgrade.cattle.io/agent-plan created

각 Plan의 핵심 설정을 정리하면 다음과 같다.

설정 서버 Plan 에이전트 Plan
nodeSelector control-plane=true control-plane 레이블 없음 (DoesNotExist)
concurrency 1 (한 번에 1대씩) 1 (한 번에 1대씩)
cordon true true
prepare 없음 server-plan 완료 대기
channel latest latest

업그레이드 진행

Plan을 생성하면 컨트롤러가 즉시 대상 버전을 확인하고 업그레이드를 시작한다.

[root@week07-k8s-node1 ~]# kubectl -n system-upgrade get plans -o wide
NAME          IMAGE                  CHANNEL                                             VERSION   COMPLETE   MESSAGE   APPLYING
agent-plan    rancher/rke2-upgrade   https://update.rke2.io/v1-release/channels/latest             False                ["week07-k8s-node2"]
server-plan   rancher/rke2-upgrade   https://update.rke2.io/v1-release/channels/latest             False                ["week07-k8s-node1"]

APPLYING 컬럼에서 각 Plan이 어떤 노드에 적용 중인지 확인할 수 있다. 서버 Plan은 node1에, 에이전트 Plan은 node2에 적용된다.

Job과 Pod 상태를 확인하면:

[root@week07-k8s-node1 ~]# kubectl -n system-upgrade get jobs
NAME                                                              STATUS    COMPLETIONS   DURATION   AGE
apply-agent-plan-on-week07-k8s-node2-with-58646b4639f2f26-e4fc9   Running   0/1           3m56s      3m56s
apply-server-plan-on-week07-k8s-node1-with-58646b4639f2f2-5ef49   Running   0/1           3m56s      3m56s

서버 Plan Job과 에이전트 Plan Job이 동시에 생성되지만, 에이전트 Plan의 prepare 컨테이너가 서버 Plan 완료를 대기하므로 실제 업그레이드는 순차적으로 진행된다.

[root@week07-k8s-node1 ~]# kubectl get pod -n system-upgrade -owide
NAME                                                              READY   STATUS      RESTARTS        AGE     IP              NODE               NOMINATED NODE   READINESS GATES
apply-agent-plan-on-week07-k8s-node2-with-58646b4639f2f26-v4bbd   0/1     Init:1/2    0               4m11s   10.0.2.15       week07-k8s-node2   <none>           <none>
apply-server-plan-on-week07-k8s-node1-with-58646b4639f2f2-9wrjh   0/1     Completed   0               13s     192.168.10.11   week07-k8s-node1   <none>           <none>
apply-server-plan-on-week07-k8s-node1-with-58646b4639f2f2-mjft5   0/1     Unknown     0               4m11s   192.168.10.11   week07-k8s-node1   <none>           <none>
system-upgrade-controller-5f667989c7-6hbq4                        1/1     Running     1 (2m24s ago)   6m1s    10.42.0.7       week07-k8s-node1   <none>           <none>

서버 노드(node1)의 업그레이드 Pod가 Completed 상태가 되면, 에이전트 노드(node2)의 업그레이드가 시작된다. 서버 노드의 첫 번째 Pod(mjft5)가 Unknown 상태인 것은 RKE2 서버 프로세스 재시작으로 인해 컨트롤러가 일시적으로 연결이 끊겼기 때문이다. 컨트롤러가 복구된 후 새 Pod(9wrjh)를 생성해 업그레이드를 완료한다.

업그레이드 완료 확인

두 Job 모두 완료된다.

[root@week07-k8s-node1 ~]# kubectl -n system-upgrade get jobs
NAME                                                              STATUS     COMPLETIONS   DURATION   AGE
apply-agent-plan-on-week07-k8s-node2-with-58646b4639f2f26-e4fc9   Complete   1/1           5m9s       5m44s
apply-server-plan-on-week07-k8s-node1-with-58646b4639f2f2-5ef49   Complete   1/1           4m5s       5m44s

서버 노드 업그레이드에 약 4분, 에이전트 노드 업그레이드에 약 5분이 소요되었다.

컨트롤러 로그에서도 각 Plan의 완료를 확인할 수 있다.

[root@week07-k8s-node1 ~]# kubectl logs -n system-upgrade -l app.kubernetes.io/name=system-upgrade-controller
...
I0219 16:08:56.172680       1 event.go:389] "Event occurred" object="system-upgrade/server-plan" fieldPath="" kind="Plan" apiVersion="upgrade.cattle.io/v1" type="Normal" reason="JobComplete" message="Job completed on Node week07-k8s-node1"
I0219 16:08:56.199939       1 event.go:389] "Event occurred" object="system-upgrade/server-plan" fieldPath="" kind="Plan" apiVersion="upgrade.cattle.io/v1" type="Normal" reason="Complete" message="Jobs complete for version v1.35.1-rke2r1. Hash: 58646b4639f2f26db717305a88bd392986d5f96e4cd53d78dc1852d9"
I0219 16:10:01.001496       1 event.go:389] "Event occurred" object="system-upgrade/agent-plan" fieldPath="" kind="Plan" apiVersion="upgrade.cattle.io/v1" type="Normal" reason="JobComplete" message="Job completed on Node week07-k8s-node2"
I0219 16:10:01.020012       1 event.go:389] "Event occurred" object="system-upgrade/agent-plan" fieldPath="" kind="Plan" apiVersion="upgrade.cattle.io/v1" type="Normal" reason="Complete" message="Jobs complete for version v1.35.1-rke2r1. Hash: 58646b4639f2f26db717305a88bd392986d5f96e4cd53d78dc1852d9"

서버 Plan이 먼저 완료되고, 그 다음 에이전트 Plan이 완료된 순서를 확인할 수 있다.

노드 버전을 확인한다.

[root@week07-k8s-node1 ~]# kubectl get node -o wide
NAME               STATUS   ROLES                       AGE   VERSION          INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                      KERNEL-VERSION                  CONTAINER-RUNTIME
week07-k8s-node1   Ready    control-plane,etcd,master   28h   v1.35.1+rke2r1   192.168.10.11   <none>        Rocky Linux 9.6 (Blue Onyx)   5.14.0-570.52.1.el9_6.aarch64   containerd://2.1.5-k3s1
week07-k8s-node2   Ready    <none>                      23h   v1.35.1+rke2r1   10.0.2.15       <none>        Rocky Linux 9.6 (Blue Onyx)   5.14.0-570.52.1.el9_6.aarch64   containerd://2.1.5-k3s1

두 노드 모두 v1.35.1+rke2r1로 업그레이드 완료되었다.

업그레이드 Job의 권한 확인

업그레이드 Job이 생성하는 Pod의 볼륨 설정을 확인하면, 호스트 루트(/)가 마운트되어 있다.

[root@week07-k8s-node1 ~]# kubectl describe pod -n system-upgrade | grep ^Volumes: -A4
Volumes:
  host-root:
    Type:          HostPath (bare host directory volume)
    Path:          /
    HostPathType:  Directory

업그레이드 Pod가 호스트의 모든 경로에 read/write 접근이 가능하다는 것을 알 수 있다. 노드의 RKE2 바이너리를 교체하고 서비스를 재시작하기 위해 이러한 권한이 필요하다.


결과

system-upgrade-controller를 이용해 RKE2 클러스터를 v1.34에서 v1.35로 자동 업그레이드했다.

항목 내용
업그레이드 방식 system-upgrade-controller + Plan CRD (declarative)
설치 구성 CRD, Deployment, ServiceAccount, ClusterRoleBinding, ConfigMap
Plan 구성 서버 Plan + 에이전트 Plan (최소 2개)
업그레이드 순서 에이전트 Plan의 prepare 블록이 서버 Plan 완료 대기
소요 시간 서버 약 4분, 에이전트 약 5분
업그레이드 Job 권한 Host namespace(IPC, NET, PID) + CAP_SYS_BOOT + 호스트 루트 마운트

수동 업그레이드와 비교하면, 자동 업그레이드는 kubectl apply 한 번으로 전체 클러스터 업그레이드를 선언할 수 있다는 점이 장점이다. 특히 노드가 많은 환경에서 일일이 SSH 접속할 필요 없이 컨트롤러가 순차적으로 처리해 주므로 운영 부담이 크게 줄어든다. 다만 업그레이드 Job에 높은 권한이 필요하다는 점은 보안 관점에서 고려해야 한다.


참고



hit count

댓글남기기