# Deployment architecture ## Reconciliation boundaries ```text Gitea main | +-- Argo CD root | -> AppProjects + ApplicationSets | -> generated Applications | -> Kubernetes | +-- approved Terraform runner -> one of three Vault states -> Vault API ``` Argo CD는 Kubernetes desired state만 관리합니다. 최초 Argo 설치와 bootstrap 전용 AppProject/root seed, 문서화된 recovery 외에는 직접 cluster mutation을 하지 않습니다. Terraform은 Config Management Plugin이나 Argo hook 안에서 실행하지 않습니다. GHCR은 image artifact registry이며 desired state source가 아닙니다. ## Kubernetes ownership | Layer | 역할 | |---|---| | `platform/control-plane/argocd` | AppProject와 ApplicationSet control plane | | `platform/shared-services/*/base` | 환경 중립을 목표로 하는 공유 cluster service base | | `systems/*/base` | 환경 중립을 목표로 하는 bounded-context backing system base | | `workloads/*/base` | first-party 애플리케이션 base | | `clusters/dev-k3s/overlays/*` | dev namespace, host, digest, Vault role/path, NetworkPolicy를 합친 최종 구성 | 현재 concrete ownership은 Vault가 platform shared service, PostgreSQL/Keycloak이 `systems/auth-system`, 두 서버가 workload입니다. Argo CD Application은 base가 아니라 최종 cluster overlay만 source로 사용합니다. 현재 Keycloak base의 `start-dev`와 Vault base의 TLS-off/single-node identity는 dev-specific 예외입니다. 내부 Service 참조는 짧은 DNS로 namespace 중립화했지만, 남은 값을 overlay로 추출하는 작업은 후속 리팩터링입니다. 지원하지 않는 production overlay는 존재하지 않습니다. Production trust, approval, TLS, availability contract가 확정될 때 별도로 설계합니다. ## Bootstrap progression ```text Argo root -> Sealed Secrets + Vault autoSync -> Vault init -> vault-foundation -> vault-workloads -> runtime secret seed -> Vault Agent Injector autoSync gate -> auth-system autoSync gate -> PostgreSQL Healthy -> vault-database -> Keycloak/client sync ready -> auth-server autoSync gate -> auth-server Healthy -> api-server autoSync gate ``` 이 순서는 Application sync wave로 강제하지 않습니다. 각 전환은 health와 plan/diff를 확인한 별도 PR입니다. Gate가 닫힌 동안에도 generated Application은 OutOfSync diff를 보여 줍니다. ## In-application ordering `auth-server`의 한 sync operation 안에서는 다음 ordering을 사용합니다. - generated ConfigMap과 일반 리소스: wave `0` - database migration Sync hook: wave `5` - Deployment: wave `10` - north-south route: wave `20` `auth-system`의 Keycloak client sync도 idempotent Sync hook이며 deadline, backoff, `BeforeHookCreation,HookSucceeded` cleanup을 사용합니다. Application 간 준비 순서와 Application 내부 hook 순서를 혼동하지 않습니다. ## Stateful lifecycle Vault PVC에는 `Prune=confirm,Delete=confirm`이 명시되어 있습니다. PostgreSQL PVC는 StatefulSet `volumeClaimTemplates`가 생성하며 현재 manifest에 별도 Argo prune annotation이 없습니다. Namespace와 generated Application 삭제 보호만 믿지 말고 PostgreSQL retention/backup을 직접 확인해야 합니다. Path, namespace, Application 이름을 이동할 때는 다음을 별도 migration으로 다룹니다. 1. 기존 live object와 owner를 inventory합니다. 2. 새 owner가 같은 object를 안전하게 추적할 수 있는지 render/diff로 확인합니다. 3. Stateful data backup과 rollback 지점을 확보합니다. 4. 기존 owner를 non-cascading 방식으로 제거한 뒤 새 owner를 연결합니다. 이번 리팩터링에서는 `platform` namespace의 auth-system을 `auth-system-dev`로 옮기는 live 작업을 실행하지 않았습니다. ## Image promotion과 GHCR 정상 promotion에서 first-party image CI는 검증한 정확한 GHCR digest를 Gitea workflow에 전달합니다. Workflow는 전용 branch와 digest 변경 PR을 만들고 validation과 승인을 거쳐 merge된 뒤 Argo CD가 배포합니다. Renovate는 외부 chart, third-party image와 Terraform provider만 갱신하며 두 first-party GHCR package는 비활성화합니다. 따라서 동일 image field를 promotion workflow와 Renovate가 동시에 쓰지 않습니다. Private GHCR pull credential만 SealedSecret으로 Git에 저장합니다. 평문 credential이나 registry token은 manifest, Actions log, Terraform state에 남기지 않습니다. 현재 short-SHA tag는 migration 시점의 예외입니다. Registry 검증 없이 임의 digest를 만들지 않고 다음 정상 promotion에서 immutable digest로 교체합니다.