CNPE — 클라우드 네이티브 플랫폼 엔지니어 (전문가) · 생성 버튼을 넘어 변경과 회수까지 · 실습
요청은 성공했는데 앱은 왜 준비되지 않을까
목표
실제 Crossplane 2.4에서 네임스페이스형 App을 만들고, 잘못된 준비 조건·팀 권한·변경 수렴·삭제 지연을 검증합니다.
선언이나 상태 표시 하나만으로 성공을 추정하지 않고 실제 파드와 HTTP 및 소유 UID를 함께 확인합니다.
왜 중요한가
셀프서비스는 생성 버튼만 자동화하는 작업이 아닙니다. 요청의 입력·권한을 제한하고 생성된 앱을 관측하며,
변경과 삭제까지 책임져야 합니다. 같은 이름의 새 객체나 다른 팀의 응답이 이번 요청의 성공으로 섞이면 안 됩니다.
개인 VM 안에서 실행되는 k3s·Crossplane·함수·실제 컨테이너를 사용합니다. KWOK나 가짜 Ready 파드가 아닙니다.
설치에는 수분이 걸릴 수 있습니다. 예상 실습 시간은 55분이며, 더 필요하면 만료 전에 시간을 연장하세요.
세션 종료 시 VM과 파일이 회수됩니다. 필요한 기록은 먼저 내려받고 VM 밖 운영 클러스터에는 적용하지 마세요.
준비된 것과 도구
- API: platform.labhub.local/v1alpha1의 App. 자기 팀은 team-a, 요청 이름은 parcel입니다.
- 허용 입력: replicas는 1~3, channel은 stable·preview. 개발자 계정은 system:serviceaccount:team-a:developer입니다.
- Crossplane은 Deployment와 Service를 구성하고, 고정된 Python 앱은 채널과 응답 파드 이름을 반환합니다.
- 상태 없는 짧은 GET 앱은 SIGTERM을 처리하며 종료 유예는 5초입니다. 데이터베이스나 긴 작업에 이 값을 그대로 적용하지 마세요.
- team-b/sentinel은 별도의 정상 Composition을 쓰는 보존 대상입니다. 학습자 구성 수정과 독립적으로 유지합니다.
- 도우미: python3 /opt/fixtures/cnpe_crossplane_lab.py 뒤에 명령과 단계 번호를 붙입니다.
act는 해당 단계의 파일을 적용하거나 명시된 실험을 수행합니다. observe는 한 번 조회합니다.
capture는 현재 설정을 고치지 않고 최대 75초 관측한 뒤 지정한 JSON을 저장합니다.
grade는 파일을 수정하거나 자원을 바꾸지 않습니다. 준비 중인 상태라면 재설치하지 말고 같은 실행을 다시 조회하세요.
기존에 검증된 관측 파일은 뒤 단계에서 같은 상태가 사라져도 보존합니다. 파일을 손으로 성공 JSON으로 고치지 마세요.
단계
1. /root/cnpe-api/initial.json에 apiVersion=platform.labhub.local/v1alpha1, kind=App, metadata.name=parcel, metadata.namespace=team-a, spec.replicas=1, spec.channel=stable을 작성하세요. act 1은 developer 계정으로 이 요청을 적용합니다. capture 1로 accepted.json을 저장하여 Synced=True지만 Ready=False인 요청과 실제 HTTP stable 응답을 함께 확인하세요.
2. /opt/fixtures/cnpe-crossplane-composition.json을 /root/cnpe-api/composition.json으로 복사하세요. spec.pipeline[0].input.resources[0].readinessChecks[0].matchCondition.type의 Ready만 Available로 고칩니다. 나머지 템플릿·선택자·이미지·권한은 유지합니다. act 2로 적용하고 capture 2로 ready.json에 실제 준비 완료를 기록하세요.
3. team-a의 developer가 자기 팀 App은 만들 수 있지만 다른 팀 App과 직접 Deployment는 만들 수 없는지 조회하세요. act 3으로 실제 두 권한 거절과 replicas=4 스키마 거절을 시험합니다. capture 3으로 boundaries.json을 저장합니다. 관리자 성공이나 실패한 권한 조회를 거절의 증거로 쓰지 않습니다.
4. /root/cnpe-api/update.json에 같은 App·이름·팀으로 replicas=2, channel=preview 요청을 작성하세요. act 4로 적용하고 capture 4로 updated.json을 저장합니다. 새 요청을 만들지 않고 같은 UID를 유지하며, 현재 세대·갱신 복제수·가용 수·파드 소유 관계·실제 preview 응답을 확인하세요.
5. act 5로 하위 Deployment만 3복제로 바꾸는 실험을 수행하세요. 이 도우미는 실제 패치 응답의 복제수·세대·UID를 보관합니다. App 요청은 2를 유지합니다. capture 5로 reconciled.json에 그보다 뒤의 세대에서 2로 복귀한 증거를 저장하세요. 최종 숫자 2만 보고 실험했다고 판단하지 않습니다.
6. act 6으로 교육용 finalizer labhub.io/handoff-hold를 넣고 parcel 삭제를 요청하세요. capture 6으로 deleting.json을 저장합니다. deletionTimestamp가 있지만 같은 UID가 여전히 조회되는 상태입니다. 이 표식이 모든 하위 자원의 생존까지 보장한다고 해석하지 않습니다.
7. act 7로 교육용 보류 표식 하나만 제거하세요. 다른 finalizer를 강제로 지우지 않습니다. capture 7로 absent.json을 저장하여 App·Deployment·ReplicaSet·Service·Pod 목록 조회가 모두 성공하고 대상이 사라졌는지 확인합니다. team-b/sentinel은 같은 UID와 실제 HTTP 정상 상태를 유지해야 합니다.
8. /root/cnpe-api/diagnose.py에 diagnose(e)를 작성하세요. 아래 진단 계약에 따라 문자열 하나를 반환합니다. 필수 필드 누락·bool 대신 숫자나 문자열·관측 실패·상태 모순을 unknown으로 남기세요. 이 단계는 이전 자원을 재생성하지 않는 독립적인 코드 과제입니다.
8단계 진단 계약
입력 e에는 observed·accepted·synced·ready·deleting·absent 여섯 필드가 모두 있어야 하며 값은 실제 bool입니다.
이 함수는 이미 대조된 현재 상태 요약을 입력받습니다. accepted는 현재 요청 객체가 존재한다는 뜻입니다.
1. 타입·필수 필드 오류 또는 observed=False이면 unknown입니다.
2. absent=True이면 accepted·synced·ready·deleting이 모두 False일 때만 absent, 아니면 unknown입니다.
3. synced=True인데 accepted=False이거나 ready=True인데 synced=False이면 unknown입니다.
4. 앞 모순이 없고 deleting=True이면 accepted=True일 때 deleting, 아니면 unknown입니다.
5. 나머지는 ready=True이면 ready, 다음 synced=True이면 synced, 다음 accepted=True이면 accepted, 모두 False이면 not_accepted입니다.
absent는 정상 조회로 확인한 부재일 뿐, 과거 요청의 성공적인 회수 이력까지 뜻하지 않습니다. 회수는 7단계에서 별도 확인합니다.
참고
App→Deployment→ReplicaSet→Pod 소유 관계와 현재 응답 파드를 대조합니다. 이름만 같은 다른 객체의 기록은 거절합니다.
관측 파일은 같은 실습 실행의 과거 상태를 학습하기 위한 자료이며 암호학적 원격 증명이나 부정행위 방지 장치는 아닙니다.
2단계 이후의 채점은 현재 Composition을, 3단계는 현재 XRD와 권한도 확인합니다. 4·5단계는 삭제 전이면 현재 앱도 확인합니다.
삭제한 뒤에는 7단계의 실제 회수 증거가 있어야 과거 기록을 재검사할 수 있습니다. 단계 준비는 기존 파일과 후속 진행을
덮어쓰지 않고, 필요한 앞 단계만 채웁니다. 8단계는 클러스터 상태를 되돌리지 않습니다.
교육용 finalizer 하나가 모든 하위 자원을 보존하는 것은 아닙니다. 네임스페이스 권한 분리는 네트워크 정책·쿼터 전체의 증명이 아닙니다.
공식 문서: [Composition](https://docs.crossplane.io/v2.4/get-started/get-started-with-composition/) ·
[Finalizers](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/).
단계 8개
- 요청을 만들고 준비 실패 관측
- 앱에 맞는 준비 조건 복구
- 제한된 계정의 실제 거절 시험
- 같은 요청을 새 채널로 변경
- 하위 자원 변경이 되돌아오는 이유
- 삭제 요청과 실제 부재 구분
- 내 자원만 회수됐는지 확인
- 모르는 상태를 남기는 진단기