297 lines
12 KiB
Markdown
297 lines
12 KiB
Markdown
# 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/environment` — `lab|dev|staging|prod|canary|dr`
|
|
- `example.com/team` — 소유 팀 (예: `identity-sre`)
|
|
- `example.com/tier` — `frontend|backend|data|platform`
|
|
- `example.com/data-classification` — `public|internal|confidential|restricted`
|
|
- `example.com/cost-center` — FinOps tag
|
|
- `example.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/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 시)
|
|
|
|
## 추천 디렉터리 구조
|
|
|
|
```text
|
|
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 표가 환경 문서의 일부
|