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)을 어떻게 문서화할지

를 먼저 고정한다.

이 문서의 목표:

  • lab / 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 양쪽에 박는다

기본 환경:

  • lab — 폐기 가능한 실험 환경
  • dev
  • staging
  • prod

lab은 운영 승격 단계가 아니다. 필요 시 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/environmentlab|dev|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은 불변 2종만

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

허용:

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

금지 (selector에 넣지 말 것):

  • app.kubernetes.io/version (배포 때마다 바뀜)
  • app.kubernetes.io/component (역할 재분류 시 immutable selector 충돌)
  • 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의 gitops/ 디렉터리가 Kubernetes desired state의 SoT
  • CI/ArgoCD/Flux가 kubectl apply --server-side로 push
  • 서버별 scp / vim 절대 금지
  • 멀티 서버 bootstrap AddOn도 Git 관리(bootstrap/에서 최소 설치 후 gitops/clusters/ root로 인계)

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에서 상세히 다룬다. 이 문서에서는 원칙만 박는다.

  • gitops/{apps,platform,policies,tenants}/<unit>/base — 공통 shape, 환경-agnostic
  • 각 unit의 overlays/{lab,dev,staging,prod} — patches / images / replicas / resources / labels
  • gitops/clusters/<env>/<cluster> — catalog를 선택하는 실제 rollout entrypoint

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

11. catalog와 rollout 책임 분리

gitops/ 하위 소유권은 다음 축으로 고정한다.

  • apps/ — application-facing workload와 그 app이 독점 소유하는 data/operation
  • platform/ — 여러 app이 공유하는 platform service와 operator
  • policies/ — admission, security와 governance policy
  • tenants/ — namespace, RBAC, quota와 tenant boundary
  • clusters/ — 환경/클러스터별 최종 조립과 rollout entrypoint

stateful 여부보다 실제 lifecycle owner를 우선합니다. 예를 들어 app 전용 PostgreSQL과 Flyway는 해당 app catalog가, 공용 MinIO와 Vault는 platform이 소유합니다. 각 축은 독립된 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 시)

추천 디렉터리 구조

bootstrap/
  foundation/
  gitops/
gitops/
  apps/
    auth-server/
      base/
      overlays/{lab,staging,prod}/
    identity-postgres/
    auth-migration/
  platform/
    ingress-nginx/
    cert-manager/
    secret-delivery/
  policies/
    baseline/
  tenants/
    identity/
  clusters/
    lab/main/stages/
    staging/main/stages/
    prod/kr-main/stages/
    prod/kr-dr/stages/
scripts/
  bin/
  ci/
  tasks/

프로젝트 기준 요약

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