三个节点 — join、drain 与 kubelet 恢复
한국어 원문으로 표시합니다.
목표
노드 세 대에 ssh 로 들어가 kubeadm 클러스터를 조립하고, 워커 하나를 비워 정비한 뒤, 재부팅하고 돌아오지 않는 다른 워커를 되살립니다. CKA 실기가 요구하는 "여러 노드를 오가며 일하는 손" 을 진짜 VM 세 대에서 익힙니다.
왜 중요한가
한 대짜리 클러스터에서는 노드가 무엇인지 보이지 않습니다. 노드가 여럿이 되는 순간 세 가지가 달라집니다.
첫째, 노드의 주소가 문제가 됩니다. 이 실습의 세 VM 은 첫 NIC 의 주소가 모두 10.0.2.2 이고, 실제 클러스터 망은 두 번째 NIC(eth1)입니다.
kubelet 의 --node-ip, API 서버의 advertise 주소, CNI 의 터널 NIC 가 모두 eth1 을 가리켜야 합니다 — 다중 NIC 서버에서 쿠버네티스를 세울 때 실제로 가장 많이 틀리는 곳입니다.
둘째, 정비가 생깁니다. 노드를 끄기 전에 파드를 비우는 drain 과, 되돌리는 uncordon 은 운영의 일상입니다.
셋째, kubelet 은 파드가 아니라 systemd 서비스입니다. 노드가 NotReady 일 때 kubectl 로는 원인에 닿지 않고, 그 노드에 들어가 서비스를 봐야 합니다.
단계
- controlplane 에서
ssh node01·ssh node02로 각 노드에 들어가hostname과 eth1 의 IPv4 주소를 확인하세요. 세 노드를/root/cluster/nodes.txt에이름 주소한 줄씩(controlplane·node01·node02) 적습니다. - controlplane 에서
kubeadm init을--kubernetes-version v1.36.4 --apiserver-advertise-address <controlplane 의 eth1 주소> --pod-network-cidr 172.20.0.0/16 --service-cidr 172.21.0.0/16로 실행하고,/etc/kubernetes/admin.conf를/root/.kube/config로 복사하세요. controlplane 노드의 InternalIP 가 eth1 주소여야 합니다. - 미리 받아 둔
/root/cluster/kube-flannel.yml(Flannel v0.28.9)에서net-conf.json의Network를172.20.0.0/16로 바꾸고,kube-flannel컨테이너 args 에--iface=eth1을 더해 적용하세요. controlplane 이 Ready 가 되고 CoreDNS 가 Available 이어야 합니다. - controlplane 에서 조인 명령을 만들어(
kubeadm token create --print-join-command) node01 과 node02 에서 실행하세요. 세 노드가 모두 Ready 이고, node01·node02 의 InternalIP 가 각자의 eth1 주소여야 합니다. - default 네임스페이스에 Deployment
web을 이미지registry.k8s.io/e2e-test-images/agnhost:2.53, 명령/agnhost netexec --http-port=8080, 레플리카 2 로 만드세요. 두 파드가 워커에서 Ready 이고, controlplane 에서 각 파드 IP 의:8080/hostname이 그 파드 이름을 돌려줘야 합니다. - node01 을
kubectl drain으로 비우고(DaemonSet 파드는 남겨 둡니다) 그 출력 전체를/root/cluster/drain.log에 저장하세요. node01 에 DaemonSet 이 아닌 파드가 하나도 없어야 하고,web은 두 파드가 모두 Ready 여야 합니다. - node02 를 재부팅하세요(
ssh node02 reboot). 돌아온 뒤 node02 가 NotReady 로 남는 원인을 node02 에서 찾아 고치고, 다음 재부팅에도 스스로 돌아오게 하세요. - node01 을 다시 스케줄 가능하게 하세요. 세 노드가 모두 Ready·스케줄 가능이고, Flannel DaemonSet 이 세 노드에서 준비되어 있으며,
web의 두 파드가 Ready 여야 합니다.
참고
- 이 터미널은 controlplane 입니다. 다른 노드는
ssh node01·ssh node02로 들어가고exit로 돌아옵니다. 세 노드 모두 root 입니다. - 세 노드에 containerd 2.3.5 와 kubeadm·kubelet·kubectl 1.36.4 가 설치돼 있고, 런타임 설정(SystemdCgroup·ip_forward·br_netfilter)과 이미지 받기는 끝나 있습니다. 클러스터는 아직 없습니다.
- 대역은 파드
172.20.0.0/16·서비스172.21.0.0/16입니다. 이 VM 들이 도는 바깥 클러스터가 10.244/16·10.96/12 를 쓰기 때문입니다. - 흔한 실수: advertise 주소 없이 init 하는 것. 워커가 10.0.2.2:6443 으로 API 서버를 찾아가 자기 자신에게 붙으려 합니다. 되돌리려면
kubeadm reset -f부터 해야 합니다. - 흔한 실수: 7단계에서
systemctl start kubelet만 하는 것. 지금은 돌아오지만 다음 재부팅에 같은 일이 납니다. - 세션이 끝나면 세 VM 이 함께 사라집니다. 60분이 넘으면 화면의 +시간으로 연장하세요.
세 노드에 들어가 두 번째 NIC 를 확인한다
controlplane 에서 ssh node01·ssh node02 로 각 노드에 들어가 hostname 과 eth1 의 IPv4 주소를 확인하세요. 세 노드를 /root/cluster/nodes.txt 에 이름 주소 한 줄씩(controlplane·node01·node02) 적습니다.
ip -4 addr 를 보면 NIC 가 둘입니다. 첫 NIC(enp1s0)의 주소가 세 노드 모두 같다는 것을 확인해 보세요 — 그 주소로는 노드를 구별할 수 없습니다. ssh node01 'ip -4 -o addr show eth1' 처럼 명령만 보내도 됩니다.
eth1 주소로 컨트롤 플레인을 세운다
controlplane 에서 kubeadm init 을 --kubernetes-version v1.36.4 --apiserver-advertise-address <controlplane 의 eth1 주소> --pod-network-cidr 172.20.0.0/16 --service-cidr 172.21.0.0/16 로 실행하고, /etc/kubernetes/admin.conf 를 /root/.kube/config 로 복사하세요. controlplane 노드의 InternalIP 가 eth1 주소여야 합니다.
advertise 주소를 주지 않으면 kubeadm 은 기본 경로의 NIC(10.0.2.2)를 고릅니다. 그러면 워커가 그 주소로 API 서버를 찾아가 자기 자신에게 붙습니다. kubelet 쪽 주소는 /etc/default/kubelet 의 --node-ip 가 정합니다 — 이미 적혀 있으니 읽어 보세요.
Flannel 에게 어느 NIC 로 터널을 만들지 알려 준다
미리 받아 둔 /root/cluster/kube-flannel.yml(Flannel v0.28.9)에서 net-conf.json 의 Network 를 172.20.0.0/16 로 바꾸고, kube-flannel 컨테이너 args 에 --iface=eth1 을 더해 적용하세요. controlplane 이 Ready 가 되고 CoreDNS 가 Available 이어야 합니다.
Flannel 은 기본 경로의 NIC 로 VXLAN 을 만듭니다. 그 NIC 의 주소가 세 노드 모두 같으면 터널의 목적지가 전부 자기 자신이 됩니다 — 노드는 Ready 인데 다른 노드의 파드에 닿지 않는 증상입니다. args 는 --kube-subnet-mgr 줄 아래에 같은 들여쓰기로 한 줄 더합니다.
워커 둘을 조인한다
controlplane 에서 조인 명령을 만들어(kubeadm token create --print-join-command) node01 과 node02 에서 실행하세요. 세 노드가 모두 Ready 이고, node01·node02 의 InternalIP 가 각자의 eth1 주소여야 합니다.
조인 명령은 워커에서 root 로 실행합니다 — ssh node01 '<조인 명령>'. 조인이 끝나도 Flannel 파드가 그 노드에 뜨기까지 Ready 는 잠시 늦습니다. 조인 출력의 WARNING 줄도 읽어 두세요. 나중에 쓸모가 있습니다.
다른 노드의 파드에 실제로 닿는지 본다
default 네임스페이스에 Deployment web 을 이미지 registry.k8s.io/e2e-test-images/agnhost:2.53, 명령 /agnhost netexec --http-port=8080, 레플리카 2 로 만드세요. 두 파드가 워커에서 Ready 이고, controlplane 에서 각 파드 IP 의 :8080/hostname 이 그 파드 이름을 돌려줘야 합니다.
kubectl create deployment web --image=… --replicas=2 -- /agnhost netexec --http-port=8080 한 줄이면 됩니다. controlplane 에는 control-plane 테인트가 있어 파드가 워커로만 갑니다. curl 이 멈춘다면 Flannel 의 NIC 부터 의심하세요.
node01 을 정비하려고 비운다
node01 을 kubectl drain 으로 비우고(DaemonSet 파드는 남겨 둡니다) 그 출력 전체를 /root/cluster/drain.log 에 저장하세요. node01 에 DaemonSet 이 아닌 파드가 하나도 없어야 하고, web 은 두 파드가 모두 Ready 여야 합니다.
드레인은 cordon(스케줄 금지) + 축출입니다. Flannel·kube-proxy 같은 DaemonSet 파드는 축출해도 곧바로 같은 노드에 다시 생기므로 --ignore-daemonsets 로 건너뜁니다. 축출된 web 파드는 node02 로 옮겨 갑니다. 출력은 2>&1 | tee 로 화면과 파일에 함께 남기면 됩니다.
재부팅한 node02 가 돌아오지 않는다
node02 를 재부팅하세요(ssh node02 reboot). 돌아온 뒤 node02 가 NotReady 로 남는 원인을 node02 에서 찾아 고치고, 다음 재부팅에도 스스로 돌아오게 하세요.
노드가 NotReady 면 그 노드의 kubelet 부터 봅니다 — systemctl status kubelet. 지금 떠 있는지(active)와 부팅 때 켜지는지(enabled)는 다른 질문입니다. 조인할 때 나온 WARNING 을 기억하나요?
정비를 끝내고 되돌린다
node01 을 다시 스케줄 가능하게 하세요. 세 노드가 모두 Ready·스케줄 가능이고, Flannel DaemonSet 이 세 노드에서 준비되어 있으며, web 의 두 파드가 Ready 여야 합니다.
drain 을 되돌리는 것은 uncordon 입니다. 되돌려도 이미 node02 로 옮겨 간 파드가 저절로 돌아오지는 않습니다 — 스케줄러는 새 파드를 놓을 때만 판단합니다.