- Published on
Argo CD Sync 실패? AppProject RBAC로 해결하기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Argo CD를 운영하다 보면 애플리케이션 자체 YAML은 멀쩡해 보이는데도 Sync가 실패하는 경우가 있습니다. 특히 멀티테넌시(팀/서비스별 격리)를 위해 AppProject를 도입한 환경에서는, AppProject RBAC 및 화이트리스트 정책이 Sync를 막는 케이스가 자주 발생합니다.
이 글에서는 “왜 Sync가 막히는지”를 로그 패턴으로 빠르게 진단하고, AppProject의 destinations, sourceRepos, clusterResourceWhitelist, namespaceResourceWhitelist를 어떻게 조정해야 하는지 실무 기준으로 정리합니다.
관련해서 Argo CD가 리소스를 비교하는 단계에서 터지는 comparisonError도 자주 함께 보이므로, 해당 케이스는 아래 글도 같이 참고하면 좋습니다.
AppProject가 Sync를 막는 대표 증상
AppProject가 원인일 때는 대체로 다음 중 하나로 나타납니다.
1) 대상 클러스터/네임스페이스가 허용되지 않음
Argo CD UI 또는 이벤트에서 다음과 비슷한 메시지가 보입니다.
application destination server ... is not permitted in project ...namespace ... is not permitted in project ...
이 경우 spec.destinations가 현재 앱의 spec.destination을 허용하지 않는 상태입니다.
2) Git 리포지토리가 허용되지 않음
application source repo ... is not permitted in project ...
이 경우 spec.sourceRepos에 Git URL 패턴이 포함되지 않았습니다.
3) 리소스 종류가 화이트리스트에 없어 생성/패치가 막힘
resource ... is not permitted in project ...cluster-scoped resource ... is not permitted in project ...
이 경우 namespaceResourceWhitelist 또는 clusterResourceWhitelist에 해당 group/kind가 없습니다.
진단 순서: “어디서 막히는지”를 먼저 고정
운영 중에는 원인을 빨리 좁히는 게 중요합니다. 아래 순서대로 확인하면 대부분 5분 내로 결론이 납니다.
1) Application 이벤트와 컨트롤러 로그 확인
먼저 애플리케이션 이벤트를 봅니다.
kubectl -n argocd describe application my-app
다음으로 argocd-application-controller 로그에서 거부 메시지를 찾습니다.
kubectl -n argocd logs deploy/argocd-application-controller \
--since=30m | grep -i "not permitted" -n
not permitted가 보이면 거의 확정적으로 AppProject 정책 문제입니다.
2) Application이 참조하는 AppProject 확인
kubectl -n argocd get application my-app -o jsonpath='{.spec.project}{"\n"}'
그리고 프로젝트 리소스를 확인합니다.
kubectl -n argocd get appproject my-project -o yaml
AppProject 핵심 필드: Sync 실패를 만드는 4가지 제한
여기서부터는 “실제로 Sync를 막는 설정”을 필드별로 정리합니다.
1) destinations: 배포 대상(클러스터/네임스페이스) 제한
destinations는 어디에 배포할 수 있는지를 결정합니다.
server: 대상 클러스터 API 서버 주소namespace: 허용 네임스페이스
예시:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: platform
namespace: argocd
spec:
destinations:
- server: https://kubernetes.default.svc
namespace: team-a
- server: https://kubernetes.default.svc
namespace: team-b
운영 팁:
- 단일 클러스터라면
server는 보통https://kubernetes.default.svc로 고정됩니다. - 네임스페이스를
*로 풀어버리면 편하지만, 멀티테넌시 의미가 약해집니다. - “특정 팀은 특정 네임스페이스만” 같은 정책은
destinations에서 강제하는 게 가장 안전합니다.
2) sourceRepos: 허용 Git 리포지토리 제한
sourceRepos는 어떤 Git/Helm repo에서 매니페스트를 가져올 수 있는지를 제한합니다.
spec:
sourceRepos:
- https://github.com/my-org/*
- https://gitlab.example.com/platform/*
자주 하는 실수:
- 실제 앱이 사용하는 URL이
ssh인데 프로젝트에는https만 넣은 경우 - GitHub Enterprise 도메인이 다른데
github.com만 넣은 경우
앱의 spec.source.repoURL과 프로젝트 sourceRepos 패턴이 정확히 매칭되는지 확인하세요.
3) namespaceResourceWhitelist: 네임스페이스 범위 리소스 허용
대부분의 Kubernetes 리소스는 네임스페이스 범위입니다. 예를 들면 Deployment, Service, ConfigMap 등이죠.
AppProject에서 이를 제한하려면 namespaceResourceWhitelist를 사용합니다.
spec:
namespaceResourceWhitelist:
- group: ""
kind: ConfigMap
- group: ""
kind: Secret
- group: apps
kind: Deployment
- group: networking.k8s.io
kind: Ingress
운영에서 흔한 Sync 실패 사례:
- HPA를 추가했는데
autoscaling그룹이 화이트리스트에 없어 Sync 실패 ExternalSecret같은 CRD를 도입했는데 해당group/kind누락
이때는 컨트롤러 로그에서 거부된 리소스의 group과 kind를 그대로 화이트리스트에 추가하면 됩니다.
4) clusterResourceWhitelist: 클러스터 범위 리소스 허용
클러스터 범위 리소스는 영향도가 크기 때문에, AppProject에서 가장 엄격하게 다루는 게 일반적입니다.
대표적인 클러스터 범위 리소스:
NamespaceCustomResourceDefinitionClusterRole,ClusterRoleBindingMutatingWebhookConfiguration,ValidatingWebhookConfiguration
예시:
spec:
clusterResourceWhitelist:
- group: ""
kind: Namespace
- group: rbac.authorization.k8s.io
kind: ClusterRole
- group: rbac.authorization.k8s.io
kind: ClusterRoleBinding
주의:
- 팀 단위 프로젝트에
ClusterRoleBinding을 열어주면 사실상 권한 승격 통로가 될 수 있습니다. - 운영 환경에서는 “플랫폼 팀 프로젝트”와 “서비스 팀 프로젝트”를 분리하고, 서비스 팀에는 클러스터 범위 리소스를 거의 열어주지 않는 구성이 안전합니다.
실전 예시: Sync 실패를 유발하는 패턴과 수정안
여기서는 자주 겪는 3가지 상황을 예시로 해결 흐름을 보여드립니다.
케이스 A: namespace is not permitted로 Sync 실패
앱이 team-a 네임스페이스로 배포되는데 프로젝트에는 team-b만 허용된 상황입니다.
해결: destinations에 team-a 추가
spec:
destinations:
- server: https://kubernetes.default.svc
namespace: team-a
- server: https://kubernetes.default.svc
namespace: team-b
케이스 B: Helm 차트 repo가 막혀 Sync 실패
앱이 Helm을 쓰며 repoURL이 사설 차트 저장소인데 sourceRepos에 누락된 상황입니다.
해결: sourceRepos에 repo URL 패턴 추가
spec:
sourceRepos:
- https://github.com/my-org/*
- https://charts.example.com/*
케이스 C: CRD 기반 리소스가 “not permitted”로 Sync 실패
예: ExternalSecret을 배포하려는데 허용되지 않음.
해결: 해당 CRD 리소스의 group/kind를 namespaceResourceWhitelist에 추가
spec:
namespaceResourceWhitelist:
- group: external-secrets.io
kind: ExternalSecret
만약 CRD 자체를 Argo CD로 설치하려고 한다면(예: 플랫폼 레벨 애드온), 그건 보통 클러스터 범위 리소스이므로 별도의 프로젝트에서 CustomResourceDefinition을 허용하는 방식으로 분리하는 게 좋습니다.
AppProject “RBAC”에서 자주 혼동하는 지점
AppProject 관련 이슈를 RBAC이라고 부르긴 하지만, 실제로는 두 층이 섞여 있습니다.
- Kubernetes RBAC: Argo CD 컨트롤러의 ServiceAccount가 클러스터에서 무엇을 할 수 있는지
- Argo CD Project 정책: Argo CD가 “이 프로젝트는 무엇을 배포해도 되는지”를 자체적으로 제한
즉, Kubernetes RBAC이 충분해도 AppProject가 막으면 Sync가 실패합니다. 반대로 AppProject를 풀어도 Kubernetes RBAC이 부족하면 forbidden 류 에러가 납니다.
빠른 구분 팁:
- AppProject 문제: 로그에
not permitted in project가 자주 등장 - Kubernetes RBAC 문제:
User system:serviceaccount:... cannot ...같은forbidden메시지
운영 설계 팁: 멀티테넌시에서 안전하게 풀어주는 방법
무작정 *로 풀어버리면 장애는 빨리 해결되지만, 나중에 더 큰 사고로 돌아올 수 있습니다. 아래 원칙을 추천합니다.
1) 프로젝트를 “플랫폼”과 “서비스”로 분리
- 플랫폼 프로젝트: Ingress Controller, External Secrets, Cert Manager 등 클러스터 공용 구성
- 서비스 프로젝트: 각 팀/서비스의 네임스페이스 범위 리소스만
이렇게 나누면 clusterResourceWhitelist를 서비스 프로젝트에서 최소화할 수 있습니다.
2) 신규 리소스 도입 시 화이트리스트 갱신을 변경관리로 포함
예를 들어 HPA를 도입하는 스프린트라면:
- 애플리케이션 YAML 변경
- AppProject의
namespaceResourceWhitelist에autoscaling그룹 추가
를 같은 변경 단위로 묶어야 배포 당일에 Sync 실패로 시간을 낭비하지 않습니다.
3) 문제를 “비교 단계”와 “적용 단계”로 나누어 보기
Sync 실패가 비교 단계에서 터지면 comparisonError로 보이는 경우가 많고, 적용 단계에서 막히면 not permitted 또는 forbidden이 자주 보입니다. 비교 단계 이슈는 아래 글을 같이 보면 진단 속도가 올라갑니다.
변경 적용 방법: AppProject 수정 후 반영 확인
AppProject를 수정했다면 다음 순서로 확인합니다.
- 변경 적용
kubectl -n argocd apply -f appproject.yaml
- 애플리케이션 재동기화
argocd app sync my-app
- 여전히 실패한다면, 어떤 리소스가 막히는지 다시
describe로 이벤트 확인
kubectl -n argocd describe application my-app
체크리스트: Sync 실패가 AppProject 원인인지 1분 판별
argocd-application-controller로그에not permitted in project가 있는가Application.spec.project가 의도한 프로젝트를 가리키는가AppProject.spec.destinations에 대상server/namespace가 포함되는가AppProject.spec.sourceRepos에 실제repoURL이 매칭되는가- 새로 도입한 리소스의
group/kind가 화이트리스트에 포함되는가
마무리
Argo CD에서 Sync가 실패할 때 AppProject를 의심해야 하는 순간은 생각보다 많습니다. 특히 멀티테넌시 환경에서는 AppProject가 “최후의 안전장치” 역할을 하므로, 장애를 빨리 해결하되 정책을 무너뜨리지 않는 방향으로 수정하는 게 중요합니다.
핵심은 간단합니다.
- 어디로 배포하는지:
destinations - 어디서 가져오는지:
sourceRepos - 무엇을 배포하는지:
namespaceResourceWhitelist,clusterResourceWhitelist
이 4가지만 로그와 함께 매칭해도, Sync 실패의 상당수를 재현 가능하게 설명하고 안정적으로 해결할 수 있습니다.