Ubuntu kubelet 10250 포트 오류 해결 방법
kubeadm init 시 발생하는 address already in use 오류와 kubectl logs 및 exec 실패를 유발하는 방화벽 문제를 해결합니다. 포트 10250 점유 프로세스 확인법과 제어 평면 통신을 위한 필수 설정 가이드를 제공합니다.
포트 10250의 의미
포트 10250은 kubelet API가 사용하는 포트이며, 이 포트와 관련된 모든 오류는 두 가지 상반된 문제 중 하나입니다. 포트를 이미 다른 프로세스가 점유하고 있어 kubeadm init이 실행을 거부하거나, 반대로 포트에 접근할 수 없어 정상적으로 보이는 노드임에도 kubectl logs 및 kubectl exec 명령이 실패하는 경우입니다.
kubelet은 Kubernetes가 모든 노드에서 실행하는 에이전트입니다. 이 에이전트는 컨테이너를 시작하고 상태를 제어 평면(control plane)에 보고합니다. 또한 TCP 10250 포트에서 대기하며 제어 평면이 호출하는 HTTPS API를 제공합니다. 사용자가 kubectl logs, kubectl exec, kubectl attach 또는 kubectl port-forward 명령을 실행하면 API 서버가 해당 포트로 연결을 시도합니다. metrics-server는 동일한 포트에서 /metrics/resource를 수집하며, 이것이 kubectl top node이 작동하는 원리입니다.
해당 API는 인증을 거쳐야 합니다. kubeadm은 익명 접근을 차단하고 kubelet이 클러스터 CA(인증 기관)를 참조하도록 설정하므로, 자격 증명이 없는 요청은 컨테이너 내부 셸을 열어주는 대신 Unauthorized 응답을 반환합니다. 이 점을 기억하십시오. 포트가 외부에서 접근 가능한지 확인하는 가장 빠른 방법이기 때문입니다. 포트 개념이 생소하다면 Linux에서 포트의 실제 의미를 참조하십시오. 이 가이드는 해당 모델을 전제로 작성되었습니다.
두 가지 실패 유형 모두 하나의 요구 사항에서 비롯됩니다. kubelet이 시작되기 전에는 포트 10250이 비어 있어야 하며, 실행 중에는 제어 평면에서 해당 포트에 접근할 수 있어야 합니다.
두 가지 문제 중 무엇에 해당하는지 확인하기
해당 노드에서 아래 명령어를 실행하십시오. 아래의 모든 명령어는 사용자 본인의 서버에서 직접 실행하는 것입니다.
sudo ss -lntp | grep 10250
sudo systemctl status kubelet --no-pagerss -lntp은 각 프로세스가 점유 중인 리스닝 TCP 소켓 목록을 출력합니다. -l는 리스닝 상태를 의미하며, -n은 포트를 숫자로 표시하고, -t은 TCP로 범위를 제한하며, -p는 소유 프로세스를 보여줍니다. 마지막 플래그는 root 권한이 필요합니다. 권한이 없으면 프로세스 열이 비어 있어 아무런 정보도 얻을 수 없습니다.
줄 끝이 users:(("kubelet",pid=1043,fd=23))으로 끝나면 kubelet이 실행 중이며 해당 포트를 점유하고 있다는 뜻입니다. 포트가 비어 있을 것으로 예상했다면, 이것이 바로 원인입니다. ss를 실행했는데 아무것도 출력되지 않는데도 컨트롤 플레인이 이 노드에 접근할 수 없다면, 포트를 서비스하는 프로세스가 없으므로 방화벽 문제는 아닙니다. 규칙을 수정하기 전에 kubelet이 왜 중단되었는지 먼저 파악하십시오.
systemctl status kubelet는 상황의 나머지 절반을 보여줍니다. active (running)에서 시작 시간이 몇 분 전으로 표시되는 것은 정상입니다. kubeadm init이나 kubeadm join을 실행하기 전에 kubelet이 몇 초마다 재시작되는 것 역시 정상입니다. 패키지 단위 서비스는 설치 시점에 시작되지만, 설정 파일이 없으면 종료되기 때문입니다. 업스트림 문서에 따르면 kubelet이 kubeadm의 지시를 기다리는 동안 발생하는 이러한 크래시 루프는 예상된 동작입니다. systemd의 재시작 동작이 생소하다면, systemd 서비스 유형과 재시작 정책의 작동 원리에서 이 섹션의 배경 지식을 확인할 수 있습니다.
kubeadm init 실행 시 포트 10250이 이미 사용 중인 이유
kubeadm init는 디스크에 내용을 기록하기 전에 사전 점검을 수행합니다. 이 점검 과정에서 컨트롤 플레인이 필요로 하는 각 포트에 바인딩을 시도하며, 바인딩에 실패하면 포트 10250을 명시하며 오류와 함께 중단됩니다. 이는 버그가 아닙니다. 첫 번째 클러스터의 잔재 위에 두 번째 클러스터를 구축하는 것을 kubeadm init이 거부하는 것입니다.
실무에서는 다음 네 가지가 주된 원인입니다.
- 중간에 실패한 이전의
kubeadm init또는kubeadm join작업. kubelet이 이미 설정을 받아 실행 중이며 해당 포트를 점유하고 있습니다. - 시작은 되었으나 완료되지 않은
kubeadm reset. reset은 kubelet을 중단시키지만 유닛을 비활성화하지는 않으므로, 다음 재부팅 시 리스너가 다시 살아납니다. - 동일한 서버에 설치된 k3s 또는 다른 Kubernetes 배포판. k3s는 kubelet을 내장하고 있으며, 이 kubelet 또한 10250 포트를 바인딩합니다.
- 아직 kubeadm을 실행하지 않은 서버에서 apt를 통해 설치되어 자체 systemd 유닛으로 시작된
kubelet패키지.
무언가를 변경하기 전에 다음 명령으로 원인을 파악하십시오.
sudo ss -lntp 'sport = :10250'
systemctl list-units --type=service --state=running | grep -Ei 'kubelet|k3s|k0s'리스너가 k3s에 속해 있다면, 어떤 클러스터를 사용할지 결정하고 하나를 선택해야 합니다. k3s와 kubeadm은 동일한 포트와 동일한 CNI(container network interface) 디렉터리를 점유하므로 한 서버에서 공존할 수 없습니다. k3s 설치 프로그램은 서버 노드의 경우 /usr/local/bin/k3s-uninstall.sh에, 에이전트 노드의 경우 k3s-agent-uninstall.sh에 제거 스크립트를 생성해 둡니다.
kubelet을 종료해도 포트가 해제되지 않는 이유
sudo pkill kubelet은 약 10초 동안 10250 포트를 해제합니다. 패키지에 포함된 unit 파일에는 재시작 정책이 설정되어 있으므로, systemd가 새로운 kubelet을 시작하고 다시 동일한 포트에 바인딩합니다. 다음 명령으로 직접 정책을 확인할 수 있습니다.
systemctl show kubelet -p Restart -p RestartSec
sudo systemctl stop kubelet
sudo ss -lntp | grep 10250Restart=always과 RestartSec=10은 해당 unit 파일에 기본으로 포함된 설정이며, 이것이 바로 kill를 실행했을 때 잠시 성공한 것처럼 보이다가 다시 실패하는 이유입니다. systemctl stop은 포트를 해제하는 올바른 방법입니다. systemd는 사용자가 중지하라고 명령한 unit을 더 이상 재시작하지 않기 때문입니다.
클러스터의 절반을 담당하는 노드에서는 포트를 비우는 것만으로는 충분하지 않습니다. /var/lib/kubelet/config.yaml, /etc/kubernetes/pki 아래의 인증서, 그리고 /etc/kubernetes/manifests에 있는 모든 정적 파드 매니페스트는 그대로 남아 있습니다. 이후 사전 점검(preflight check) 과정에서 이 파일들로 인해 오류가 발생하며, 점검을 강제로 통과시키면 인증서와 설정이 일치하지 않는 클러스터가 생성됩니다. 대신 노드를 올바르게 초기화(reset)하십시오.
노드를 깔끔하게 초기화하기
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d
rm -rf $HOME/.kube
sudo systemctl stop kubelet
sudo ss -lntp | grep -E '10250|6443|2379'-f 플래그는 확인 메시지를 생략합니다. reset은 init 또는 join이 수행한 작업을 최대한 되돌립니다. 이 과정에서 로컬 파일과 설정이 제거되고, 컨트롤 플레인 노드의 로컬 etcd 멤버가 삭제되며, /etc/kubernetes/pki의 인증서가 정리되고, kubelet 설정과 매니페스트가 삭제됩니다.
문서에는 reset 후에도 남아 있는 항목이 명시되어 있으며, 각 항목은 종종 문제를 일으킵니다. reset은 /etc/cni/net.d을 정리하지 않으므로, 이전 CNI 플러그인 설정이 그대로 남아 새 클러스터가 이를 읽어 들일 수 있습니다. 또한 kube-proxy가 호스트에 적용한 iptables, nftables 또는 IPVS 규칙도 정리하지 않습니다. $HOME/.kube도 건드리지 않기 때문에, kubectl은 존재하지 않는 클러스터와 계속 통신을 시도하며 새로운 문제처럼 보이는 인증서 오류를 반환합니다.
남아 있는 패킷 규칙은 처리하기 까다롭습니다. 수동으로 테이블을 플러시하면 ufw가 설치한 규칙까지 모두 삭제되는데, 이는 Ubuntu의 ufw가 동일한 백엔드를 통해 작성되기 때문입니다. 이 경우 sudo ufw reload을 실행하기 전까지 서버는 필터링되지 않은 상태가 됩니다. 어차피 노드를 재구축하는 상황이라면 reset 후 재부팅을 수행하십시오. 재부팅은 kube-proxy가 추가한 런타임 규칙을 지워주며, 절반만 플러시된 규칙 세트를 정리하는 것보다 시간이 적게 듭니다. iptables 규칙과 nftables 규칙이 서로의 출력에 나타나는 이유에서 내부적으로 어떤 일이 일어나는지 설명합니다.
마지막으로 ss 명령을 실행했을 때 아무것도 출력되지 않아야 합니다. 10250, 6443 또는 2379 포트에서 리스너가 확인되지 않는다면 해당 노드는 새로운 kubeadm init을 수행할 준비가 된 것입니다.
Why kubectl logs and kubectl exec time out on port 10250
This is the opposite complaint, and it does not announce itself as a port problem. The cluster comes up. Nodes are Ready. Pods run. Then one command fails:
Error from server: Get "https://10.0.0.12:10250/containerLogs/default/web-0/web": dial tcp 10.0.0.12:10250: i/o timeoutRead that message from the end. The API server tried to open a TCP connection to the node on port 10250 and got no answer. i/o timeout means the packets were dropped in silence, so something is filtering them: the host firewall on the node, or your provider's separate network firewall in the control panel. connect: connection refused in the same position means the opposite. The packet arrived and nothing was listening, so the kubelet is down. That is the same pair of causes described in connection refused versus connection timed out, read here on a different port.
Nodes stay Ready through all of this because node status travels the other way. The kubelet connects out to the API server on port 6443 and posts its own heartbeat, and that needs nothing inbound on 10250. So a blocked 10250 gives you a cluster that schedules pods normally and fails only on logs, exec, port-forward and metrics.
kubectl top node answering error: Metrics API not available is the same fault seen through metrics-server, whose log names the node and the port:
unable to fully scrape metrics from node worker-1: unable to fetch metrics from node worker-1: Get "https://10.0.0.12:10250/metrics/resource": dial tcp 10.0.0.12:10250: i/o timeout방화벽 규칙을 수정하기 전에 경로를 테스트하십시오
컨트롤 플레인 노드에서 워커 노드의 주소를 대상으로 다음 명령을 실행하십시오:
nc -zv 10.0.0.12 10250
curl -sk -o /dev/null -w '%{http_code}\n' https://10.0.0.12:10250/healthznc -z은 연결을 열고 닫은 뒤, 포트가 연결을 수락하면 succeeded!을 출력합니다. curl 명령줄을 사용하는 것이 더 나은 테스트 방법인데, 이는 단순히 포트가 열려 있는지를 확인하는 것이 아니라 kubelet이 실제로 서비스를 제공하고 있음을 증명하기 때문입니다. 이 명령은 401를 출력합니다. 이것이 정상적인 결과입니다. TLS(transport layer security) 핸드셰이크가 완료되었고, kubelet이 인증되지 않은 요청을 거부한 것인데, 이는 kubelet이 수행해야 할 정확한 동작입니다. -k은 인증서 확인을 건너뛰는데, 신뢰 체인이 아닌 경로를 테스트하는 것이므로 문제없습니다.
타임아웃으로 끝나는 긴 지연 시간은 패킷이 드롭되고 있음을 의미합니다. curl: (7) Failed to connect가 즉시 반환된다면 해당 호스트의 포트가 닫혀 있는 것입니다. 노트북이 아닌 컨트롤 플레인 노드에서 테스트하십시오. 이 환경에서 접근 권한이 중요한 유일한 머신은 컨트롤 플레인뿐이기 때문입니다.
컨트롤 플레인과 워커 노드에 필요한 포트
다음은 업스트림에서 명시하는 인바운드 포트입니다. 컨트롤 플레인 노드에서는 API 서버를 위해 TCP 6443 포트를 열어야 하며, 이는 kubectl를 실행하는 모든 대상에 개방되어야 합니다. etcd 클라이언트 및 피어 API를 위해 TCP 2379에서 2380 포트가 필요하며, 이는 API 서버와 etcd 자체에서 사용합니다. kubelet API를 위한 TCP 10250 포트는 노드 자체와 컨트롤 플레인에서 사용합니다. kube-scheduler를 위한 TCP 10259 포트와 kube-controller-manager를 위한 TCP 10257 포트는 노드 자체에서만 사용합니다.
워커 노드에서는 kubelet API를 위해 TCP 10250 포트가 필요하며, 이는 노드 자체와 컨트롤 플레인에서 사용합니다. kube-proxy를 위한 TCP 10256 포트는 노드 자체와 상태 확인을 수행하는 로드 밸런서에서 사용합니다. NodePort 서비스를 위한 TCP 및 UDP 30000에서 32767 포트는 기본 범위이며, 해당 서비스가 필요한 모든 대상에서 접근할 수 있어야 합니다.
사용 중인 CNI 플러그인은 위 목록 외에 별도의 포트를 추가로 사용합니다. VXLAN 모드의 Flannel과 Calico는 노드 간 UDP 4789 포트가 필요합니다. BGP를 사용하는 Calico는 TCP 179 포트가 필요합니다. 이 섹션의 모든 포트를 개방하더라도 플러그인 문서에 명시된 포트를 노드 간에 열어두지 않으면 서로 다른 노드에 있는 파드끼리 통신할 수 없습니다.
10250 포트를 인터넷에 노출하지 않고 열기
kubelet API는 노드 내의 모든 컨테이너에서 프로세스를 시작할 수 있습니다. 10250 포트가 열려 있다는 것은 노드에 대한 root 권한이 열려 있는 것과 같으므로, 소스 주소를 기반으로 접근을 제한해야 합니다. 절대로 어디서든 접근할 수 있도록 허용해서는 안 됩니다.
sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp comment 'kubelet API'
sudo ufw allow from 10.0.0.0/24 to any port 10256 proto tcp comment 'kube-proxy'
sudo ufw status numbered10.0.0.0/24을 노드들이 공유하는 네트워크 대역으로 교체하십시오. ufw status numbered은 활성화된 규칙을 인덱스와 함께 나열하므로, 잘못된 규칙은 sudo ufw delete <number>을 사용하여 삭제할 수 있습니다. VPS를 위한 ufw 기초에서 어떤 규칙이 실제로 적용되는지 결정하는 규칙 우선순위 체계를 다룹니다.
ufw 설정 하나가 Kubernetes를 단독으로 중단시킬 수 있습니다. 노드를 가로지르는 Pod 트래픽은 로컬로 전달되지 않고 포워딩되는데, ufw는 기본적으로 포워딩된 패킷을 차단합니다. /etc/default/ufw 파일에서 DEFAULT_FORWARD_POLICY="ACCEPT"를 설정하고 sudo ufw reload을 실행하십시오. 이 설정이 없으면 10250 포트가 완전히 열려 있더라도 노드 간 Pod 대 Pod 트래픽은 실패합니다.
제공업체의 방화벽도 확인하십시오. 대부분의 VPS 패널은 서버 앞단에 위치하며 ufw status에서는 보이지 않는 네트워크 수준의 방화벽을 제공합니다. 패킷이 서버에 도달하지 못하면 노드에 추가한 규칙은 아무런 효과가 없습니다.
포트는 연결 가능하지만 요청이 실패하는 경우
일부 10250 오류는 응답이 지연되지 않고 즉시 반환되는데, 이는 연결은 성공했으나 요청이 거부되었음을 의미합니다. metrics-server 로그에 x509: certificate signed by unknown authority이 나타난다면 kubelet이 자체 서명된 인증서를 제공하고 있으며, 스크래퍼가 이를 신뢰하지 않는다는 뜻입니다. 일반적인 해결책은 kubelet 서빙 인증서 순환(serving certificate rotation)을 활성화하여 클러스터 CA가 인증서에 서명하게 한 뒤 인증서 서명 요청(CSR)을 승인하는 것입니다. 또는 테스트 클러스터라면 위험을 감수하고 --kubelet-insecure-tls 옵션을 사용하여 metrics-server를 실행할 수 있습니다.
Forbidden가 포함된 메시지가 nodes/proxy 또는 nodes/metrics과 함께 나타난다면 이는 RBAC(역할 기반 접근 제어) 실패입니다. 호출자가 kubelet에 도달했으나, kubelet이 API 서버에 해당 ID가 하위 리소스를 사용할 권한이 있는지 확인했고 API 서버가 이를 거부한 상황입니다. 호출자의 ClusterRole을 수정하십시오. 방화벽 설정 변경은 아무런 도움이 되지 않습니다. 차단된 것이 아니기 때문입니다.
소규모 클러스터 하나만 필요한 경우
처음으로 VPS 한 대에 kubeadm을 설치하다가 이러한 오류가 발생한다면, kubeadm이 정말 필요한지 고려해 보십시오. VPS에서의 단일 노드 k3s 클러스터를 사용하면 kubelet, kube-proxy, CNI가 이미 구성된 상태로 한 번의 명령어를 통해 작동하는 Kubernetes API를 얻을 수 있습니다. 해당 환경에서도 포트 10250은 여전히 존재하며 동일한 규칙이 적용되지만, 더 이상 제어 평면(control plane)을 직접 구성할 필요는 없습니다.
FAQ
Kubernetes에서 포트 10250은 어떤 용도로 사용됩니까?
모든 노드(컨트롤 플레인 및 워커 노드)에서 kubelet이 사용하는 인증된 HTTPS API입니다. API 서버는 kubectl logs, kubectl exec, kubectl attach 및 kubectl port-forward 작업을 위해 이 포트에 연결하며, metrics-server는 kubectl top 데이터를 제공받기 위해 이 포트에서 /metrics/resource를 수집합니다. 노드 상태 확인에는 이 포트를 사용하지 않는데, 이는 kubelet이 포트 6443을 통해 API 서버로 직접 하트비트 신호를 보내기 때문입니다. 따라서 포트 10250이 차단되면 노드는 Ready 상태로 표시되며 로그 확인 및 exec 명령이 실패하게 됩니다.
포트 10250에서 수신 대기 중인 프로세스를 어떻게 찾습니까?
노드에서 sudo ss -lntp | grep 10250 명령을 실행하십시오. 출력 결과의 마지막에 있는 users:((...)) 필드에서 프로세스 이름과 PID를 확인할 수 있습니다. sudo 권한이 중요합니다. root 권한이 없으면 프로세스 열이 비어 있게 됩니다. 소유자가 kubelet인 경우, sudo systemctl status kubelet --no-pager 명령을 통해 정상적으로 작동 중인지 아니면 재시작 루프에 빠졌는지 확인할 수 있습니다. 소유자가 k3s라면 한 서버에 두 개의 Kubernetes 배포판이 설치된 것이므로 하나를 제거해야 합니다.
방화벽에서 포트 10250을 반드시 열어야 합니까?
네, 노드 간 통신을 위해 필요합니다. 컨트롤 플레인은 자기 자신을 포함한 모든 노드의 10250 포트에 접근할 수 있어야 합니다. 그렇지 않으면 로그, exec, port-forward 및 메트릭 기능이 모두 실패합니다. sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp와 같이 노드들이 공유하는 네트워크 대역으로 소스 IP를 제한하십시오. 인터넷 전체에 이 포트를 개방해서는 안 됩니다. 해당 포트에 인증할 수 있는 모든 주체는 노드 내의 모든 컨테이너에서 프로세스를 실행할 수 있기 때문입니다.
특정 노드의 파드에서만 kubectl logs가 실패하는 이유는 무엇입니까?
차단이 노드 단위로 발생하며, API 서버는 파드가 호스팅된 특정 노드에 직접 연결하기 때문입니다. 오류 메시지를 확인하십시오. 연결을 시도한 노드의 IP 주소가 포함되어 있습니다. 그런 다음 컨트롤 플레인 노드에서 nc -zv <node-ip> 10250 명령을 실행하십시오. 타임아웃이 발생한다면 해당 노드의 방화벽이나 클라우드 제공업체의 네트워크 방화벽 문제일 가능성이 큽니다. connection refused 오류가 발생한다면 해당 노드에서 kubelet이 실행 중이지 않은 것이므로, 해당 노드에서 systemctl status kubelet를 확인하십시오.