kubectl debug --target 플래그, 이거 모르는 분 꽤 많더라

오늘 알게 된 건 아니지만, 최근에 팀 내부 트러블슈팅 가이드 다듬다가 다시 한 번 짚어둘 만하다 싶어서 짧게 적는다. kubectl debug로 ephemeral container 띄울 때 --target 빠뜨리고 쓰는 분들이 의외로 많다. 1.25부터 EphemeralContainers feature gate가 GA로 켜진 게 벌써 한참 됐는데도.
무슨 문제냐면
distroless 이미지를 쓰는 파드에 들어가서 curl이나 netstat 한 번 쳐보고 싶을 때 보통 이렇게 쓴다.
kubectl debug -it my-pod -n prod --image=nicolaka/netshoot
이걸로 컨테이너는 잘 붙는다. 근데 막상 들어가서 ps를 쳐보면 본인 프로세스만 보인다. 본 컨테이너의 PID가 안 보인다는 얘기다. /proc/<pid>/net/tcp를 들여다볼 수도 없고, nsenter로 네트워크 네임스페이스에 들어갈 PID 자체가 없다.
왜냐면 ephemeral container도 결국 파드 안의 별도 컨테이너고, 기본적으로 PID 네임스페이스는 분리돼 있다. 네트워크 네임스페이스는 파드 단위로 공유라서 tcpdump나 ss는 잘 되지만, 프로세스가 보이는 건 별개 문제다.
--target을 쓰면 달라진다
kubectl debug -it my-pod -n prod \
--image=nicolaka/netshoot \
--target=app \
--profile=general
--target=app을 지정하면 그 컨테이너의 PID 네임스페이스를 공유한다. 이제 ps -ef에 본 애플리케이션 프로세스가 잡힌다. 그러면 다음 같은 게 가능해진다.
cat /proc/<app_pid>/environ으로 환경변수 확인 (특히 distroless라env도 못 쓸 때 유용하다)ls -la /proc/<app_pid>/cwd로 작업 디렉토리 확인cat /proc/<app_pid>/maps로 mmap 매핑 확인- 같은 PID에
gdbattach해서 stack trace 뜨기 (단, capabilities 필요)
--profile은 1.27부터 들어온 옵션인데, general이 무난하다. sysadmin은 SYS_PTRACE 같은 권한까지 다 붙여줘서 gdb attach까지 가능. 기본값은 legacy라 권한이 거의 없다. 모르고 그냥 쓰면 "왜 ptrace가 안 되지?"하고 5분 날린다.
한 가지 주의할 점
ephemeral container는 한 번 붙이면 못 뗀다. 파드 spec에 박혀버린다. 그래서 운영 파드에서 막 붙이고 다니면 describe가 점점 지저분해진다. 디버깅 끝나면 deploy rollout으로 파드를 새로 띄우는 게 깔끔하다. 우리 팀은 incident 끝나면 무조건 rollout restart 한 번 돌리는 컨벤션을 두고 있다.
그리고 --target 컨테이너가 죽으면 PID 네임스페이스도 같이 사라진다. CrashLoopBackOff 상태 파드에는 못 붙는다는 얘기다. 이때는 kubectl debug 대신 --copy-to로 파드 복사본을 만들어서 entrypoint 바꿔 띄우는 게 답이다.
별 거 아닌 팁인데, 정작 incident 한가운데서는 머리가 안 돌아간다. 다음에 distroless 파드 디버깅할 일 생기면 그냥 --target이랑 --profile=general 두 개만 떠올리면 된다.