CNPE — 클라우드 네이티브 플랫폼 엔지니어 (전문가) · 생성 버튼을 넘어 변경과 회수까지 · 이론
요청이 저장됐는데 앱은 왜 준비되지 않았을까
한 줄 요약
셀프서비스 API는 설치 명령을 숨기는 버튼이 아니라, 사용자의 요청을 실제 자원의 상태로 계속 연결하는 계약입니다. 요청 수락, 동기화, 준비 완료는 서로 다른 질문의 답입니다.
왜 이게 필요했나
개발자가 “테스트용 앱 하나 주세요”라고 요청할 때마다 플랫폼 팀이 Deployment와 Service를 손으로 만들면, 전달 과정에 누락이 생깁니다. 어떤 앱은 서비스가 없고 어떤 앱은 복제수만 다릅니다. 이를 해결하려고 템플릿을 공유해도 복사한 뒤의 변경과 삭제는 다시 각자의 책임이 됩니다. 셀프서비스는 반복 요청을 간단한 API로 만들고, 그 뒤의 자원을 컨트롤러가 지속적으로 관리하도록 하는 접근입니다.
그러나 API가 간단해졌다고 운영 책임이 사라지지는 않습니다. HTTP 요청이 성공했다는 이유로 사용자에게 “앱이 준비됐습니다”라고 알렸는데 컨테이너 이미지는 아직 내려받는 중일 수 있습니다. API 서버는 선언을 저장했고, 컨트롤러는 그 선언을 처리하고 있으며, 앱은 자기 준비 절차를 수행합니다. 세 작업의 담당자가 다르기 때문에 완료 시점도 다릅니다.
어떻게 동작하나
이번 실습의 App은 replicas와 channel 두 입력만 받습니다. XRD는 이 API의 이름·범위·입력 형식을 정의합니다. replicas는 1부터 3까지이고 channel은 stable 또는 preview입니다. 이는 사람에게 보여 주는 도움말만이 아닙니다. 상한을 넘는 요청은 API 서버가 거절해야 하고, 거절한 객체는 저장되지 않아야 합니다. 개발자 계정은 자신의 네임스페이스에서 App을 다루지만 하위 Deployment를 직접 만들 권한은 받지 않습니다.
Composition은 App을 어떤 실제 자원으로 구성할지 정합니다. 이번에는 함수가 Deployment와 Service를 만들고, 요청 이름을 자원 이름·선택자에 연결합니다. 요청의 복제수는 Deployment로, 채널은 컨테이너 환경변수로 전달됩니다. Service가 다른 앱의 라벨을 선택한다면 HTTP가 성공하더라도 내 요청이 준비됐다는 증거가 아닙니다. 그래서 이름뿐 아니라 소유 UID와 파드의 소유 계보를 대조합니다.
| 관측 | 확인한 사실 | 아직 확인하지 못한 사실 |
| --- | --- | --- |
| App 생성 성공 | API가 요청을 받아 저장함 | 컨테이너가 시작했는가 |
| Synced=True | 컨트롤러가 요청을 처리한 상태 | 준비 조건과 실제 앱이 정상인가 |
| Ready=True | 구성에 정의한 준비 조건을 충족함 | 그 조건이 업무의 성공을 충분히 표현하는가 |
| 해당 파드의 HTTP 응답 | 실제 코드가 기대한 채널로 응답함 | 다음 변경이나 삭제도 정상적으로 처리되는가 |
Ready의 의미는 준비 조건을 어떻게 작성했는지에 달려 있습니다. Deployment는 일반적으로 Available 조건을 제공하지만, 실습의 잘못된 Composition은 Ready 조건을 찾습니다. 컨테이너와 HTTP는 정상이어도 찾는 조건이 없으니 상위 App은 준비되지 않았다고 보고합니다. 이때 준비 검사를 없애면 표시만 초록색으로 만들 수는 있습니다. 하지만 다음에는 실제로 준비되지 않은 앱까지 성공으로 표시할 수 있어 문제를 해결한 것이 아닙니다.
현장에서 만나는 모습
입력의 유효성과 권한도 서로 다릅니다. replicas=4를 자기 팀에 만드는 요청은 스키마를 위반합니다. 반대로 유효한 replicas=1이라도 다른 팀에 만드는 요청은 권한을 위반합니다. 두 실패를 모두 “요청 오류”라고 합치면 사용자는 값을 바꿔야 하는지, 대상 팀을 고쳐야 하는지 알 수 없습니다. 플랫폼 엔지니어는 실제 응답과 저장 여부를 확인하고 원인을 분리해 설명해야 합니다.
팀 경계 시험에서는 관리자가 아니라 제한된 서비스 계정으로 요청합니다. 관리자의 성공을 보고 사용자도 사용할 수 있다고 판단하면 권한 누락을 놓칩니다. 반대로 권한 조회가 실패했을 때 결과를 자동으로 no로 바꾸면, 테스트 도구가 고장 난 것을 격리 성공으로 착각합니다. 관측 실패는 별도로 남겨야 합니다.
다음 읽기에서 확인할 것
생성 이후의 변경과 삭제에서도 같은 원칙이 필요합니다. 다음 읽기에서는 관측 세대·소유 UID·finalizer를 연결하고, 이어지는 실습에서 실제 App 요청 파일과 해당 파드의 HTTP를 대조합니다. 선언 파일만 제출하지 않고 현재 자원과 소유 관계를 기록하는 이유를 먼저 정리하세요.
공식 문서
- [Crossplane Composition 시작하기](https://docs.crossplane.io/v2.4/get-started/get-started-with-composition/)
- [함수의 Patch and Transform 준비 검사](https://docs.crossplane.io/v2.4/guides/function-patch-and-transform/)
- [Kubernetes RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)