distroless 컨테이너에 sh가 없을 때, kubectl debug 한 줄로 끝내기

ephemeral container를 붙이면 끝난다
오늘 알게 된 건데, 의외로 kubectl debug 안 써본 분들 꽤 많더라.
우리 팀은 보안팀 권고로 작년부터 베이스 이미지를 distroless로 통일했다. 공격 표면 줄이고 CVE 대응 줄이는 데는 좋은데, 막상 운영 중에 컨테이너 안으로 들어가서 뭐 좀 보려고 하면 막막하다. kubectl exec -it pod sh 치면 OCI runtime exec failed: exec: "sh": executable file not found in $PATH 떨어지는 그 상황. 옛날에는 이걸 우회하려고 디버깅용 -debug 태그 이미지를 따로 빌드해서 RollingUpdate로 갈아끼우는 짓을 했다. 지금 생각하면 좀 한심한데, 그땐 그게 최선처럼 보였다.
kubectl debug는 1.23부터 베타, 1.25부터 stable이다. 핵심은 실행 중인 파드를 건드리지 않고, 그 파드의 process namespace를 공유하는 임시 컨테이너를 옆에 붙이는 것. 재시작도 없고, 파드 spec도 안 바뀐다.
kubectl debug -it pod/api-server-7d9 \
--image=nicolaka/netshoot \
--target=api-server \
--share-processes
--target을 주면 그 컨테이너의 PID 네임스페이스를 공유한다. 그 다음 ps -ef 치면 distroless 컨테이너 안 프로세스가 그대로 보이고, /proc/1/root/로 들어가면 그 컨테이너의 루트 파일시스템도 읽을 수 있다. 설정 파일 한번 까보고 싶을 때, 환경 변수 확인하고 싶을 때 이게 다 된다.
# debug 컨테이너 안에서
cat /proc/1/root/etc/app/config.yaml
ls /proc/1/environ | xargs -0 -n1
자주 쓰는 디버그 이미지 두 개
nicolaka/netshoot— 네트워크 이슈 잡을 때.dig,tcpdump,mtr,iperf3다 들어있다. DNS 이상하다 싶을 때 일단 이거.busybox:1.37— 단순히 파일 좀 보거나 curl 한번 때리고 싶을 때. 가벼움.
이미지 풀 비용 때문에 사내 미러에 미리 캐시해두는 걸 추천한다. 장애 한가운데서 외부 레지스트리가 throttle 걸리면 디버깅 자체가 막힌다.
한 가지 함정
ephemeral container는 한번 붙이면 파드 살아있는 동안 못 떼낸다. 리소스 limit도 못 걸고 restartPolicy도 없다. 디버깅 다 했으면 파드를 그냥 새로 띄우는 편이 깔끔하다. Deployment면 어차피 다시 굴러갈 테니까.
또 하나, --target을 쓰려면 컨테이너 런타임이 process namespace 공유를 지원해야 하는데, EKS·GKE·AKS 다 containerd 기반이라 문제 없을 거다. 베어메탈 클러스터에서 옛날 docker shim 쓰는 곳이면 한번 확인해봐라.
이거 모르고 디버깅 이미지 별도 배포하는 분들 있으면, 이번 주에 한번 시도해보면 좋을 것 같다. 진짜로 운영 중 파드 건드리지 않고 들어가는 게 가능하다는 게 처음엔 좀 신기했다.