Argocd multi-cluster

Argocd 멀티 클러스터 아키텍처에서는 하나의 Argocd 에서 다중 클러스터에 application 을 배포한다.

시나리오

  1. AWS 에는 Shared(Management, 관리계정), Dev, Prd 계정이 존재하고 각 계정에 EKS 클러스터가 존재한다고 가정
  2. Argocd 는 Shared 계정에 존재하는 eks-shared 에 설치
  3. eks-shared 에 존재하는 Argocd 에서 eks-dev, eks-prd 에 application 배포

Prerequisite

  1. aws eks update-kubeconfig 를 통해 shared, dev, prd 계정의 EKS 클러스터가 각각 .kube/config에 등록 (argocd cli 사용시 필요)
  2. argocd cli

1. 다중 클러스터 설정

Argocd가 관리계정에 설치가 되었다면 argocd cluster add <eks-context-name> --name <alias> 커맨드를 통해 각각 dev, prd 계정의 EKS 클러스터를 추가한다.

$ argocd login argocd.bys.digital --grpc-web
Username: admin
Password:

$ argocd cluster list --grpc-web
SERVER                          NAME                 VERSION  STATUS   MESSAGE                                                  PROJECT
https://kubernetes.default.svc  bys-shared-eks-main           Unknown  Cluster has no applications and is not being monitored.

$ argocd cluster add eks-main --name bys-dev-eks-main --grpc-web
WARNING: This will create a service account `argocd-manager` on the cluster referenced by context `eks-main` with full cluster level privileges. Do you want to continue [y/N]? y
{"level":"info","msg":"ServiceAccount \"argocd-manager\" already exists in namespace \"kube-system\"","time":"2026-01-26T22:50:58+09:00"}
{"level":"info","msg":"ClusterRole \"argocd-manager-role\" updated","time":"2026-01-26T22:50:58+09:00"}
{"level":"info","msg":"ClusterRoleBinding \"argocd-manager-role-binding\" updated","time":"2026-01-26T22:50:58+09:00"}
{"level":"info","msg":"Using existing bearer token secret \"argocd-manager-long-lived-token\" for ServiceAccount \"argocd-manager\"","time":"2026-01-26T22:50:58+09:00"}
Cluster 'https://A8845D3F0E5C385227204D33B8635ABC.sk1.ap-northeast-2.eks.amazonaws.com' added

$ argocd cluster add eks-test --name bys-dev-eks-test --grpc-web
WARNING: This will create a service account `argocd-manager` on the cluster referenced by context `eks-test` with full cluster level privileges. Do you want to continue [y/N]? y
{"level":"info","msg":"ServiceAccount \"argocd-manager\" already exists in namespace \"kube-system\"","time":"2026-01-26T22:51:14+09:00"}
{"level":"info","msg":"ClusterRole \"argocd-manager-role\" updated","time":"2026-01-26T22:51:14+09:00"}
{"level":"info","msg":"ClusterRoleBinding \"argocd-manager-role-binding\" updated","time":"2026-01-26T22:51:14+09:00"}
{"level":"info","msg":"Using existing bearer token secret \"argocd-manager-long-lived-token\" for ServiceAccount \"argocd-manager\"","time":"2026-01-26T22:51:14+09:00"}
Cluster 'https://FD553DA42E597A1A4A5076908E2794B5.gr7.ap-northeast-2.eks.amazonaws.com' added


$ argocd cluster list --grpc-web
SERVER                                                                         NAME                 VERSION  STATUS   MESSAGE                                                  PROJECT
https://kubernetes.default.svc                                                 bys-shared-eks-main           Unknown  Cluster has no applications and is not being monitored.
https://A8845D3F0E5C385227204D33B8635ABC.sk1.ap-northeast-2.eks.amazonaws.com  bys-dev-eks-main              Unknown  Cluster has no applications and is not being monitored.
https://FD553DA42E597A1A4A5076908E2794B5.gr7.ap-northeast-2.eks.amazonaws.com  bys-dev-eks-test              Unknown  Cluster has no applications and is not being monitored.

각각의 클러스터들이 추가된것을 확인할 수 있다.

여기서 많은 시간을 소비했는데, 상태 값이 Status: Unknown, MESSAGE: Cluster has no applications and is not being monitored. 이렇게 나와서 등록이 제대로 안된 줄 알았다. 여러 문서들을 확인해본 결과 최초 Application 이 배포되지 않아서 인 것으로 확인했다. 메세지가 너무 부정확하다.

2. Argocd project

ArgoCD 에서는 Project 레벨로 Application을 관리할 수 있다.

$ argocd proj list --grpc-web
NAME     DESCRIPTION  DESTINATIONS  SOURCES  CLUSTER-RESOURCE-WHITELIST  NAMESPACE-RESOURCE-BLACKLIST  SIGNATURE-KEYS  ORPHANED-RESOURCES  DESTINATION-SERVICE-ACCOUNTS
default               *,*           *        */*                         <none>                        <none>          disabled            <none>

$ argocd proj create test

# 최초 생성시에는 DESTINATIONS, SOURCES, CLUSTER-RESOURCE-WHITELIST 모두 none 임을 알 수 있다. 이 상태에서는 해당 project 에서 application 배포가 불가하다.  
$ argocd proj list --grpc-web
NAME     DESCRIPTION  DESTINATIONS  SOURCES  CLUSTER-RESOURCE-WHITELIST  NAMESPACE-RESOURCE-BLACKLIST  SIGNATURE-KEYS  ORPHANED-RESOURCES  DESTINATION-SERVICE-ACCOUNTS
default               *,*           *        */*                         <none>                        <none>          disabled            <none>
test                  <none>        <none>   <none>                      <none>                        <none>          disabled            <none>

$ argocd proj create shared
$ argocd proj add-source shared '*' --grpc-web
$ argocd proj add-destination shared '*' '*' --grpc-web
$ argocd proj allow-cluster-resource shared '*' '*' --grpc-web

$ argocd proj create dev
$ argocd proj add-source dev '*' --grpc-web
$ argocd proj add-destination dev '*' '*' --grpc-web
$ argocd proj allow-cluster-resource dev '*' '*' --grpc-web


$ argocd proj list
NAME     DESCRIPTION  DESTINATIONS  SOURCES  CLUSTER-RESOURCE-WHITELIST  NAMESPACE-RESOURCE-BLACKLIST  SIGNATURE-KEYS  ORPHANED-RESOURCES  DESTINATION-SERVICE-ACCOUNTS
default               *,*           *        */*                         <none>                        <none>          disabled            <none>
dev                   *,*           *        */*                         <none>                        <none>          disabled            <none>
shared                *,*           *        */*                         <none>                        <none>          disabled            <none>
test                  <none>        <none>   <none>                      <none>                        <none>          disabled            <none>

이번 테스트에서는 소스/목적지를 모두 허용해주고 논리적인 분리만 진행하도록 한다.

3. Application

단일 클러스터를 Argocd 로 관리할 때는 아래와 같은 Application을 통해 관리하고 있었다. 여기서 spec.destination.server를 변경해주던가 또는 spec.destination.name을 통해 타겟을 변경해줄 수 있다.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: aws-load-balancer-controller
  namespace: argocd
spec:
  project: default
  sources:
    - repoURL: 'https://gitlab.bys.digital/bys/argocd-apps.git'
      targetRevision: main
      ref: values
    - repoURL: https://aws.github.io/eks-charts
      chart: aws-load-balancer-controller
      targetRevision: 1.16.0
      helm:
        releaseName: aws-load-balancer-controller
        valueFiles:
          - $values/shared-ap2-eks-main/aws-lbc/values.yaml
  destination:
    server: https://kubernetes.default.svc  
    namespace: kube-system
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=false
      - ServerSideApply=true  # 권장

아래 Application은 마찬가지로 Shared 계정의 argocd 에 배포하지만, spec,destination 의 target 클러스터 변경을 통해 앱을 배포할 수 있다.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: aws-load-balancer-controller
  namespace: argocd
spec:
  project: default
  sources:
    - repoURL: 'https://gitlab.bys.digital/bys/argocd-apps.git'
      targetRevision: main
      ref: values
    - repoURL: https://aws.github.io/eks-charts
      chart: aws-load-balancer-controller
      targetRevision: 1.16.0
      helm:
        releaseName: aws-load-balancer-controller
        valueFiles:
          - $values/dev-ap2-eks-test/aws-lbc/values.yaml
  destination:
    name: bys-dev-eks-test # 변경
    namespace: kube-system
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=false
      - ServerSideApply=true  # 권장

4. ApplicationSet

단일 클러스터를 Argocd 로 관리할 때는 Application을 통해 관리하고 있었고, 멀티 클러스터가 되었을 때도 프로젝트를 나누어서 각각에 대한 Application을 만들어주려고 했는데 클러스터가 늘어날 수록 너무 불필요한 작업이 반복될 것 같았다. 몇 가지 내용을 확인해본 결과 ApplicationSet 를 배포함으로서 관리할 수 있는 방안이 있었다. AWS-LBC, Karpenter 와 같은 시스템 서비스들의 경우 ApplicationSet 으로 관리하는것이 훨씬 좋은 선택이었다.

아래와 같이 spec.generators설정을 통해 클러스터를 리스트업 한다.
ApplicationSet

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: aws-load-balancer-controller
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - list:
        elements:
          - project: shared
            cluster: bys-shared-eks-main
            valuesPath: shared-ap2-eks-main/aws-lbc/values.yaml
          - project: dev
            cluster: bys-dev-eks-main
            valuesPath: dev-ap2-eks-main/aws-lbc/values.yaml
          - project: dev
            cluster: bys-dev-eks-test
            valuesPath: dev-ap2-eks-test/aws-lbc/values.yaml
  template:
    metadata:
      name: '-aws-load-balancer-controller'
      labels:
        app.kubernetes.io/name: aws-load-balancer-controller
        app.kubernetes.io/instance: ''
        cluster: ''
    spec:
      project: ''
      sources:
        - repoURL: 'https://gitlab.bys.digital/bys/argocd-apps.git'
          targetRevision: main
          ref: values
        - repoURL: https://aws.github.io/eks-charts
          chart: aws-load-balancer-controller
          targetRevision: 1.16.0
          helm:
            releaseName: aws-load-balancer-controller
            valueFiles:
              - '$values/'
      destination:
        name: ''  # 🔑 모든 클러스터에 name 사용
        namespace: kube-system
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=false
          - ServerSideApply=true

ApplicationSet을 배포하면 아래와 같이 리스트된 클러스터에 모두 일괄 배포되는 것을 볼 수 있다.

$ argocd app list
NAME                                                     CLUSTER              NAMESPACE    PROJECT  STATUS  HEALTH   SYNCPOLICY  CONDITIONS  REPO                                            PATH  TARGET
argocd/bys-dev-eks-main-aws-load-balancer-controller     bys-dev-eks-main     kube-system  default  Synced  Healthy  Auto-Prune  <none>      https://gitlab.bys.digital/bys/argocd-apps.git        main
argocd/bys-dev-eks-test-aws-load-balancer-controller     bys-dev-eks-test     kube-system  default  Synced  Healthy  Auto-Prune  <none>      https://gitlab.bys.digital/bys/argocd-apps.git        main
argocd/bys-shared-eks-main-aws-load-balancer-controller  bys-shared-eks-main  kube-system  default  Synced  Healthy  Auto-Prune  <none>      https://gitlab.bys.digital/bys/argocd-apps.git        main

마지막으로 Application 들이 정상 배포되면 아래와 같이 클러스터의 상태 정보도 모두 Unknown 에서 Successful로 변경되는 것을 볼 수 있다.

$ argocd cluster list
SERVER                                                                         NAME                 VERSION  STATUS      MESSAGE  PROJECT
https://FD553DA42E597A1A4A5076908E2794B5.gr7.ap-northeast-2.eks.amazonaws.com  bys-dev-eks-test     1.34     Successful
https://kubernetes.default.svc                                                 bys-shared-eks-main  1.34     Successful
https://A8845D3F0E5C385227204D33B8635ABC.sk1.ap-northeast-2.eks.amazonaws.com  bys-dev-eks-main     1.34     Successful

결론적으로, 각각의 클러스터에서 관리되는 App의 경우는 Applications 로 배포를 진행하고, 멀티 클러스터에 일괄 배포를 위해서는 ApplicationSets 를 통해 관리할 수 있다.


📚 References

[1] ApplicationSet

[2] Generators