refactor(gitops): establish platform ownership boundaries
This commit is contained in:
+150
-29
@@ -1,38 +1,159 @@
|
||||
# Argo CD layout
|
||||
# Argo CD architecture
|
||||
|
||||
`bootstrap/argocd/root-application.yaml`이 유일한 수동 seed입니다. 이
|
||||
Application은 `clusters/dev-k3s`를 source로 사용하고 다음 리소스를
|
||||
소유합니다.
|
||||
## Control-plane ownership
|
||||
|
||||
Controller 설치 후 다음 두 bootstrap object를 순서대로 수동 seed합니다.
|
||||
|
||||
1. `bootstrap/argocd/control-plane-project.yaml`
|
||||
2. `bootstrap/argocd/root-application.yaml`
|
||||
|
||||
`gitops-control-plane` AppProject는 canonical Gitea repository와 in-cluster
|
||||
`argocd` namespace, AppProject/ApplicationSet kind만 허용합니다. 단일 root
|
||||
Application은 이 Project를 사용하고 다음 control-plane 구성을 source로
|
||||
사용합니다.
|
||||
|
||||
```text
|
||||
clusters/dev-k3s
|
||||
platform/control-plane/argocd
|
||||
├── projects
|
||||
└── applications
|
||||
├── foundation
|
||||
│ ├── sealed-secrets
|
||||
│ ├── vault
|
||||
│ └── vault-agent-injector
|
||||
├── platform
|
||||
│ └── auth-system
|
||||
└── workloads
|
||||
├── auth-server
|
||||
└── api-server
|
||||
│ ├── platform-addons.yaml
|
||||
│ ├── platform-services.yaml
|
||||
│ ├── systems.yaml
|
||||
│ └── workloads.yaml
|
||||
└── application-sets
|
||||
├── platform-addons.yaml
|
||||
├── platform-services.yaml
|
||||
├── systems.yaml
|
||||
└── workloads.yaml
|
||||
```
|
||||
|
||||
AppProject는 root sync wave `-10`, foundation은 `0~1`, platform은 `10`,
|
||||
workload는 `20`입니다. 이 wave는 child Application 객체 생성 순서만
|
||||
표현하며 서로 다른 Application의 readiness dependency로 사용하지
|
||||
않습니다. Vault Agent와 workload는 필요한 Vault/DB API가 준비될 때까지
|
||||
자체 retry 가능한 형태여야 합니다.
|
||||
Root는 AppProject와 ApplicationSet까지만 직접 소유합니다. 각
|
||||
ApplicationSet의 list inventory가 실제 child Application을 생성합니다.
|
||||
Routine 변경에 category별 root나 직접 `kubectl apply`를 추가하지 않습니다.
|
||||
|
||||
모든 child Application은 auto-sync, prune, self-heal을 사용합니다.
|
||||
Application 삭제와 parent prune은 확인이 필요하며, shared resource
|
||||
소유권 충돌은 `FailOnSharedResource=true`로 실패시킵니다.
|
||||
## AppProject boundary
|
||||
|
||||
Sync hook이 있는 `auth-server`와 `auth-system`에는 selective sync 옵션을
|
||||
사용하지 않습니다. DB migration과 Keycloak client sync는 같은
|
||||
Application 내부 wave로 순서를 제어합니다.
|
||||
| AppProject | 소유 범위 | 허용 destination |
|
||||
|---|---|---|
|
||||
| `platform-addons` | Sealed Secrets, Vault Agent Injector 같은 외부 cluster addon | `kube-system`, `vault` |
|
||||
| `platform-services` | 저장소가 소유하는 Vault shared service | `vault` |
|
||||
| `systems` | Project Auth 전용 PostgreSQL, Keycloak, sync job | `auth-system-dev` |
|
||||
| `workloads` | first-party `auth-server`, `api-server` | `auth-dev`, `api-dev` |
|
||||
|
||||
클러스터가 하나이고 child Application 수가 적으므로 현재는 명시적
|
||||
Application을 사용합니다. 두 번째 클러스터나 실제 production이 생길
|
||||
때 foundation/platform/workload별 ApplicationSet 도입을 검토합니다.
|
||||
Project는 ApplicationSet 파일마다 고정하며 inventory 값으로 template하지
|
||||
않습니다. 이렇게 해야 element 변경으로 권한 경계를 넘을 수 없습니다.
|
||||
각 Project는 필요한 source repository, destination, resource kind만
|
||||
allowlist합니다.
|
||||
|
||||
## ApplicationSet contract
|
||||
|
||||
ApplicationSet은 strict Go template와 list generator를 사용합니다.
|
||||
|
||||
- `goTemplate: true`
|
||||
- `goTemplateOptions: ["missingkey=error"]`
|
||||
- 공통 element: `component`, `cluster`, `server`, `namespace`, quoted string
|
||||
`autoSync`
|
||||
- Git source element: ownership grammar를 따르는 `path`; `targetRevision`은
|
||||
template의 `main`으로 고정
|
||||
- Helm addon element: allowlisted `repoURL`, `chart`, chart `revision`,
|
||||
`helmValues`
|
||||
- 파일별 고정 project
|
||||
|
||||
필수 key가 빠지면 빈 문자열로 잘못 배포하지 않고 render가 실패해야 합니다.
|
||||
Application 이름, destination, source path는 같은 element에서 파생하되
|
||||
project와 Git repository/revision trust boundary는 template하지 않습니다.
|
||||
외부 addon의 Helm `repoURL`은 element에서 template되지만 고정 AppProject의
|
||||
`sourceRepos` allowlist 밖 URL은 sync할 수 없습니다.
|
||||
|
||||
## `autoSync` stage gate
|
||||
|
||||
`autoSync`는 bootstrap 준비 상태와 routine reconciliation을 분리합니다.
|
||||
Inventory schema는 Go template 비교를 위해 boolean이 아닌 quoted string
|
||||
`"true"`/`"false"`를 사용합니다. Template patch는 값이 `"true"`인
|
||||
element에만 다음 정책을 추가합니다.
|
||||
|
||||
```yaml
|
||||
syncPolicy:
|
||||
automated:
|
||||
enabled: true
|
||||
prune: true
|
||||
selfHeal: true
|
||||
```
|
||||
|
||||
초기 gate는 다음과 같습니다.
|
||||
|
||||
| Application | 초기 `autoSync` | 열기 전 확인 |
|
||||
|---|---:|---|
|
||||
| Sealed Secrets | `"true"` | Argo가 chart source를 읽을 수 있음 |
|
||||
| Vault | `"true"` | dev PVC와 NetworkPolicy 변경 검토 |
|
||||
| Vault Agent Injector | `"false"` | Vault init, `vault-foundation`, `vault-workloads`, runtime secret seed 완료 |
|
||||
| `auth-system` | `"false"` | Injector Healthy와 Vault login/secret capability 확인 |
|
||||
| `auth-server` | `"false"` | PostgreSQL Healthy, `vault-database`, Keycloak/sync 준비 완료 |
|
||||
| `api-server` | `"false"` | `auth-server` Healthy와 호출 경로 확인 |
|
||||
|
||||
Gate는 단계별 PR로 하나씩 엽니다. `false`여도 Application 생성과 diff
|
||||
표시는 계속되며 수동 Sync 자체를 기술적으로 막지는 않습니다. 따라서
|
||||
Argo RBAC에서 sync 권한을 제한하고, 수동 Sync에는 명시적 change record를
|
||||
요구합니다.
|
||||
|
||||
비활성 기간 동안 쌓인 모든 diff가 gate를 여는 순간 함께 반영됩니다.
|
||||
`autoSync: "true"` PR은 현재 live-to-desired 전체 diff를 검토한 뒤
|
||||
승인해야 합니다.
|
||||
|
||||
## Ordering과 failure handling
|
||||
|
||||
Control-plane sync wave는 AppProject(`-10`)를 ApplicationSet(`-5`)보다
|
||||
먼저 생성합니다. 네 ApplicationSet은 모두 같은 wave이고 element별
|
||||
stage/wave field는 없습니다. Generated Application의 실제 readiness
|
||||
순서는 `autoSync` 전환과 health 확인이 담당합니다.
|
||||
|
||||
Vault Agent, workload와 hook은 선행 API가 늦게 준비될 때 retry할 수 있어야
|
||||
합니다. `auth-system`의 Keycloak client sync와 `auth-server`의 database
|
||||
migration은 idempotent Sync hook입니다. Hook을 사용하는 Application에는
|
||||
`ApplyOutOfSyncOnly=true`를 설정하지 않습니다.
|
||||
|
||||
Generated Application은 automated 상태에서 `PruneLast=true`와
|
||||
`FailOnSharedResource=true`를 사용합니다. ApplicationSet은
|
||||
`applicationsSync: create-update`와 `preserveResourcesOnDeletion: true`를
|
||||
사용합니다. Parent prune과 Application 삭제에는 확인을 요구합니다.
|
||||
CRD와 cluster-wide RBAC를 포함할 수 있는 `platform-addons`는
|
||||
Application-level `Prune=confirm`도 사용하므로 chart upgrade의 삭제는
|
||||
별도 승인이 필요합니다.
|
||||
Stateful path rename이나 ownership 이동은 별도 migration으로 수행하며
|
||||
ApplicationSet element를 먼저 삭제하지 않습니다.
|
||||
|
||||
`create-update`에서는 generator element를 제거해도 기존 generated
|
||||
Application이 자동 삭제되지 않고 stale 상태로 남습니다. Element 제거와
|
||||
Application/resource decommission은
|
||||
[application decommission runbook](../runbooks/application-decommission.md)의
|
||||
inventory, gate, backup, 명시적 삭제 절차를 따릅니다.
|
||||
|
||||
## Generator 확장 기준
|
||||
|
||||
현재는 cluster가 `dev-k3s` 하나이므로 네 ApplicationSet의 explicit List
|
||||
generator가 가장 쉽게 검토됩니다. 존재하지 않는 production이나 미래
|
||||
cluster를 위해 Matrix abstraction을 미리 만들지 않습니다.
|
||||
|
||||
두 번째 실제 cluster가 생겨 `cluster`, `server`와 cluster별 gate를 여러
|
||||
component에서 반복하게 될 때 `clusters/<cluster>/config.yaml`을 Git files
|
||||
generator로 읽고 component inventory와 Matrix generator로 결합합니다.
|
||||
그때도 AppProject는 ApplicationSet template에 고정하고, cluster별
|
||||
`autoSync`는 quoted string과 승인 gate로 유지합니다.
|
||||
|
||||
## Further reading
|
||||
|
||||
- Argo CD: [cluster bootstrapping](https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/),
|
||||
[ApplicationSet modification policy](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Controlling-Resource-Modification/),
|
||||
[ApplicationSet deletion](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Application-Deletion/),
|
||||
[automated sync semantics](https://argo-cd.readthedocs.io/en/stable/user-guide/auto_sync/)
|
||||
- Kubernetes:
|
||||
[Kustomize](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/)
|
||||
- Terraform: [state refactoring](https://developer.hashicorp.com/terraform/language/state/refactor),
|
||||
[`terraform_remote_state` security warning](https://developer.hashicorp.com/terraform/language/state/remote-state-data),
|
||||
[write-only arguments](https://developer.hashicorp.com/terraform/language/manage-sensitive-data/write-only)
|
||||
- Vault:
|
||||
[JWT/OIDC authentication](https://developer.hashicorp.com/vault/docs/auth/jwt)
|
||||
|
||||
## Repository-only change
|
||||
|
||||
이 구조와 gate 설계는 2026-07-26 현재 Git에서만 작성·검증했습니다. 실제
|
||||
cluster migration이나 sync는 이 리팩터링 리뷰 범위에 포함되지 않습니다.
|
||||
|
||||
Reference in New Issue
Block a user