CRD 와 오퍼레이터 · 부모를 지웠는데 자식이 남았다 · 실습
부모를 지웠는데 자식이 남았다 — 소유 참조와 삭제 전파
목표
소유자 참조의 네 필드가 각각 무엇을 결정하는지 직접 만들어 확인하고, --cascade 세 가지의 차이를 오브젝트로 증명한 뒤, Terminating 에 멈춘 오브젝트를 진단하는 보고서를 만든다.
왜 중요한가
오퍼레이터가 만드는 자원은 혼자 살지 않는다. 부모 하나가 사라지면 그 아래 붙은 것들도 함께 정리돼야 하는데, 쿠버네티스에는 이 일을 하는 참조 카운트가 없다. 대신 자식이 부모를 가리키는 ownerReferences 한 줄이 있고, 가비지 컬렉터가 그 참조를 따라 정리한다. 그래서 참조를 잘못 쓰면 고장이 조용히 난다 — uid 가 틀리면 방금 만든 자식이 몇 초 만에 사라지고, 네임스페이스를 넘으면 경고 이벤트 하나만 남기고 자식이 정리된다. 반대로 파이널라이저는 정리가 끝날 때까지 삭제를 붙잡아 두는 장치라, 그 정리를 할 컨트롤러가 없으면 오브젝트가 영원히 Terminating 에 머문다. 운영자가 실제로 마주치는 화면은 대개 이 둘 중 하나이고, 둘 다 원인을 알아내는 절차가 필요하다.
단계
1. /root/op-ownership/site-crd.yaml 에 CRD sites.own.labhub.io 를 쓰세요 — 그룹 own.labhub.io, 종류 Site, 복수형 sites, 버전 v1 하나이고 스키마에는 spec.region(string) 만 둡니다. 네임스페이스 op-own 를 만들고 /root/op-ownership/site-alpha.yaml 로 Site alpha(region kr-east)를 적용한 뒤, 그 오브젝트의 metadata.uid 를 /root/op-ownership/alpha-uid.txt 에 한 줄로 저장하세요.
2. /root/op-ownership/alpha-good.yaml 에 ConfigMap alpha-good(네임스페이스 op-own)을 쓰되 ownerReferences 에 Site alpha 를 적으세요 — apiVersion own.labhub.io/v1, kind Site, name alpha, uid 는 1단계에서 받은 실제 값입니다. /root/op-ownership/alpha-bad.yaml 에는 같은 내용의 ConfigMap alpha-bad 를 쓰되 uid 만 00000000-0000-0000-0000-000000000000 으로 적습니다. 둘 다 적용하고 잠시 기다린 뒤 kubectl -n op-own get cm 의 출력을 /root/op-ownership/gc-uid.txt 에 저장하세요.
3. 네임스페이스 op-own-remote 를 만들고 /root/op-ownership/alpha-remote.yaml 에 ConfigMap alpha-remote 를 쓰세요 — 네임스페이스는 op-own-remote 인데 ownerReferences 는 op-own 에 있는 Site alpha 를 (실제 uid 로) 가리킵니다. 적용한 뒤 잠시 기다렸다가 kubectl -n op-own-remote get events 의 출력을 /root/op-ownership/xns-event.txt 에 저장하세요.
4. /root/op-ownership/site-beta.yaml 로 Site beta(region kr-west)를 만들고, 그 자식으로 ConfigMap beta-1 과 beta-2 를 /root/op-ownership/beta-children.yaml 한 파일에 담아(둘을 --- 로 잇습니다) 실제 uid 로 소유자 참조를 걸어 적용하세요. 그다음 kubectl -n op-own delete site beta --cascade=background 로 지우고, 자식이 사라질 때까지 기다린 뒤 kubectl -n op-own get cm 출력을 /root/op-ownership/cascade-background.txt 에 저장하세요.
5. /root/op-ownership/site-gamma.yaml 로 Site gamma(region jp-east)를 만들고 자식 ConfigMap gamma-1·gamma-2 를 /root/op-ownership/gamma-children.yaml 에 담아 실제 uid 로 걸어 적용하세요. kubectl -n op-own delete site gamma --cascade=orphan 으로 지운 뒤 kubectl -n op-own get cm gamma-1 -o jsonpath='{.metadata.ownerReferences}' 의 결과를 /root/op-ownership/cascade-orphan.txt 에 gamma-1-ownerrefs=<값, 비어 있으면 none> 한 줄로 저장하세요.
6. /root/op-ownership/site-delta.yaml 로 Site delta(region us-west)를 만들고, /root/op-ownership/delta-child.yaml 로 ConfigMap delta-1 을 만드세요 — 소유자 참조에 실제 uid 와 함께 blockOwnerDeletion: true 를 넣고, 이 ConfigMap 자신에게 파이널라이저 own.labhub.io/hold 를 답니다. 그다음 kubectl -n op-own delete site delta --cascade=foreground --wait=false 를 실행하고, 잠시 뒤 kubectl -n op-own get site delta -o json 을 /root/op-ownership/foreground-stuck.json 에 저장하세요. 이 Site 는 이 실습이 끝날 때까지 그대로 둡니다.
7. /root/op-ownership/site-epsilon.yaml 로 Site epsilon(region eu-west)을 파이널라이저 own.labhub.io/drain 과 함께 만들고, 자식 ConfigMap epsilon-data 를 실제 uid 로 걸어 /root/op-ownership/epsilon-child.yaml 로 적용하세요. kubectl -n op-own delete site epsilon --wait=false 를 실행한 직후의 오브젝트를 /root/op-ownership/finalizer-pending.json 에 저장하고, 정리 작업(자식 ConfigMap 을 직접 지우기)을 한 뒤 무엇을 정리했는지 /root/op-ownership/epsilon-cleanup.txt 에 한 줄 이상 적으세요. 마지막으로 파이널라이저를 떼어 Site 가 실제로 사라지게 하세요.
8. /root/op-ownership/stuck-report.sh 를 만드세요 — op-own 에서 (1) deletionTimestamp 가 찍힌 Site 를 STUCK Site/<이름> finalizers=<쉼표로 이은 목록> 으로, (2) blockOwnerDeletion 이 참인 소유자 참조를 가진 ConfigMap 을 BLOCKER ConfigMap/<이름> owner=<종류>/<이름> finalizers=<목록> 으로 찍고, 두 종류를 합쳐 정렬해 표준출력에만 내보냅니다. STUCK 줄이 하나라도 있으면 0 이 아닌 코드로, 없으면 NONE 을 찍고 0 으로 끝나야 합니다. 출력을 /root/op-ownership/stuck-report.txt 에 저장하고, 누가 무엇을 왜 막고 있는지 /root/op-ownership/diagnosis.txt 에 사람이 읽을 문장으로 적으세요(막고 있는 자식의 이름과 그 파이널라이저 이름이 반드시 들어가야 합니다).
참고
- 소유자 참조의 필수 네 필드는
apiVersion·kind·name·uid입니다. --cascade는background(기본)·foreground·orphan세 가지입니다.kubectl delete … --wait=false를 쓰면 멈춘 상태를 그대로 관찰할 수 있습니다.- 파이널라이저 목록에서 하나를 빼려면
kubectl patch --type=json의 remove 연산이 편합니다. - 흔한 실수: 부모를 다시 만든 뒤 옛 uid 를 그대로 써서 자식이 곧바로 지워진다.
- 흔한 실수: Terminating 이 안 풀린다고 파이널라이저부터 떼어 정리를 건너뛴다.
- 참고: https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/
- 참고: https://kubernetes.io/docs/concepts/architecture/garbage-collection/
- 참고: https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/
단계 8개
- 부모가 될 타입을 만든다
- uid 를 틀리면 가비지 컬렉터가 자식을 지운다
- 네임스페이스를 넘는 소유는 없다
- background 는 부모부터 지운다
- orphan 은 자식을 남기고 끈만 끊는다
- foreground 는 자식을 기다리다 멈춘다
- 파이널라이저를 떼기 전까지 삭제는 끝나지 않는다
- Terminating 에 멈춘 것을 진단한다