refactor: 구조 변경
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# ADR 0001: Internal Gitea is canonical
|
||||
|
||||
Status: accepted
|
||||
|
||||
The internal repository
|
||||
`https://git.learn.hyeonworks.com/donghyeon.kang/project-gitops` is the only
|
||||
writable deployment source.
|
||||
|
||||
Argo CD and Gitea Actions use this URL. A GitHub copy may exist only as a
|
||||
read-only mirror with monitored replication; it must never be an independent
|
||||
deployment branch. GHCR remains an image registry and does not require GitHub
|
||||
to host the GitOps source.
|
||||
@@ -0,0 +1,55 @@
|
||||
# ADR 0002: Terraform state ownership
|
||||
|
||||
Status: accepted
|
||||
|
||||
Updated: 2026-07-26
|
||||
|
||||
Terraform의 현재 범위는 Vault API 객체입니다. Kubernetes 리소스는 Argo
|
||||
CD가 소유하며, machine/network provisioning은 provider와 운영 경계가
|
||||
확정될 때 별도 root로 추가합니다.
|
||||
|
||||
`dev-k3s`는 정확히 세 state를 사용합니다.
|
||||
|
||||
| State | 소유 객체 |
|
||||
|---|---|
|
||||
| `vault-foundation` | KV/database/Transit mounts, Kubernetes auth backend/config, delegated automation policy와 선택적 분리 CI JWT auth role |
|
||||
| `vault-workloads` | workload ACL policy, Kubernetes auth role, `project-auth-jwt` Transit key |
|
||||
| `vault-database` | `database/config/auth-system-postgres-dev` connection과 `auth-db-migration-dev` dynamic role |
|
||||
|
||||
Resource 또는 Vault API path 하나는 한 state에만 속합니다. State 사이는
|
||||
이름 contract와 실행 순서만 공유하며 `terraform_remote_state`로 서로의
|
||||
snapshot을 읽지 않습니다. Backend는 encryption, versioning, access
|
||||
control, locking을 제공해야 합니다.
|
||||
|
||||
Privilege delegation의 경계는 다음과 같습니다.
|
||||
|
||||
- `vault-foundation`은 bootstrap 또는 보안 관리자 승인 때만 실행합니다.
|
||||
Routine CI identity를 두지 않습니다.
|
||||
- `vault-foundation`이 workload/database 전용 automation policy와,
|
||||
OIDC/JWT trust가 검증된 경우 서로 분리된 CI JWT login role을 생성합니다.
|
||||
두 role의 exact claim map은 최소 한 공통 discriminator key에서 서로 다른
|
||||
값을 가져야 하므로 동일 scalar-claim JWT가 둘 다 선택할 수 없습니다.
|
||||
- `vault-workloads`와 `vault-database`는 각각의 short-lived identity를
|
||||
소비할 뿐 자신에게 권한을 부여하는 객체를 소유하지 않습니다.
|
||||
- Delegated identity는 자신이 맡은 정확한 policy, auth role, database
|
||||
path만 CRUD할 수 있습니다.
|
||||
- Broad `platform-admin` 또는 상시 cluster-internal Vault administrator를
|
||||
routine automation에 연결하지 않습니다.
|
||||
|
||||
실제 CI issuer가 repository, protected ref와 job discriminator claim을
|
||||
어떤 형식으로 발행하는지 먼저 검증합니다. 그 계약을 확인할 수 없으면 JWT
|
||||
auth를 활성화하지 않고 bootstrap용 short-lived token만 사용합니다.
|
||||
Foundation의 future change는 routine identity가 아니라 encrypted unseal
|
||||
custody를 사용한 승인된 generated-root ceremony가 필요합니다.
|
||||
|
||||
Secret payload는 Terraform resource/data source로 관리하지 않습니다.
|
||||
Provider token과 PostgreSQL credential은 ephemeral variable과 write-only
|
||||
argument를 통해 실행 시점에만 전달합니다. Vault init material, token,
|
||||
password, plan과 state를 Git에 저장하지 않습니다.
|
||||
|
||||
기존 `vault-core`에서 세 state로 바꾸는 작업은 선언 이동과 state ownership
|
||||
이관을 분리해 수행합니다. Source에서는 `removed { destroy = false }`,
|
||||
destination에서는 import를 사용하고, 양쪽 plan의 destroy가 0인지 확인하기
|
||||
전에는 apply하지 않습니다. Broad `platform-admin`/`vault-operator`와
|
||||
미사용 Keycloak/PostgreSQL operator policy/role은 새 state로 옮기지
|
||||
않으며 consumer가 없음을 확인한 별도 decommission에서 제거합니다.
|
||||
@@ -0,0 +1,25 @@
|
||||
# ADR 0003: Vault topology
|
||||
|
||||
Status: accepted for dev, production decision pending
|
||||
|
||||
동일한 단일 노드 K3s 안의 두 Vault는 failure domain을 분리하지 못하면서
|
||||
초기화, Transit credential, rotation과 staged apply 절차를 추가했다.
|
||||
따라서 `dev-k3s`는 단일 self-hosted Vault로 단순화한다.
|
||||
|
||||
Dev profile:
|
||||
|
||||
- single-node integrated Raft
|
||||
- Shamir 1-of-1 init/unseal
|
||||
- TLS 미적용
|
||||
- 명시적 backup/recovery runbook
|
||||
|
||||
이 구성은 production에 사용할 수 없다. production은 다음 중 하나를
|
||||
선택해야 한다.
|
||||
|
||||
- managed Vault
|
||||
- workload cluster 밖의 독립 HA Vault
|
||||
- 최소 3-node integrated-Raft + TLS + KMS/HSM auto-unseal + PDB,
|
||||
anti-affinity와 정기 restore exercise
|
||||
|
||||
production Vault가 결정되기 전에는 production manifest와 Terraform root를
|
||||
만들지 않는다.
|
||||
@@ -0,0 +1,44 @@
|
||||
# ADR 0004: Single Argo CD root and stage-gated ApplicationSets
|
||||
|
||||
Status: accepted
|
||||
|
||||
Updated: 2026-07-26
|
||||
|
||||
Argo CD 설치 후 bootstrap 전용 `gitops-control-plane` AppProject와 단일
|
||||
root Application을 순서대로 수동 seed합니다. Root는
|
||||
`gitops/clusters/dev-k3s`를 source로 사용합니다. 이 cluster root가
|
||||
`gitops/platform/control-plane/argocd`의 AppProject와 ApplicationSet을 소유하고,
|
||||
ApplicationSet이 platform addon/shared service, system, workload
|
||||
Application을 생성합니다. Bootstrap Project는 canonical repository,
|
||||
`argocd` namespace와 AppProject/ApplicationSet kind만 허용합니다.
|
||||
|
||||
반복 `kubectl apply`와 category별 root wrapper는 사용하지 않습니다.
|
||||
Routine deployment는 Git merge만으로 시작합니다.
|
||||
|
||||
각 ApplicationSet inventory 항목은 다음 계약을 명시합니다.
|
||||
|
||||
- 고유 이름, AppProject, source, destination namespace
|
||||
- component/cluster/path와 automated reconciliation 허용 여부인 quoted
|
||||
string `autoSync`
|
||||
|
||||
새 항목과 외부 준비 조건이 있는 항목은 `autoSync: "false"`로 시작합니다.
|
||||
현재 bootstrap은 Sealed Secrets와 Vault만 열린 상태에서 시작해
|
||||
foundation/workloads state와 secret seed, injector, auth-system, database,
|
||||
first-party workload 순서로 별도 PR gate를 엽니다. Template은
|
||||
`autoSync: "true"`인 항목에만 automated sync, prune, self-heal을
|
||||
생성합니다.
|
||||
Gate가 닫힌 Application의 수동 sync도 change record와 명시적 operator
|
||||
판단을 요구합니다.
|
||||
|
||||
Sync wave는 AppProject(`-10`)를 ApplicationSet(`-5`)보다 먼저 생성합니다.
|
||||
모든 ApplicationSet은 같은 wave이며 element별 stage field는 없습니다.
|
||||
서로 다른 generated Application의 readiness는 gate와 runbook이
|
||||
제어합니다. Workload와 hook은 Vault/DB가 늦게 준비되는 상황을 retry할
|
||||
수 있고 idempotent해야 합니다.
|
||||
|
||||
Root는 AppProject와 ApplicationSet만 prune 대상으로 봅니다. Generated
|
||||
Application의 owner는 ApplicationSet이며 `create-update`에서는 element
|
||||
제거만으로 삭제되지 않습니다. Application/resource 해체는 별도
|
||||
decommission runbook과 확인 승인을 사용합니다. Shared resource 소유권
|
||||
충돌은 sync를 실패시킵니다. Sync hook을 사용하는 Application에는
|
||||
`ApplyOutOfSyncOnly=true`를 사용하지 않습니다.
|
||||
@@ -0,0 +1,33 @@
|
||||
# ADR 0005: Cluster-first repository layout
|
||||
|
||||
Status: accepted
|
||||
|
||||
Updated: 2026-07-26
|
||||
|
||||
현재는 단일 `dev-k3s`와 소수 workload를 다루므로 GitOps configuration
|
||||
monorepo를 유지합니다. Application source repository와 deployment
|
||||
configuration repository는 분리합니다. 이 저장소 자체는 독립 reference
|
||||
lab이며 범용 platform product로 간주하지 않습니다.
|
||||
|
||||
- `gitops/platform`, `gitops/apps/systems`, `gitops/apps/workloads`:
|
||||
ownership별 base; 환경 중립은 목표 contract
|
||||
- `gitops/clusters/<cluster>`: GitOps controller가 읽는 cluster root
|
||||
- `gitops/clusters/<cluster>/overlays`: cluster-specific final composition
|
||||
- `gitops/platform/control-plane/argocd/projects`: Argo 권한 경계
|
||||
- `gitops/platform/control-plane/argocd/application-sets`: reconciliation inventory
|
||||
- `infrastructure`: Kubernetes manifest와 분리된 external API IaC
|
||||
- `bootstrap`: controller가 존재하기 전의 최소 seed
|
||||
|
||||
`gitops/clusters`가 배포 가능한 최종 상태를 소유합니다. Argo CD는
|
||||
catalog base를 직접 source로 사용하지 않습니다. `foundation`은 directory
|
||||
taxonomy가 아니라 bootstrap ordering/stage이고, 구체적인 ownership 분류는
|
||||
ADR 0007을 따릅니다.
|
||||
|
||||
현재 Keycloak base의 `start-dev`와 Vault base의
|
||||
TLS-off/single-node identity는 이 contract를 위반하는 알려진 리팩터링
|
||||
부채입니다. 다른 환경을 추가하기 전에 해당 값을 component overlay나
|
||||
configuration input으로 분리합니다.
|
||||
|
||||
Production 접근권한, 소유 팀, Terraform backend 또는 release cadence가
|
||||
실제로 갈라질 때 platform GitOps, workload GitOps, IaC repository 분리를
|
||||
재검토합니다. 존재하지 않는 환경의 skeleton은 유지하지 않습니다.
|
||||
@@ -0,0 +1,33 @@
|
||||
# ADR 0006: Gateway API first, Istio deferred
|
||||
|
||||
Status: accepted
|
||||
|
||||
현재 단일 노드 K3s와 auth/api 중심 workload에는 service mesh 운영 비용을
|
||||
정당화할 mTLS identity, L7 authorization, canary traffic policy 또는
|
||||
multi-team 요구가 없다. 이번 개편에는 Istio를 설치하지 않는다.
|
||||
|
||||
선행 작업:
|
||||
|
||||
1. Traefik Gateway API provider와 GatewayClass 검증
|
||||
2. Ingress를 Gateway/HTTPRoute로 이관
|
||||
3. north-south TLS
|
||||
4. 내부 호출의 ingress hairpin 제거
|
||||
5. Vault/PostgreSQL native TLS
|
||||
6. NetworkPolicy regression test와 observability/SLO
|
||||
|
||||
Istio 요구가 실제화되면 sidecar가 아니라 ambient mode로 제한 pilot한다.
|
||||
초기 범위는 api-server와 auth-server이며 Vault, Vault injector,
|
||||
PostgreSQL은 제외한다. ztunnel L4부터 시작하고 L7 정책이 필요할 때만
|
||||
waypoint를 추가한다.
|
||||
|
||||
다음 기능 요구 중 두 개 이상과 운영 선행조건이 모두 충족될 때 ADR을
|
||||
재검토한다.
|
||||
|
||||
- ServiceAccount identity 기반 east-west mTLS
|
||||
- path/JWT 기반 L7 authorization
|
||||
- canary traffic split/retry/timeout/outlier detection
|
||||
- 지속적인 서비스·namespace·팀 증가
|
||||
- application instrumentation만으로 해결하기 어려운 장애 분석
|
||||
|
||||
현재 Ingress를 즉시 제거하지 않는다. TLS, DNS, GatewayClass 계약이
|
||||
확정되기 전 가상의 Gateway 설정을 배포하지 않기 위함이다.
|
||||
@@ -0,0 +1,65 @@
|
||||
# ADR 0007: Repository ownership boundaries
|
||||
|
||||
Status: accepted
|
||||
|
||||
Date: 2026-07-26
|
||||
|
||||
## Context
|
||||
|
||||
기존 layout은 Vault, PostgreSQL, Keycloak과 Project Auth 구성을 모두
|
||||
`platform` 또는 `foundation`으로 표현했습니다. 이 이름은 설치 순서를
|
||||
보여 주지만 누가 소비하고 변경을 책임지는지 구분하지 못했습니다.
|
||||
클러스터별 최종 구성도 `manifests`라는 일반 이름 아래 섞여 있어 base와
|
||||
overlay의 관계가 불명확했습니다.
|
||||
|
||||
이 저장소는 하나의 실제 사내 플랫폼을 배포하는 저장소가 아니라 Project
|
||||
Auth를 예제로 한 독립 GitOps reference lab입니다. 따라서 존재하지 않는
|
||||
팀/환경을 가정한 추상화보다 현재 리소스의 실제 owner와 lifecycle을
|
||||
명확히 해야 합니다.
|
||||
|
||||
## Decision
|
||||
|
||||
최상위 Kubernetes desired state를 다음 소유권으로 분류합니다.
|
||||
|
||||
- `gitops/platform`: 여러 system이 사용할 수 있고 독립 lifecycle을 가진
|
||||
cluster capability
|
||||
- `gitops/apps/systems`: 특정 bounded context가 소유하는 backing services와
|
||||
domain configuration
|
||||
- `gitops/apps/workloads`: 별도 source repository와 release digest를 가진
|
||||
first-party 실행 애플리케이션
|
||||
- `gitops/clusters/<cluster>/overlays`: 위 base에 namespace, image, host,
|
||||
secret reference, network boundary를 결합한 최종 구성
|
||||
|
||||
Vault는 `gitops/platform/shared-services/vault`에 둡니다. Sealed Secrets와 Vault
|
||||
Agent Injector는 cluster addon inventory로 관리합니다. PostgreSQL,
|
||||
Keycloak, realm/client sync는 Project Auth 전용이므로
|
||||
`gitops/apps/systems/auth-system`으로 이동합니다. `auth-server`와
|
||||
`api-server`는 `gitops/apps/workloads`에 유지합니다.
|
||||
|
||||
Project Auth backing system의 namespace는 `auth-system-dev`로 정하고,
|
||||
Vault KV 경로도 `systems/auth-system` 또는 실제 workload owner를
|
||||
반영하도록 바꿉니다.
|
||||
|
||||
`foundation`은 ownership directory로 사용하지 않습니다. 준비 순서는
|
||||
분리된 ApplicationSet category, 명시적 `autoSync` gate, runbook과 workload
|
||||
retry/idempotency로 표현합니다.
|
||||
|
||||
## Consequences
|
||||
|
||||
- 디렉터리 경로만 보고 owner와 blast radius를 추론할 수 있습니다.
|
||||
- Base는 환경 중립 contract를 목표로 하고 Argo CD는 cluster overlay만
|
||||
source로 사용합니다. 현재 Keycloak/Vault base의 dev-only 값은 알려진
|
||||
후속 리팩터링 대상입니다.
|
||||
- auth-system 이동은 namespace, DNS, NetworkPolicy, Vault policy/path와
|
||||
Terraform role binding을 함께 바꾸는 migration입니다. 단순 파일 이동으로
|
||||
취급하면 안 됩니다.
|
||||
- ApplicationSet 도입은 반복 YAML을 줄이지만 각 파일에 project를 고정하고
|
||||
Git 항목에는 component, cluster, destination, path와 quoted `autoSync`를
|
||||
명시하도록 요구합니다. Git revision은 template의 `main`으로 고정합니다.
|
||||
- 새 capability가 공용인지 system 전용인지 애매하면 소비자 수, owner,
|
||||
release cadence가 분리되는지를 먼저 검토합니다.
|
||||
- 실제 production 요구가 생기기 전에는 production skeleton을 만들지
|
||||
않습니다.
|
||||
|
||||
세부 path와 예시는
|
||||
`docs/architecture/repository-taxonomy.md`를 따른다.
|
||||
@@ -0,0 +1,45 @@
|
||||
# ADR 0008: Adopt the Kubernetes template lifecycle layout
|
||||
|
||||
Status: accepted
|
||||
|
||||
Date: 2026-07-26
|
||||
|
||||
## Context
|
||||
|
||||
기존 저장소는 `clusters`, `platform`, `systems`, `workloads`, `iac/terraform`,
|
||||
`policies`와 `hack`을 각각 최상위에 두었습니다. 리소스 소유권은 구분했지만
|
||||
bootstrap, infrastructure, GitOps desired state의 수명주기 경계가 저장소
|
||||
최상위에서 일관되게 드러나지 않았습니다.
|
||||
|
||||
## Decision
|
||||
|
||||
`k8s-template`의 수명주기 구조를 canonical repository layout으로 채택합니다.
|
||||
|
||||
- Argo CD 최초 seed는 `bootstrap/gitops/argocd`에 둡니다.
|
||||
- Vault Terraform component와 실행 root는 각각
|
||||
`infrastructure/components`와 `infrastructure/live`에 둡니다.
|
||||
- Kubernetes desired state는 `gitops/platform`, `gitops/apps`,
|
||||
`gitops/clusters` 아래에만 둡니다.
|
||||
- 기존 system/workload 소유권 분류는 `gitops/apps/systems`와
|
||||
`gitops/apps/workloads` 하위에서 유지합니다.
|
||||
- 환경별 Vault ACL은 이를 소비하는 `infrastructure/live` state root의
|
||||
`policies`에 함께 둡니다. Kubernetes admission 정책용
|
||||
`gitops/policies`와 혼합하지 않습니다.
|
||||
- `gitops/clusters/dev-k3s`를 Argo CD bootstrap root의 유일한 source로
|
||||
사용하고, 이 root가 permission-scoped ApplicationSet control plane을
|
||||
조립합니다.
|
||||
- 공통 `_template`, 예제, 구조/보안 검증을 유지하고 프로젝트별 검증을 그
|
||||
위에 추가합니다.
|
||||
|
||||
## Consequences
|
||||
|
||||
- 최상위 경로만으로 bootstrap, infrastructure, GitOps lifecycle을 구분할
|
||||
수 있습니다.
|
||||
- 기존 AppProject, ApplicationSet, `autoSync` gate와 세 Vault state의
|
||||
소유권은 유지됩니다.
|
||||
- backend 예시는 각 live root에, Vault ACL은 소비 state에 가까이 위치합니다.
|
||||
- Argo CD source path와 Terraform module source가 변경되므로 이미 연결된
|
||||
live 시스템의 이관은 별도 diff, orphan/prune 검토와 승인 없이 실행하지
|
||||
않습니다.
|
||||
- 이 ADR의 구현은 Git 작업 트리에서만 수행하며 live cluster, Vault,
|
||||
Terraform backend를 변경하지 않습니다.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Architecture Decision Records
|
||||
|
||||
프로젝트의 장기 구조에 영향을 주는 선택은 ADR로 남깁니다.
|
||||
|
||||
파일명은 `NNNN-kebab-case-title.md`를 사용하고 다음 형식을 따릅니다.
|
||||
|
||||
```markdown
|
||||
# NNNN. 제목
|
||||
|
||||
- 상태: 제안 | 승인 | 폐기 | 대체
|
||||
- 날짜: YYYY-MM-DD
|
||||
- 결정자: 팀 또는 역할
|
||||
|
||||
## 배경
|
||||
|
||||
## 결정
|
||||
|
||||
## 결과
|
||||
|
||||
## 대안
|
||||
```
|
||||
|
||||
기존 결정을 바꿀 때 문서를 지우지 말고 새 ADR에서 이전 ADR을 대체했다고
|
||||
표시합니다.
|
||||
|
||||
현재 프로젝트 결정은 이 디렉터리의 `0001`부터 순서대로 관리합니다.
|
||||
Reference in New Issue
Block a user