Files
2026-08-28 17:24:26 +09:00

7.7 KiB

Argo CD architecture

Control-plane ownership

Controller 설치 후 다음 두 bootstrap object를 순서대로 수동 seed합니다.

  1. bootstrap/gitops/argocd/control-plane-project.yaml
  2. bootstrap/gitops/argocd/root-application.yaml

gitops-control-plane AppProject는 canonical Gitea repository와 in-cluster argocd namespace, AppProject/ApplicationSet kind만 허용합니다. 단일 root Application은 이 Project를 사용하고 gitops/clusters/dev-k3s를 source로 사용합니다. Cluster root는 다음 control-plane 구성을 참조합니다.

gitops/platform/control-plane/argocd
├── projects
│   ├── platform-addons.yaml
│   ├── platform-services.yaml
│   ├── systems.yaml
│   └── workloads.yaml
└── application-sets
    ├── platform-addons.yaml
    ├── platform-services.yaml
    ├── systems.yaml
    └── workloads.yaml

Cluster root는 AppProject와 ApplicationSet까지만 직접 소유합니다. 각 ApplicationSet의 list inventory가 실제 child Application을 생성합니다. Routine 변경에 category별 root나 직접 kubectl apply를 추가하지 않습니다.

AppProject boundary

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

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에만 다음 정책을 추가합니다.

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=trueFailOnSharedResource=true를 사용합니다. ApplicationSet은 applicationsSync: create-updatepreserveResourcesOnDeletion: 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의 inventory, gate, backup, 명시적 삭제 절차를 따릅니다.

Generator 확장 기준

현재는 cluster가 dev-k3s 하나이므로 네 ApplicationSet의 explicit List generator가 가장 쉽게 검토됩니다. 존재하지 않는 production이나 미래 cluster를 위해 Matrix abstraction을 미리 만들지 않습니다.

두 번째 실제 cluster가 생겨 cluster, server와 cluster별 gate를 여러 component에서 반복하게 될 때 gitops/clusters/<cluster>/config.yaml을 Git files generator로 읽고 component inventory와 Matrix generator로 결합합니다. 그때도 AppProject는 ApplicationSet template에 고정하고, cluster별 autoSync는 quoted string과 승인 gate로 유지합니다.

Further reading

Repository-only change

이 구조와 gate 설계는 2026-07-26 현재 Git에서만 작성·검증했습니다. 실제 cluster migration이나 sync는 이 리팩터링 리뷰 범위에 포함되지 않습니다.