init: k8s 폴더 구조init

This commit is contained in:
donghyeon-ka
2026-07-26 17:11:25 +09:00
commit 0912b5030f
61 changed files with 2124 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
# Repository Structure
## 설계 목표
이 구조는 특정 제품의 파일 배치보다 변경 주기와 소유권을 우선합니다.
- 한 리소스에는 한 명확한 소유자가 있다.
- 재사용 구현과 실제 배포 진입점을 분리한다.
- 소규모 구성은 선택 영역을 생략할 수 있다.
- 규모가 커져도 기존 경계를 바꾸지 않고 같은 종류의 leaf를 추가한다.
- 사람이 실행하는 명령과 CI 검증이 같은 진입점을 사용한다.
## 소유권 매트릭스
| 대상 | 소유 경로 | 직접 실행 여부 | 변경 주기 |
|---|---|---:|---|
| state backend, 초기 identity | `bootstrap/foundation` | 예 | 매우 낮음 |
| 네트워크, IAM, DNS, 클러스터 | `infrastructure/live` | 예 | 낮음 |
| 재사용 IaC 단위 | `infrastructure/components` | 아니요 | 중간 |
| 재사용 IaC 조합 | `infrastructure/stacks` | 아니요 | 중간 |
| GitOps 컨트롤러와 root 연결 | `bootstrap/gitops` | 예 | 낮음 |
| 클러스터 desired state | `gitops/clusters` | reconcile 진입점 | 지속적 |
| cluster-wide addon | `gitops/platform` | 아니요 | 중간 |
| 애플리케이션 배포 정의 | `gitops/apps` | 아니요 | 높음 |
| 정책과 tenant 정의 | `gitops/policies`, `gitops/tenants` | 아니요 | 중간 |
Catalog 영역(`components`, `stacks`, `platform`, `policies`, `tenants`, `apps`)은
직접 배포하지 않습니다. 실제 진입점이 필요한 항목만 조합해서 참조합니다.
## 의존 방향
```text
bootstrap/foundation
│ output
infrastructure/components ◀── infrastructure/stacks
▲ ▲
└──────── infrastructure/live ─┘
│ cluster endpoint/identity
bootstrap/gitops
│ root reference
platform ─┐
policies ─┼────────▶ gitops/clusters
tenants ─┤
apps ─┘
```
역방향 의존은 만들지 않습니다. 예를 들어 reusable component가 특정
`live/prod` 값을 읽거나, app base가 특정 cluster overlay를 참조하면 안 됩니다.
## Infrastructure 경계
### `components`
네트워크, identity, registry, Kubernetes cluster처럼 작고 응집된 재사용
단위입니다. Terraform/OpenTofu를 선택했다면 일반적으로 backend가 없는 child
module에 해당합니다.
### `stacks`
여러 component를 반복해서 같은 방식으로 조합할 때만 사용합니다. 작은 프로젝트는
이 계층 없이 `live`가 component를 직접 호출할 수 있습니다. stack이 다른 stack을
깊게 중첩하기보다는 live root에서 평평하게 조합하는 방식을 권장합니다.
### `live`
실제로 plan/apply하는 root입니다. leaf 하나는 다음을 만족해야 합니다.
- 독립된 state와 locking
- 명시적인 provider와 backend 설정
- 고정된 component/module/chart 버전
- 비밀이 아닌 환경 입력만 저장소에 커밋
- 출력값과 downstream contract 문서화
작은 구성은 `live/dev/cluster`로 충분합니다. 계정과 리전이 늘어나면
`live/<provider>/<account>/<region>/<environment>/<stack>`처럼 경로를 확장합니다.
자동화는 경로의 고정 깊이에 의존하지 말고 실행 가능한 root 파일을 기준으로
대상을 찾도록 작성합니다.
서로 다른 환경의 root가 상대 경로로 다른 환경 구현을 import하면 안 됩니다.
공유가 필요하면 versioned component나 명시적인 remote output/data contract를
사용합니다.
## GitOps 경계
### `clusters`
클러스터가 reconcile하는 유일한 진입점입니다. 공통 리소스를 복사하지 않고
platform, policy, tenant, app catalog에서 필요한 항목만 참조합니다.
작은 구성은 `clusters/dev/main`, 다중 리전 구성은
`clusters/<environment>/<region>/<cluster>` 형태를 사용할 수 있습니다. 여기에도
고정된 경로 깊이를 강제하지 않습니다.
### `platform`
cluster-wide controller와 addon을 둡니다. 예시는 다음과 같습니다.
- ingress/gateway, external DNS, certificate
- storage class/CSI, autoscaling
- metrics, logs, traces, alerting
- secret operator와 delivery controller
각 component는 `base`와 필요한 `overlays`를 같은 디렉터리 안에 응집시킵니다.
환경 차이는 전체 파일 복사 대신 Kustomize patch 또는 별도 values로 표현합니다.
### `policies`, `tenants`, `apps`
- `policies`: cluster-wide admission 규칙, 거버넌스와 예외
- `tenants`: 구체적인 namespace, RBAC, quota, limit range, NetworkPolicy
- `apps`: application source code가 아닌 배포 정의
애플리케이션 팀이 별도 저장소를 소유하면 `apps`에는 그 저장소/OCI artifact를
참조하는 GitOps 리소스만 둘 수 있습니다.
Cloud DNS zone/delegation과 cloud IAM role은 `infrastructure`가 소유합니다.
External DNS controller, 동적 record 요청과 Kubernetes ServiceAccount binding은
`gitops/platform`이 소유합니다. 두 계층 사이에는 zone ID, role ARN 같은
명시적인 output contract만 전달합니다.
## Bootstrap 경계
Bootstrap은 선언형 관리가 스스로 시작될 수 없는 최소 범위만 담당합니다.
- `foundation`: state backend, 최초 CI identity와 같은 선행 조건
- `gitops`: Flux 또는 Argo CD 중 선택한 컨트롤러 설치와 root reference
ingress, cert-manager, observability 같은 addon은 bootstrap이 아니라 GitOps가
소유합니다. bootstrap 이후의 변경을 계속 수동 명령으로 누적하지 않습니다.
## 규모 확장 기준
| 단계 | 추가하는 것 | 그대로 유지하는 것 |
|---|---|---|
| 소형 | 단일 live root, 단일 cluster root, 최소 platform | lifecycle/ownership 경계 |
| 중형 | reusable stack, staging/prod, 정책, 관측성 | component와 entrypoint 분리 |
| 대형 | 계정·리전별 state, 다중 cluster, tenants, CODEOWNERS | 한 리소스 한 소유자 |
| 조직 분리 | lifecycle/team별 repository 분리 가능 | 각 repository 내부의 동일한 계약 |
repository를 분리하는 시점은 폴더 수가 아니라 권한, 배포 주기와 소유 팀이
실제로 달라졌을 때입니다.
## 설계 참고 자료
- [Kubernetes: Kustomize를 이용한 선언형 객체 관리](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/)
- [Flux: GitOps repository 구조](https://fluxcd.io/flux/guides/repository-structure/)
- [OpenTofu: reusable module](https://opentofu.org/docs/language/modules/)
- [OpenTofu: 평평한 module composition](https://opentofu.org/docs/language/modules/develop/composition/)
@@ -0,0 +1,37 @@
# 0001. 수명주기와 소유권 경계
- 상태: 승인
- 날짜: 2026-07-26
- 결정자: repository maintainers
## 배경
인프라 저장소는 규모가 커지면서 cloud provisioning, cluster bootstrap,
platform addon과 application 배포가 뒤섞이기 쉽습니다. 이 경우 동일 리소스를
여러 도구가 관리하거나, 작은 변경이 불필요하게 넓은 권한과 state를 요구합니다.
## 결정
저장소를 다음 세 수명주기로 분리합니다.
1. `bootstrap`: 선언형 관리가 시작되기 위한 최소 선행 조건
2. `infrastructure`: cloud/cluster 리소스 provisioning
3. `gitops`: Kubernetes desired state의 지속적 reconciliation
재사용 구현은 catalog 영역에 두고, `infrastructure/live`
`gitops/clusters`만 실제 환경 진입점으로 사용합니다. 하나의 리소스는 하나의
수명주기와 하나의 도구만 소유합니다.
## 결과
- state, 권한과 배포 실패 범위를 작게 유지할 수 있습니다.
- 소규모는 선택 디렉터리를 사용하지 않고도 시작할 수 있습니다.
- 계정, 리전과 클러스터가 늘어날 때 같은 leaf를 추가해 확장할 수 있습니다.
- 초기에는 디렉터리가 더 많아 보이지만 각 위치의 책임이 명확해집니다.
## 대안
- 환경별 전체 복사: 시작은 단순하지만 공통 변경의 drift와 중복이 빠르게 증가합니다.
- 도구별 최상위 폴더: 구현 도구는 잘 보이지만 리소스 소유권과 수명주기가 섞입니다.
- 모든 리소스를 단일 state로 관리: 작은 데모에는 가능하지만 권한과 장애 범위가
지나치게 커집니다.
+24
View File
@@ -0,0 +1,24 @@
# Architecture Decision Records
프로젝트의 장기 구조에 영향을 주는 선택은 ADR로 남깁니다.
파일명은 `NNNN-kebab-case-title.md`를 사용하고 다음 형식을 따릅니다.
```markdown
# NNNN. 제목
- 상태: 제안 | 승인 | 폐기 | 대체
- 날짜: YYYY-MM-DD
- 결정자: 팀 또는 역할
## 배경
## 결정
## 결과
## 대안
```
기존 결정을 바꿀 때 문서를 지우지 말고 새 ADR에서 이전 ADR을 대체했다고
표시합니다.
+142
View File
@@ -0,0 +1,142 @@
# Getting Started
## 예제를 먼저 확인하기
실제 파일을 만들기 전에 두 예제를 설계 참고 자료로 사용합니다.
1. `examples/minimal`에서 단일 환경의 canonical 폴더와 조립 방식을 확인합니다.
2. 다음 명령으로 platform과 app이 cluster root에서 합쳐지는 결과를 확인합니다.
```bash
kubectl kustomize examples/minimal/gitops/clusters/dev/main
```
3. 다중 계정·리전·클러스터가 필요하면 `examples/scaled/README.md`에서 경로와
state 분리 기준을 선택합니다.
4. 실제 파일은 `examples`에서 복사하지 않고 각 책임 폴더의 `_template`에서
생성합니다.
```text
examples ──▶ 구조 선택 ──▶ _template 복사 ──▶ live/clusters 구현
```
## 1. 프로젝트 선택 기록
구현을 추가하기 전에 다음 항목을 결정하고 `docs/decisions`에 ADR을 작성합니다.
- cloud/on-prem provider와 account/project 구조
- Terraform, OpenTofu, Pulumi 등 IaC 엔진
- Kustomize 중심 또는 Helm 사용 범위
- Flux 또는 Argo CD 등 GitOps 컨트롤러
- External Secrets 또는 SOPS 등 비밀 관리 방식
- admission policy와 observability 운영 범위
선택하지 않은 도구의 빈 폴더를 모두 만들 필요는 없습니다.
Terraform/OpenTofu를 선택했다면 IaC 파일을 추가하기 전에 다음 선택 파일을
만들고 한 값만 활성화합니다.
```bash
cp infrastructure/.iac-engine.example infrastructure/.iac-engine
```
`.iac-engine`에는 주석을 제외하고 `tofu` 또는 `terraform` 한 줄만 남깁니다.
선택한 도구와 version을 CI에도 설치·고정하고 project-specific
`init -backend=false`/`validate` 검사를 추가합니다.
## 2. 프로젝트 메타데이터 설정
1. `README.md`의 제목과 프로젝트 범위를 바꿉니다.
2. `.github/CODEOWNERS.example`을 실제 소유자로 수정한 뒤 `CODEOWNERS`로
이름을 바꿉니다.
3. `SECURITY.md`에 조직의 보안 연락처와 SLA를 추가합니다.
4. 선택한 도구 버전을 프로젝트의 버전 관리 방식으로 고정합니다.
5. branch protection과 required check를 설정합니다.
## 3. Foundation bootstrap
`bootstrap/foundation` 아래에 remote state, locking, 초기 CI identity 등
다른 인프라가 의존하는 최소 구성을 작성합니다.
Foundation은 일반 infrastructure state와 분리하고 변경 권한을 좁게 유지합니다.
이미 조직 공통 foundation이 있다면 이 폴더에는 외부 의존 계약과 초기화 방법만
문서화해도 됩니다.
## 4. Infrastructure 작성
작은 프로젝트는 component와 live root만으로 시작합니다.
```bash
cp -R infrastructure/components/_template infrastructure/components/kubernetes-cluster
mkdir -p infrastructure/live/dev
cp -R infrastructure/live/_template infrastructure/live/dev/cluster
```
동일한 조합이 여러 환경에서 반복될 때만 stack을 추가합니다.
```bash
cp -R infrastructure/stacks/_template infrastructure/stacks/cluster
```
`live` root마다 backend/state를 분리하고, provider credential은 파일에 저장하지
않습니다.
## 5. Desired state 조립
필요한 catalog 템플릿을 복사합니다.
```bash
cp -R gitops/platform/_template gitops/platform/core
cp -R gitops/apps/_template gitops/apps/example-api
mkdir -p gitops/clusters/dev
cp -R gitops/clusters/_template gitops/clusters/dev/main
```
component의 `base`에 공통값을 두고, 환경 차이가 있을 때만 overlay를 추가합니다.
마지막으로 cluster `kustomization.yaml`이 사용할 component를 참조하게 합니다.
복사된 README의 `__REPLACE_ME_*__` 값을 모두 실제 메타데이터로 바꿉니다.
controller에 연결하기 전에 실제 cluster root를 로컬에서 렌더해 확인합니다.
## 6. GitOps bootstrap
desired-state root가 준비되고 클러스터가 생성되면 `bootstrap/gitops`에서 GitOps
컨트롤러 하나를 선택해 설치합니다. 이 단계에는 다음만 포함합니다.
- controller 설치 또는 설치 선언
- repository/OCI source 연결
- 검증된 `gitops/clusters/<...>` root reconcile 연결
- controller가 secret manager에 접근하는 최소 identity
일반 platform addon과 application은 이 단계에 넣지 않습니다.
## 7. 검증
```bash
make doctor
make check
kubectl kustomize examples/minimal/gitops/clusters/dev/main
kubectl kustomize gitops/clusters/dev/main
```
프로젝트에서 실제 IaC, Helm, policy 파일을 추가하면 필요한 validator를
`scripts/validate.sh`에 명시적으로 추가하고 CI에서도 같은 `make check`를
호출합니다. 도구가 없을 때 조용히 성공하도록 만들지 않습니다.
첫 환경은 다음 조건을 모두 만족하면 완료된 것으로 봅니다.
- 실제 `live` root의 대상, state, owner와 실행 절차가 작성되어 있다.
- 실제 cluster root가 필요한 catalog base/overlay를 참조하고 비어 있지 않다.
- cluster root 렌더 결과와 IaC plan이 리뷰 가능하다.
- GitOps bootstrap root가 `_template`이 아닌 실제 cluster root를 가리킨다.
- 비밀 관리, rollback과 담당자 연락 경로가 문서화되어 있다.
## 8. 운영 준비
- production apply 승인 및 concurrency lock
- backup/restore와 disaster recovery runbook
- cluster와 addon upgrade 정책
- secret rotation과 접근 감사
- alert routing과 담당자
- 비용, 용량, SLO 기준
운영 준비가 끝나기 전에는 예제 값을 production에 재사용하지 않습니다.
+16
View File
@@ -0,0 +1,16 @@
# Runbooks
운영자가 긴급 상황에서도 그대로 실행할 수 있는 절차를 둡니다. 프로젝트를
운영하기 전에 최소한 다음 runbook을 준비합니다.
- foundation/state 접근 복구
- 실패한 plan/apply 복구와 state lock 처리
- GitOps controller 복구와 reconciliation 중지/재개
- cluster 및 핵심 addon upgrade/rollback
- secret rotation과 credential 노출 대응
- backup restore와 disaster recovery
- 인증서, DNS, ingress 장애 대응
- 관측성 또는 alert pipeline 장애 대응
각 문서는 `목적`, `사전 조건`, `영향`, `절차`, `검증`, `롤백`,
`에스컬레이션` 섹션을 포함해야 합니다.
+15
View File
@@ -0,0 +1,15 @@
# Runbook 제목
## 목적
## 사전 조건
## 영향
## 절차
## 검증
## 롤백
## 에스컬레이션