-
Kubernetes Namespace 삭제 전쟁: 18시간의 Terminating과의 싸움실제 경험과 인사이트를 AI와 함께 정리한 글 2025. 10. 22. 15:37
Kubernetes Namespace 삭제 전쟁: 18시간의 Terminating과의 싸움
TL;DR
KubeBlocks 0.9→1.0.1 업그레이드 중 네임스페이스가 18시간 동안 Terminating 상태에 멈췄습니다.
서버 재부팅도 소용없었습니다. 결국 해결한 방법은?핵심 교훈:
kubectl delete만으로는 부족합니다. Finalizer를 이해하고 제거해야 합니다.
1. 시작: 단순한 삭제가 18시간의 여정이 되다
# 2025-10-22 오전 10시 $ kubectl delete namespace imprun-system kb-system namespace "imprun-system" deleted namespace "kb-system" deleted # 30분 후... $ kubectl get ns NAME STATUS AGE imprun-system Terminating 18h # 😱 kb-system Terminating 73d # 😱😱😱예상 소요 시간: 30초
실제 소요 시간: 18시간 이상 (그리고 계속 진행 중)
2. 첫 번째 시도: "시간이 해결해주겠지"
오전 10시 30분
$ kubectl get ns imprun-system NAME STATUS AGE imprun-system Terminating 30m"Kubernetes가 알아서 정리하고 있을 거야. 조금만 기다려보자."
오후 3시
$ kubectl get ns imprun-system NAME STATUS AGE imprun-system Terminating 5h"뭔가 잘못됐다..."
3. Finalizer 발견: 네임스페이스가 멈춘 진짜 이유
Finalizer란 무엇인가?
Kubernetes의 안전장치: 리소스 삭제 전 반드시 완료해야 할 작업 목록
metadata: finalizers: - kubernetes.io/pv-protection - instanceset.workloads.kubeblocks.io/finalizerFinalizer 동작 원리
정상 흐름: 1. kubectl delete 실행 2. deletionTimestamp 설정 → Terminating 상태 3. Controller가 finalizer 작업 처리 4. Controller가 finalizer 제거 5. 리소스 완전 삭제 우리의 상황: 1. kubectl delete 실행 ✅ 2. deletionTimestamp 설정 ✅ 3. Controller 작동 불가 ❌ ← 여기서 멈춤! 4. (영원히 대기...)왜 Controller가 작동하지 않았나?
$ kubectl logs -n kb-system -l app.kubernetes.io/name=kubeblocks --tail=50 | grep ERROR ERROR: deployments.apps "kubeblocks" is forbidden: User "system:serviceaccount:kb-system:kubeblocks" cannot get resource "deployments" ERROR: Failed to watch *v1.ComponentDefinition: json: cannot unmarshal string into Go struct field SystemAccount근본 원인:
- KubeBlocks 0.9 → 1.0.1 업그레이드 중 RBAC 권한 문제
- 0.9 버전 ComponentDefinition이 1.0.1 Controller와 호환 불가
- Controller가 작동하지 않아 Finalizer 처리 불가능
4. 두 번째 시도: "재부팅하면 되겠지"
오후 4시
$ sudo systemctl restart kubelet결과: 변화 없음
오후 5시
# Swap 때문에 kubelet이 시작 실패했다는 것을 발견 $ sudo swapoff -a $ sudo sed -i '/ swap / s/^/#/' /etc/fstab $ sudo systemctl restart kubelet결과: kubelet은 정상이지만 네임스페이스는 여전히 Terminating
오후 6시 (절박한 마음으로)
$ sudo reboot재부팅 후:
$ kubectl get ns | grep -E "imprun-system|kb-system" imprun-system Terminating 18h # 여전히... kb-system Terminating 73d # 여전히...충격: 재부팅해도 Terminating 상태는 그대로였다!
5. 세 번째 시도: Finalizer 수동 제거 작전
5-1. 네임스페이스 내 리소스 확인
$ kubectl get all,cm,secret,svc -n imprun-system service/mongodb-mongodb-headless ClusterIP None ... configmap/mongodb-mongodb-env 2 secret/mongodb-conn-credential Opaque 85-2. Finalizer 확인
$ kubectl get service mongodb-mongodb-headless -n imprun-system -o jsonpath='{.metadata.finalizers}' ["instanceset.workloads.kubeblocks.io/finalizer"]발견: 모든 리소스에 KubeBlocks finalizer가 붙어있음!
5-3. Finalizer 제거 시도 #1
$ kubectl patch service mongodb-mongodb-headless -n imprun-system \ -p '{"metadata":{"finalizers":null}}' --type=merge service/mongodb-mongodb-headless patched ✅희망의 빛! 하지만...
$ kubectl patch configuration.apps.kubeblocks.io mongodb-mongodb -n imprun-system \ -p '{"metadata":{"finalizers":[]}}' --type=merge Error: spec.systemAccounts[1].statement: Invalid value: "string": spec.systemAccounts[1].statement in body must be of type object: "string"문제: 0.9 버전 리소스는 1.0.1 validation을 통과하지 못함!
6. 네 번째 시도: CRD 자체를 제거하자
왜 CRD를 삭제하는가?
리소스 계층 구조: CRD (ComponentDefinition 정의) ↓ ComponentDefinition 인스턴스들 (mongodb, postgresql 등) ↓ Cluster 인스턴스들 ↓ Service, ConfigMap, Secret 등CRD를 삭제하면 모든 인스턴스가 자동 삭제됩니다!
실행
# 1. CRD finalizer 제거 $ kubectl patch crd componentdefinitions.apps.kubeblocks.io \ -p '{"metadata":{"finalizers":[]}}' --type=merge customresourcedefinition.apiextensions.k8s.io/componentdefinitions.apps.kubeblocks.io patched # 2. CRD 삭제 $ kubectl delete crd componentdefinitions.apps.kubeblocks.io customresourcedefinition.apiextensions.k8s.io "componentdefinitions.apps.kubeblocks.io" deleted결과 확인
$ kubectl get componentdefinition Error from server (NotFound): Unable to list "apps.kubeblocks.io/v1, Resource=componentdefinitions": the server could not find the requested resource성공! CRD가 완전히 사라짐
7. 최종 돌파: 모든 리소스 Finalizer 제거
남은 리소스 정리
# KubeBlocks 관련 모든 리소스의 finalizer 제거 $ kubectl get configurations,backuppolicies,backupschedules -n imprun-system -o name | \ xargs -I {} kubectl patch {} -n imprun-system -p '{"metadata":{"finalizers":[]}}' --type=merge configuration.apps.kubeblocks.io/mongodb-mongodb patched backuppolicy.dataprotection.kubeblocks.io/mongodb-mongodb-backup-policy patched backupschedule.dataprotection.kubeblocks.io/mongodb-mongodb-backup-schedule patched일반 리소스 Finalizer 제거
$ kubectl patch service mongodb-mongodb-headless -n imprun-system \ -p '{"metadata":{"finalizers":null}}' --type=merge $ kubectl patch configmap mongodb-mongodb-env -n imprun-system \ -p '{"metadata":{"finalizers":null}}' --type=merge $ kubectl patch secret mongodb-conn-credential -n imprun-system \ -p '{"metadata":{"finalizers":null}}' --type=merge네임스페이스 Finalizer 확인 및 제거
$ kubectl get namespace imprun-system -o jsonpath='{.spec.finalizers}' ["kubernetes"] $ kubectl patch namespace imprun-system -p '{"spec":{"finalizers":[]}}' --type=merge namespace/imprun-system patched드디어...
$ sleep 5 && kubectl get ns | grep imprun-system (출력 없음) $ kubectl get ns | grep kb-system (출력 없음)🎉 18시간 만에 드디어 삭제 성공!
8. 핵심 교훈 및 실전 가이드
교훈 1: 재부팅은 만능이 아니다
"Terminating 네임스페이스는 etcd에 메타데이터로 남아있습니다.
재부팅해도 etcd 데이터는 그대로이므로 문제가 해결되지 않습니다."교훈 2: Finalizer를 이해해야 한다
# Finalizer의 본질 metadata: finalizers: ["some-controller.io/cleanup"] # 의미: "이 리소스를 삭제하기 전에 some-controller에게 물어봐야 해" # Controller가 죽으면? → 영원히 Terminating교훈 3: 버전 호환성 문제는 조기 발견해야 한다
업그레이드 전 체크리스트:
# 1. Controller 로그 확인 kubectl logs -n kb-system -l app.kubernetes.io/name=kubeblocks --tail=100 # 2. CRD 호환성 확인 kubectl get componentdefinition -o yaml | head -50 # 3. 문제 발견 시 즉시 롤백
9. 실전 해결 플레이북
상황 1: 네임스페이스가 Terminating에서 멈춤
# Step 1: 내부 리소스 확인 kubectl api-resources --verbs=list --namespaced -o name | \ while read resource; do count=$(kubectl get $resource -n <namespace> 2>/dev/null | grep -v '^NAME' | wc -l) [ $count -gt 0 ] && echo "$resource: $count" done # Step 2: Finalizer 있는 리소스 찾기 kubectl get all,cm,secret,svc -n <namespace> -o json | \ jq '.items[] | select(.metadata.finalizers != null) | {kind: .kind, name: .metadata.name, finalizers: .metadata.finalizers}' # Step 3: Finalizer 제거 kubectl patch <resource-type> <name> -n <namespace> \ -p '{"metadata":{"finalizers":null}}' --type=merge # Step 4: 네임스페이스 finalizer 제거 kubectl patch namespace <namespace> \ -p '{"spec":{"finalizers":[]}}' --type=merge상황 2: CRD 리소스가 삭제 안됨
# Validation 에러로 patch 실패 시 → CRD 자체를 삭제 kubectl patch crd <crd-name> -p '{"metadata":{"finalizers":[]}}' --type=merge kubectl delete crd <crd-name>상황 3: 모든 것이 실패했을 때 (최후의 수단)
# etcd에서 직접 삭제 MSYS_NO_PATHCONV=1 kubectl exec -n kube-system etcd-<node> -- \ etcdctl --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ del /registry/namespaces/<namespace-name>⚠️ 주의: etcd 직접 수정은 클러스터를 망가뜨릴 수 있습니다!
10. 예방이 최선
KubeBlocks 업그레이드 시 올바른 순서
# ❌ 잘못된 방법 (우리가 했던 것) helm upgrade kubeblocks kubeblocks/kubeblocks --version 1.0.1 # ✅ 올바른 방법 # 1. 백업 kubectl get clusters -A -o yaml > clusters-backup.yaml kubectl get componentdefinitions -o yaml > compdef-backup.yaml # 2. 0.9 ComponentDefinition 및 Addon 정리 kubectl delete addon --all kubectl delete componentdefinition --all # 3. 0.9 KubeBlocks 완전 제거 helm uninstall kubeblocks -n kb-system kubectl delete crd -l app.kubernetes.io/name=kubeblocks # 4. 1.0.1 CRD 설치 kubectl create -f https://github.com/apecloud/kubeblocks/releases/download/v1.0.1/kubeblocks_crds.yaml # 5. 1.0.1 KubeBlocks 설치 helm install kubeblocks kubeblocks/kubeblocks \ --namespace kb-system \ --create-namespace \ --version 1.0.1 # 6. Cluster 복구 kubectl apply -f clusters-backup.yamlFinalizer 관리 베스트 프랙티스
# 1. 개발/테스트 환경에서는 terminationPolicy를 Delete로 spec: terminationPolicy: Delete # 빠른 정리 # 2. 프로덕션에서는 DoNotTerminate로 spec: terminationPolicy: DoNotTerminate # 실수 방지 # 3. 삭제 전 명시적 정리 kubectl delete clusters --all --wait=true -n <namespace> kubectl delete namespace <namespace>
11. 마치며
이번 사건의 타임라인
- 10:00 - 네임스페이스 삭제 시작
- 10:30 - "조금만 기다려보자"
- 15:00 - "뭔가 잘못됐다"
- 16:00 - kubelet 재시작 (실패)
- 17:00 - Swap 문제 해결
- 18:00 - 서버 재부팅 (실패)
- 19:00 - Finalizer 발견
- 20:00 - 수동 제거 시작
- 21:00 - Validation 에러 발생
- 22:00 - CRD 삭제 시도
- 23:00 - 모든 리소스 정리
- 04:00 (다음날) - 드디어 성공! 🎉
총 소요 시간: 18시간
배운 것
- Finalizer는 강력한 안전장치지만, Controller가 죽으면 독이 된다
- 재부팅은 만능이 아니다 - etcd는 영원하다
- 버전 업그레이드는 신중하게 - 호환성 확인 필수
- CRD 계층 구조를 이해하면 빠른 해결 가능
- 문서화가 중요 - 다음에는 이 삽질을 안 하기 위해
당신이 지금 Terminating 지옥에 있다면
- 숨을 고르세요 (재부팅은 소용없습니다)
- 이 문서의 "실전 해결 플레이북"을 따라하세요
- Finalizer를 찾고 제거하세요
- CRD 계층을 이해하고 위에서부터 정리하세요
- 그래도 안되면 etcd... (하지만 신중하게!)
작성일: 2025-10-22
환경: Kubernetes v1.28.15, KubeBlocks 0.9 → 1.0.1
삽질 시간: 18시간
얻은 교훈: 무가(無價)끝.
'실제 경험과 인사이트를 AI와 함께 정리한 글' 카테고리의 다른 글
Oracle Cloud + Tailscale + Kubernetes 완벽 가이드(0) (0) 2025.10.26 Kubernetes 기반 데이터베이스 Operator - MongoDB Replica Set 관리 (1) 2025.10.22 Kubernetes 민감정보 관리 완벽 가이드: Secret, 암호화, 그리고 실전 전략 (0) 2025.10.22 Next.js SSR 환경에서 API URL 환경변수 관리 전략 (0) 2025.10.22 CI/CD 파이프라인 구축: GitHub Actions로 완전 자동화하기 (0) 2025.10.22