refactor: 폴더 구조 변경

This commit is contained in:
donghyeon-ka
2026-08-02 00:22:19 +09:00
parent f9c463f87a
commit be2f8e4863
1869 changed files with 4565 additions and 295591 deletions
+3 -3
View File
@@ -25,7 +25,7 @@
| 키 | 허용 값 |
| --- | --- |
| `example.com/environment` | `dev` \| `staging` \| `prod` |
| `example.com/environment` | `lab` \| `dev` \| `staging` \| `prod` |
| `example.com/owner-team` | 팀 slug (e.g., `auth-platform`, `sre`) |
| `example.com/cost-center` | 회계 코스트 센터 ID |
| `example.com/data-classification` | `public` \| `internal` \| `confidential` \| `restricted` |
@@ -149,7 +149,7 @@ spec:
1. **prod 환경**: `<registry>/<path>@sha256:<digest>` 형태 digest pin **필수**. 뮤터블 태그(`:1`, `:latest`, `:main`) 금지.
2. **staging**: digest 권장, 최소 semver tag(`:1.24.0`) 허용. 절대 `:latest` 금지.
3. **dev**: semver tag 허용, `:latest` 지양 (로컬 / 노드 cache invalidation 이슈).
3. **lab / dev**: semver tag 허용, `:latest` 지양 (로컬 / 노드 cache invalidation 이슈). `lab` 은 폐기 가능한 단일 클러스터 실험 환경에만 사용한다.
4. `imagePullPolicy`:
- digest 사용 시 `IfNotPresent` (이미지 콘텐츠는 immutable)
- 뮤터블 태그 사용 시 `Always`
@@ -265,7 +265,7 @@ spec:
이 문서의 규약은 CI 에서 기계 검증된다.
- 실행: `k8s/scripts/ci/validate-docs.sh`
- 실행: `scripts/ci/validate-docs.sh`
- Lint 설정 위치: `.kube-linter.yaml` (repo root)
- 목표 스코어:
- syntax 에러: **0**
@@ -12,7 +12,7 @@
이 문서의 목표:
- dev / staging / prod 환경 분리를 **label·namespace·selector 레벨에서** 일관되게 만든다
- lab / dev / staging / prod 환경 분리를 **label·namespace·selector 레벨에서** 일관되게 만든다
- 서비스별 리소스 소유권(팀·도메인·컴포넌트)을 label로 쿼리 가능하게 한다
- K3s packaged component와 사용자 AddOn을 혼동하지 않는다
- 멀티 서버에서 `manifests/` 디렉터리를 source-of-truth로 쓰는 사고를 원천 차단한다
@@ -35,11 +35,13 @@
기본 환경:
- `lab` — 폐기 가능한 실험 환경
- `dev`
- `staging`
- `prod`
필요 시 `sandbox` / `canary` / `dr`을 추가할 수 있으나 dev/staging/prod 의미를 흐리지 않는다.
`lab`은 운영 승격 단계가 아니다. 필요 시 `canary` / `dr`을 추가할 수 있으나
dev/staging/prod 의미를 흐리지 않는다.
각 리소스는 두 곳에 동시에 환경이 드러나야 한다.
@@ -95,7 +97,7 @@
well-known label 6종으로 표현되지 않는 축은 다음 키로 고정한다.
- `example.com/environment``dev|staging|prod|canary|dr`
- `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`
@@ -107,7 +109,7 @@ well-known label 6종으로 표현되지 않는 축은 다음 키로 고정한
- `app.kubernetes.io/environment` 사용 (well-known set에 없음)
- 도메인 없는 커스텀 키 (`environment: prod` 같은 top-level key)
### 6. selector에 들어가는 label은 **불변 3종만**
### 6. selector에 들어가는 label은 **불변 2종만**
Deployment / StatefulSet의 `selector.matchLabels`는 일단 apply 후 수정 불가다. 여기에는 운영 중 **절대 바뀌지 않는** 값만 넣는다.
@@ -115,11 +117,11 @@ 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/component` (역할 재분류 시 immutable selector 충돌)
- `app.kubernetes.io/managed-by` (툴 교체 시 drift)
- `example.com/environment` (overlay에서 주입되면 selector immutable 위반)
@@ -146,10 +148,11 @@ Deployment / StatefulSet의 `selector.matchLabels`는 일단 apply 후 수정
기본:
- Git repo의 `k8s/` 디렉터리가 SoT
- Git repo의 `gitops/` 디렉터리가 Kubernetes desired state의 SoT
- CI/ArgoCD/Flux가 `kubectl apply --server-side`로 push
- 서버별 scp / vim 절대 금지
- 멀티 서버 bootstrap AddOn도 Git 관리(예: `k8s/bootstrap/*`를 첫 서버에만 배치)
- 멀티 서버 bootstrap AddOn도 Git 관리(`bootstrap/`에서 최소 설치 후
`gitops/clusters/` root로 인계)
### 9. GitOps apply는 Server-Side Apply가 기본
@@ -168,20 +171,25 @@ kubectl diff --server-side -k <overlay>
이후 `kustomize.md`에서 상세히 다룬다. 이 문서에서는 원칙만 박는다.
- `k8s/base/` — 공통 shape, 환경-agnostic
- `k8s/overlays/{dev,staging,prod}/` — patches / images / replicas / resources / labels
- `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. `app/managing/plugins` 책임 분리
### 11. catalog와 rollout 책임 분리
`k8s/base/` 하위는 다음 3축으로 고정한다.
`gitops/` 하위 소유권은 다음 축으로 고정한다.
- `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)
- `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
이 축은 **소유 팀이 다르다**는 가정 위에 있다. 각 축은 독립된 Git owner (CODEOWNERS)를 가진다.
stateful 여부보다 실제 lifecycle owner를 우선합니다. 예를 들어 app 전용
PostgreSQL과 Flyway는 해당 app catalog가, 공용 MinIO와 Vault는 platform이
소유합니다. 각 축은 독립된 Git owner(CODEOWNERS)를 가질 수 있습니다.
### 12. 상태 저장 / 외부 공개 범위를 architecture 단계에서 분류
@@ -247,50 +255,41 @@ K3s multi-server에서는 아래가 모든 서버에서 동일해야 한다(불
## 추천 디렉터리 구조
```text
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
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/
```
## 프로젝트 기준 요약
- 환경 3종(`dev`/`staging`/`prod`) + namespace prefix 고정
- 환경 4종(`lab`/`dev`/`staging`/`prod`) + namespace prefix 고정
- well-known `app.kubernetes.io/*` 6개 + 자체 도메인 운영 label 필수
- `app.kubernetes.io/environment` 사용 금지, `example.com/environment`로 대체
- selector에는 불변 3종
- selector에는 불변 2종(`name`/`instance`)
- K3s packaged component는 초기에 disable 여부 결정, 직접 수정 금지
- `manifests/`는 apply sink, Git이 SoT
- `kubectl apply --server-side` GitOps 기본
+5 -2
View File
@@ -90,7 +90,8 @@ Flyway migration은 **독립 실행 단계**다.
- CI/CD 명시 단계
- 운영자 명시 실행 절차
장기 실행 Deployment에 넣지 않는다. Flyway 예시는 `examples/infra/flyway.md` 참조.
장기 실행 Deployment에 넣지 않는다. Flyway 예시는
`docs/examples/infra/flyway.md` 참조.
### 5. migration Job은 배포 흐름 안에서 app보다 먼저 실행
migration을 app보다 **선행**시키는 것은 manifest 메타데이터로 선언한다.
@@ -139,7 +140,9 @@ metadata:
4. `flyway info` (결과 확인)
5. app rollout
`validateOnMigrate=true` 기본값이 있더라도, 운영 runbook에서는 validate 단계를 **분리 Job** 또는 **initContainer**로 분리한다. `examples/infra/flyway.md` 참조.
`validateOnMigrate=true` 기본값이 있더라도, 운영 runbook에서는 validate 단계를
**분리 Job** 또는 **initContainer**로 분리한다.
`docs/examples/infra/flyway.md` 참조.
### 8. migration source는 Git이 source of truth
중요한 것은 아래다.
+1 -1
View File
@@ -75,7 +75,7 @@ Pod 내부에서 TLS 재암호화가 필요 없으면 Pod는 HTTP로 수신한
기본:
- `KC_DB=postgres`
- `KC_DB_URL=jdbc:postgresql://keycloak-db-rw:5432/keycloak` (CloudNativePG `-rw` RW endpoint 권장)
- `KC_DB_URL=jdbc:postgresql://keycloak-db-rw:5432/keycloak` (CloudNativePG `-rw` RW endpoint 권장) <!-- gitleaks:allow -->
- `KC_DB_USERNAME`, `KC_DB_PASSWORD` → Secret `secretKeyRef`
- Keycloak schema와 auth-server schema는 **다른 DB 또는 다른 database**로 분리
+64 -76
View File
@@ -57,10 +57,10 @@ Kustomize는 Kubernetes 리소스를 **template-free**로 조합하고 환경별
overlay 한 디렉터리의 `kustomization.yaml`은 짧아야 한다. diff가 몇 백 줄을 넘으면 base 설계 실패 신호.
권장 구조:
권장 unit overlay 구조:
```
overlays/prod/
gitops/apps/auth-server/overlays/prod/
kustomization.yaml
patches/
auth-replicas.yaml
@@ -70,21 +70,26 @@ overlays/prod/
postgres-storage.yaml
```
### 4. 디렉터리 구조는 base / components / overlays 3축
### 4. catalog unit과 cluster entrypoint를 분리
```
k8s/
base/
app/units/<domain>/<service>/
managing/<job>/
plugins/<platform>/
components/
<reusable-cross-cutting>/
overlays/
<env>/[region/]
gitops/
apps/<unit>/
base/
components/
overlays/<env>/
platform/<unit>/
base/
components/
overlays/<env>/
policies/<unit>/
tenants/<unit>/
clusters/<env>/<region-or-cluster>/
```
`components/`"Kustomize Components"로, 여러 overlay에서 재사용.
`components/`소유 unit 내부의 Kustomize Component입니다. 여러 catalog를
조립하는 최종 경계는 `clusters/`이며 catalog 디렉터리를 controller root로
직접 사용하지 않습니다.
### 5. `commonLabels` 금지, `labels:` 사용
@@ -108,15 +113,14 @@ labels:
기존 `commonLabels` 사용 코드는 migration plan을 세워 교체. selector에 이미 들어간 label이 있다면 해당 리소스를 **재배포** (delete + recreate) 없이는 변경 불가.
### 6. selector에는 불변 3종만
### 6. selector에는 불변 2종만
overlay에서 selector를 건드리지 않는다. selector에 허용되는 label은:
- `app.kubernetes.io/name`
- `app.kubernetes.io/instance`
- `app.kubernetes.io/component`
3종은 base에서 고정. overlay가 `labels:`로 추가하는 label은 반드시 `includeSelectors: false`.
2종은 base에서 고정. overlay가 `labels:`로 추가하는 label은 반드시 `includeSelectors: false`.
### 7. `patches:` (v5 스타일) 사용, `patchesStrategicMerge` / `patchesJson6902` 금지
@@ -146,7 +150,7 @@ patches:
multiple overlay에서 공통으로 끼워야 하는 변경(예: mTLS 활성화, sidecar 주입, monitoring label 추가)은 component로.
```
components/
gitops/apps/auth-server/components/
with-istio-sidecar/
kustomization.yaml # kind: Component
patches/
@@ -215,11 +219,15 @@ field manager 이름을 환경별로 통일해야 `managedFields` 충돌이 예
CI가 아래를 순서대로 실행:
```bash
kubectl kustomize overlays/prod > /tmp/rendered.yaml
kubectl kustomize gitops/clusters/prod/kr-main/all > /tmp/rendered.yaml
kubeconform -strict -summary -schema-location default -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' /tmp/rendered.yaml
kubectl diff --server-side --field-manager=ci -k overlays/prod
kubectl diff --server-side --field-manager=ci -k gitops/clusters/prod/kr-main/stages/50-apps
```
`all`은 schema/policy 감사용이고 diff/apply는 의존성이 준비된 개별 stage를
대상으로 합니다. 여러 stage 변경은 승인된 orchestrator/controller가 순서와
health gate를 보장해야 합니다.
- kubeconform / kubeval: schema validation
- kyverno / OPA Gatekeeper: policy validation (post-render)
- conftest: opa policy bundle 실행
@@ -254,62 +262,41 @@ v2.1에서 `bases:`가 `resources:`로 통합됨. 신규 파일에서 `bases:`
## 추천 폴더 구조
```text
k8s/
base/
app/
kustomization.yaml
units/
identity/
auth/
kustomization.yaml
deployment.yaml
service.yaml
servicemonitor.yaml
pdb.yaml
hpa.yaml
keycloak/
kustomization.yaml
data/
postgres-identity/
kustomization.yaml
statefulset.yaml
service-headless.yaml
service.yaml
managing/
flyway-migrate-identity/
gitops/
apps/
auth/
base/
kustomization.yaml
job.yaml
backup-postgres/
kustomization.yaml
cronjob.yaml
plugins/
ingress-nginx/
cert-manager/
external-secrets/
kube-prometheus-stack/
fluent-bit/
components/
with-service-monitor/
with-pdb-tier1/
with-topology-spread-zone/
with-network-policy-deny-default/
overlays/
dev/
kustomization.yaml
staging/
kustomization.yaml
prod/
kr-main/
kustomization.yaml
patches/
kr-dr/
kustomization.yaml
patches/
scripts/
render.sh
diff.sh
apply.sh
validate.sh
deployment.yaml
service.yaml
servicemonitor.yaml
pdb.yaml
hpa.yaml
components/
with-service-monitor/
with-pdb-tier1/
with-topology-spread-zone/
overlays/{lab,staging,prod}/
postgres-identity/
base/
overlays/{lab,staging,prod}/
flyway-migrate-identity/
base/
overlays/{lab,staging,prod}/
platform/
ingress-nginx/
cert-manager/
secret-delivery/
policies/
network-policy-deny-default/
clusters/
lab/main/stages/
staging/main/stages/
prod/kr-main/stages/
prod/kr-dr/stages/
scripts/
bin/
ci/
```
## 프로젝트 기준 요약
@@ -317,9 +304,10 @@ k8s/
- Kustomize v5 문법 기준, `commonLabels` 금지, `labels:` 사용
- `patches:` 단일 키, `target:` + `path:` 또는 `patch:` inline
- `components:`로 cross-cutting 재사용
- selector에는 불변 3종만 (name / instance / component)
- selector에는 불변 2종만 (name / instance)
- generator는 configMap만 기본, secret은 External Secrets
- `kubectl apply --server-side --field-manager=<id>` 전제
- render + schema + policy 검증을 CI에서 강제
- base / components / overlays 3축 디렉터
- catalog unit의 base/components/overlays와 cluster entrypoint를 분
- `stages/`만 apply하고 `all/`은 render/schema/policy audit에만 사용
- overlay diff는 짧아야 한다 (base 재작성 금지)
+4 -1
View File
@@ -88,7 +88,10 @@
기본:
- `spec.configuration.name`에 root credential Secret (MINIO_ROOT_USER, MINIO_ROOT_PASSWORD)
- Vault KV에 root credential 저장, VSO로 Secret 동기화
- 앱용 access는 `mc admin user svcacct add` 로 service account 발급
- 앱용 access는 `mc admin accesskey create`로 서버 생성 credential을 발급하고
inline policy로 권한을 축소한다
- 생성 결과의 secret key는 한 번만 수신해 stdin으로 Vault에 기록하며,
`--secret-key` 또는 `mc admin user add`로 secret을 argv에 전달하지 않는다
- service account는 최소 권한 policy 바인딩
### 7. TLS는 기본 활성화
@@ -126,18 +126,17 @@ Flux 플랫폼에서는 `Kustomization.spec.dependsOn` 으로 순서를 명시
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: auth-server
name: prod-50-apps
namespace: flux-system
spec:
interval: 5m
path: ./k8s/overlays/prod
path: ./gitops/clusters/prod/main/stages/50-apps
prune: true
sourceRef:
kind: GitRepository
name: platform
dependsOn:
- name: cert-manager
- name: postgres-operator
- name: prod-40-operations
```
### 11. node 작업 (drain / cordon) 은 PDB 존중 흐름