/인프라/Kubernetes Pod가 Terminating에서 멈췄을 때: 원인 5가지와 안전한 강제 삭제
인프라kubernetesPod Terminating

Kubernetes Pod가 Terminating에서 멈췄을 때: 원인 5가지와 안전한 강제 삭제

Kubernetes Pod가 Terminating에서 멈춰 삭제 안 될 때 finalizer·grace period·노드 NotReady·볼륨 detach 5가지 원인을 진단하고, --force의 위험성과 데이터 손상 없는 안전한 강제 삭제 절차를 정리합니다.

Kubernetes Pod가 Terminating에서 멈췄을 때: 원인 5가지와 안전한 강제 삭제

Kubernetes Pod가 Terminating에서 멈췄을 때: 원인 5가지와 안전한 강제 삭제

kubectl delete pod를 쳤는데 몇 분이 지나도 STATUS가 Terminating에서 꿈쩍도 안 한다. CI 파이프라인은 멈추고, ArgoCD는 동기화가 안 끝나고, 야간 호출이 울린다. 이 글은 K8s_Troubleshooting_Guide 시리즈 5편으로, 바로 이 "Terminating 무한 대기" 상황만 집중적으로 다룹니다.

📌 4편까지 우리는 Pending(스케줄 불가), CrashLoopBackOff(컨테이너 재기동 루프), ImagePullBackOff 같은 시작/실행 단계의 상태를 다뤘습니다. 이번 편은 그 반대편, 즉 Pod가 죽어가는 종료(Termination) 단계 한정입니다. 같은 증상처럼 보여도 진단 포인트가 완전히 다르니 헷갈리지 마세요.

kubectl delete는 '명령'이 아니라 '요청'이다

가장 먼저 이해해야 할 점은, kubectl delete pod가 Pod를 즉시 죽이는 명령이 아니라는 것입니다. 이 명령은 API 서버에 Pod의 metadata.deletionTimestamp 필드를 찍는 요청일 뿐입니다. deletionTimestamp가 찍히는 순간부터 아래 라이프사이클이 시작됩니다.

  1. deletionTimestamp가 설정되고, Pod는 Terminating 상태로 표시됨
  2. kubelet이 컨테이너에 preStop 훅을 실행하고 SIGTERM 전송
  3. terminationGracePeriodSeconds(기본 30초) 동안 종료를 기다림
  4. 시간이 지나면 SIGKILL로 강제 종료
  5. 모든 finalizers가 제거되어야 API 서버가 Pod 객체를 실제로 삭제

즉 Terminating이 안 끝난다는 건, 이 5단계 어딘가에서 막혔다는 뜻입니다. 원인을 모르고 --force부터 때리면 StatefulSet에서 데이터가 깨질 수 있으니, 먼저 어디서 막혔는지 구분하는 게 핵심입니다.

원인 5가지 진단 매트릭스

#원인증상진단 명령핵심 YAML 필드해결 방향
1프로세스가 SIGTERM 무시grace period(30초)만큼 정확히 버티다 사라짐kubectl logs <pod> 로 종료 처리 로그 확인spec.terminationGracePeriodSeconds앱에 시그널 핸들러 추가, PID 1 문제 점검
2preStop 훅 지연/행grace period를 넘겨 계속 Terminatingkubectl describe pod 이벤트, lifecycle.preStopspec.containers[].lifecycle.preStop훅 타임아웃 단축, sleep 값 점검
3finalizer 잔류deletionTimestamp는 찍혔는데 영원히 안 사라짐kubectl get pod -o yaml | grep -A5 finalizersmetadata.finalizers컨트롤러 복구 또는 finalizer 수동 제거
4노드 NotReady (kubelet 불통)특정 노드의 Pod만 다수 Terminatingkubectl describe node <node>status.conditions (Ready=Unknown)노드 복구 우선, 안 되면 강제 삭제
5볼륨 detach 실패PVC 붙은 Pod만 안 사라짐, VolumeAttachment 잔류kubectl get volumeattachmentstatus / CSI 이벤트CSI 드라이버·스토리지 상태 확인

진단 루틴 체크리스트

--force를 누르기 전에 아래 순서대로 1분만 확인하세요. (다시 강조: Terminating 상태 한정 진단입니다.)

