13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] 플랫폼 엔지니어링

  • [Kubernetes] Kubernetes CRD 심층 분석: 커스텀 리소스 정의 및 활용 전략

    [Kubernetes] Kubernetes CRD 심층 분석: 커스텀 리소스 정의 및 활용 전략

    [Kubernetes] Kubernetes CRD 심층 분석: 커스텀 리소스 정의 및 활용 전략

    Kubernetes CRD는 쿠버네티스(Kubernetes, 컨테이너 오케스트레이션 플랫폼)를 단순히 “파드를 띄우는 도구”에서 끝내지 않고, 우리 조직의 운영 규칙을 클러스터 안으로 끌고 들어오게 만드는 핵심 확장 포인트입니다. 처음엔 저도 이게 그냥 YAML 하나 더 만드는 기능인가 싶었는데, 실제로 써보니까 운영 지식을 API로 승격시키는 도구에 가깝더라고요. 특히 반복되는 인프라 작업이 많거나, 특정 서비스 배포 규칙을 팀 표준으로 강제하고 싶을 때 Kubernetes CRD의 진가가 확실히 드러납니다.

    제가 홈랩과 실무 환경에서 Kubernetes를 오래 만지면서 느낀 건, 사람이 문서 보고 수동으로 맞추는 방식은 결국 어딘가에서 틀어진다는 점이었습니다. “이 서비스는 TLS가 꼭 있어야 한다”, “이 애플리케이션은 백업 정책이 필요하다”, “이 데이터베이스는 복구 절차가 정해져 있다” 같은 규칙을 문서로만 남겨두면요. 언젠가는 빠집니다. 근데 Kubernetes CRD와 Operator 패턴(Operator Pattern, 운영 자동화 패턴)을 같이 쓰면, 그 규칙을 선언형(Declarative, 원하는 상태를 선언하는 방식)으로 관리할 수 있거든요.

    쿠버네티스 API 서버, etcd, CustomResourceDefinition, 커스텀 컨트롤러의 관계를 한눈에 보여주는 개요 이미지 위치입니다.

    Kubernetes CRD란 무엇인가요?

    쉽게 말해 CRD(CustomResourceDefinition, 커스텀 리소스 정의)는 쿠버네티스에 새로운 리소스 타입을 추가하는 방법입니다. Deployment, Service, Ingress(인그레스, 외부 트래픽 진입점)처럼 원래 있던 리소스 말고, 예를 들어 Database, BackupPolicy, AppRelease 같은 우리만의 리소스를 만들 수 있는 거죠.

    여기서 사람들이 많이 헷갈리는 포인트가 하나 있습니다. CRD 자체는 결국 스키마(schema, 데이터 구조 정의)를 등록하는 일이거든요. 즉, API 서버가 “아, 이제 이런 모양의 리소스도 받을 수 있구나” 하고 이해하게 만드는 단계예요. 그런데 실제로 그 리소스를 보고 뭔가 동작하게 만드는 건 보통 컨트롤러(Controller, 상태를 감시하고 맞추는 제어 루프)가 담당합니다. 저도 처음엔 CRD만 만들면 자동으로 뭔가 굴러갈 줄 알았는데, 아무 일도 안 일어나서 삽질 좀 했습니다 ㅎㅎ

    기본 구성 요소

    • CRD: 새로운 리소스 타입의 정의
    • Custom Resource: 실제로 생성하는 인스턴스
    • Controller: 원하는 상태와 현재 상태를 맞추는 로직
    • Operator: 특정 운영 지식을 코드로 구현한 컨트롤러 묶음
    항목 역할 비유
    CRD 새 API 타입 등록 양식(template) 만들기
    Custom Resource 실제 선언 데이터 양식에 값 채워 제출
    Controller 선언을 실제 상태로 반영 담당자가 요청 처리
    Operator 도메인 운영 자동화 숙련 엔지니어의 절차를 자동화

    왜 Kubernetes 확장에 CRD를 쓰는가

    Kubernetes 확장이라고 하면 예전에는 API Aggregation(집계 API)처럼 더 무거운 접근도 있었지만, 대부분의 팀에게는 CRD가 훨씬 현실적입니다. 이유는 분명합니다.

    1. 기존 kubectl과 선언형 워크플로우를 그대로 활용할 수 있습니다.
    2. RBAC(Role-Based Access Control, 역할 기반 접근 제어), admission, GitOps와 잘 맞습니다.
    3. 도메인 개념을 YAML로 표준화할 수 있습니다.
    4. Operator 패턴과 결합하면 반복 작업을 코드로 고정할 수 있습니다.

    예를 들어 운영팀에서 매번 데이터베이스 인스턴스를 만들 때 스토리지 클래스, 백업 주기, 리소스 제한, 비밀 정보(secret) 연결을 공통 규칙으로 적용한다고 해보겠습니다. 이걸 문서로 적어두는 대신 Database라는 커스텀 리소스를 만들고, 컨트롤러가 이를 보고 StatefulSet, Service, Secret 참조, 백업 Job을 구성하게 만들 수 있습니다. 이게 바로 커스텀 리소스와 Operator 패턴이 실무에서 힘을 발휘하는 지점입니다.

    혹시 이런 경험 있으신가요? 배포 문서는 있는데 팀원마다 해석이 조금씩 달라서 결과가 달라지는 상황이요. 저는 그걸 몇 번 겪고 나서, 사람이 읽는 문서와 시스템이 읽는 선언을 분리해야겠다고 생각했습니다. Kubernetes CRD는 바로 그 중간을 메워줍니다.

    실전 구현 1: CRD 정의하기

    이제 가장 단순한 예제로 가보겠습니다. AppClaim이라는 커스텀 리소스를 만들어서, 애플리케이션 배포에 필요한 최소 정보를 선언한다고 가정해보죠. 여기서는 이해를 위해 구조를 단순화했습니다.

    1. API 그룹(group)과 버전(version)을 정합니다.
    2. 리소스 이름과 복수형(plural)을 정합니다.
    3. OpenAPI v3 스키마로 필드를 정의합니다.
    4. 추가 프린터 컬럼으로 kubectl 가독성을 높입니다.
    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
      name: appclaims.platform.example.com
    spec:
      group: platform.example.com
      scope: Namespaced
      names:
        plural: appclaims
        singular: appclaim
        kind: AppClaim
        shortNames:
          - ac
      versions:
        - name: v1alpha1
          served: true
          storage: true
          schema:
            openAPIV3Schema:
              type: object
              properties:
                spec:
                  type: object
                  required:
                    - image
                    - replicas
                  properties:
                    image:
                      type: string
                    replicas:
                      type: integer
                      minimum: 1
                    port:
                      type: integer
                      minimum: 1
                      maximum: 65535
                    expose:
                      type: boolean
                status:
                  type: object
                  properties:
                    phase:
                      type: string
                    readyReplicas:
                      type: integer
          subresources:
            status: {}
          additionalPrinterColumns:
            - name: Image
              type: string
              jsonPath: .spec.image
            - name: Replicas
              type: integer
              jsonPath: .spec.replicas
            - name: Phase
              type: string
              jsonPath: .status.phase

    이 YAML의 핵심은 spec과 status를 분리한 거예요. spec은 사용자가 원하는 상태이고, status는 컨트롤러가 관찰한 결과입니다. 이 구분이 흐려지면 나중에 운영이 엄청 꼬입니다. 저도 예전에 status 비슷한 값을 spec에 억지로 넣었다가 reconcile loop(리컨실 루프, 상태 동기화 루프)가 지저분해져서 다시 갈아엎은 적이 있습니다.

    kubectl apply -f appclaim-crd.yaml
    kubectl get crd appclaims.platform.example.com

    정상 적용되면 이제 클러스터는 AppClaim이라는 새 리소스 타입을 이해합니다. 여기서 중요한 포인트! 아직은 타입만 생긴 상태입니다. 배포는 자동으로 안 됩니다.

    Kubernetes CRD YAML을 작성하고 적용하는 실전 구성 이미지

    CRD YAML을 작성하고 kubectl로 적용하는 실전 구성 이미지를 넣는 위치입니다.

    실전 구현 2: 커스텀 리소스 생성과 컨트롤러 연결

    다음은 실제 인스턴스를 하나 만들어보겠습니다.

    apiVersion: platform.example.com/v1alpha1
    kind: AppClaim
    metadata:
      name: demo-web
    spec:
      image: nginx:stable
      replicas: 2
      port: 80
      expose: true
    kubectl apply -f demo-appclaim.yaml
    kubectl get appclaims
    kubectl describe appclaim demo-web

    이제 컨트롤러가 이 리소스를 감시하면서 Deployment와 Service를 생성하도록 만들 수 있습니다. 실제 프로덕션에서는 Go와 controller-runtime을 많이 쓰지만, 여기서는 흐름 이해가 목적이니 의사 코드 수준으로 보겠습니다.

    def reconcile(appclaim):
        desired_deployment = build_deployment(appclaim.spec)
        desired_service = build_service(appclaim.spec)
    
        apply_object(desired_deployment)
    
        if appclaim.spec.get("expose"):
            apply_object(desired_service)
    
        update_status(
            appclaim,
            phase="Ready",
            ready_replicas=get_ready_replicas(appclaim.metadata.name)
        )

    실제로 써보니까 이 구조가 왜 좋은지 금방 체감됩니다. 사용자는 AppClaim만 만들면 되고, 배포 세부 구현은 컨트롤러가 맡습니다. 즉, 사용자 인터페이스는 단순하게, 운영 로직은 중앙집중화되는 거죠. 팀 규모가 커질수록 이 차이가 큽니다.

    Operator 패턴과의 연결

    Operator 패턴은 단순 생성 자동화를 넘어섭니다. 설치, 업그레이드, 백업, 복구, 장애 대응 같은 운영 절차를 컨트롤러 안에 녹이는 방식입니다. 대표적으로 데이터베이스, 메시지 큐, 모니터링 스택 같은 상태 저장 워크로드에서 자주 쓰입니다. Kubernetes CRD는 이 Operator의 입력 포맷이 되는 경우가 많고요.

    만약 앱 수준이 아니라 데이터베이스 운영 자동화까지 가고 싶다면, 다음 글에서 StatefulSet과 Operator 설계 포인트도 이어서 다뤄볼 예정입니다. 이전 글에서 다룬 Helm 차트 구조화 내용이 있다면 그것도 같이 보시면 흐름이 더 잘 잡힙니다.

    ⚠️ 주의사항과 트러블슈팅

    여기서부터가 진짜 실무 구간입니다. CRD는 만들기보다 버전 관리와 호환성에서 더 많이 흔들립니다. 제가 직접 해보니 아래 이슈는 거의 한 번씩 다 밟게 되더라고요.

    1. v1alpha1을 너무 오래 끌고 가는 문제

    처음엔 빠르게 실험하려고 v1alpha1로 시작합니다. 저도 그랬고요. 근데 이 상태로 사용자 수가 늘어나면 필드 변경이 무서워집니다. 기존 리소스와의 호환성을 고민해야 하거든요. 가능하면 초기에 필드 의미를 명확히 하고, status 구조도 대충 넣지 말고 의도를 분명히 하세요.

    2. 스키마 검증을 느슨하게 잡는 문제

    “컨트롤러에서 알아서 처리하면 되지”라고 생각하고 schema를 대충 열어두면 나중에 이상한 값이 다 들어옵니다. port에 문자열이 들어오거나, replicas가 0이 되거나, 필수 필드가 비는 식이죠. 그러면 에러 위치가 API 서버가 아니라 애플리케이션 쪽으로 밀려서 디버깅이 더 어려워집니다.

    3. status 업데이트 충돌

    컨트롤러가 status를 자주 갱신하면 resourceVersion 충돌이 날 수 있습니다. 이럴 때는 status subresource를 쓰고, 재시도 로직과 idempotent(멱등성 있는) 설계를 같이 가져가야 합니다. “한 번 더 실행돼도 결과가 같아야 한다” 이 원칙이 정말 중요합니다.

    4. finalizer 처리 누락

    외부 리소스를 만들었다면 finalizer(파이널라이저, 삭제 전 정리 훅)를 빼먹으면 안 됩니다. 삭제 이벤트가 왔을 때 외부 DNS, 스토리지, 인증서 같은 부수 리소스를 정리하지 않으면 유령 자원이 남습니다. 저도 예전에 테스트 환경에서 볼륨이 계속 쌓여서 왜 그런가 봤더니 finalizer 처리 누락이 원인이더라고요.

    kubectl get appclaim demo-web -o yaml
    kubectl api-resources | grep appclaim
    kubectl explain appclaim.spec

    위 명령어 세 개는 문제 생겼을 때 정말 자주 씁니다. 특히 kubectl explain은 Kubernetes CRD가 의도한 구조대로 등록됐는지 빠르게 확인할 때 유용합니다. 💡 작은 팁인데, 추가 프린터 컬럼을 잘 만들어두면 운영 가시성이 크게 좋아집니다.

    Kubernetes CRD 트러블슈팅과 검증 흐름을 보여주는 이미지

    스키마 검증 오류, 컨트롤러 동기화, kubectl 진단 흐름을 설명하는 트러블슈팅 다이어그램 위치입니다.

    검증과 결과 확인

    그럼 무엇을 검증해야 “Kubernetes CRD가 잘 동작한다”고 볼 수 있을까요? 단순히 리소스가 생성됐다는 것만으로는 부족합니다. 저는 보통 아래 순서로 봅니다.

    1. CRD가 정상 등록됐는지 확인합니다.
    2. Custom Resource 생성이 스키마에 맞게 통과하는지 봅니다.
    3. 컨트롤러가 원하는 하위 리소스를 생성하는지 확인합니다.
    4. status가 실제 상태를 반영하는지 확인합니다.
    5. 삭제 시 정리 로직이 정상 동작하는지 검증합니다.
    kubectl get crd
    kubectl get appclaims
    kubectl get deployment,service
    kubectl describe appclaim demo-web
    kubectl delete appclaim demo-web

    정상 흐름이라면 AppClaim을 만들었을 때 Deployment와 Service가 생성되고, 준비가 끝나면 status.phase 같은 필드가 업데이트됩니다. 삭제 시에는 관련 리소스가 정리돼야 하고요. 이 지점에서 드디어 “아, 이제 사람이 수작업으로 안 해도 되네” 하는 느낌이 옵니다. 🎉 이거 진짜 편하더라고요.

    검증 항목 확인 방법 기대 결과
    CRD 등록 kubectl get crd 리소스 타입 노출
    스키마 검증 잘못된 필드로 생성 시도 API 서버에서 거부
    하위 리소스 생성 Deployment/Service 조회 의도한 객체 생성
    Status 반영 kubectl describe 상태 필드 갱신
    삭제 정리 delete 후 잔여 리소스 확인 누수 없이 정리

    커스텀 리소스 상태와 생성된 워크로드를 검증하는 결과 화면 이미지를 넣는 위치입니다.

    언제 CRD를 쓰고, 언제 쓰지 말아야 하나

    모든 문제를 CRD로 풀 필요는 없습니다. 이건 정말 중요합니다. 저도 한때 뭐든지 커스텀 리소스로 만들면 멋있어 보인다고 생각했었는데, 나중엔 운영 복잡도만 올라간 적이 있었습니다.

    • CRD를 쓰면 좋은 경우: 반복되는 운영 절차가 있고, 선언형 API로 표준화할 가치가 있을 때
    • CRD가 과한 경우: 단순 스크립트 한 번이면 끝나는 작업일 때
    • Operator가 필요한 경우: 설치 이후에도 지속적인 상태 관리, 복구, 업그레이드가 필요할 때
    • Helm만으로 충분한 경우: 정적 템플릿 배포가 중심이고 런타임 제어가 크지 않을 때

    한 줄로 정리하면 이렇습니다. 새로운 API가 팀의 언어가 될 수준인지 먼저 보세요. 그 정도가 아니면 단순화하는 편이 낫습니다.

    정리와 FAQ

    Kubernetes CRD는 단순 기능 추가가 아니라, 팀의 운영 지식을 쿠버네티스 API로 모델링하는 방법입니다. CRD, 커스텀 리소스, Kubernetes 확장, Operator 패턴을 한 세트로 이해하면 왜 많은 플랫폼 팀이 이 접근을 쓰는지 감이 오실 겁니다. 저도 처음엔 용어가 너무 많아서 복잡했는데, 결국 핵심은 하나였습니다. “원하는 상태를 잘 정의하고, 그걸 시스템이 계속 맞추게 하자.” 이 철학만 잡히면 훨씬 편해집니다. ✅

    다음 단계로는 컨트롤러 프레임워크(controller-runtime), 상태 전이 설계, CRD 버전 업그레이드 전략까지 이어서 보시면 좋습니다. 다음 글에서 다룰 예정입니다.

    Kubernetes CRD와 Operator 패턴 역할 비교 요약 이미지

    CRD, 커스텀 리소스, 컨트롤러, Operator의 역할을 요약 비교하는 인포그래픽 위치입니다.

    자주 묻는 질문

    Q. CRD만 만들면 자동화가 되나요?
    아닙니다. CRD는 타입 등록이고, 실제 자동화는 보통 컨트롤러가 담당합니다.

    Q. CRD와 Operator는 같은 건가요?
    같지 않습니다. CRD는 API 정의이고, Operator는 운영 지식을 담은 자동화 로직입니다. 둘이 함께 쓰이는 경우가 많습니다.

    Q. 기존 Deployment나 Helm으로 충분한데도 Kubernetes CRD가 필요할까요?
    반복 운영 규칙을 API로 표준화할 필요가 없다면 굳이 안 써도 됩니다. 과한 설계는 오히려 부담이 됩니다.

    Q. 처음 만들 때 가장 중요한 한 가지는 뭔가요?
    spec과 status의 역할을 명확히 나누고, 스키마 검증을 초기에 엄격하게 잡는 겁니다. 여기서 흔들리면 나중에 계속 고생합니다.

  • [k8s] Backstage 운영 후기: 개발자 포털 1년 회고와 정착 과정

    [k8s] Backstage 운영 후기: 개발자 포털 1년 회고와 정착 과정

    Backstage 운영 후기: 개발자 포털 1년 회고와 정착 과정

    Backstage 운영 후기를 정리해보려고 합니다. 개발팀이 커질수록 문서는 여기저기 흩어지고, 서비스는 늘어나고, 누구에게 뭘 물어봐야 하는지도 점점 모호해지더라고요. 저도 인프라 일을 오래 하면서 이런 상황을 정말 많이 봤습니다. 처음엔 위키만 잘 정리하면 되겠지 싶었는데, 실제로 써보니 위키만으로는 서비스 카탈로그(Service Catalog, 서비스 목록 체계), 템플릿(Template, 표준 생성 양식), 권한 관리(Permission, 접근 제어)까지 한 번에 풀기 어렵더라고요. 그래서 선택한 게 바로 Backstage였습니다.

    이 글은 화려한 성공담보다는, 실제에 가까운 Backstage 도입 사례 기록입니다. 도입 검토부터 초기 구축, 팀 정착, 그리고 1년 운영하면서 느낀 한계까지 솔직하게 적어보겠습니다. 지금 개발자 포털 구축을 고민 중이라면 시행착오를 줄이는 데 도움이 될 겁니다.

    Backstage 운영 후기를 설명하는 개발자 포털 전체 아키텍처 이미지

    Backstage를 중심으로 Git 저장소, CI/CD, 문서, 모니터링 도구가 연결되는 전체 구조를 한눈에 보여주는 이미지입니다.

    Backstage란 무엇이고 왜 도입했나

    쉽게 말해 Backstage는 개발자 포털을 구축할 때 쓰는 오픈소스 프레임워크입니다. 서비스 목록을 모아두는 화면이기도 하고, 팀 표준을 배포하는 플랫폼이기도 하고, 신규 서비스 생성 흐름을 자동화하는 입구이기도 하죠. 처음엔 “이거 그냥 내부 위키랑 뭐가 다르지?” 싶었는데, 막상 운영해보니 차이가 꽤 컸습니다.

    제가 체감한 가장 큰 차이는 세 가지였습니다.

    • 서비스를 문서가 아니라 엔티티(Entity, 관리 대상 객체)로 다룬다: 팀, 시스템, API, 컴포넌트를 관계로 연결할 수 있습니다.
    • 소유권(Ownership, 담당 주체)이 드러난다: 장애가 났을 때 누가 관리하는지 찾는 시간이 줄었습니다.
    • 표준화가 강제된다: 새 저장소를 만들 때부터 템플릿으로 기본 구조를 맞출 수 있거든요.

    특히 플랫폼 엔지니어링(Platform Engineering, 개발 생산성 플랫폼 설계) 관점에서 좋았던 건, 운영팀이 “지침”만 주는 게 아니라 “실행 가능한 기본값”을 제공할 수 있다는 점이었습니다. 말로만 표준을 외치면 잘 안 지켜집니다. 그런데 템플릿에 녹여두면 생각보다 잘 따라오더라고요. 이거 진짜 편했습니다.

    참고로 Backstage는 CNCF 인큐베이팅 프로젝트이기도 합니다. 그래서 CNCF Backstage라는 표현을 쓰더라도, 제품명이라기보다 CNCF 생태계에 속한 Backstage를 가리키는 말로 이해하는 편이 자연스럽습니다.

    Backstage 도입 전에 먼저 정한 기준

    Backstage는 기능이 많지만, 처음부터 다 하려 들면 거의 반드시 무너집니다. 저도 처음엔 카탈로그, 문서, 템플릿, 플러그인(Plugin, 확장 기능), 권한까지 한 번에 붙이려다가 시행착오를 꽤 겪었습니다. 그래서 기준을 다시 잡았습니다.

    1. 첫 3개월 목표는 검색성과 소유권 정리

    처음부터 모든 자동화를 노리진 않았습니다. 가장 먼저 해결한 건 “우리 서비스가 몇 개인지”, “이 서비스 담당 팀이 누구인지”, “배포 파이프라인이 어디 있는지”를 한 화면에서 보이게 하는 것이었습니다.

    2. 카탈로그 품질이 화면보다 먼저다

    Backstage는 화면보다 데이터가 중요합니다. <code>catalog-info.yaml이 엉망이면 나중에 검색도, 관계도, 문서 연결도 다 지저분해집니다. 그래서 초기에 메타데이터 규칙부터 정했습니다.

    3. 운영팀이 다 입력하지 않는다

    플랫폼은 중앙집중형으로 굴리면 오래 못 갑니다. 각 팀이 자기 서비스 메타데이터를 유지하게 하고, 운영팀은 템플릿과 검증 규칙을 관리하는 식으로 분리했습니다.

    항목 초기 목표 1년 뒤 목표
    서비스 카탈로그 핵심 서비스 등록 전 팀 기본 등록
    소유권 표시 팀 단위 매핑 온콜/문서 링크까지 연결
    소프트웨어 템플릿 1~2개 표준 템플릿 언어/런타임별 확장
    TechDocs 중요 서비스만 문서 작성 습관 정착
    권한 관리 최소 권한 팀/역할 기반 세분화

    Backstage 개발자 포털 구축: 첫 배포까지

    개발자 포털 구축 자체는 생각보다 빨리 됩니다. 문제는 그 다음부터입니다. 기본 앱을 만들고, 로컬에서 띄우고, 카탈로그 엔티티를 등록해보는 것까지는 공식 흐름이 꽤 잘 되어 있습니다.

    1. 새 Backstage 앱 생성
    2. 기본 실행 확인
    3. 카탈로그 엔티티 등록
    4. 인증과 조직 구조 연결
    5. 템플릿과 문서 기능 추가

    저는 초기에 아주 단순한 구조로 시작했습니다. 로컬에서 먼저 확인하고, 이후 컨테이너(Container, 실행 격리 환경)로 배포하는 흐름이었습니다. 현재 공식 시작 가이드 기준으로는 아래처럼 앱을 만든 뒤 yarn start로 프런트엔드와 백엔드를 함께 띄우는 방식이 가장 기본입니다.

    npx @backstage/create-app@latest
    cd my-backstage-app
    yarn install
    yarn start

    처음 화면이 뜨면 “생각보다 금방 되네?” 싶습니다. 그런데 여기서 중요한 포인트가 있습니다. 로컬 실행 성공은 시작일 뿐입니다. 실제 운영에서 중요한 건 인증, 카탈로그 입력 규칙, 저장소 구조, 문서 관리 방식입니다.

    서비스 등록은 아래처럼 아주 기본적인 엔티티 파일부터 시작했습니다.

    apiVersion: backstage.io/v1alpha1
    kind: Component
    metadata:
      name: payment-api
      description: Payment service for internal platform
      tags:
        - backend
        - critical
    spec:
      type: service
      lifecycle: production
      owner: team-platform
      system: commerce-platform

    이 단계에서 중요한 건 멋진 화면이 아니라 명명 규칙(Naming Convention, 이름 규칙)입니다. 이름이 제각각이면 검색이 바로 망가집니다. 실제로 써보니까 team-, svc- 같은 접두사(prefix, 앞부분 식별자)를 과하게 쓰는 것도 오히려 헷갈리더라고요. 적당히 단순해야 오래 갑니다.

    Backstage 운영 후기의 핵심인 서비스 카탈로그와 엔티티 관계 구성 이미지

    Component, API, System, Group 엔티티가 서로 연결된 카탈로그 구조를 보여주는 이미지입니다.

    Backstage 운영 후기에서 편해진 지점: 카탈로그, 템플릿, 문서

    카탈로그(Catalog, 서비스 자산 목록)

    Backstage 운영 후기에서 빼놓기 어려운 핵심은 카탈로그입니다. 저는 처음에 이걸 “서비스 목록 화면” 정도로 생각했었는데, 1년 써보니 사실상 운영 기준점이더라고요. 서비스가 장애를 냈을 때 저장소와 문서, 대시보드, 소유 팀을 한 번에 찾을 수 있다는 게 큽니다.

    소프트웨어 템플릿(Software Templates, 표준 생성 템플릿)

    이 기능은 팀 표준을 퍼뜨릴 때 정말 강력했습니다. 신규 서비스 생성 시 README, 기본 디렉터리 구조, CI 설정, 카탈로그 파일까지 자동으로 넣어주게 만들면 편차가 많이 줄어듭니다.

    apiVersion: scaffolder.backstage.io/v1beta3
    kind: Template
    metadata:
      name: simple-service-template
      title: Simple Service Template
    spec:
      owner: team-platform
      type: service
      parameters:
        - title: Service Info
          required:
            - name
            - owner
          properties:
            name:
              type: string
              title: Service Name
            owner:
              type: string
              title: Owner Team
      steps:
        - id: fetch-base
          name: Fetch Base
          action: fetch:template
          input:
            url: ./template
            values:
              name: ${{ parameters.name }}
              owner: ${{ parameters.owner }}

    처음엔 템플릿을 많이 만들수록 좋다고 생각했는데, 오히려 반대였습니다. 템플릿 종류가 많아지면 사용자가 뭘 선택해야 하는지 모르거든요. 그래서 1년 운영 후 기준은 명확해졌습니다. “가장 많이 쓰는 패턴부터 적게 만든다.” 이게 맞았습니다.

    TechDocs(테크독스, 문서 자동 게시)

    문서도 생각보다 영향이 컸습니다. 개발자 포털 구축에서 문서가 분리되어 있으면 결국 다시 검색 지옥으로 돌아갑니다. Backstage 안에서 문서가 보이게 만들면 적어도 “어디에 있는지”를 찾는 시간은 줄어듭니다. 다만 문서 품질 자체는 도구가 해결해주지 않습니다. 이건 운영 문화의 문제더라고요.

    1년 운영하면서 겪은 문제들

    여기부터가 진짜 운영 후기입니다. 설치보다 운영이 훨씬 어렵습니다.

    1. 카탈로그 등록률이 생각보다 안 올라갑니다

    플랫폼팀이 좋다고 생각하는 것과 현업팀이 귀찮다고 느끼는 건 늘 다릅니다. 특히 초기에는 “왜 이걸 또 적어야 하죠?”라는 반응이 있었습니다. 저도 처음엔 안내 문서를 길게 써놨었는데, 효과가 크지 않더라고요.

    해결은 단순했습니다.

    • 템플릿 생성 시 catalog-info.yaml 자동 포함
    • 필수 필드 최소화
    • 리뷰 체크리스트에 소유권 항목 추가
    • 등록 안 된 서비스는 운영 지표에서 제외

    강제와 편의의 균형이 중요합니다. 너무 세게 밀면 반감이 생기고, 너무 느슨하면 아무도 안 합니다.

    2. 조직도와 실제 책임 구조가 다릅니다

    Group(그룹, 팀 조직 엔티티)를 예쁘게 모델링해도 현실은 늘 예외가 있습니다. 명목상 팀과 실제 운영 담당이 다를 때가 있거든요. 그래서 저는 조직도 그대로만 넣지 않고, 서비스 기준 소유권을 별도로 정리했습니다. 여기서 중요한 건 HR 기준이 아니라 장애 대응 기준입니다.

    3. 플러그인 욕심이 커집니다

    Backstage는 플러그인이 많고 확장성이 좋아서, 운영하다 보면 이것저것 붙이고 싶어집니다. 그런데 여기서 많이 흔들립니다. 저도 한동안 대시보드, 품질 지표, 배포 이력, API 문서, 비용 정보까지 한 화면에 다 넣어보려 했는데요. 결과적으로는 정보 밀도가 너무 높아져서 오히려 안 보게 되더라고요.

    정말 자주 보는 정보만 전면에 두고, 나머지는 링크로 넘기는 구조가 오래 갑니다.

    4. 권한 관리가 늦어지면 나중에 더 아픕니다

    초기엔 내부 도구니까 대충 열어두자 싶을 수 있습니다. 저도 솔직히 그랬습니다. 그런데 문서, 운영 대시보드, 템플릿 액션이 늘어나면 접근 제어가 갑자기 중요해집니다. 특히 프로덕션 관련 링크나 자동화 액션이 연결되기 시작하면 더 그렇습니다.

    backend:
      auth:
        keys:
          - secret: ${BACKEND_SECRET}
    permission:
      enabled: true

    다만 실제 운영에서는 설정만 넣는다고 끝나지 않습니다. 현재 공식 문서 기준으로는 app-config.yaml의 permission.enabled: true 설정과 함께, 백엔드에 permission policy를 연결하는 작업이 같이 필요합니다. 여기서는 방향만 보여드리는 예시로 봐주시면 됩니다.

    개발자 포털 구축 과정에서 인증과 템플릿 자동화 흐름을 보여주는 이미지

    SSO 로그인 이후 템플릿 실행, 저장소 생성, 카탈로그 등록으로 이어지는 운영 자동화 흐름을 보여주는 이미지입니다.

    검증: 무엇이 실제로 달라졌나

    정량 지표를 과하게 꾸미고 싶진 않습니다. 다만 1년 운영하면서 체감 변화는 분명했습니다.

    • 신규 입사자 온보딩 시 서비스 위치 파악이 빨라졌습니다.
    • 운영 중 “이 서비스 누가 보나요?”라는 질문이 줄었습니다.
    • 표준 저장소 구조가 어느 정도 정착됐습니다.
    • 문서 링크와 대시보드 링크를 찾는 시간이 줄었습니다.

    특히 장애 대응 때 차이가 컸습니다. 예전에는 메신저 검색부터 했거든요. 이제는 포털에서 서비스를 보고, 담당 팀을 보고, 관련 문서와 대시보드로 넘어가는 흐름이 꽤 자연스러워졌습니다. 이럴 때는 “아, 이거 도입하길 잘했네” 싶더라고요.

    반대로 기대보다 덜 바뀐 것도 있습니다.

    • 문서 최신화는 도구만으로 해결되지 않았습니다.
    • 카탈로그 품질은 각 팀의 습관에 크게 좌우됐습니다.
    • 포털 접속 빈도는 팀마다 차이가 컸습니다.

    그래서 CNCF Backstage를 만능 해결책으로 보면 실망할 수 있습니다. 이건 포털 소프트웨어이면서 동시에 운영 문화 도구입니다. 플랫폼 엔지니어링의 일부이지, 전부는 아니더라고요.

    Backstage 운영 후기 결과를 보여주는 대시보드와 성과 시각화 이미지

    서비스 소유권 가시성, 온보딩 속도 개선, 문서 연결 상태 등을 대시보드 형태로 요약한 이미지입니다.

    Backstage 도입 사례로 정리하는 운영 팁

    중요한 포인트만 추리면 이렇습니다.

    1. 카탈로그부터 시작하세요. 검색성과 소유권이 먼저입니다.
    2. 템플릿은 적게 만드세요. 제일 많이 쓰는 패턴 한두 개가 효과적입니다.
    3. 문서 품질 책임은 각 팀에 남겨두세요. 플랫폼팀이 대신 다 맡기는 어렵습니다.
    4. 권한 모델을 초기에 잡으세요. 뒤늦게 손보면 연결 범위가 너무 넓어집니다.
    5. 플러그인은 신중하게 늘리세요. 첫 화면은 단순해야 계속 보게 됩니다.
    상황 추천 접근 피해야 할 접근
    초기 도입 카탈로그 중심 MVP 모든 기능 동시 도입
    템플릿 설계 반복 패턴 우선 팀별 전용 템플릿 난립
    문서 운영 작성 책임 분산 플랫폼팀 단독 관리
    권한 관리 초기 정책 정의 운영 후반 일괄 정리
    플러그인 확장 핵심 정보만 노출 한 화면에 모든 정보 집약

    정리와 다음 단계

    Backstage 운영 후기를 한 줄로 줄이면 이렇습니다. “설치는 빠른데, 정착은 결국 운영 설계의 문제다.” 저도 처음엔 도구만 잘 깔면 해결될 줄 알았는데, 실제로 써보니 카탈로그 품질, 조직 책임, 템플릿 설계, 권한 정책 같은 기본기가 훨씬 중요했습니다.

    Backstage 도입 사례를 찾고 있다면 너무 크게 시작하지 않는 편이 낫습니다. 개발자 포털은 예쁜 화면이 아니라 팀의 작업 흐름을 바꾸는 도구입니다. 그래서 작게 시작해서, 자주 쓰는 흐름부터 붙이는 쪽이 훨씬 잘 되더라고요. 이전 글에서 다룬 홈랩 기반 GitOps 실험도 함께 읽어보시면 도입 배경을 더 자연스럽게 이어서 보실 수 있습니다.

    다음 글에서는 TechDocs 운영 방식이나 소프트웨어 템플릿 표준화 전략을 더 깊게 다뤄볼 예정입니다.

    도입 전후 차이, 추천 시작 순서, 운영 시 주의할 점을 한 장으로 정리한 요약 인포그래픽입니다.

    자주 묻는 질문

    Backstage는 작은 팀에도 필요할까요?

    작은 팀이라면 반드시 필요하다고 보긴 어렵습니다. 다만 서비스 수가 늘고, 팀이 분화되고, 온보딩 비용이 커지기 시작하면 가치가 확실히 보입니다.

    개발자 포털 구축에서 가장 먼저 해야 할 일은 뭔가요?

    서비스 목록과 소유권 정리입니다. 화려한 자동화보다 먼저, 누가 어떤 서비스를 책임지는지 보여야 합니다.

    플랫폼 엔지니어링과 Backstage는 같은 말인가요?

    같지는 않습니다. Backstage는 플랫폼 엔지니어링을 구현할 때 쓰는 여러 도구 중 하나에 가깝습니다. 운영 방식과 조직 합의가 더 중요합니다.

    CNCF Backstage를 도입하면 문서 문제가 해결되나요?

    문서 위치 문제는 꽤 좋아집니다. 다만 문서 최신화 문제까지 자동으로 해결되진 않습니다. 그건 팀 습관과 리뷰 문화가 같이 따라와야 합니다.