이 글은 이번 세션에서 다룬 내용을 운영 관점 Runbook 형태로 하나로 합친 총 편집본입니다.
핵심은 “운영 중인 클러스터를 멈추지 않고” 다음을 안전하게 수행하는 방법입니다.
- 노드 패치/업그레이드 시 파드 안전 이동:
cordon/drain/uncordon - 컨트롤 플레인/워커를 kubeadm로 마이너 업그레이드: 1.28 → 1.29 (패키지 저장소/apt 명령/CNI/daemon-reload 포함)
- 장애/유실 대비 백업/복원 전략: 매니페스트/API 덤프 + etcd 스냅샷/파일 백업 + restore 실전
- restore 이후 흔한 이슈: RBAC 권한 롤백(Forbidden) 트러블슈팅
1. 노드 유지보수: cordon / drain / uncordon의 정확한 의미
1) cordon: “새 파드 스케줄링만 막기”
kubectl cordon <node>
- 노드를 Unschedulable(스케줄 불가) 상태로 표시합니다.
- 기존에 떠 있던 파드는 그대로 유지됩니다.
- 스케줄러가 “새 파드”를 이 노드에 올리지 않습니다.
결론:
cordon은 “노드에 새로 뭔가 더 올라오는 걸 막고 싶을 때” 쓰는 스위치입니다.
기존 파드를 다른 노드로 옮기지는 않습니다.
2) drain: “새 파드 막기 + 기존 파드 Eviction(퇴거)”
kubectl drain <node> --ignore-daemonsets
- 기본적으로
cordon도 같이 수행(= 스케줄 불가) - 그리고 해당 노드의 파드들을 퇴거(Eviction) 시켜 다른 노드로 재스케줄되게 만듭니다.
- DaemonSet 파드는 원래 “각 노드에 1개씩”이라 옮기지 않는 것이 일반적 → 그래서
--ignore-daemonsets가 사실상 필수 옵션처럼 사용됩니다.
엄밀히 말해 “이동”이 아니라 “종료 후 다른 노드에서 재생성”입니다.
3) uncordon: “스케줄 가능 상태로 원복”
kubectl uncordon <node>
- Unschedulable 표시를 해제합니다.
- 다시 새 파드를 스케줄할 수 있게 됩니다.
- drain으로 다른 노드로 간 파드는 자동으로 원래 노드로 돌아오지 않습니다.
(추후 스케줄링 이벤트가 생길 때만 다시 배치될 수 있음)
2. cordon만 쓰는 경우 vs drain이 필요한 경우
1) “cordon만 하고 패치”는 언제 적절한가?
- 노드에서 기존 파드를 죽이지 않고
- “아주 짧은 작업”만 하고
- 해당 노드에서 이미 돌아가는 파드가 짧은 순간 장애가 나도 괜찮거나, 혹은 노드/앱이 실제로 끊기지 않는 작업일 때
다만 현실적으로 “OS 패치 + 재부팅”이 들어가면 그 순간 노드는 NotReady가 되므로,
그 노드 위 파드로 들어오는 트래픽은 실패할 수 있습니다.
(아래 “트래픽/상태” 섹션 참고)
결론:
cordon은 “새 파드 유입 차단”이 목적이지, “무중단 보장” 스위치가 아닙니다.
2) “안전하게 비우고 작업”하려면 drain
- 노드가 언제 복귀할지 확실치 않거나
- 재부팅/커널 패치 등으로 노드가 내려갈 게 확실하거나
- 무중단을 더 강하게 보장하고 싶다면
kubectl drain <node> --ignore-daemonsets
# 작업(패치/업그레이드/재부팅)
kubectl uncordon <node>
3. drain은 “standalone Pod”에서 어떻게 동작하나?
1) 컨트롤러(Deployment/ReplicaSet/StatefulSet/Job 등)로 관리되는 파드
- drain 시 evict되면 다른 노드에서 컨트롤러가 다시 만들어줍니다.
- 그래서 운영 워크로드는 기본적으로 컨트롤러로 띄우는 게 정석입니다.
2) standalone Pod(어떤 컨트롤러에도 속하지 않은 Pod)
- drain 시 이 파드는 “다른 노드에서 자동 재생성”되지 않습니다.
- 그래서
kubectl drain은 보통 거부/에러를 내고 멈추는 방향이 기본 동작입니다.
해결 옵션:
- (권장) standalone 파드를 먼저 컨트롤러로 전환하거나 수동 제거
- (마지막 수단) 강제로 진행:
kubectl drain <node> --ignore-daemonsets --force
--force는 “이 파드가 다시 안 떠도 감수하고 지울게”에 가깝습니다.
추가로 운영에서 자주 마주치는 요소:
- PDB(PodDisruptionBudget): “동시 퇴거 허용량”을 제한하여 drain이 대기/실패할 수 있습니다.
- 로컬 데이터(
emptyDir등) 때문에 데이터 유실이 우려되면 주의가 필요합니다.
4. “노드가 5분 다운되면 자동 drain인가?” (정확한 해석)
아닙니다. “자동 drain”과 같은 개념이 아닙니다.
클러스터에서 노드가 NotReady가 되면, 컨트롤 플레인의 Node Controller가 일정 시간 기다린 뒤(기본값이 흔히 5분으로 설명되는 구간) 다음을 수행합니다.
- 해당 노드의 파드들을 강제로 종료 처리(상태 정리/eviction) 하거나
- 컨트롤러가 관리하는 파드는 다른 노드에 재생성되도록 유도
하지만 이는:
kubectl drain이 수행하는 “정돈된 퇴거 + 스케줄 불가 표시 + PDB 기반 eviction 흐름”과 동일하지 않습니다.- standalone 파드는 그냥 사라질 수 있습니다.
- 노드가 다시 살아나면 “파드가 없는 빈 노드”처럼 보일 수 있습니다.
결론: “5분 후 자동 drain”이 아니라, 노드 장애 처리 타임아웃 이후 컨트롤 플레인이 ‘죽은 노드로 간주하고 정리/재스케줄’을 시작하는 것에 가깝습니다.
5. Unschedulable / NotReady와 트래픽의 관계
1) Unschedulable(cordon/drain 상태)
- 의미: 새 파드를 여기 스케줄하지 마
- 그러나 기존 파드는 살아있고, kubelet도 정상이라면 서비스 트래픽은 계속 처리될 수 있습니다.
- 즉 “Unschedulable이면 서비스가 트래픽을 안 보낸다”는 해석은 틀릴 수 있습니다.
2) NotReady(노드가 다운/네트워크 단절/kubelet 문제)
- 노드가 NotReady면 해당 노드의 엔드포인트는 서비스에서 제외되는 방향으로 수렴합니다.
- 다만 완전히 즉시 0초로 전환되는 게 아니라, 감지/전파/엔드포인트 갱신까지 시간이 걸 수 있어 “짧은 실패 구간”이 생길 수 있습니다.
운영 결론: 무중단 확률을 올리려면 cordon만으로는 부족할 수 있고, drain으로 트래픽을 다른 노드로 옮긴 뒤 작업하는 게 더 안전합니다.
6. kubeadm으로 Kubernetes 1.28 → 1.29 업그레이드 실전 정리
6.1 Kubernetes 버전과 “버전 스큐(version skew)”
- Kubernetes 버전은
Major.Minor.Patch - 모든 컴포넌트가 반드시 완전히 동일 버전일 필요는 없고, API Server를 기준으로 아래쪽 버전을 허용하는 범위가 있습니다.
- 핵심 규칙: 다른 컴포넌트가 API Server보다 높은 버전이면 안 됨
또한 Kubernetes는 일반적으로 “최근 마이너 3개”만 지원하므로,
업그레이드는 보통 마이너를 한 번에 하나씩이 권장됩니다.
6.2 패키지 저장소(repository)란?
Ubuntu/Debian에서 apt는 “패키지를 어디서 다운로드할지”를 저장소 목록으로 관리합니다.
/etc/apt/sources.list/etc/apt/sources.list.d/*.list
여기에 “이 URL의 패키지 인덱스를 사용해라”를 등록합니다.
문서에서 본 이 줄의 의미
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /
deb: Debian 패키지 저장소 선언signed-by=...: 이 저장소 패키지의 신뢰 검증에 쓸 GPG 키(키링) 지정https://pkgs.k8s.io/.../v1.34/...: “v1.34 라인” 패키지 인덱스를 보겠다는 뜻
(마이너 버전 별로 저장소가 분리되므로 업그레이드 타겟 마이너에 맞춰 변경)
6.3 sudo apt-get update는 뭘 하나?
sudo apt-get update
- 저장소에 있는 “패키지 목록 인덱스”를 로컬로 새로 받아옵니다.
- 설치가 아니라, 목록 갱신입니다.
- 저장소 URL을 바꿨다면 거의 항상 먼저 실행해야 합니다.
6.4 apt-cache madison kubeadm은 뭘 하나?
apt-cache madison kubeadm
- 현재 설정된 저장소에서
kubeadm패키지의 설치 가능한 버전 목록을 보여줍니다. - 그래서 “1.29 라인에서 가장 최신 패치 버전(예: 1.29.3-1.1)”을 선택하는 데 씁니다.
6.5 왜 kubeadm이 kubelet을 안 올려주나?
kubeadm의 역할은 주로:
- 클러스터 부트스트랩/업그레이드(컨트롤 플레인 매니페스트/구성 갱신)
- 인증서/구성 관리
반면 kubelet은:
- 각 노드의 OS 패키지/서비스(systemd)로 동작하고,
- 노드별로 설치 방법/관리 방식이 다를 수 있어(패키지 매니저/이미지/관리형 서비스 등)
kubeadm이 일괄로 “패키지 업그레이드”까지 책임지지 않습니다.
그래서 업그레이드 플로우가 분리됩니다.
- kubeadm으로 컨트롤 플레인 업그레이드
- 각 노드에서 kubelet(및 kubectl) 패키지 업그레이드 + kubelet 재시작
6.6 업그레이드 전체 흐름(핵심 Runbook)
Step 0) 준비: 저장소를 목표 마이너로 맞추기(pkgs.k8s.io)
- 컨트롤 플레인, 워커 모든 노드에서 수행
예: /etc/apt/sources.list.d/kubernetes.list의 URL 마이너를 다음 버전으로 변경 후
sudo apt-get update
Step 1) 컨트롤 플레인 업그레이드
- kubeadm 업그레이드
sudo apt-get install -y kubeadm=<1.29.x-...>
- 계획 확인(드라이런 성격)
sudo kubeadm upgrade plan
- 적용
sudo kubeadm upgrade apply v1.29.x
- 컨트롤 플레인 노드 drain → kubelet/kubectl 업그레이드 → kubelet 재시작 → uncordon
kubectl drain <control-plane-node> --ignore-daemonsets
sudo apt-get install -y kubelet=<1.29.x-...> kubectl=<1.29.x-...>
sudo systemctl restart kubelet
kubectl uncordon <control-plane-node>
Step 2) 워커 노드 업그레이드(노드별 반복)
워커는 “한 번에 하나씩”이 일반적인 무중단 전략입니다.
- 워커 노드에서 kubeadm 업그레이드
sudo apt-get install -y kubeadm=<1.29.x-...>
sudo kubeadm upgrade node
- 컨트롤 플레인에서 워커 drain
kubectl drain <worker-node> --ignore-daemonsets
- 워커에서 kubelet/kubectl 업그레이드 + kubelet 재시작
sudo apt-get install -y kubelet=<1.29.x-...> kubectl=<1.29.x-...>
sudo systemctl restart kubelet
- 컨트롤 플레인에서 uncordon
kubectl uncordon <worker-node>
6.7 CNI 플러그인이란?
- Kubernetes 네트워크를 “파드 IP 할당/라우팅/네트워크 정책” 등으로 구현하는 컴포넌트입니다.
- Calico, Cilium, Flannel 등이 대표적입니다.
- 업그레이드 시점에 CNI가 “해당 Kubernetes 버전과 호환되는 버전”을 요구할 수 있어 문서에서 “필요 시 CNI도 업그레이드”를 안내합니다.
6.8 sudo systemctl daemon-reload는 뭐고 왜 하나?
sudo systemctl daemon-reload
- systemd가 관리하는 서비스 유닛(.service) 정의를 다시 읽어들이는 명령입니다.
- 일반적으로 아래 상황에서 필요합니다.
- 유닛 파일 또는 drop-in 설정을 변경했을 때
- 복원/업데이트 과정에서 서비스 정의가 바뀌었을 때
kubelet/etcd를 “패키지/설정 변경 후 재기동”하는 흐름에서 종종 등장합니다.
7. 외부 트래픽은 “마스터(컨트롤 플레인)”과 관계 있나?
일반적인 운영 구성에서:
- 외부 요청은 보통 LoadBalancer/Ingress/NodePort 등을 통해 워커 노드의 파드로 들어갑니다.
- 컨트롤 플레인(API Server 등)은 “클러스터 관리/스케줄링/컨트롤” 경로에 있고,
일반 사용자 트래픽의 데이터 플레인 경로에 직접 서지 않는 게 보통입니다.
단, 예외:
- 컨트롤 플레인 노드에 워크로드가 스케줄되도록 열어둔 경우(taint 제거 등)
- NodePort를 컨트롤 플레인 노드 IP로 직접 때리는 특이 구성
8. Kubernetes 백업/복원 전략: 매니페스트 + API 덤프 + etcd
8.1 “정답에 가까운” 백업 계층
- GitOps(선언적 YAML): 가장 권장
- API 덤프/도구(Velero 등): 선언적이 완벽하지 않은 현실 보완
- etcd 백업: 클러스터 상태 자체를 시점 복구
8.2 kubectl get all -o yaml은 중복 배포가 아니라 “덤프”다
kubectl get all --all-namespaces -o yaml > cluster-all.yaml
- 이 자체는 배포가 아니라 조회 결과 저장입니다.
- 다만 이 파일을 그대로
apply하면 “복원용”으로 부적합할 수 있습니다.- Pod 같은 하위 리소스까지 섞이고
- status/managedFields/resourceVersion 등이 포함되어 충돌/혼란 가능
복원용에 더 가까운 형태는 “컨트롤러/구성 중심으로 선택 덤프”입니다.
kubectl get deploy,sts,ds,svc,ing,cm,secret -A -o yaml > workload-config.yaml
9. etcd 백업 3종 비교: etcdctl(TLS) / etcdctl(무옵션) / etcdutl
9.1 (A) etcdctl + TLS 옵션(표준/권장)
ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/etcd-snapshot.db
- kubeadm etcd는 보통 HTTPS + mTLS라 이게 가장 확실합니다.
- 결과물: 단일
.db스냅샷 파일
9.2 (B) etcdctl 무옵션: etcdctl snapshot save snapshot.db
etcdctl snapshot save snapshot.db
- endpoints/TLS/인증서 정보를 안 주고 기본값에 의존합니다.
- kubeadm 기본(HTTPS+mTLS)에서는 대체로 실패 가능성이 큽니다.
- “짧게” 쓰고 싶다면 환경변수로 고정:
export ETCDCTL_API=3
export ETCDCTL_ENDPOINTS=https://127.0.0.1:2379
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/server.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/server.key
etcdctl snapshot save snapshot.db
9.3 (C) etcdutl backup: data-dir 파일 레벨 백업
etcdutl backup \
--data-dir /var/lib/etcd \
--backup-dir /backup/etcd-backup
- 실행 중 etcd에 접속하는 방식이 아니라 파일 기반 백업
- 결과물: 디렉터리(backend + WAL 등 여러 파일)
보통 정기 백업/복원 “표준”은 (A) 스냅샷 기반이 더 단순합니다.
9.4 restore는 etcdctl로도 되고 etcdutl로도 되나?
.db스냅샷(etcdctl snapshot save)을 복원:etcdutl snapshot restore또는etcdctl snapshot restore모두 가능
etcdutl backup으로 만든 디렉터리 백업은 “스냅샷 복원”이 아니라- etcd를 내린 뒤 data-dir을 백업본으로 되돌리고 재기동하는 방식이 일반적
9.5 ETCDCTL_API=3는 꼭 붙여야 하나?
대부분 v3 환경에서는 없어도 동작하는 경우가 많지만,
환경/버전 차이를 줄이기 위해 붙이는 것을 권장합니다(일관성/안전).
10. etcd restore 실전 Runbook (kubeadm static pod 기준)
10.1 restore 전에 무엇을 꺼야 하나?
- ✅ etcd는 내려야 합니다(데이터 디렉터리 교체 작업이므로)
- ✅ kube-apiserver도 같이 내리는 것이 안전합니다(복원 중 etcd 의존 충돌 방지)
- 노드 전원 OFF는 보통 불필요합니다.
static pod “끄기/켜기” (manifest 이동)
끄기:
sudo mkdir -p /etc/kubernetes/manifests.disabled
sudo mv /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests.disabled/
sudo mv /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests.disabled/
켜기(순서 중요: etcd → apiserver):
sudo mv /etc/kubernetes/manifests.disabled/etcd.yaml /etc/kubernetes/manifests/
sudo mv /etc/kubernetes/manifests.disabled/kube-apiserver.yaml /etc/kubernetes/manifests/
10.2 restore 대상 디렉터리를 새로 만들었다면, etcd.yaml에서 무엇을 수정해야 하나?
핵심 구분:
--data-dir=...: 컨테이너 내부 경로volumeMounts.mountPath: 컨테이너 내부 마운트 경로volumes.hostPath.path: 호스트 실제 디렉터리
가장 흔한(그리고 안전한) 패턴: hostPath만 바꿔서 “호스트 데이터 경로”를 갈아끼운다
예: restore를 호스트에 /var/lib/etcd-from-backup로 해뒀다면,etcd.yaml에서 보통 이 부분을 수정합니다.
volumes:
- name: etcd-data
hostPath:
path: /var/lib/etcd-from-backup
type: DirectoryOrCreate
이게 성립하려면(대부분 kubeadm 기본):
--data-dir=/var/lib/etcd(컨테이너 내부)mountPath: /var/lib/etcd(컨테이너 내부)
가 유지되어야 합니다.
즉, 컨테이너는 여전히 /var/lib/etcd를 보는데,
그 /var/lib/etcd가 “호스트의 /var/lib/etcd-from-backup를 마운트한 것”이 되어
복원 데이터가 적용됩니다.
주의: DirectoryOrCreate 함정
경로가 비어있거나 restore가 실제로 안 들어갔는데도 디렉터리가 생성되어
빈 etcd로 새로 올라오는 착시가 생길 수 있습니다.
restore 디렉터리 안에 member/ 같은 내용이 있는지 반드시 확인하세요.
11. restore 후 Forbidden이 뜨는 이유(RBAC 롤백)
etcd restore는 “클러스터 상태 전체를 스냅샷 시점으로 롤백”합니다.
즉 RBAC(ClusterRoleBinding 등)도 롤백되므로, 스냅샷 시점에 권한 구성이 다르면:
kubernetes-admin cannot list deployments ... Forbidden
같은 증상이 날 수 있습니다.
운영적으로는 보통:
- “올바른(최근) 스냅샷”으로 복원하는 게 1순위
- 긴급 복구로 권한을 재구성하는 플로우가 2순위입니다.
12. 운영 체크리스트(실수 방지)
노드 유지보수
- 워크로드는 가능한 한 컨트롤러(Deployment/StatefulSet)로 운영
- drain 시:
--ignore-daemonsets기본- standalone 파드 있으면 사전 처리(또는 최후
--force) - PDB 때문에 drain이 막힐 수 있음
kubeadm 업그레이드
- 저장소 URL의 마이너 버전 경로(pkgs.k8s.io)를 목표 버전으로 변경
apt-get update후apt-cache madison으로 설치 가능한 버전 확인- 컨트롤 플레인 먼저, 워커는 1대씩
- kubelet은 수동 업그레이드 + 재시작 필요
etcd 백업/복원
- kubeadm etcd는 HTTPS+mTLS가 일반적 → etcdctl은 TLS 옵션(또는 환경변수)로 실행
- restore 전에는 etcd/apiserver 내리고 진행
- restore를 다른 디렉터리에 했다면 etcd.yaml에서 대개
hostPath.path를 그 경로로 변경 - restore 이후 RBAC 롤백으로 Forbidden 가능
'CKA' 카테고리의 다른 글
| Security - Authentication 기초 (0) | 2025.12.31 |
|---|---|
| Security - 개요 (0) | 2025.12.31 |
| Cluster Maintenance - 백업 방법(yaml 및 etcd) (0) | 2025.12.31 |
| Cluster Maintenance - 쿠버네티스 릴리즈 버전 이해 및 버전 업그레이드 방법 (0) | 2025.12.31 |
| Cluster Maintenance - Cordon/Drain/Uncordon (1) | 2025.12.31 |