Published on

EKS에서 kubectl exec·logs가 안 될 때 진단법

Authors

서버가 살아 있고 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 timeout
  • no route to host
  • connection refused

API Server(또는 apiserver가 위치한 네트워크)에서 노드의 10250으로 못 감.

C. 업그레이드/스트리밍(WebSocket/SPDY) 문제

  • error: unable to upgrade connection: ...
  • websocket: bad handshake
  • EOF (간헐적으로 끊김)

→ 중간 프록시/방화벽/보안장비가 Upgrade 헤더, HTTP2, 장시간 연결을 깨는 경우가 많습니다.

2) EKS에서 exec/logs가 지나가는 통신 경로

핵심은 “클라이언트가 노드로 직접 붙는가?” 입니다.

  • 일반적인 kubectl get/list/watchkubectl → EKS API Server로 끝납니다.
  • 하지만 kubectl logs/exec/port-forwardEKS 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%는 아래 둘 중 하나입니다.

  1. RBAC: pods/exec(create), pods/log(get) 권한 부족
  2. 네트워크: Control Plane → Node kubelet(10250) 경로 차단(보안그룹/NACL/라우팅) 또는 WebSocket/SPDY 업그레이드를 중간 프록시가 방해

위 체크리스트대로 can-i로 권한을 먼저 자르고, -v=9로 업그레이드 힌트를 본 뒤, 마지막으로 10250 인바운드/소스 SG를 확인하면 대부분의 케이스를 빠르게 해결할 수 있습니다.