Files
k8s-template/docs/architecture/repository-structure.md
T

6.6 KiB

Repository Structure

설계 목표

이 구조는 특정 제품의 파일 배치보다 변경 주기와 소유권을 우선합니다.

  • 한 리소스에는 한 명확한 소유자가 있다.
  • 재사용 구현과 실제 배포 진입점을 분리한다.
  • 소규모 구성은 선택 영역을 생략할 수 있다.
  • 규모가 커져도 기존 경계를 바꾸지 않고 같은 종류의 leaf를 추가한다.
  • 사람이 실행하는 명령과 CI 검증이 같은 진입점을 사용한다.

소유권 매트릭스

대상 소유 경로 직접 실행 여부 변경 주기
state backend, 초기 identity bootstrap/foundation 매우 낮음
네트워크, IAM, DNS, 클러스터 infrastructure/live 낮음
재사용 IaC 단위 infrastructure/components 아니요 중간
재사용 IaC 조합 infrastructure/stacks 아니요 중간
GitOps 컨트롤러와 root 연결 bootstrap/gitops 낮음
클러스터 desired state gitops/clusters reconcile 진입점 지속적
cluster-wide addon gitops/platform 아니요 중간
애플리케이션 배포 정의 gitops/apps 아니요 높음
정책과 tenant 정의 gitops/policies, gitops/tenants 아니요 중간

Catalog 영역(components, stacks, platform, policies, tenants, apps)은 직접 배포하지 않습니다. 실제 진입점이 필요한 항목만 조합해서 참조합니다.

의존 방향

bootstrap/foundation
        │ output
        ▼
infrastructure/components ◀── infrastructure/stacks
        ▲                              ▲
        └──────── infrastructure/live ─┘
                         │ cluster endpoint/identity
                         ▼
                 bootstrap/gitops
                         │ root reference
                         ▼
  platform ─┐
  policies ─┼────────▶ gitops/clusters
  tenants  ─┤
  apps     ─┘

역방향 의존은 만들지 않습니다. 예를 들어 reusable component가 특정 live/prod 값을 읽거나, app base가 특정 cluster overlay를 참조하면 안 됩니다.

Infrastructure 경계

components

네트워크, identity, registry, Kubernetes cluster처럼 작고 응집된 재사용 단위입니다. Terraform/OpenTofu를 선택했다면 일반적으로 backend가 없는 child module에 해당합니다.

stacks

여러 component를 반복해서 같은 방식으로 조합할 때만 사용합니다. 작은 프로젝트는 이 계층 없이 live가 component를 직접 호출할 수 있습니다. stack이 다른 stack을 깊게 중첩하기보다는 live root에서 평평하게 조합하는 방식을 권장합니다.

live

실제로 plan/apply하는 root입니다. leaf 하나는 다음을 만족해야 합니다.

  • 독립된 state와 locking
  • 명시적인 provider와 backend 설정
  • 고정된 component/module/chart 버전
  • 비밀이 아닌 환경 입력만 저장소에 커밋
  • 출력값과 downstream contract 문서화

작은 구성은 live/dev/cluster로 충분합니다. 계정과 리전이 늘어나면 live/<provider>/<account>/<region>/<environment>/<stack>처럼 경로를 확장합니다. 자동화는 경로의 고정 깊이에 의존하지 말고 실행 가능한 root 파일을 기준으로 대상을 찾도록 작성합니다.

서로 다른 환경의 root가 상대 경로로 다른 환경 구현을 import하면 안 됩니다. 공유가 필요하면 versioned component나 명시적인 remote output/data contract를 사용합니다.

GitOps 경계

clusters

클러스터가 reconcile하는 유일한 진입점입니다. 공통 리소스를 복사하지 않고 platform, policy, tenant, app catalog에서 필요한 항목만 참조합니다.

작은 구성은 clusters/dev/main, 다중 리전 구성은 clusters/<environment>/<region>/<cluster> 형태를 사용할 수 있습니다. 여기에도 고정된 경로 깊이를 강제하지 않습니다.

platform

cluster-wide controller와 addon을 둡니다. 예시는 다음과 같습니다.

  • ingress/gateway, external DNS, certificate
  • storage class/CSI, autoscaling
  • metrics, logs, traces, alerting
  • secret operator와 delivery controller

각 component는 base와 필요한 overlays를 같은 디렉터리 안에 응집시킵니다. 환경 차이는 전체 파일 복사 대신 Kustomize patch 또는 별도 values로 표현합니다.

policies, tenants, apps

  • policies: cluster-wide admission 규칙, 거버넌스와 예외
  • tenants: 구체적인 namespace, RBAC, quota, limit range, NetworkPolicy
  • apps: application source code가 아닌 배포 정의

애플리케이션 팀이 별도 저장소를 소유하면 apps에는 그 저장소/OCI artifact를 참조하는 GitOps 리소스만 둘 수 있습니다.

Cloud DNS zone/delegation과 cloud IAM role은 infrastructure가 소유합니다. External DNS controller, 동적 record 요청과 Kubernetes ServiceAccount binding은 gitops/platform이 소유합니다. 두 계층 사이에는 zone ID, role ARN 같은 명시적인 output contract만 전달합니다.

Bootstrap 경계

Bootstrap은 선언형 관리가 스스로 시작될 수 없는 최소 범위만 담당합니다.

  • foundation: state backend, 최초 CI identity와 같은 선행 조건
  • gitops: Flux 또는 Argo CD 중 선택한 컨트롤러 설치와 root reference

ingress, cert-manager, observability 같은 addon은 bootstrap이 아니라 GitOps가 소유합니다. bootstrap 이후의 변경을 계속 수동 명령으로 누적하지 않습니다.

규모 확장 기준

단계 추가하는 것 그대로 유지하는 것
소형 단일 live root, 단일 cluster root, 최소 platform lifecycle/ownership 경계
중형 reusable stack, staging/prod, 정책, 관측성 component와 entrypoint 분리
대형 계정·리전별 state, 다중 cluster, tenants, CODEOWNERS 한 리소스 한 소유자
조직 분리 lifecycle/team별 repository 분리 가능 각 repository 내부의 동일한 계약

repository를 분리하는 시점은 폴더 수가 아니라 권한, 배포 주기와 소유 팀이 실제로 달라졌을 때입니다.

설계 참고 자료