LabHub

CBA — Backstage 인증 어소시에이트 · 소프트웨어 카탈로그 · 이론

엔티티와 관계 — 소유권이 카탈로그의 심장인 이유

LabHub 에서 이어서 보기

한 줄 요약

카탈로그는 서비스 목록이 아니라 관계 그래프 입니다. 엔티티 종류를 외우는 것보다, 각 종류가 어떤 질문에 답하기 위해 존재하는지를 이해하는 편이 시험과 실무 모두에 유용합니다.

왜 이게 필요했나

"서비스 목록"만 있으면 이런 질문에 답할 수 없습니다.

그래서 Backstage 는 엔티티를 여러 종류로 나누고 그 사이의 관계를 저장합니다.

어떻게 동작하나

엔티티 여덟 종류

| kind | 답하는 질문 | 대표 필드 |
| --- | --- | --- |
| Component | 우리가 만들고 배포하는 소프트웨어 조각 | spec.type(service/website/library), spec.lifecycle, spec.owner, spec.system |
| API | 컴포넌트가 노출하거나 소비하는 인터페이스 | spec.type(openapi/asyncapi/graphql/grpc), spec.definition |
| Resource | 컴포넌트가 필요로 하는 인프라 | spec.type(database/s3-bucket/queue) |
| System | 함께 동작하는 엔티티들의 묶음 | spec.owner, spec.domain |
| Domain | 여러 시스템을 아우르는 사업 영역 | spec.owner |
| Group | 팀·조직 단위 | spec.type(team), spec.children, spec.profile |
| User | 사람 | spec.memberOf |
| Location | 다른 엔티티가 있는 위치를 가리키는 표지판 | spec.type, spec.targets |

여기에 Template(스캐폴더용)이 더해집니다. 그리고 spec.lifecycle 은 자유 문자열이지만 관례적으로 experimental / production / deprecated 를 씁니다 — 폐기 예정 서비스를 목록에서 걸러 내는 데 쓰입니다.

관계(relations)는 계산되는 것이다

중요한 포인트입니다. catalog-info.yaml 에 쓰는 것은 spec.owner, spec.system, spec.providesApis 같은 선언 이고, 카탈로그가 이를 읽어 양방향 관계 를 계산해 저장합니다.

spec.owner: group:team-checkout      →  ownedBy / ownerOfspec.system: commerce                →  partOf / hasPartspec.providesApis: [checkout-api]    →  providesApi / apiProvidedByspec.consumesApis: [payments-api]    →  consumesApi / apiConsumedByspec.dependsOn: [resource:checkout-db] →  dependsOn / dependencyOf

그래서 providesApis 를 한쪽에만 써도 API 엔티티 페이지에서 "이 API 를 제공하는 컴포넌트"가 보입니다. 반대 방향을 손으로 쓸 필요가 없습니다. 시험에서 "관계를 양쪽 파일에 모두 적어야 하는가?" 를 물으면 답은 아니오입니다.

엔티티 참조 형식

관계를 쓸 때 다른 엔티티를 가리키는 문자열 형식이 정해져 있습니다.

[<kind>:][<namespace>/]<name>

세 부분 중 kind 와 namespace 는 생략 가능하고, 생략하면 문맥에 따른 기본값이 적용됩니다. namespace 의 기본값은 default 입니다.

| 쓴 것 | 해석 |
| --- | --- |
| team-checkout | 문맥의 기본 kind + default 네임스페이스 |
| group:team-checkout | group:default/team-checkout |
| group:payments/team-checkout | 네임스페이스까지 명시 |

spec.owner 같은 필드는 기본 kind 가 정해져 있어 team-checkout 만 써도 동작하지만, 명시적으로 group: 을 붙이는 편이 리뷰에서 훨씬 안전합니다. 사람이 읽을 때 그것이 팀인지 개인인지 바로 보이기 때문입니다.

왜 catalog-info.yaml 은 코드 옆에 사는가

카탈로그를 채우는 방식은 세 가지입니다.

1. 정적 등록 — Backstage 설정 파일에 URL 목록을 적는다. 소규모에는 되지만 늘어나면 관리 불가.
2. Location 엔티티 — 표지판 엔티티가 다른 파일들을 가리킨다. 계층적으로 묶을 때 유용.
3. 디스커버리(discovery) — 조직의 저장소를 훑어 catalog-info.yaml 을 찾아 자동 등록. 실무의 정답.

3 번이 성립하려면 파일이 코드와 같은 저장소 에 있어야 합니다. 이 배치가 만드는 효과가 큽니다.

반대로 카탈로그를 중앙 저장소 한 곳에 몰아 두면, 그 파일을 고칠 동기가 있는 사람이 아무도 없어집니다. 카탈로그가 썩는 가장 흔한 경로입니다.

소유권이 심장인 이유

카탈로그에서 딱 하나만 정확해야 한다면 그것은 소유자 입니다.

그래서 소유자는 사람(User)이 아니라 팀(Group) 이어야 합니다. 사람은 퇴사하고 팀은 인수됩니다. 시험에서 자주 나오는 지점이고, 실무에서 카탈로그가 무너지는 첫 번째 원인이기도 합니다.

현장에서 만나는 모습

저자의 홈랩에서 쿠버네티스 워크로드에 app.kubernetes.io/name, app.kubernetes.io/part-of 같은 표준 라벨을 붙이는 습관이 이 카탈로그 사고와 정확히 같은 것입니다. Helm 차트 모범 사례에서도 app.kubernetes.io/name, instance, version, component, part-of, managed-by 여섯 개를 표준으로 붙이라고 권합니다.

같은 소유권·소속 정보를 클러스터 라벨과 카탈로그 엔티티 두 곳에 일관되게 표현 하는 것이 실무의 모습입니다. 그리고 이 둘을 연결해 주는 것이 Backstage 의 Kubernetes 플러그인입니다 — 엔티티에 애너테이션 backstage.io/kubernetes-id 를 달아 두면, 그 값과 같은 라벨을 가진 워크로드를 클러스터에서 찾아 엔티티 페이지에 보여 줍니다.

홈랩 GPU 노드에 붙인 gpu.homelab/tier=xlarge 같은 의미 기반 라벨도 같은 정신입니다. 물리적 사실을 사람이 결정에 쓸 수 있는 어휘로 번역하는 것 — 카탈로그가 시스템·도메인으로 서비스를 묶는 이유와 다르지 않습니다.

다음 실습에서 할 것

/root/cba-catalog/ 에 Component, API, Resource, System, Domain, Group, User, Location 엔티티 파일을 직접 작성합니다. 그다음 같은 소유권 정보를 실제 클러스터의 라벨로 표현해 kubectl 로 확인하고, 마지막에 모든 엔티티의 참조 문자열을 정규화된 형식으로 뽑아 봅니다.