- Published on
EKS에서 kubelet node not found 오류 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
EKS 운영 중 노드가 NotReady로 남거나 Pod가 스케줄링되지 않는 상황에서, 노드의 kubelet 로그에 다음과 같은 메시지가 반복되는 경우가 있습니다.
node "ip-10-0-1-23.ap-northeast-2.compute.internal" not foundError getting node "...": nodes "..." not foundfailed to get node info/failed to ensure lease exists같은 파생 에러
이 메시지는 단순히 “노드가 없다”는 뜻이 아니라, kubelet이 Kubernetes API 서버에서 ‘자기 자신에 해당하는 Node 오브젝트’를 조회/갱신하지 못한다는 의미입니다. EKS에서는 이 문제가 보통 아래 3갈래로 수렴합니다.
- 노드가 아예 클러스터에 등록되지 못함(부트스트랩 실패, 인증/권한 문제, API 접근 불가)
- 노드 이름 불일치(kubelet이 생각하는 노드명과 API에 생성된 노드명이 다름)
- 등록은 되었는데 kubelet 자격증명/CSR/인증서가 꼬임(rotation/디스크 이미지/시간 불일치 등)
이 글은 “증상 → 빠른 분기 진단 → 원인별 해결” 순서로 정리합니다. CNI 초기화 문제로 NotReady가 나는 케이스는 별도 원인이므로, 해당 증상이라면 먼저 EKS kubelet NotReady - CNI plugin not initialized 해결도 함께 확인하는 것이 좋습니다.
1. 증상 패턴과 먼저 확인할 것
1) 컨트롤 플레인에서 노드가 보이는지
가장 먼저 “노드 오브젝트가 존재하는지”부터 확인합니다.
kubectl get nodes -o wide
- 노드가 목록에 없다 → 부트스트랩/인증/네트워크 문제로 등록 자체가 실패했을 가능성이 큼
- 노드는 있는데 NotReady → kubelet이 갱신을 못 하거나(CSR/인증서/권한), CNI/런타임 등 노드 내부 문제
또한 Node 이름을 정확히 확인합니다.
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
kubelet 로그에 찍히는 노드명과, 위 결과가 다르면 이름 불일치가 유력합니다.
2) kubelet 로그에서 실제 에러 문맥 확인
EKS AMI(amazon-linux-2 기반)라면 보통 다음처럼 확인합니다.
sudo journalctl -u kubelet -n 200 --no-pager
Bottlerocket이라면 SSH가 없을 수 있으니 SSM 기반 디버깅이 필요합니다. 이 경우 EKS Bottlerocket 노드 SSH 없이 SSM으로 접속·디버깅를 참고하세요.
kubelet 로그에서 다음 키워드를 같이 찾으면 분기 진단이 빨라집니다.
unable to register node/failed to register node(등록 실패)x509/certificate/csr(인증서 문제)Unauthorized/Forbidden(권한 문제)dial tcp/i/o timeout(API 서버 네트워크 접근 문제)
2. 원인 A: aws-auth 미설정/권한 문제로 노드 등록 실패
EKS에서 워커 노드(EC2)가 API 서버에 Node를 생성/갱신하려면, 노드 인스턴스 프로파일(IAM Role)이 클러스터 RBAC에 매핑되어야 합니다. 이 매핑은 보통 kube-system/aws-auth ConfigMap의 mapRoles로 이뤄집니다.
진단
노드가 kubectl get nodes에 아예 없고, kubelet 로그에 Unauthorized/Forbidden 또는 등록 실패가 섞여 있으면 의심합니다.
컨트롤 플레인에서 aws-auth를 확인합니다.
kubectl -n kube-system get configmap aws-auth -o yaml
일반적으로 노드 역할은 아래처럼 system:bootstrappers, system:nodes 그룹에 들어가야 합니다.
해결
노드 그룹이 사용하는 IAM Role ARN을 확인한 뒤, aws-auth에 추가합니다.
# kubectl -n kube-system edit configmap aws-auth
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789012:role/eks-nodegroup-role
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
적용 후 신규 노드가 조인되는지 확인합니다.
kubectl get nodes
자주 하는 실수
- rolearn 오타 또는 다른 환경(다른 계정/리전)의 Role을 넣음
- Managed Node Group인데 수동으로 role을 바꾸면서
aws-auth를 갱신하지 않음 username템플릿을 커스텀하면서 노드 이름과 불일치가 발생
3. 원인 B: kubelet 노드 이름 불일치(특히 hostname-override)
kubelet은 기본적으로 OS의 hostname(EC2의 private DNS) 기반으로 노드명을 정합니다. 그런데 다음과 같은 상황이면 API에 생성된 Node 이름과 kubelet이 참조하는 이름이 달라져 node not found가 반복될 수 있습니다.
/etc/hostname을 커스텀하거나 cloud-init에서 hostname을 변경- kubelet에
--hostname-override를 설정 - AMI 커스터마이징 과정에서 hostname 규칙이 바뀜
- Auto Scaling으로 재생성된 인스턴스가 이전 노드명과 충돌/혼선
진단
노드에서 kubelet이 어떤 이름을 쓰는지 확인합니다.
# kubelet 실행 옵션 확인(환경에 따라 경로가 다를 수 있음)
ps -ef | grep kubelet | grep -v grep
hostname
cat /etc/hostname
컨트롤 플레인에서 실제 Node 오브젝트 이름도 비교합니다.
kubectl get nodes -o wide
kubelet 로그에 나오는 node "..." not found의 ...가 kubectl get nodes에 존재하지 않으면 거의 확정입니다.
해결 1) hostname-override 제거/일관화
가능하면 EKS 기본 패턴(EC2PrivateDNSName)을 따르도록 되돌리는 것이 가장 안전합니다.
- custom
--hostname-override를 제거 - cloud-init에서 hostname 변경 로직 제거
변경 후 kubelet 재시작:
sudo systemctl restart kubelet
sudo journalctl -u kubelet -n 100 --no-pager
해결 2) 잘못 생성된 Node 오브젝트 정리
이름이 꼬인 상태에서 “죽은 노드 오브젝트”가 남아 있으면, 새 노드가 같은 이름을 쓰지 못하거나 kubelet이 엉뚱한 객체를 찾을 수 있습니다.
kubectl delete node <stale-node-name>
그 다음 노드를 재부팅하거나(또는 ASG에서 교체) 다시 조인시키는 편이 깔끔합니다.
4. 원인 C: 부트스트랩 실패(클러스터 엔드포인트/CA/토큰 문제)
EKS 노드는 보통 /etc/eks/bootstrap.sh <cluster-name>를 통해 kubeconfig와 인증서 설정을 구성합니다. 여기서 실패하면 노드 등록이 진행되지 않고, kubelet은 계속 API에서 Node를 찾지 못하는 형태로 흔들릴 수 있습니다.
진단
UserData/부트스트랩 로그를 확인합니다.
# AL2 계열에서 자주 보는 로그 위치
sudo tail -n 200 /var/log/cloud-init-output.log
sudo tail -n 200 /var/log/messages
다음과 같은 신호를 찾습니다.
- 클러스터 이름 오타
- 리전/엔드포인트 잘못 참조
- 프라이빗 엔드포인트인데 노드 서브넷에서 API 접근 불가
- 프록시/방화벽으로 443 차단
네트워크 레벨에서 API 서버 접근도 확인합니다.
# 클러스터 엔드포인트는 aws eks describe-cluster로 확인
CLUSTER_ENDPOINT="https://XXXXXXXX.gr7.ap-northeast-2.eks.amazonaws.com"
curl -k -sS -m 3 $CLUSTER_ENDPOINT >/dev/null || echo "cannot reach endpoint"
> -k는 TLS 검증을 생략하는 옵션입니다. 여기서는 “네트워크로 접근 가능한지”만 빠르게 보려는 목적이며, 근본 해결은 CA/인증서 체인을 올바르게 맞추는 것입니다.
만약 이미지 풀이나 통신에서 x509 관련 오류가 함께 보인다면, 노드의 CA 번들/프록시 MITM/사설 인증서 이슈일 수 있습니다. 이 경우는 Kubernetes ErrImagePull x509 인증서 오류 해결도 같이 보면 진단에 도움이 됩니다.
해결
- UserData에서
bootstrap.sh에 전달하는 클러스터 이름/옵션 재확인 - 클러스터 엔드포인트 접근 경로 확인
- Public endpoint 사용 시: 노드 SG/NACL/라우팅에서 443 egress 가능해야 함
- Private endpoint 사용 시: 노드가 클러스터 VPC 내부에서 엔드포인트로 라우팅 가능해야 함(서브넷/라우트/SG)
- 프록시를 쓰는 환경이면
NO_PROXY에 EKS endpoint, VPC CIDR,169.254.169.254(IMDS) 등을 포함
5. 원인 D: kubelet 클라이언트 인증서/CSR 꼬임(시간/디스크 이미지 복제)
kubelet은 /var/lib/kubelet/pki/ 등에 클라이언트 인증서와 키를 저장하고 이를 기반으로 API 서버에 인증합니다. 다음 상황에서 인증서가 꼬이거나 만료/불일치가 발생할 수 있습니다.
- AMI를 “이미 조인된 노드”에서 떠서
/var/lib/kubelet이 오염된 상태로 복제 - 시간 동기화 문제로 인증서 유효기간 판정이 틀어짐
- 디스크 스냅샷/복원으로 이전 인증서가 남음
이때 kubelet 로그에는 node not found 외에도 x509: certificate ... 또는 Unauthorized가 섞여 나올 수 있습니다.
진단
인증서 파일 존재와 날짜를 확인합니다.
sudo ls -al /var/lib/kubelet/pki || true
date
CSR이 쌓이는지도 확인합니다(컨트롤 플레인에서).
kubectl get csr
특정 노드에서 반복적으로 CSR이 생성되거나, 승인되지 못하고 Pending에 머무는 패턴이 보이면 이 축을 의심합니다.
해결(가장 확실한 방법: 노드 교체)
운영 환경에서는 해당 노드를 “고치기”보다 ASG/NodeGroup에서 노드를 교체하는 것이 가장 안전합니다.
- Managed Node Group:
kubectl drain→ 인스턴스 종료 → 새 인스턴스 조인 - Self-managed ASG: 인스턴스 종료 후 desired capacity로 재생성
부득이하게 노드에서 정리해야 한다면(비권장), kubelet 상태를 멈추고 kubelet 디렉터리를 정리한 뒤 재부트스트랩하는 방식이 필요합니다.
sudo systemctl stop kubelet
sudo rm -rf /var/lib/kubelet/pki
sudo systemctl start kubelet
sudo journalctl -u kubelet -n 200 --no-pager
환경에 따라 kubeconfig 재생성이 필요할 수 있으므로, 근본적으로는 “깨끗한 이미지로 새 노드”가 정답인 경우가 많습니다.
6. 원인 E: API 서버는 되는데 Node Lease/갱신이 안 됨(권한/네트워크)
kubelet은 kube-node-lease 네임스페이스의 Lease 오브젝트를 갱신해 자신의 생존을 알립니다. 여기서 실패하면 노드가 NotReady로 빠지고, 로그에 node 조회 실패/lease 실패가 섞여 보일 수 있습니다.
진단
- kubelet 로그에서
Failed to ensure lease exists또는kube-node-lease관련 에러 확인 - 네트워크 간헐 장애(특히 NAT/방화벽)로 API 서버 연결이 끊기는지 확인
# 노드에서 API endpoint로 지속 연결 확인(간단 테스트)
for i in {1..20}; do
curl -k -sS -m 2 $CLUSTER_ENDPOINT >/dev/null && echo ok || echo fail
sleep 1
done
해결
- 노드 서브넷의 egress 경로(NAT GW/IGW), SG/NACL에서 443 아웃바운드 확인
- 사내 방화벽/프록시가 있다면 API endpoint 예외 처리
aws-auth/RBAC 커스텀으로system:nodes권한을 훼손하지 않았는지 점검
7. 현장에서 통하는 “10분 진단 체크리스트”
아래 순서대로 하면 대부분의 kubelet node not found는 빠르게 원인이 좁혀집니다.
- 컨트롤 플레인에서 노드 존재 여부
kubectl get nodes -o wide
- 노드가 없다면:
aws-auth에서 노드 Role 매핑 확인
kubectl -n kube-system get cm aws-auth -o yaml
- 노드가 있는데 이름이 다르다면: kubelet의 hostname/override 확인
hostname
ps -ef | grep kubelet | grep -v grep
- 부트스트랩 로그 확인
sudo tail -n 200 /var/log/cloud-init-output.log
- 네트워크(443)로 API 접근 가능한지
curl -k -sS -m 3 $CLUSTER_ENDPOINT >/dev/null || echo "cannot reach endpoint"
- 인증서/CSR 꼬임 징후 확인
kubectl get csr
sudo ls -al /var/lib/kubelet/pki || true
여기까지 했는데도 애매하면, 운영 관점에서는 해당 인스턴스를 오래 붙잡기보다 드레인 후 교체가 평균 복구시간(MTTR)을 크게 줄입니다.
8. 재발 방지 팁
- 커스텀 AMI를 만들 때
/var/lib/kubelet,/etc/kubernetes, CNI 관련 상태 디렉터리를 “깨끗한 상태”로 유지(조인된 노드에서 그대로 스냅샷 뜨지 않기) - hostname을 임의로 바꾸지 말고, 바꿔야 한다면
aws-auth의username템플릿/노드명 규칙과 일관성 유지 - Private endpoint를 쓴다면 노드 서브넷에서 엔드포인트로의 라우팅/보안그룹을 IaC로 고정하고 변경 이력을 추적
- Bottlerocket/Managed Node Group 사용 시에는 “노드 수리”보다 “노드 교체”를 기본 운영 패턴으로 잡기
결론
EKS에서 kubelet의 node not found는 대개 (1) 노드가 클러스터에 등록되지 못했거나, (2) 노드 이름이 꼬였거나, (3) kubelet 인증/부트스트랩 상태가 망가진 경우입니다. kubectl get nodes로 존재 여부를 먼저 가르고, aws-auth 매핑과 hostname 일치 여부, 부트스트랩 로그/네트워크 접근성, CSR/인증서 상태를 순서대로 확인하면 원인을 빠르게 특정할 수 있습니다. 운영 환경에서는 원인 분석 후에도 불확실성이 남으면 드레인 후 노드 교체가 가장 확실한 복구 전략입니다.