"git push만 하면 자동으로 배포된다"는 말을 그대로 믿고 진행했다면 어디서 막혔을지, 그리고 실제로 어디서 막혔는지 정리.
전통적인 배포는 "명령형(imperative)"이다 — 사람이나 CI가 kubectl apply, ssh + 스크립트 실행 같은 동작을 실행시킨다. 클러스터의 실제 상태와, "이게 맞는 상태다"라고 기록해둔 문서가 따로 놀기 쉽다.
GitOps는 반대다: **Git 저장소 안의 YAML이 "유일한 진실"**이고, 클러스터는 그 상태를 계속 "따라가려고" 하는 대상이다. 사람은 클러스터에 직접 명령하지 않고 git에 커밋만 한다. 클러스터 쪽의 별도 컨트롤러(여기선 ArgoCD)가 git을 주기적으로 보고, git과 클러스터 실제 상태가 다르면 알아서 맞춘다.
이번 작업에서 이 원칙을 지키려고 한 부분: DB 비밀번호/GEMINI_API_KEY 같은 시크릿은 git에 넣지 않고 kubectl로 클러스터에 직접 주입했다. 순수 GitOps 원칙에서 보면 예외지만, public 레포에 평문 시크릿을 커밋하는 것보다는 훨씬 안전한 실용적 타협.
git 저장소에 YAML을 올려놓는 것만으로는 아무 일도 안 일어난다. "이 git 레포의 이 경로를, 이 클러스터의 이 네임스페이스에 동기화해라"는 매핑을 ArgoCD에게 알려주는 리소스가 필요한데, 그게 Application이다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: target-tracking-service
namespace: argocd
spec:
source:
repoURL: https://github.com/sm010422/k3s-msa-infrastructure.git
targetRevision: HEAD
path: apps/target-tracking-service
destination:
server: https://kubernetes.default.svc
namespace: c4i
syncPolicy:
automated: { prune: true, selfHeal: true }
syncOptions: [CreateNamespace=true]
이번에 가장 중요했던 발견: 이 YAML 파일이 리포 안에 있다는 사실 자체는 아무 의미가 없다. Application도 결국 하나의 쿠버네티스 리소스라서, argocd 네임스페이스에 kubectl apply로 실제로 생성해야 ArgoCD 컨트롤러가 그 존재를 인식한다.
kubectl get application -n argocd # → No resources found
사용자가 공유한 4단계 계획("이미지 push → 매니페스트 반영 → git push → ArgoCD가 감지해서 자동 배포")은 이 Application이 이미 등록되어 있다는 전제 위에서만 성립한다.
kubectl apply -f argocd/applications/target-tracking-service.yaml