도입부: 장애 상황에서 kubectl debug는 가장 빠른 “현장 검증” 도구이지만, 세션이 끝난 뒤 무엇을 했는지/어떻게 끝났는지 남지 않아 분석의 실마리가 사라질 수 있습니다. 이 글에서는 그 원인이 Kubernetes API 설계에 있음을 짚고, 팀 차원에서 명령어(행위) 중심으로 증거를 남기는 “권장 경로”를 어떻게 만들지 정리합니다.
kubectl debug의 ‘증거 공백’이란?
kubectl debug는 임시 컨테이너(ephemeral container)를 파드에 붙여서 문제를 조사하는 워크플로우입니다. 문제는 디버그가 끝난 뒤에, 우리가 기대하는 “종료 컨텍스트”가 Kubernetes API에 안 남을 수 있다는 점입니다.
예를 들면 아래 같은 것들입니다.
- 종료 코드(exit code)
- 시작/종료 시간, 실행 시간(duration)
- 디버그 세션이 어떤 상태로 끝났는지(정상 종료/강제 종료 등)
이게 kubectl의 버그라기보다는, ephemeral container를 다루는 Kubernetes API 설계 특성 때문에 파드 상태 변화 이후 디버그 세션의 종료 정보를 더 이상 노출하지 않는 상황이 생깁니다. 결과적으로 “장애 시점에 무슨 조치를 했는지”라는 증거가 공중분해될 수 있습니다.
“무조건 로그로 남기자”가 생각보다 어려운 이유
대화에서 제가 느낀 포인트는 이거였습니다. “그럼 무조건 로그에 남기면 되지 않나?”라는 아이디어는 직관적이지만, 실제로는 무엇을 증거로 남길 것인지를 먼저 정해야 합니다.
디버그 증거는 대략 네 갈래로 나뉩니다.
- 컨테이너 stdout/stderr (출력)
- 실행한 명령/인자 (행위)
- 종료 코드/실행 시간 (종료 컨텍스트)
- 누가/언제/어느 대상에 붙었는지 (Audit, 책임추적)
여기서 저는 “전부 다 무조건 남기기”는 트레이드오프가 너무 크다고 봅니다.
- 운영 부담: 수집/저장 비용, 파이프라인 복잡도 증가
- 보안/컴플라이언스: 출력이나 명령에 토큰/개인정보가 섞일 수 있음
- 속도 저하: 긴급 대응 시 절차가 길어지면 다시 비표준 루트로 회귀함
그래서 대화에서는 **“최소 증거를 정하고, 통일된 권장 경로로 자연스럽게 유도”**하는 쪽으로 결론이 기울었습니다.
우리가 선택한 최소 증거: “명령어(행위)”
사용자 대화에서 “필수로 남겨야 할 증거는 명령어”라고 정리한 게 핵심입니다. 저는 이 기준이 꽤 실용적이라고 생각합니다.
- 출력은 민감정보가 섞일 가능성이 높고 저장 비용도 커질 수 있습니다.
- 종료 코드/실행 시간은 Kubernetes API만으로는 공백이 생길 수 있습니다.
- 반면 “무엇을 시도했는지(행위)”는 재현성과 책임 추적의 중심이 됩니다.
다만 “명령어”라고 해도 범위를 나눠야 합니다.
- (A)
kubectl debug로 생성한 스펙(이미지/args 등): API 서버에 남길 수 있는 영역 - (B) 쉘로 들어가서 사람이 타이핑한 커맨드: 기본적으로는 남기기 어렵고, 남기려면 별도 장치 필요
현실적으로 팀 표준의 첫 단계는 (A)를 확실히 남기는 것입니다.
권장 경로로 “통일화”하는 설계: Debug를 래핑(wrapping)하자
대화에서 좋다고 했던 우회 방식은, 디버그를 즉흥적인 세션이 아니라 표준 실행 단위(Job/Pod 등) 로 “래핑”해서 흔적을 남기게 만드는 접근입니다.
다만 여기서 우리는 “강제(금지/차단)”까지는 가지 않고, 다음을 목표로 합니다.
- 사람들이 긴급 상황에서도 쉽게 쓸 수 있는 권장 경로 제공
- 그 경로를 타면 자동으로 명령어(행위)가 기록됨
- 필요하면 나중에 정책으로 점진 강화 가능
예시 1) “디버그 요청”을 Job으로 표준화하기 (명령어가 Spec에 남음)
아래는 개념 예시입니다. 실제로는 내부 템플릿/CLI로 감싸서 더 쉽게 만들면 좋습니다.
apiVersion: batch/v1
kind: Job
metadata:
name: debug-netcheck-20260521
namespace: prod
labels:
app.kubernetes.io/purpose: debug
spec:
template:
metadata:
labels:
app.kubernetes.io/purpose: debug
spec:
restartPolicy: Never
serviceAccountName: debug-runner
containers:
- name: debug
image: nicolaka/netshoot:latest
args:
- /bin/sh
- -lc
- |
echo "[whoami] $(whoami)"
echo "[date] $(date -Is)"
nslookup myservice.prod.svc.cluster.local
curl -sv http://myservice.prod.svc.cluster.local:8080/healthz
이 방식의 장점은 명확합니다.
- “명령어(행위)”가
spec.template.spec.containers[].args에 명시적으로 남습니다. - 누가 만들었는지는 Kubernetes Audit(또는 GitOps PR)로 추적 가능합니다.
- Job은
status에 종료 상태가 남기 때문에(성공/실패, 완료 시간 등) “종료 컨텍스트” 공백도 일부 완화됩니다.
대신 트레이드오프도 있습니다.
- 즉흥적으로 파드에 붙는 것보다 한 단계 무겁습니다.
- RBAC/ServiceAccount/NetworkPolicy 설계가 필요합니다.
- 커맨드에 민감정보가 섞이면 Spec 자체가 위험해집니다(마스킹/금지 규칙 필요).
예시 2) kubectl debug 자체는 두되, “생성 행위”를 Audit으로 남기기
만약 “일단은 가볍게 시작”하고 싶다면, kubectl debug를 완전히 대체하기보다 API 서버 Audit 로그로 누가 어떤 ephemeral container를 만들었는지를 남기는 것부터 시작할 수 있습니다.
- 장점: 기존 대응 흐름을 거의 안 바꿉니다.
- 한계: “컨테이너 안에서 뭘 쳤는지”까지는 남기기 어렵습니다.
그래도 최소한 아래는 남길 수 있습니다.
- 누가(사용자/SA)
- 언제
- 어느 네임스페이스/파드에
- 어떤 ephemeral container spec(이미지, command/args 등)을 추가했는지
인사이트: “편의성”이 표준화를 이깁니다
제가 운영에서 자주 본 실패 패턴은, 표준 경로가 “좋은데 느린” 경우입니다. 장애 상황에서는 결국 가장 빠른 길로 사람들이 돌아갑니다. 그래서 권장 경로를 성공시키려면 기술보다 제품화가 중요합니다.
- 템플릿(Job/Pod) YAML을 주는 것에서 끝내지 말고, 내부 CLI로
debug run --target pod/x -- cmd...수준까지 낮추기 - 기본적으로는 “명령어(행위)”만 남기고, 출력/아티팩트 수집은 옵션으로 두기
- 민감정보가 섞일 수 있으니, 커맨드 기록 시 최소한의 마스킹/금지 규칙(예:
--password,Authorization:패턴) 고려하기
결국 목표는 “강제”가 아니라, 사람들이 자발적으로 표준 경로를 타도 손해가 없게 만드는 것입니다.
마무리
kubectl debug의 증거 공백은 도구의 문제가 아니라 Kubernetes API 설계 특성에서 비롯될 수 있고, 그래서 “끝난 뒤에 남는 증거”를 운영 차원에서 보완해야 합니다. 저는 그 시작점으로 명령어(행위) 기록을 최소 표준으로 잡고, 이를 자연스럽게 남기는 래핑된 Job/Pod 기반 권장 경로를 만드는 접근이 가장 현실적이라고 봅니다.
