[12. 노드 관리]
출제 비중: 중간. drain 과 uncordon 은 업그레이드 문제에 항상 끼어 나오고 단독 문제로도 자주 나온다.
명령이 짧아서 점수를 쉽게 딸 수 있는 영역.
관련 문제: 0033 (노드 유지보수 drain) - 통과
1. 왜 필요한가
노드는 OS 패치, 커널 업그레이드, 하드웨어 교체 때문에 가끔 내려야 한다.
그냥 끄면 그 위의 Pod 이 갑자기 죽는다.
그래서 새 Pod 이 안 오게 막고, 기존 Pod 을 다른 노드로 옮긴 다음 작업한다.
4절 업그레이드에서 쓴 drain 과 uncordon 이 이것이다.
2. 세 가지 동작
명령 새 Pod 스케줄 기존 Pod 노드 오브젝트
cordon 막음 그대로 둠 유지
drain 막음 (cordon 포함) 다른 노드로 내보냄 유지
delete node - 오브젝트와 함께 정리 삭제
uncordon 다시 허용 돌아오지 않음 유지
uncordon 해도 Pod 이 돌아오지 않는다.
쿠버네티스는 이미 잘 돌고 있는 Pod 을 다시 옮기지 않는다.
새로 만들어지는 Pod 부터 그 노드에 배치된다.
cordon 의 정체는 taint 다.
cordon 하면 spec.unschedulable: true 가 켜지고
node.kubernetes.io/unschedulable:NoSchedule taint 가 자동으로 붙는다.
0005 taint 문제와 같은 원리.
kubectl get no 에서 보이는 모습
cka-worker2 Ready,SchedulingDisabled
Ready 는 그대로다. 노드는 건강하고 스케줄만 막힌 상태.
3. drain 이 멈추는 경우와 옵션
drain 은 안전을 위해 위험한 상황에서 멈춘다.
각 옵션은 "이 위험은 감수하겠다"는 뜻이다.
멈추는 이유 옵션 옵션을 주면
DaemonSet Pod 이 있음 --ignore-daemonsets DaemonSet Pod 은 남겨 둠 (노드마다 떠야 하는 Pod)
emptyDir 쓰는 Pod 이 있음 --delete-emptydir-data emptyDir 데이터가 지워지는 걸 허용
컨트롤러 없이 만든 Pod --force 그 Pod 을 삭제. 다시 만들어 줄 주인이 없어 영원히 사라짐
PodDisruptionBudget 위반 없음 기다리거나 PDB 를 확인. --force 로도 우회 안 됨
Deployment Pod 은 옮겨지는 게 아니라 새로 생긴다.
drain 은 Pod 을 지우고, ReplicaSet 이 개수를 맞추려고 다른 노드에 새 Pod 을 만든다.
그래서 Pod 이름과 IP 가 바뀐다.
PDB 는 순단 방지 장치다.
"이 앱은 최소 2개는 떠 있어야 한다" 같은 규칙이고, drain 은 이걸 어기지 않으려고 기다린다.
4. 노드 제거와 재가입
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data 1) 비우기
kubectl delete node <node> 2) 클러스터에서 제거
kubeadm reset 3) 노드 안에서 kubeadm 흔적 정리
kubeadm token create --print-join-command 4) CP 에서 join 명령 발급
5) 노드에서 출력된 join 명령 실행
7절 HA 에서 배운 join 명령이 여기서 다시 쓰인다.
5. 노드 준비 작업 (새 노드 join 전)
준비 이유 명령
swap off kubelet 은 기본적으로 swap 이 켜져 있으면 시작 안 함 swapoff -a
커널 모듈 overlay = 컨테이너 파일시스템 modprobe overlay
br_netfilter = 브리지 트래픽을 iptables 가 보게 함 modprobe br_netfilter
sysctl Pod 트래픽을 노드가 전달하고 iptables 규칙을 적용하게 함 net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-iptables=1
런타임 설치 CRI 구현체가 있어야 kubelet 이 컨테이너를 띄움 패키지 설치 후 systemctl enable --now
sysctl 은 재부팅해도 유지되게 설정한다.
/etc/sysctl.d/ 아래 파일에 적고 sysctl --system 으로 적용.
공식 문서 검색어 "container runtimes" 페이지에 전체 명령이 있다.
6. 노드 정보 확인
kubectl get no -o wide 버전, OS, 커널, 런타임, IP
kubectl describe no <node> Conditions, Taints, Allocatable, 떠 있는 Pod, Events
kubectl top no CPU, 메모리 실제 사용량
describe 에는 -o 옵션이 없다.
kubectl describe no -o wide
-> error: unknown shorthand flag: 'o' in -o
-o 는 get 전용. describe 는 항상 사람이 읽는 형식으로만 나온다.
7. describe node 읽는 법 (cka-worker 실제 출력 기준)
Labels
kubernetes.io/arch=arm64, kubernetes.io/hostname=cka-worker
-> nodeSelector, affinity 에서 쓰는 값. 맥북이 Apple Silicon 이라 arm64.
Annotations
projectcalico.org/IPv4Address: 172.20.0.4/16
-> CNI(Calico)가 이 노드에 기록한 정보. 10절에서 본 calico-kubeconfig 로 쓴 것.
Taints / Unschedulable
Taints: <none>, Unschedulable: false
-> cordon 하면 여기가 바뀐다. drain 확인할 때 제일 먼저 볼 곳.
Lease
RenewTime: ...
-> kubelet 이 "나 살아있다"를 주기적으로 갱신하는 기록 (kube-node-lease 네임스페이스).
갱신이 끊기면 노드가 NotReady 로 바뀐다.
Conditions
NetworkUnavailable False CalicoIsUp CNI 가 정상
MemoryPressure False 메모리 여유
DiskPressure False 디스크 여유
PIDPressure False 프로세스 수 여유
Ready True KubeletReady kubelet 정상
-> 트러블슈팅의 출발점. NotReady 면 여기 Reason 과 Message 를 먼저 본다.
CNI 가 없으면 Ready 의 Message 에 "cni config uninitialized" 가 나온다.
Pressure 가 True 면 kubelet 이 Pod 을 쫓아내기 시작한다.
Capacity / Allocatable
cpu 10, memory 8024724Ki, pods 110
-> Capacity = 노드 전체. Allocatable = Pod 에 줄 수 있는 양.
실무 노드는 kubelet/OS 몫을 떼어서 Allocatable 이 더 작다. kind 는 떼지 않아 같다.
pods: 110 = 노드당 최대 Pod 수 기본값.
System Info
Kernel, OS, Container Runtime Version, Kubelet Version
-> get no -o wide 와 같은 정보. 업그레이드 후 kubelet 버전 확인에 쓴다.
Kube-Proxy Version 이 비어 있는 건 더 이상 채우지 않는 필드라서 정상.
PodCIDR
192.168.2.0/24
-> 이 노드의 Pod 이 받을 IP 대역. 노드마다 다르다.
Non-terminated Pods
prod-web (0021 에서 만든 것), calico-node, kube-proxy
-> 지금 이 노드에 떠 있는 Pod 과 각자의 requests/limits.
drain 전에 무엇이 나갈지 볼 때 유용하다.
Allocated resources
cpu requests 250m (2%)
-> 이 노드에 뜬 Pod 들의 requests 합.
핵심: Allocated resources 와 top 의 차이
describe 의 Allocated resources requests 합계 (예약된 양)
kubectl top no 실제 사용량
스케줄러는 실제 사용량이 아니라 requests 합계로 판단한다.
top 에서 CPU 가 한가해 보여도 requests 합이 꽉 차면 새 Pod 은 Pending.
문제 0024 (Pending Pod 리소스) 가 이 원리다.
8. 함정
- cordon 만 하고 끝냄 -> 기존 Pod 은 그대로 남는다. "Pod 을 내보내라"면 drain.
- 작업 후 습관적으로 uncordon -> "그 상태로 두라"는 문제면 지문 위반.
- --force 를 습관적으로 붙임 -> 컨트롤러 없는 Pod 이 영원히 사라진다. 필요할 때만.
- drain 을 노드 안에서 실행 -> drain 은 kubectl 명령. 노드 밖에서 친다.
- cordon 된 노드를 그대로 두고 다른 문제로 넘어감
-> 다음 문제의 Pod 이 그 노드에 안 뜬다. 실습 후 원복하거나 q reset.
9. 문제 0033 풀이
요구사항
- cka-worker2 의 Pod 을 안전하게 내보냄 (DaemonSet 무시, emptyDir 삭제 허용)
- 새 Pod 이 스케줄되지 않게
- uncordon 하지 말고 그 상태로 둠
명령
kubectl get po -A -o wide --field-selector spec.nodeName=cka-worker2 내보낼 Pod 확인
kubectl drain cka-worker2 --ignore-daemonsets --delete-emptydir-data
kubectl get no cka-worker2 Ready,SchedulingDisabled
kubectl get po -A -o wide --field-selector spec.nodeName=cka-worker2 DaemonSet Pod 만 남음
./bin/q check 33
- 괄호 안 조건 두 개가 옵션 두 개와 1대1 대응.
- drain 이 cordon 을 포함하므로 "새 Pod 스케줄 금지"를 위해 cordon 을 따로 칠 필요 없다.
- 컨트롤러 없는 단독 Pod 때문에 거부되면 --force 추가 (그 Pod 은 재생성되지 않고 삭제됨).
실습 후 원복
kubectl uncordon cka-worker2