Files
project-infra/docs/standards/infra/architecture-environments.md
T

12 KiB

infra architecture / environments 기준

목적

이 문서는 1000+ 서비스 규모의 K3s 기반 production 클러스터에서

  • 환경을 어떻게 나눌지
  • namespace / label / selector를 어떻게 고정할지
  • K3s 기본 컴포넌트와 GitOps source of truth를 어떻게 구분할지
  • cross-cluster / multi-region / DR(RPO·RTO)을 어떻게 문서화할지

를 먼저 고정한다.

이 문서의 목표:

  • dev / staging / prod 환경 분리를 label·namespace·selector 레벨에서 일관되게 만든다
  • 서비스별 리소스 소유권(팀·도메인·컴포넌트)을 label로 쿼리 가능하게 한다
  • K3s packaged component와 사용자 AddOn을 혼동하지 않는다
  • 멀티 서버에서 manifests/ 디렉터리를 source-of-truth로 쓰는 사고를 원천 차단한다
  • 이후 storage / secrets / ingress / workload / observability 표준의 전제 조건을 고정한다

공식 의미 (근거)

  • Kubernetes well-known label set (공식, SIG-Apps 공인): app.kubernetes.io/{name,instance,version,component,part-of,managed-by} — 총 6개. environment는 포함되지 않는다 (https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/).
  • app.kubernetes.io/* 외의 운영 차원(environment, team, tier, region 등)은 자체 도메인 네임스페이스(example.com/*)를 붙여 선언해야 한다.
  • K3s는 coredns, traefik, local-storage, metrics-server를 packaged component로 제공한다.
  • /var/lib/rancher/k3s/server/manifests 아래 파일은 서버 시작 시와 파일 변경 시 자동 적용된다(AddOn auto-deploy).
  • packaged component manifest는 K3s가 재기록하므로 직접 수정 금지.
  • 멀티 서버 K3s는 AddOn 파일을 자동 동기화하지 않는다.
  • Kustomize v5+부터는 labels: 필드(기본 includeSelectors: false)가 commonLabels보다 안전한 기본이다. commonLabels는 항상 selector.matchLabels에 주입되며, Deployment/StatefulSet의 selector는 immutable이므로 운영 중 label 추가만으로 apply가 실패한다.
  • GitOps 기본 apply 방식은 Server-Side Apply (kubectl apply --server-side --field-manager=...)다. CI/ArgoCD/Flux 모두 SSA 기본.

기본 규칙

1. 환경은 명시적으로 분리하고 label + namespace 양쪽에 박는다

기본 환경:

  • dev
  • staging
  • prod

필요 시 sandbox / canary / dr을 추가할 수 있으나 dev/staging/prod 의미를 흐리지 않는다.

각 리소스는 두 곳에 동시에 환경이 드러나야 한다.

  • metadata.namespace — 물리적 격리
  • metadata.labels["example.com/environment"] — 쿼리·정책용 (well-known label에는 환경이 없으므로 자체 도메인 label 사용)

2. 환경 간 혼합 배포 전면 금지

하나의 namespace / hostname / PVC / Secret / TLS cert scope 안에서 서로 다른 환경 리소스가 섞이지 않는다.

금지:

  • auth-dev, auth-prod가 같은 namespace 공유
  • dev와 prod가 같은 ingress host (auth.example.com) 공유
  • staging과 prod가 같은 PostgreSQL schema / S3 bucket / Vault mount 공유
  • NetworkPolicy / ResourceQuota / LimitRange가 환경 경계를 걸치지 않음

3. namespace 전략은 “환경 prefix + 서비스 이름” 고정

1000+ 서비스 스케일에서 초기에 하나의 포맷을 박는다. 본 표준 권장은:

<env>-<domain>-<service>

예:

  • prod-identity-auth
  • prod-identity-keycloak
  • staging-identity-auth
  • dev-identity-auth
  • prod-platform-ingress-nginx
  • prod-data-postgres-identity

이유:

  • kubectl -n prod-* 와일드카드 RBAC / 모니터링 쿼리가 쉬움
  • prod- prefix로 PodSecurity admission (pod-security.kubernetes.io/enforce=restricted)을 one-shot으로 강제 가능
  • default namespace는 production workload 배포 전면 금지

4. app.kubernetes.io/* 6종은 전 리소스 필수

모든 워크로드·서비스·ingress·PVC·ConfigMap·Secret에 아래 6개가 반드시 붙는다.

  • app.kubernetes.io/name — 애플리케이션 이름 (예: auth)
  • app.kubernetes.io/instance — 인스턴스 (예: auth-prod)
  • app.kubernetes.io/version — semver 또는 image tag
  • app.kubernetes.io/component — 역할 (예: api, worker, database)
  • app.kubernetes.io/part-of — 상위 도메인 (예: identity-platform)
  • app.kubernetes.io/managed-by — 관리 도구 (예: kustomize, argocd, flux)

5. 운영 차원 label은 자체 도메인으로 선언

well-known label 6종으로 표현되지 않는 축은 다음 키로 고정한다.

  • example.com/environmentdev|staging|prod|canary|dr
  • example.com/team — 소유 팀 (예: identity-sre)
  • example.com/tierfrontend|backend|data|platform
  • example.com/data-classificationpublic|internal|confidential|restricted
  • example.com/cost-center — FinOps tag
  • example.com/slo-tiertier-1|tier-2|tier-3

금지:

  • app.kubernetes.io/environment 사용 (well-known set에 없음)
  • 도메인 없는 커스텀 키 (environment: prod 같은 top-level key)

6. selector에 들어가는 label은 불변 3종만

Deployment / StatefulSet의 selector.matchLabels는 일단 apply 후 수정 불가다. 여기에는 운영 중 절대 바뀌지 않는 값만 넣는다.

허용:

  • app.kubernetes.io/name
  • app.kubernetes.io/instance
  • app.kubernetes.io/component

금지 (selector에 넣지 말 것):

  • app.kubernetes.io/version (배포 때마다 바뀜)
  • app.kubernetes.io/managed-by (툴 교체 시 drift)
  • example.com/environment (overlay에서 주입되면 selector immutable 위반)

7. K3s packaged component는 “기본 제공”일 뿐 “무조건 사용”이 아니다

다음 컴포넌트는 클러스터 bootstrap 초기에 유지/비활성 결정을 박는다.

  • traefik
  • servicelb
  • local-storage
  • metrics-server
  • coredns (교체는 특수 케이스)

기본:

  • 무엇을 끄는지 Git에 기록
  • packaged manifest 직접 수정 금지 — --disable 플래그 또는 HelmChartConfig
  • prod 1000-서비스 스케일에서는 traefik / servicelb 모두 disable 후 ingress-nginx DaemonSet + MetalLB/외부 LB 조합이 일반적

8. /var/lib/rancher/k3s/server/manifests는 source of truth 아님

이 디렉터리는 AddOn auto-deploy 경로다. 멀티 서버 환경에서 자동 동기화가 안 되므로, Git이 source of truth고 이 디렉터리는 apply sink에 지나지 않는다.

기본:

  • Git repo의 k8s/ 디렉터리가 SoT
  • CI/ArgoCD/Flux가 kubectl apply --server-side로 push
  • 서버별 scp / vim 절대 금지
  • 멀티 서버 bootstrap AddOn도 Git 관리(예: k8s/bootstrap/*를 첫 서버에만 배치)

9. GitOps apply는 Server-Side Apply가 기본

kubectl apply --server-side --field-manager=<ci-id> -k <overlay>
kubectl diff --server-side -k <overlay>

이유:

  • multi-controller 환경(ArgoCD + HPA + VPA + operator)에서 ownership 충돌을 managedFields로 명시적 해결
  • last-applied-configuration annotation 2MB 한계 회피
  • 3-way merge 실패로 인한 silent drift 제거

10. base는 환경 중립, overlay는 환경 차이만

이후 kustomize.md에서 상세히 다룬다. 이 문서에서는 원칙만 박는다.

  • k8s/base/ — 공통 shape, 환경-agnostic
  • k8s/overlays/{dev,staging,prod}/ — patches / images / replicas / resources / labels

overlay는 base를 재작성하지 않는다. overlay diff가 100줄을 넘으면 base 설계 실패 신호다.

11. app/managing/plugins 책임 분리

k8s/base/ 하위는 다음 3축으로 고정한다.

  • app/units/<domain>/<service>/ — 애플리케이션 유닛 (auth, keycloak, test-server)
  • managing/ — Job/CronJob 운영 작업 (flyway-migrate, backup, restore, bootstrap admin)
  • plugins/ — 플랫폼 (ingress-controller, cert-manager, external-secrets, observability, policy)

이 축은 소유 팀이 다르다는 가정 위에 있다. 각 축은 독립된 Git owner (CODEOWNERS)를 가진다.

12. 상태 저장 / 외부 공개 범위를 architecture 단계에서 분류

모든 서비스는 아래 2축으로 초기 분류한다.

workload 성격 stateless / stateful / job / cronjob / daemonset
공개 범위 public / internal-only / operator-only / cluster-only

예:

  • auth-server — stateless / public
  • keycloak — stateless(앱) + stateful(외부 DB) / public (관리 포트는 internal-only)
  • vault — stateful / operator-only (+ cluster-only service endpoint)
  • minio-tenant — stateful / internal-only
  • postgres-identity — stateful / cluster-only
  • fluent-bit — daemonset / cluster-only
  • flyway-migrate — job / cluster-only

13. SLO·RPO·RTO를 환경 문서에서 먼저 박는다

환경 분리가 의미 있으려면 각 환경의 목표를 숫자로 고정해야 한다. 서비스 tier별로 아래 항목을 환경 문서에서 표로 둔다.

tier availability SLO RPO RTO backup 주기 multi-AZ PDB minAvailable
tier-1 99.95% 5m 15m 15m required 50%
tier-2 99.9% 1h 1h 1h required 1
tier-3 99.5% 24h 4h 24h optional 0

tier는 example.com/slo-tier label로 리소스마다 붙는다.

14. 멀티 서버 K3s는 critical config를 Git에서 통일

K3s multi-server에서는 아래가 모든 서버에서 동일해야 한다(불일치 시 critical configuration value mismatch로 join 실패).

  • cluster-cidr, service-cidr, cluster-dns, cluster-domain
  • disable 플래그 세트
  • flannel-backend / CNI 관련
  • embedded-registry 활성화 여부

기본:

  • /etc/rancher/k3s/config.yaml Git 관리
  • 서버별 ad-hoc 수정 금지
  • 신규 서버 조인 전 config.yaml diff 확인

15. 공개 범위별 ingress host 패턴 고정

  • public: <service>.example.com
  • internal: <service>.internal.example.com
  • operator: <service>.ops.example.com (mTLS + SSO 필수)
  • cluster-only: ingress 없음, ClusterIP + NetworkPolicy로만 접근

16. 네이밍 규칙 정리 (요약)

  • namespace: <env>-<domain>-<service>
  • Deployment/StatefulSet 이름: <service> (namespace로 환경 구분, 이름에 env 중복 금지)
  • Service 이름: Deployment 이름과 동일 (headless면 -headless suffix)
  • PVC 이름: <service>-<purpose>-<ordinal> (StatefulSet volumeClaimTemplate은 자동)
  • Kustomize overlay 디렉터리: overlays/<env>/<region>/ (멀티 region 시)

추천 디렉터리 구조

k8s/
  base/
    app/
      shared/
      units/
        identity/
          auth/
            kustomization.yaml
          keycloak/
            kustomization.yaml
        data/
          postgres-identity/
            kustomization.yaml
    managing/
      flyway-migrate-identity/
      backup-postgres/
    plugins/
      ingress-nginx/
      cert-manager/
      external-secrets/
      kube-prometheus-stack/
  overlays/
    dev/
      kustomization.yaml
    staging/
      kustomization.yaml
    prod/
      kustomization.yaml
      region-kr-main/
      region-kr-dr/
  bootstrap/
    k3s-addons-disabled/
  scripts/
    render.sh
    diff.sh
    apply.sh

프로젝트 기준 요약

  • 환경 3종(dev/staging/prod) + namespace prefix 고정
  • well-known app.kubernetes.io/* 6개 + 자체 도메인 운영 label 필수
  • app.kubernetes.io/environment 사용 금지, example.com/environment로 대체
  • selector에는 불변 3종만
  • K3s packaged component는 초기에 disable 여부 결정, 직접 수정 금지
  • manifests/는 apply sink, Git이 SoT
  • kubectl apply --server-side GitOps 기본
  • SLO / RPO / RTO 표가 환경 문서의 일부