Bash
# 1. deletionTimestamp가 정말 찍혀 있는지 (= 삭제 요청은 들어갔는지)
kubectl get pod <name> -o jsonpath='{.metadata.deletionTimestamp}'

# 2. finalizer가 남아있는지 (원인 3의 핵심)
kubectl get pod <name> -o yaml | grep -A5 finalizers

# 3. grace period 설정값 확인
kubectl get pod <name> -o jsonpath='{.spec.terminationGracePeriodSeconds}'

# 4. 이벤트로 preStop / SIGKILL 흐름 확인
kubectl describe pod <name>

# 5. Pod가 떠 있던 노드의 상태 확인 (원인 4)
kubectl describe node <node> | grep -A5 Conditions

# 6. 볼륨 detach 잔류 확인 (원인 5)
kubectl get volumeattachment | grep <pv-name>

판단 기준: finalizers 배열에 값이 있으면 → 원인 3. 노드 Condition이 Ready=Unknown이면 → 원인 4. 둘 다 깨끗한데 grace period만큼 정확히 버티면 → 원인 1·2. VolumeAttachment가 남아 있으면 → 원인 5입니다.

안전한 강제 삭제 절차

진단으로 원인을 좁혔다면 이제 정리할 차례입니다. 명령의 의미부터 정확히 알고 갑시다.

Bash
kubectl delete pod <name> --grace-period=0 --force

이 명령은 grace period를 0으로 만들고, API 서버의 객체를 즉시 제거합니다. 문제는 노드가 살아있다면 컨테이너가 아직 실행 중일 수 있는데도, 컨트롤 플레인은 "Pod가 사라졌다"고 믿는다는 점입니다.

⚠️ 경고 — StatefulSet에서 --force를 함부로 쓰지 마세요. StatefulSet은 web-0, web-1처럼 고유 ID와 PVC를 보장합니다. 노드가 살아있는 상태에서 강제 삭제하면, 컨트롤러가 같은 ID의 Pod를 새 노드에 띄우는데 기존 컨테이너가 여전히 같은 볼륨에 쓰고 있을 수 있습니다. 두 인스턴스가 동시에 쓰면 split-brain·데이터 손상이 발생합니다. DB·Kafka·etcd 같은 stateful 워크로드에서 특히 치명적입니다.

finalizer가 원인일 때

컨트롤러 버그나 GitOps 환경(ArgoCD 등)에서 finalizer가 잔류해 동기화가 멈추는 사례가 많습니다. 정상 절차는 finalizer를 책임지는 컨트롤러를 복구하는 것이지만, 컨트롤러가 이미 사라졌다면 수동 제거가 필요합니다.

Bash
kubectl patch pod <name> -p '{"metadata":{"finalizers":null}}' --type=merge

⚠️ 경고 — finalizer 제거는 "정리 로직 건너뛰기"입니다. finalizer는 보통 "볼륨 detach 완료", "외부 LB 등록 해제" 같은 후처리를 끝낼 때까지 객체 삭제를 막는 안전장치입니다. 강제로 지우면 그 후처리가 누락된 채 객체만 사라집니다. 반드시 무엇을 위한 finalizer인지 확인한 뒤 제거하세요.

노드가 NotReady일 때 (원인 4)

가장 흔한 실수가 여기서 나옵니다. 노드가 NotReady면 kubelet이 컨트롤 플레인과 통신을 못 해 Pod를 정리할 수 없을 뿐, 컨테이너는 그 노드에서 여전히 돌고 있을 수 있습니다. 올바른 순서는:

  1. 노드 복구를 먼저 시도 (네트워크·kubelet 재기동). 복구되면 Pod는 알아서 정리됩니다.
  2. 복구 불가능하고 노드가 확실히 죽었다고 판단되면 → kubectl delete node <node> 로 노드를 제거하면 그 위의 Pod도 정리됩니다.
  3. Kubernetes 1.30+의 Graceful Node Shutdown 기능이 켜져 있으면, 계획된 노드 종료 시 kubelet이 Pod를 먼저 정상 종료시켜 이 문제 자체를 줄여줍니다.

💬 실무 경험 한마디: 저는 사고 회고에서 "Terminating 멈춤"의 원인 절반 이상이 **노드 장애(원인 4)**였습니다. 그런데 당직자들이 습관적으로 --force부터 눌렀고, stateful 워크로드에서 두 번 데이터 정합성 이슈가 났습니다. 그래서 팀 런북에 "force 전에 kubectl get pod -o yaml로 finalizer·노드 상태 3초 확인"을 강제 절차로 박아 넣었더니 재발이 사라졌습니다. 진단 한 줄이 데이터를 지킵니다.

