- Published on
EKS에서 kubectl exec·logs가 안 될 때 진단법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 살아 있고 kubectl get pod도 잘 되는데, 유독 kubectl exec/kubectl logs/kubectl port-forward만 실패하는 케이스는 EKS 운영에서 자주 만납니다. 공통점은 API Server가 노드의 kubelet(기본 10250)로 프록시/스트리밍 요청을 전달하고, exec는 특히 SPDY/HTTP2 또는 WebSocket 업그레이드 같은 “중간 장비에 취약한” 경로를 탄다는 점입니다.
이 글은 아래 질문에 답하는 형태로 진행합니다.
- 실패가 RBAC(권한) 인가, 네트워크(10250/보안그룹/NACL/프록시) 인가, WebSocket 업그레이드 차단인가?
- EKS에서 실제로 어떤 컴포넌트가 어디로 통신하는가?
- 재현/검증 커맨드와 해결 체크리스트는?
1) 먼저 증상으로 분류하기 (가장 빠른 분기)
실패 메시지는 원인을 거의 알려줍니다. 자주 나오는 패턴을 먼저 분류합니다.
A. RBAC/인증 문제
Error from server (Forbidden): pods/exec is forbidden: User ... cannot create resource "pods/exec"... cannot get resource "pods/log"
→ 권한 문제입니다. 네트워크를 보기 전에 RBAC부터 고칩니다.
B. kubelet(10250) 경로 문제
error dialing backend: dial tcp <node-ip>:10250: i/o timeoutno route to hostconnection refused
→ API Server(또는 apiserver가 위치한 네트워크)에서 노드의 10250으로 못 감.
C. 업그레이드/스트리밍(WebSocket/SPDY) 문제
error: unable to upgrade connection: ...websocket: bad handshakeEOF(간헐적으로 끊김)
→ 중간 프록시/방화벽/보안장비가 Upgrade 헤더, HTTP2, 장시간 연결을 깨는 경우가 많습니다.
2) EKS에서 exec/logs가 지나가는 통신 경로
핵심은 “클라이언트가 노드로 직접 붙는가?” 입니다.
- 일반적인
kubectl get/list/watch는 kubectl → EKS API Server로 끝납니다. - 하지만
kubectl logs/exec/port-forward는 EKS API Server → 노드의 kubelet(10250) 또는 CRI 스트리밍 경로를 사용합니다.
즉, 클러스터가 프라이빗 엔드포인트이든 퍼블릭이든 상관없이, 최종적으로는 API Server가 노드에 접근해야 합니다.
> 결론: kubectl이 API Server에 붙는 것은 되는데 exec/logs만 안 되면, “API Server ↔ 노드 kubelet” 경로를 의심하세요.
3) RBAC 체크: pods/exec·pods/log 권한이 있는가
먼저 현재 컨텍스트의 사용자/역할이 무엇인지 확인합니다.
kubectl config current-context
kubectl auth whoami
3.1 권한 빠른 진단 (can-i)
# exec는 subresource create 권한이 필요
kubectl auth can-i create pods/exec -n <ns>
# logs는 get 권한이 필요
kubectl auth can-i get pods/log -n <ns>
둘 중 하나라도 no면 RBAC 문제입니다.
3.2 최소 권한 예시(Role/RoleBinding)
운영에서 자주 필요한 최소 권한은 아래 정도입니다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-debug
namespace: default
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-debug-binding
namespace: default
subjects:
- kind: User
name: your-user-or-iam-mapped
roleRef:
kind: Role
name: pod-debug
apiGroup: rbac.authorization.k8s.io
EKS에서 IAM → Kubernetes RBAC 매핑을 쓰는 경우(aws-auth/Access Entry)라면, 매핑된 username/group이 무엇인지부터 확인해야 합니다.
4) 10250(kubelet) 문제: 보안그룹/NACL/라우팅을 확인
dial tcp <node-ip>:10250 timeout은 거의 항상 네트워크입니다.
4.1 EKS Control Plane이 노드로 들어오는 SG 규칙
EKS는 관리형 Control Plane이 노드로 kubelet 포트에 접근해야 합니다. 다음을 확인합니다.
- 노드 보안그룹 인바운드에 10250이 열려 있는가?
- 소스가 “클러스터 보안그룹(Cluster security group)” 또는 “Control plane security group”으로 허용돼 있는가?
- 커스텀 SG를 붙이면서 기본 규칙이 사라지지 않았는가?
> 실무 팁: “노드 간 통신은 되는데 exec만 안 됨”이면, 노드↔노드가 아니라 Control Plane → 노드 인바운드가 빠진 경우가 많습니다.
4.2 프라이빗 클러스터/엔드포인트 설정 영향
- API endpoint를 private-only로 바꾸거나,
- VPC 엔드포인트/프록시를 추가하거나,
- 라우팅 테이블/NACL을 강화하는 과정에서
Control Plane에서 노드로 가는 경로가 끊길 수 있습니다.
4.3 현상별 해석
connection refused: 포트는 열려 있으나 kubelet이 해당 인터페이스에서 리슨하지 않거나, 노드 로컬 방화벽/에이전트가 거부i/o timeout: SG/NACL/라우팅/방화벽에서 드롭no route to host: 라우팅/NACL 쪽 가능성이 큼
5) WebSocket/SPDY 업그레이드가 깨질 때
kubectl exec는 세션을 열고 스트리밍합니다. 네트워크 장비가 다음을 방해하면 실패합니다.
Connection: Upgrade,Upgrade: websocket헤더 차단- HTTP/2, SPDY 처리 미지원
- idle timeout이 짧아서 세션이 끊김
5.1 “프록시를 거친 kubectl” 환경을 의심
사내망에서 흔한 패턴:
- 개발자 PC → 사내 프록시(HTTP CONNECT) → EKS endpoint
- VPN/보안 에이전트가 TLS를 중간에서 검사
이때 get pods는 되지만 exec만 실패할 수 있습니다.
체크: 환경 변수
env | egrep -i 'https?_proxy|no_proxy'
EKS endpoint, Pod CIDR, Node CIDR, .cluster.local 등을 NO_PROXY에 넣지 않으면 스트리밍이 비정상 동작할 수 있습니다.
5.2 kubectl에서 디버그 로그로 원인 좁히기
kubectl -v=9 exec -it -n <ns> <pod> -- sh
로그에 Upgrade request required / bad handshake / stream error 같은 힌트가 나옵니다.
6) 네트워크 정책/보안 정책이 아닌데도 logs만 안 나오는 케이스
6.1 CNI/노드 문제로 kubelet이 불안정
노드가 압박(메모리/디스크) 상태면 kubelet/컨테이너 런타임이 스트리밍 요청을 제대로 처리 못해 EOF가 나기도 합니다.
kubectl describe node <node>에서DiskPressure,MemoryPressure확인kubectl get events -A --sort-by=.lastTimestamp | tail확인
6.2 로깅은 되는데 애플리케이션 로그가 없을 때
kubectl logs 자체는 되지만 로그가 비거나 비용이 폭증하는 흐름이라면, 로그 수집 파이프라인(CloudWatch/Fluent Bit) 쪽도 같이 점검해야 합니다. 운영 비용 관점은 아래 글이 도움이 됩니다.
7) 실전 트러블슈팅 체크리스트 (10분 루틴)
아래 순서대로 하면 불필요한 삽질을 줄일 수 있습니다.
7.1 API Server까지는 정상인가?
kubectl get nodes
kubectl get pods -A | head
여기서부터 안 되면 exec/logs 문제가 아니라 클러스터 접근 자체 문제입니다.
7.2 RBAC부터 컷
kubectl auth can-i create pods/exec -n <ns>
kubectl auth can-i get pods/log -n <ns>
7.3 특정 Pod만 문제인가, 노드 단위인가?
kubectl get pod -n <ns> -o wide
# 같은 노드의 다른 pod에도 exec가 실패하는지 확인
kubectl exec -n <ns> -it <other-pod-on-same-node> -- sh
- 같은 노드의 모든 Pod에서 실패 → 노드/10250 경로
- 특정 Pod만 실패 → Pod 상태/컨테이너 런타임/보안 정책
7.4 kubectl verbose로 업그레이드 실패 여부 확인
kubectl -v=9 logs -n <ns> <pod>
kubectl -v=9 exec -n <ns> -it <pod> -- sh
7.5 10250 네트워크(보안그룹) 점검
- 노드 SG 인바운드에 10250 허용
- 소스가 클러스터 SG/Control Plane SG로 열려 있는지 확인
- NACL/라우팅 테이블 변경 이력 확인
8) 자주 하는 실수와 예방 팁
8.1 “노드 SG를 깔끔하게” 정리하다가 10250을 지움
EKS는 관리형이지만, 노드 SG 규칙은 사용자가 건드리기 쉽습니다. IaC(Terraform/CDK)로 관리한다면 10250 관련 규칙을 명시적으로 코드화하고, 변경 PR에서 쉽게 리뷰되게 하세요.
8.2 프록시 환경에서 NO_PROXY 누락
kubectl exec/port-forward는 단순 REST 호출보다 네트워크 민감도가 높습니다. 사내 프록시를 쓴다면 EKS endpoint와 내부 도메인을 NO_PROXY에 넣는 것을 표준화하세요.
8.3 장애 시 “로그부터 보자”가 안 되는 구조
exec/logs가 막히면 장애 대응 속도가 급격히 떨어집니다. 평소에 CloudWatch/ELK 등 외부 로그 경로도 함께 준비해 두면, kubelet 스트리밍 경로가 깨져도 최소한의 관측 가능성을 유지할 수 있습니다.
관련해서, 쿠버네티스에서 간헐 502/504나 스트리밍 끊김을 다룬 튜닝 글도 함께 참고하면 네트워크 타임아웃/keepalive 감각을 잡는 데 도움이 됩니다.
9) 결론: exec/logs는 “권한” 또는 “10250+업그레이드”다
정리하면, EKS에서 kubectl exec/logs가 안 될 때의 80%는 아래 둘 중 하나입니다.
- RBAC:
pods/exec(create),pods/log(get) 권한 부족 - 네트워크: Control Plane → Node kubelet(10250) 경로 차단(보안그룹/NACL/라우팅) 또는 WebSocket/SPDY 업그레이드를 중간 프록시가 방해
위 체크리스트대로 can-i로 권한을 먼저 자르고, -v=9로 업그레이드 힌트를 본 뒤, 마지막으로 10250 인바운드/소스 SG를 확인하면 대부분의 케이스를 빠르게 해결할 수 있습니다.