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— 폐기 가능한 실험 환경devstagingprod
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-authprod-identity-keycloakstaging-identity-authdev-identity-authprod-platform-ingress-nginxprod-data-postgres-identity
이유:
kubectl -n prod-*와일드카드 RBAC / 모니터링 쿼리가 쉬움prod-prefix로 PodSecurity admission (pod-security.kubernetes.io/enforce=restricted)을 one-shot으로 강제 가능defaultnamespace는 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 tagapp.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/environment—lab|dev|staging|prod|canary|drexample.com/team— 소유 팀 (예:identity-sre)example.com/tier—frontend|backend|data|platformexample.com/data-classification—public|internal|confidential|restrictedexample.com/cost-center— FinOps tagexample.com/slo-tier—tier-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/nameapp.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 초기에 유지/비활성 결정을 박는다.
traefikservicelblocal-storagemetrics-servercoredns(교체는 특수 케이스)
기본:
- 무엇을 끄는지 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-configurationannotation 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/operationplatform/— 여러 app이 공유하는 platform service와 operatorpolicies/— admission, security와 governance policytenants/— namespace, RBAC, quota와 tenant boundaryclusters/— 환경/클러스터별 최종 조립과 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 / publickeycloak— stateless(앱) + stateful(외부 DB) / public (관리 포트는 internal-only)vault— stateful / operator-only (+ cluster-only service endpoint)minio-tenant— stateful / internal-onlypostgres-identity— stateful / cluster-onlyfluent-bit— daemonset / cluster-onlyflyway-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-domaindisable플래그 세트flannel-backend/ CNI 관련embedded-registry활성화 여부
기본:
/etc/rancher/k3s/config.yamlGit 관리- 서버별 ad-hoc 수정 금지
- 신규 서버 조인 전
config.yamldiff 확인
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면
-headlesssuffix) - 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이 SoTkubectl apply --server-sideGitOps 기본- SLO / RPO / RTO 표가 환경 문서의 일부