재발 방지: graceful shutdown 설계

강제 삭제는 응급처치일 뿐, 진짜 해법은 Pod가 SIGTERM에 얌전히 죽도록 설계하는 것입니다.

Before — grace period·preStop 미설정 (SIGKILL로 거칠게 죽음):

YAML
spec:
  containers:
    - name: api
      image: myapp:1.0
      # terminationGracePeriodSeconds 없음 → 기본 30초
      # preStop 없음 → LB가 트래픽 보내는 중에 종료될 수 있음

After — graceful shutdown 적용:

YAML
spec:
  terminationGracePeriodSeconds: 45   # 앱 종료 시간 + 여유
  containers:
    - name: api
      image: myapp:1.0
      lifecycle:
        preStop:
          exec:
            # LB가 엔드포인트에서 빠질 시간 확보 후 종료
            command: ["/bin/sh", "-c", "sleep 5"]

핵심 원칙 세 가지:

  • 앱에 SIGTERM 핸들러 구현: 새 요청은 거부하고 진행 중 요청만 마무리 후 종료
  • terminationGracePeriodSeconds 튜닝: 앱의 실제 정리 시간보다 약간 길게. 너무 길면 강제 삭제가 오래 걸리고, 너무 짧으면 SIGKILL로 잘림
  • preStop으로 LB 디레지스터 시간 확보: sleep 5 정도로 엔드포인트 제거가 전파될 시간을 줌

결론

Terminating 멈춤은 "Pod가 안 지워진다"는 하나의 증상이지만, 원인은 SIGTERM 무시 · preStop 지연 · finalizer 잔류 · 노드 NotReady · 볼륨 detach 실패의 5갈래입니다. --force는 만능 버튼이 아니라 원인을 안 뒤 마지막에 쓰는 칼입니다. 진단 → 안전한 정리 → graceful shutdown 설계 순서를 런북으로 만들어 두세요.

다음 6편에서는 컨테이너가 메모리 한계를 넘겨 죽는 OOMKilled 트러블슈팅을 다룹니다. requests/limits 튜닝과 cgroup 관점까지 파헤쳐 보겠습니다.

자주 묻는 질문 (FAQ)

Q. --grace-period=0 --force를 쓰면 데이터가 날아가나요? A. 명령 자체가 데이터를 지우진 않습니다. 하지만 노드가 살아있는데 강제 삭제하면 컨테이너가 계속 볼륨에 쓰는 동안 같은 Pod가 다른 곳에 뜨면서 동시 쓰기로 인한 손상이 발생할 수 있습니다. 위험은 명령이 아니라 split-brain에서 옵니다.

Q. StatefulSet에서 강제 삭제하면 안 되는 이유는? A. StatefulSet은 동일 식별자와 PVC의 단일 인스턴스를 보장합니다. 노드 생존 상태에서 강제 삭제하면 컨트롤러가 같은 ID·같은 볼륨의 Pod를 새로 띄워 두 인스턴스가 공존할 수 있고, DB라면 데이터 정합성이 깨집니다. 반드시 기존 컨테이너가 죽은 게 확실할 때만 진행하세요.

Q. 노드가 NotReady인데 Pod가 Terminating이면? A. kubelet이 통신 불가라 정리를 못 하는 상태입니다. 노드 복구를 먼저 시도하고, 노드가 확실히 죽었다면 kubectl delete node로 노드를 제거해 정리하세요. 노드 생존 여부를 확인하지 않은 채 Pod만 --force하는 게 가장 위험합니다.

Q. finalizer를 그냥 지워도 되나요? A. finalizer는 볼륨 detach·외부 리소스 해제 같은 후처리를 보장하는 장치입니다. 무엇을 위한 것인지 확인하지 않고 finalizers:null로 지우면 후처리가 누락됩니다. 컨트롤러 복구가 1순위, 수동 제거는 컨트롤러가 사라진 예외 상황의 최후 수단입니다.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.

작성 · Content Reviewer·검토 · 사람 편집자·발행 · 2026년 6월 11일

댓글

불러오는 중...