kubectl debug --copy-to + --share-processes, 프로덕션 Pod 안 건드리고 진짜 디버깅하기

우리 팀이 자주 쓰는 형태
오늘 점심에 동료가 "운영 Pod에 strace 한 번만 떠보면 알 것 같은데 못 들어간다"고 한참 끙끙대길래 옆에서 봤다. distroless 이미지라 shell이 없고, 그렇다고 ephemeral container를 그냥 띄우자니 같은 PID namespace가 아니라 프로세스가 안 보인다는 거였다. 사실 이거 의외로 많이들 모르고 지나가더라.
kubectl debug에 --copy-to랑 --share-processes를 같이 주면 거의 다 해결된다. 1.30 GA 이후로 옵션 동작도 안정돼서 운영에서 그냥 쓰면 된다.
원본 Pod는 그대로 두고, 동일한 spec의 복제 Pod에 디버그 컨테이너를 끼워넣는 방식. 트래픽 받는 Pod 직접 손대지 않아도 된다.
kubectl debug payment-api-7c9d-xn8qz \
-n payment \
--copy-to=payment-api-debug \
--container=debug \
--image=nicolaka/netshoot \
--share-processes \
-- sleep 3600
--copy-to가 핵심이다. 이걸로 만들어진 Pod는 Service selector에 안 잡히니까 트래픽이 안 흘러간다. 그러면서도 원본 Pod와 같은 환경 변수, 같은 ServiceAccount, 같은 sidecar들이 다 들어있다. mTLS 환경에서 디버깅할 때 이게 진짜 크다.
--share-processes는 디버그 컨테이너에서 원본 컨테이너 프로세스를 보게 해준다. 즉 ps aux 치면 우리 앱 PID가 그대로 보인다. 그 다음엔 nsenter나 strace -p <PID> 같은 게 다 동작한다.
kubectl exec -it payment-api-debug -c debug -- bash
# 이제 우리 앱 프로세스가 보인다
ps -ef | grep java
# 네트워크 namespace는 어차피 공유되니 tcpdump도 그냥 된다
tcpdump -i eth0 -w /tmp/dump.pcap host upstream-api
자주 헷갈리는 포인트
--copy-to로 만들어진 Pod도 노드 리소스를 먹는다는 걸 가끔 까먹는다. 메모리 요청량이 큰 앱이면 스케줄링이 안 될 수 있고, 운 나쁘면 노드 압박을 키운다. 운영에서 디버깅 끝나고 kubectl delete pod payment-api-debug 안 하면 다음 oncall이 와서 "이거 뭐예요?" 한다.
그리고 --share-processes는 원본 Pod에는 영향 안 준다. 원본은 그대로다. 복제본의 spec.shareProcessNamespace만 true로 켜진다.
distroless 이미지 디버깅할 땐 --image를 꼭 같이 줘야 한다. 안 그러면 디폴트로 들어가는 컨테이너가 distroless 그대로라 또 셸이 없다. 우리 팀은 사내에 netshoot 베이스에 strace, tcpdump, lsof, perf 미리 박아둔 이미지를 따로 만들어두고 쓴다. 그게 제일 편하다.
ephemeral container랑 뭐가 다른가
kubectl debug 그냥 쓰면 (--copy-to 없이) ephemeral container를 원본 Pod에 추가한다. 이쪽이 더 가볍긴 한데 원본을 건드린다는 점이 운영에선 부담스럽다. 특히 우리처럼 PDB로 Pod 개수 빡세게 제한하는 서비스는 ephemeral container 붙이는 게 원본 Pod 안정성에 영향 줄 수 있다는 사내 가이드가 있어서 못 쓴다.
--copy-to는 그 우려가 아예 없다. Service 트래픽도 안 받고, PDB 계산에도 안 들어가고, 다 쓰면 그냥 지우면 된다. 좀 무겁긴 한데 운영에서 안전한 게 더 중요하다.
혹시 다른 패턴 쓰시는 분 있으면 댓글로 알려주세요. 특히 GPU 노드처럼 복제본 띄우기 부담스러운 환경에선 어떻게 푸는지 궁금하다.