티스토리 뷰
Kubernetes Namespace 삭제 전쟁: 18시간의 Terminating과의 싸움
pak2251 2025. 10. 22. 15:37Kubernetes 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/finalizer
Finalizer 동작 원리
정상 흐름:
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 8
5-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.yaml
Finalizer 관리 베스트 프랙티스
# 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 |
- Total
- Today
- Yesterday
- troubleshooting
- Tax Analysis
- react
- ai 개발 도구
- Claude
- knowledge graph
- AI
- SHACL
- Ontology
- Tailwind CSS
- AI agent
- LLM
- LangChain
- authentication
- claude code
- Kubernetes
- PYTHON
- api gateway
- authorization
- 개발 도구
- workflow
- AI Development
- Developer Tools
- frontend
- architecture
- backend
- Next.js
- security
- Go
- Rag
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |