Skip to content

feat: Cueue 프로젝트 권한·프로비저닝 라벨 추가 (AIP-3419) - #125

Merged
juu-n-oo merged 2 commits into
developfrom
feat/AIP-3419
Oct 8, 2026
Merged

juu-n-oo merged 2 commits into
developfrom
feat/AIP-3419

Conversation

@juu-n-oo

@juu-n-oo juu-n-oo commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

AIPub 5.2 코스터 신버전에서 SchedulingQueue 가 삭제되고 Cueue(cueues.coaster.ten1010.io, Cluster scope)가 그 자리를 대신한다. Cueue 는 프로젝트마다 하나씩 프로젝트와 같은 이름으로 Coaster 가 자동 생성하는데, 그 전제 조건(네임스페이스 라벨·어노테이션, 노드 라벨)을 project-controller 가 만들어 줘야 한다. 프로젝트 역할별 Cueue 접근 권한도 함께 부여한다.

🔍 여기만은 꼭 봐주세요!!

1. list 를 부여하지 않았다 — 문서와 다른 선택이라 리뷰가 필요합니다.

Confluence 문서 §9 는 매니저·멤버에게 get/list 를 적어뒀지만 list 는 넣지 않았습니다.

  • list 는 컬렉션 요청이라 resourceNames 로 좁혀지지 않습니다. 규칙에 넣어도 매칭되지 않아 거부되고, 동작시키려면 resourceNames 를 떼야 하는데 Cueue 가 Cluster scope 라 전체 Cueue(= 전체 프로젝트 목록)가 모든 멤버에게 노출됩니다.
  • 프론트는 k8s LIST 대신 UserAuthorityReview 권한 분석으로 접근 가능한 리소스를 집계해 쓰므로 필요하지도 않습니다.
  • 기존 gpuConfigs·nodeResources 규칙이 get 만 주는 것과 같은 이유입니다.

2. 노드 라벨에 isProjectManaged 게이트를 뒀습니다.

getAllBoundProjects(V1Node) 가 project-managed 라벨을 보지 않아 비관리 노드도 바인딩으로 잡힙니다. reconcileNodeAnnotations 가 바로 그 이유로 bound-projects 어노테이션을 게이트하고 있고, 새 라벨은 같은 성격의 프로젝트 파생 상태라 동작을 맞췄습니다. 접두 제거를 게이트 앞에 둬서 노드가 프로젝트 관리에서 빠질 때 기존 라벨이 회수됩니다.

→ project-managed=false 인데 프로젝트에 바인딩된 노드는 라벨을 받지 못합니다. 의도한 동작이 맞는지 봐주세요.

3. projectNamespaceFilter() 에 annotation 비교를 추가했습니다.

informer resync period 가 0 이라 주기적 재리컨실이 없습니다. 이 비교가 빠지면 누가 node-selector 어노테이션을 지워도 Project ADD/DELETE 나 재기동 전까지 복구되지 않습니다 — 조용히 깨지는 경로라 테스트로 고정했습니다.

4. Cueue DTO 를 만들지 않았습니다 — 초판에서 걷어냈습니다.

이 티켓의 요구사항은 프로비저닝 전제 조건(라벨·어노테이션) 리컨실과 프로젝트 역할별 RBAC 둘뿐이고, 두 가지 모두 Cueue 객체 모델을 쓰지 않습니다 — RBAC 룰은 apiGroup·resource 를 문자열로 받고(CUEUE_RESOURCE_PLURAL 하나면 끝), 라벨·어노테이션 리컨실이 만지는 객체는 V1Node·V1Namespace 뿐입니다.

초판에 V1alpha1Cueue 외 nested 5종을 추가했었는데 src/main·src/test 통틀어 참조처가 0건이었습니다. 인포머·K8sApiProvider 에도 등록하지 않아 역직렬화될 경로 자체가 없어, 컴파일만 되는 dead code 였습니다. 미사용 상수 CUEUE_RESOURCE_KIND 와 함께 제거했습니다 (8173f5e).

필요해지는 시점은 Cueue validating webhook 을 넣기로 결정할 때입니다(아래 후속 작업 참조 — 현 시점에는 불필요로 결론). CRD 스키마가 kubernetes/examples/crd.yaml 과 Confluence §1 에 남아 있어 복원 비용은 낮습니다.

Changes

RBAC (ReconciliationService)

  • ProjectApiConstants 에 CUEUE_RESOURCE_PLURAL
  • cueuesApiRule 을 gpuConfigsApiRule 뒤에 추가. 매니저 get/patch/update, 멤버 get, resourceNames = 프로젝트 이름

프로비저닝 전제 조건

  • 노드: project-name.aipub.ten1010.io/<proj>: "" — reconcileNodeLabels 가 boundProjects 를 받도록 시그니처 변경
  • 네임스페이스: label cueue.coaster.ten1010.io/provisioning-enabled: "true" + annotation cueue.coaster.ten1010.io/node-selector. reconcileNamespaceAnnotations 신설 후 NamespaceReconciler 의 생성·갱신 양쪽 경로에 배선
  • OnUpdateFilterFactory.projectNamespaceFilter() 에 annotation 비교 추가

기타

  • kubernetes/examples/crd.yaml 에 Cueue CRD append
  • 테스트 24건 신규 (Cueue 룰 4 / 프로비저닝 14 / watch 필터 6)

범위 밖

cueue.coaster.ten1010.io/auto-enqueue 라벨은 이 PR 범위가 아닙니다. 프로젝트별 on/off 운영 설정이라 Project 에서 파생할 수 있는 값이 아니고, AIPub 어드민이 직접 설정합니다(aipub-admin 이 cluster-admin 바인딩이라 별도 권한 작업 불필요). 컨트롤러가 매 주기 "true" 를 심으면 끈 설정이 되살아나므로 읽지도 쓰지도 않으며, 외부가 붙인 값이 보존되는지만 테스트로 고정했습니다.

