CRD 와 오퍼레이터 · 옛 버전을 지웠더니 오브젝트가 사라졌다 · 실습
옛 버전을 지웠더니 오브젝트가 사라졌다 — 저장 버전 이전 절차
목표
두 버전 CRD 를 만들어 저장 버전을 올리고, 모든 오브젝트를 다시 쓴 뒤 status.storedVersions 를 정리하고 옛 버전을 내리는 이전 절차를 처음부터 끝까지 걸어 본다. 중간에 API 서버가 어디서 막아 주는지도 직접 본다.
왜 중요한가
CRD 는 코드가 아니라 API 계약이다. 필드를 하나 바꾸려면 새 버전을 내고, 옛 버전을 쓰던 사람들이 옮겨 갈 시간을 준 뒤에야 지울 수 있다. 이때 사람들이 가장 자주 빠뜨리는 것이 이미 저장된 오브젝트를 다시 쓰는 일이다. 저장 버전을 바꾸는 것은 '앞으로 저장할 표현' 만 바꾸는 일이라, 이미 etcd 에 들어 있는 바이트는 그대로 옛 표현이다. 그 상태에서 옛 스키마를 지우면 저장된 오브젝트를 읽을 방법이 없어진다. status.storedVersions 는 바로 그 사고를 막으라고 있는 기록이고, API 서버는 이 목록에 남아 있는 버전을 빼려는 시도를 실제로 거절한다. 이 실습은 그 안전장치를 한 번 부딪혀 본 다음, 올바른 순서로 통과한다.
단계
1. /root/crd-version/tunnel-crd.yaml 에 CRD tunnels.net.labhub.io 를 쓰세요 — 그룹 net.labhub.io, 종류 Tunnel, 복수형 tunnels, 버전 두 개입니다. v1alpha1 은 served: true·storage: true 이고 deprecated: true 와 deprecationWarning 을 붙이며 스키마에 spec.endpoint(string)·spec.port(integer) 만 둡니다. v1beta1 은 served: true·storage: false 이고 두 필드에 더해 spec.mtu(integer, default: 1400)를 둡니다. 적용한 뒤 두 버전의 상태를 /root/crd-version/versions-initial.txt 에 <버전> served=<참거짓> storage=<참거짓> 두 줄로 저장하세요.
2. 네임스페이스 crd-version 를 만들고 /root/crd-version/tunnel-east.yaml(이름 t-east, endpoint 10.30.0.11, port 4789)과 /root/crd-version/tunnel-west.yaml(이름 t-west, endpoint 10.30.0.12, port 4789)을 v1alpha1 로 적용하세요. 적용할 때 돌아오는 경고를 표준 오류까지 받아 /root/crd-version/deprecation-warning.txt 에 저장하고, 그 시점의 status.storedVersions 를 /root/crd-version/stored-initial.txt 에 한 줄로 저장하세요.
3. 같은 오브젝트를 새 버전 창으로 읽으세요 — kubectl -n crd-version get tunnels.v1beta1.net.labhub.io t-east -o yaml 의 출력을 /root/crd-version/as-v1beta1.yaml 에 저장합니다. 그리고 /root/crd-version/conversion-note.txt 에 두 줄을 적으세요 — apiVersion=<읽은 apiVersion> 과 mtu=<spec.mtu 값, 없으면 none>.
4. /root/crd-version/tunnel-crd-beta-storage.yaml 에 1단계 CRD 를 복사하되 v1alpha1 의 storage 를 거짓으로, v1beta1 의 storage 를 참으로 바꿔 적용하세요. 적용한 뒤 status.storedVersions 를 /root/crd-version/stored-after-flip.txt 에 한 줄로 저장하세요.
5. 저장 버전을 실제로 옮기세요 — crd-version 의 모든 Tunnel 을 v1beta1 로 읽어 그대로 다시 쓰면 됩니다(kubectl get … -o json | kubectl replace -f -). 두 오브젝트를 모두 다시 쓴 뒤 kubectl -n crd-version get tunnels.v1beta1.net.labhub.io -o yaml 출력을 /root/crd-version/after-rewrite.yaml 에 저장하세요.
6. 옛 버전을 spec.versions 에서 빼 보세요 — kubectl patch crd tunnels.net.labhub.io --type=json -p '[{"op":"remove","path":"/spec/versions/0"}]'. 명령의 출력과 종료 코드를 /root/crd-version/remove-blocked.txt 에 모으세요 — 첫 줄은 remove-rc=<종료 코드> 이고 그 아래에 서버가 낸 문장을 그대로 붙입니다.
7. 이제 모든 오브젝트가 v1beta1 로 저장돼 있으니 기록을 정리하세요 — kubectl patch crd tunnels.net.labhub.io --subresource=status --type=merge 로 status.storedVersions 를 ["v1beta1"] 하나로 만듭니다. 정리한 뒤의 값을 /root/crd-version/stored-pruned.txt 에 한 줄로 저장하세요.
8. /root/crd-version/tunnel-crd-retire.yaml 에 4단계 CRD 를 복사하되 v1alpha1 의 served 를 거짓으로 바꿔 적용하세요. 그다음 kubectl -n crd-version get tunnels.v1alpha1.net.labhub.io t-east 을 실행해 결과를 /root/crd-version/retired.txt 에 모으세요 — 첫 줄은 get-rc=<종료 코드> 이고 그 아래에 서버 문장을 붙입니다. 마지막으로 /root/crd-version/version-check.sh 를 만드세요 — 이 CRD 의 served·storage·stored 를 각각 한 줄씩 <이름>=<값> 으로 찍고, storedVersions 에 저장 버전이 아닌 값이 있으면 PENDING <버전> 을 찍고 0 이 아닌 코드로, 없으면 MIGRATED 를 찍고 0 으로 끝나야 합니다. 그 출력을 /root/crd-version/version-check.txt 에 저장하세요.
참고
served는 그 버전으로 요청을 받을지,storage는 그 버전으로 저장할지입니다. storage 는 정확히 하나입니다.- 특정 버전으로 조회하려면
kubectl get <복수형>.<버전>.<그룹> <이름>형태를 씁니다. status.storedVersions는 status 창에 있어kubectl patch … --subresource=status로 고칩니다.- 이 환경에는 웹훅 서버를 띄울 수단이 없어
conversion.strategy는 None 까지만 다룹니다. - 흔한 실수: 저장 버전만 바꾸고 기존 오브젝트를 다시 쓰지 않는다.
- 흔한 실수: storedVersions 를 먼저 손으로 지워 안전장치를 무력화한다.
- 참고: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/
- 참고: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
단계 8개
- 한 타입에 두 버전을 함께 제공한다
- 폐기된 버전으로 만들면 경고가 돌아온다
- 새 버전으로 읽으면 무엇이 달라지나
- 저장 버전을 올린다
- 모든 오브젝트를 한 번씩 다시 쓴다
- 아직 지울 수 없다고 API 서버가 막는다
- 기록을 정리해야 문이 열린다
- 옛 창을 닫고 이전 완료를 증명한다