Helm 차트 RBAC 와 어드민 ClusterRole 도 변경이 없습니다 — 컨트롤러 SA 는 이미 */*/*, 어드민은 cluster-admin 바인딩입니다.

하위 호환성 · 배포 영향

  • 배포 직후 기존 프로젝트 ClusterRole 이 1회 UPDATE 됩니다. ClusterRoleReconciler 가 룰 리스트를 List.equals(순서 비교)로 판정하는데 규칙을 중간에 삽입했기 때문입니다. 의도된 1회성이며 이후 멱등입니다.
  • 노드 watch 필터를 10곳 이상 컨트롤러가 공유해 배포 직후 1회 리컨실 버스트가 있습니다(전부 no-op).
  • aipub-web 변경 불필요 — 프론트는 UserAuthorityReview + 이름별 GET 폴백 구조입니다.

새로 생긴 제약

project-name.aipub.ten1010.io/ 접두로 시작하는 키를 NodeGroup 의 spec.nodeSelector 에 쓰면 안 됩니다. getBoundNodeGroupsByNodeSelector 가 부분집합 판정이라, 프로젝트 A 의 키를 selector 로 가진 NodeGroup 을 프로젝트 B 에 바인딩하면 A 의 노드가 B 에도 전이 바인딩돼 테넌트 경계가 넓어집니다. 현재 이 키를 쓰는 NodeGroup 은 없고(워크스페이스 전체 grep 0건), 상수 Javadoc 에 금지를 명시했습니다.

Test plan

자동 — 완료

  • ./gradlew compileJava compileTestJava test → 275 tests / 0 failures
  • 멱등성 — 같은 입력 2회 리컨실 시 UPDATE 없음 (노드 라벨 / 네임스페이스 라벨 / 어노테이션 / RBAC)
  • 바인딩 해제 시 project-name.* 라벨 제거
  • isProjectManaged 게이트 — 비관리 노드에 라벨이 붙지 않고, 관리에서 빠지면 회수된다
  • 외부가 붙인 auto-enqueue 및 기타 모르는 라벨·어노테이션 보존
  • RBAC — 매니저 get/patch/update, 멤버 get, 양쪽 다 list 없음
  • 네임스페이스 어노테이션 생성·제거 (project == null 시 제거)
  • watch 필터가 annotation 변경을 통과시키고 무변경은 차단
  • crd.yaml multi-doc 파싱

사람이 클러스터에서 — c10 실측 완료 (2026-10-08, project-controller:1.10.0-31)

  • kubectl auth can-i get cueues.coaster.ten1010.io/<proj> --as=oidc:<매니저> → yes
  • patch / update (매니저) → yes · list / delete (매니저) → no
  • get (멤버) → yes · list / patch / update (멤버) → no
  • 다른 프로젝트의 Cueue 접근 → Forbidden (resourceNames 정상 동작)
  • 전 프로젝트 77개 × 2역할 = 154개 ClusterRole 스윕 — 규칙 누락 0건, verb 불일치 0건
  • 프로젝트 네임스페이스에 provisioning-enabled 라벨 / node-selector 어노테이션 부착
  • 바인딩된 노드에 project-name.aipub.ten1010.io/<proj> 라벨 부착 (노드당 40~50개 프로젝트, project-managed=true 게이트 정상)
  • Coaster 가 프로젝트와 같은 이름의 Cueue 를 생성 — 77개
  • 어노테이션 → spec.nodeSelector 변환 확인 — 맨 라벨 키가 matchExpressions: [{key: …, operator: Exists}] 로 들어옴
  • status.nodes 가 실제로 채워짐 — cueue.coaster.ten1010.io/managed=true 인 vnode1.pnode12 에 바인딩된 큐(abc·hjw·say·vision-group·yshee·monitoring-test) 전부 ["vnode1.pnode12…"], observedGeneration == generation
  • 배포 직후 ClusterRole 1회 UPDATE 후 추가 갱신이 돌지 않는지 (컨트롤러 로그 확인 — 미검증)

⚠ cueues 는 Cluster scope 라 auth can-i 에 네임스페이스 한정 형식을 쓰면 경고가 뜹니다. cueues.coaster.ten1010.io/<프로젝트명> 처럼 이름을 지목해야 합니다.

ℹ status.nodes 가 비려면 바인딩 노드에 cueue.coaster.ten1010.io/managed=true 가 없을 때입니다(예: example-project). 구현 문제가 아니라 노드 라벨 전제가 AND 이기 때문입니다 — 아래 문서 보강 항목 참조.

후속 작업 (이 PR 범위 밖 — 별도 티켓 필요)

  • 매니저의 spec.nodes 쓰기 — 현 시점 조치 불필요로 결론 (2026-10-08). RBAC 은 서브필드로 좁힐 수 없어 매니저는 자기 Cueue 의 spec.nodes 에 임의 노드 이름을 넣을 수 있습니다(c10 server dry-run 으로 확인 — 남의 프로젝트 Cueue 접근은 Forbidden). 그럼에도 조치하지 않는 근거:
    • 문서 §9 가 매니저에게 patch/update 를 조건 없이 부여하고, §7 이 FE 가 사용자 권한으로 spec.priority·spec.pods 를 직접 패치하는 모델을 전제합니다. §3 필드표의 spec.nodes "AIPub에서는 사용하지 않음"(쓰는 주체 -)은 활용하지 않는다는 사실 서술이지 쓰기 금지 요구가 아닙니다.
    • 효과가 없습니다 — c10 의 coaster/coaster-scheduler-installer 가 interQueuePolicy: FirstScheduling 이고(77개 Cueue 전부 BlockedByHigherCueue=False / reason=InterQueuePolicyFirstScheduling), 이 모드에서는 상위 큐가 하위 큐·미소속 Pod 을 막지 않습니다. managed=true 노드도 현재 vnode1.pnode12 하나뿐입니다.
    • 고칠 자리가 여기가 아닙니다. 문서 §4 는 "프로젝트에 속한 노드 중에서 Cueue Managed 로 지정된 노드만" 이라 하지만 §1 CRD 의 status.nodes 계산에는 프로젝트 소속 필터가 없습니다((spec.nodes ∪ selector) ∩ managed=true). 바로잡는다면 Coaster 의 계산 쪽이 자연스럽습니다 — project-controller 에 validating webhook 을 다는 건 남의 CR 계약을 대신 지키는 꼴입니다.
    • 재검토 트리거: ① interQueuePolicy 를 FirstAllocating 으로 전환 ② Cueue Managed 노드 증가. 넣게 되면 그때 위에서 걷어낸 Cueue DTO 가 필요해집니다.
  • Confluence §4·§6 에 노드 라벨 전제가 AND 라는 점 명시 — Cueue 영향권에 들려면 cueue.coaster.ten1010.io/managed=true 외에 project.aipub.ten1010.io/project-managed=true 도 필요합니다(후자가 없으면 project-controller 가 project-name.* 라벨을 아예 붙이지 않고, 컨트롤러가 기본값을 false 로 심으므로 기본 상태가 off). §4 는 "프로젝트에 속한 노드 중에서 Cueue Managed 로 지정된 노드" 라고만 해서 첫 조건이 드러나지 않습니다. 실제로 example-project 의 status.nodes 가 [] 인 원인입니다. (@김지헌)
  • 노드 라벨 키 형태 — 실연동으로 확인 완료 — Confluence §5 가 project-name.aipub.ten1010.io/<proj> 를 "예시"라고 명시했고 키 형태는 project-controller 가 정하도록 돼 있어 그대로 썼습니다. c10 에서 Coaster 가 맨 라벨 키를 matchExpressions: [{key: project-name.aipub.ten1010.io/<proj>, operator: Exists}] 로 정상 변환했고, 그 결과로 status.nodes 까지 채워지는 것을 확인했습니다.
  • FE 의 Cueue 권한 집계 동작 확인 — list 를 주지 않은 전제가 "프론트는 UserAuthorityReview 로 집계한다" 입니다. Prioritization 화면에서 매니저/멤버가 자기 프로젝트 큐를 실제로 보고 수정할 수 있는지 확인이 필요합니다. list 미부여(현행 get by name)로 확정했으므로(2026-10-08), 안 되면 설계를 다시 열어야 합니다. (@권현경)
  • auto-enqueue 토글을 프로젝트 매니저에게 열어줄지 결정 — 현재는 어드민 전용으로 확정됐습니다. 매니저에게 열려면 네임스페이스 patch 가 필요한데, 그대로 열면 project.aipub.ten1010.io/project·allowlist 라벨까지 바꿀 수 있어 위험합니다. 연다면 경로(백엔드가 자기 SA 로 중계 등)를 따로 설계해야 합니다.
  • 기존 Prioritization 화면의 General Queue 정리 — 문서에 따르면 schedulerName === 'coaster' 인 Pending Pod 을 모으는 조건으로는 더 이상 아무것도 잡히지 않습니다. 대기 Pod 은 Cueue 의 status.pods 에서 가져와야 합니다. FE 작업이라 이 PR 밖입니다. (AIP-3242 에서 다뤄지는지 확인 필요)

🚀 Release Version

v5.2.0

@juu-n-oo juu-n-oo changed the title feat: Cueue CRD 모델·프로젝트 권한·프로비저닝 라벨 추가 (AIP-3419) feat: Cueue 프로젝트 권한·프로비저닝 라벨 추가 (AIP-3419) Oct 8, 2026
Coaster 5.2 에서 SchedulingQueue 가 Cueue 로 대체되면서, Coaster 가 프로젝트별
Cueue 를 생성할 전제 조건을 project-controller 가 제공해야 한다.

- Cueue DTO 6개. nodeSelector/conditions 는 V1LabelSelector/V1Condition 재사용.
  이 컨트롤러는 Cueue 를 읽거나 리컨실하지 않으므로 인포머에 등록하지 않는다
- 프로젝트 역할별 cueues 규칙 — 매니저 get/patch/update, 멤버 get.
  list 는 Cluster scope 라 resourceNames 로 좁혀지지 않아 부여하지 않는다
  (주면 전체 프로젝트 목록이 노출된다. 프론트는 UserAuthorityReview 로 집계한다)
- 노드에 project-name.aipub.ten1010.io/<proj> 라벨.
  reconcileNodeAnnotations 와 같은 성격의 파생 상태라 isProjectManaged 게이트를 맞췄다
- 네임스페이스에 provisioning-enabled 라벨 + node-selector 어노테이션.
  NamespaceReconciler 에 annotation 리컨실 경로를 신설했다
- projectNamespaceFilter 에 annotation 비교 추가 — informer resync period 가 0 이라
  빠지면 어노테이션이 훼손돼도 재기동 전까지 복구되지 않는다

auto-enqueue 라벨은 어드민이 직접 설정하므로 읽지도 쓰지도 않고, 외부가 붙인 값이
보존되는지만 테스트로 고정했다.
이 티켓의 요구사항은 프로비저닝 전제 조건(노드·네임스페이스 라벨/어노테이션)
리컨실과 프로젝트 역할별 Cueue RBAC 둘뿐이다. 두 가지 모두 Cueue 객체 모델을
쓰지 않는다 — RBAC 룰은 apiGroup·resource 를 문자열로 받고, 라벨·어노테이션
리컨실이 만지는 객체는 V1Node 와 V1Namespace 뿐이다.

초판에 추가한 V1alpha1Cueue 외 nested 5종은 src/main·src/test 통틀어 참조처가
0건이었다. 인포머·K8sApiProvider 에도 등록하지 않아 역직렬화될 경로 자체가
없으므로 컴파일만 되는 dead code 였다. 미사용 상수 CUEUE_RESOURCE_KIND 도 함께
제거한다(RBAC 이 쓰는 CUEUE_RESOURCE_PLURAL 은 유지).

필요해지는 시점은 Cueue validating webhook 을 넣기로 결정할 때다. CRD 스키마는
kubernetes/examples/crd.yaml 과 Confluence 에 남아 있어 복원 비용이 낮다.

테스트 275건 통과(DTO 에 테스트가 없어 수치 변동 없음).
@juu-n-oo
juu-n-oo merged commit 9955b95 into develop Oct 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant