본문 바로가기
IT/k8s

[k8s] ArgoCD GitOps CI/CD 구축: 쿠버네티스 자동 배포 완벽 가이드

by 수누다 2026. 5. 18.

배포가 두려운 분들께 — GitOps가 바꿔놓은 제 일상

솔직히 말씀드리면, 저도 예전에는 배포가 제일 무서웠어요. 스테이징에서는 멀쩡하던 게 프로덕션에 올리면 왜인지 모르게 터지고, "누가 언제 뭘 바꿨지?" 하고 kubectl로 이것저것 뒤지다 보면 어느새 새벽 2시가 되어 있는 그런 날들이요. 쿠버네티스를 도입하고 나서도 한동안은 이 상황이 크게 달라지지 않았습니다. 오히려 배포 방법이 늘어나면서 혼란이 더 심해지기도 했고요.

그러다가 ArgoCD를 알게 됐습니다. 처음엔 "또 새로운 툴이네" 하고 반신반의했는데, 막상 써보니까 진짜 다르더라고요. GitOps 방식으로 쿠버네티스 배포를 자동화하면, Git 저장소가 클러스터의 "진실의 원천(Source of Truth)"이 됩니다. 코드를 푸시하면 ArgoCD가 알아서 클러스터 상태를 맞춰주는 거예요. 오늘은 제가 홈랩과 실무에서 직접 구축하면서 겪은 경험을 바탕으로, ArgoCD를 활용한 CI/CD 파이프라인 구축 방법을 처음부터 끝까지 같이 살펴보겠습니다.

ArgoCD GitOps 전체 아키텍처 — Git 저장소 변경이 쿠버네티스 클러스터에 자동 반영되는 흐름

GitOps와 ArgoCD, 쉽게 이해하기

GitOps란 뭔가요?

GitOps를 한 문장으로 정리하면 이렇습니다. "인프라와 애플리케이션의 원하는 상태(Desired State)를 Git에 선언적으로 저장하고, 자동화 도구가 실제 상태를 그것에 맞게 유지하는 방식"이에요.

쉽게 말해서, 예전에는 "지금 클러스터에 뭐가 떠 있지?"를 확인하려면 kubectl get 명령어를 직접 쳐봐야 했잖아요. GitOps를 쓰면 Git 저장소만 봐도 현재 클러스터 상태를 알 수 있어요. 변경 이력도 다 남고, 롤백도 git revert 한 방이면 되고요.

기존 CI/CD 방식과 무엇이 다른가요?

구분 기존 Push 방식 CI/CD GitOps (ArgoCD Pull 방식)
배포 트리거 CI 파이프라인이 kubectl apply 직접 실행 ArgoCD가 Git 변경 감지 후 자동 동기화
클러스터 접근 권한 CI 서버가 클러스터 자격증명 보유 ArgoCD만 클러스터 내부에서 관리
상태 추적 파이프라인 로그 확인 필요 Git 커밋 히스토리 = 배포 이력
드리프트(Drift) 감지 수동 확인 필요 ArgoCD가 실시간 감지 및 알림
롤백 파이프라인 재실행 or 수동 Git revert + 자동 동기화

특히 드리프트(Drift) 감지가 저한테는 정말 킬러 피처였어요. 누군가 실수로 kubectl로 직접 설정을 바꿔놔도, ArgoCD가 "어? Git이랑 다른데?" 하고 바로 잡아주거든요. 이게 얼마나 안심되는지 모릅니다.

ArgoCD 설치 — 생각보다 간단합니다

사전 준비

  • 쿠버네티스 클러스터 (v1.19 이상 권장)
  • kubectl 설치 및 클러스터 접근 설정 완료
  • Git 저장소 (GitHub, GitLab, Bitbucket 모두 가능)
  • ArgoCD CLI (선택사항이지만 있으면 편해요)

1단계: ArgoCD 네임스페이스 및 설치

ArgoCD 공식 설치 방법은 정말 간단합니다. 공식 매니페스트를 그대로 적용하면 되거든요.

# ArgoCD 전용 네임스페이스 생성
kubectl create namespace argocd

# ArgoCD 공식 매니페스트 적용
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# 설치 완료 확인 (모든 Pod가 Running 상태가 될 때까지 대기)
kubectl get pods -n argocd -w

처음에 저도 이게 너무 간단해서 "이게 맞나?" 싶었는데, 맞습니다 ㅎㅎ. 잠시 기다리면 아래와 같이 Pod들이 뜨기 시작해요.

NAME                                                READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                     1/1     Running   0          2m
argocd-applicationset-controller-xxx                1/1     Running   0          2m
argocd-dex-server-xxx                               1/1     Running   0          2m
argocd-notifications-controller-xxx                 1/1     Running   0          2m
argocd-redis-xxx                                    1/1     Running   0          2m
argocd-repo-server-xxx                              1/1     Running   0          2m
argocd-server-xxx                                   1/1     Running   0          2m

2단계: ArgoCD UI 접근 설정

기본적으로 ArgoCD 서버는 외부에 노출되어 있지 않아요. 로컬에서 테스트할 때는 포트 포워딩을 쓰고, 실제 운영 환경에서는 Ingress를 설정하는 게 좋습니다.

# 로컬 테스트용 포트 포워딩
kubectl port-forward svc/argocd-server -n argocd 8080:443

# 초기 admin 비밀번호 확인
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d; echo

💡 : 초기 비밀번호는 반드시 첫 로그인 후 바꿔주세요. 그리고 argocd-initial-admin-secret은 비밀번호 변경 후 삭제하는 게 보안상 좋습니다.

3단계: ArgoCD CLI 설치 (선택 권장)

# macOS
brew install argocd

# Linux
curl -sSL -o /usr/local/bin/argocd \
  https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd

# CLI로 ArgoCD 서버에 로그인
argocd login localhost:8080 --username admin --password <위에서-확인한-비밀번호> --insecure

ArgoCD 웹 UI 대시보드 — 애플리케이션 동기화 상태와 헬스 체크 결과를 한눈에 확인

첫 번째 Application 등록 — 실전 GitOps 시작

Git 저장소 구조 준비

ArgoCD를 쓸 때 저장소 구조를 어떻게 잡느냐가 은근히 중요하더라고요. 저는 앱 코드와 쿠버네티스 매니페스트를 분리하는 방식을 선호합니다.

my-gitops-repo/
├── apps/
│   ├── dev/
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   └── kustomization.yaml
│   └── prod/
│       ├── deployment.yaml
│       ├── service.yaml
│       └── kustomization.yaml
└── README.md

예시로 사용할 간단한 Deployment와 Service 매니페스트입니다.

# apps/dev/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: nginx:1.25
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 200m
            memory: 256Mi
# apps/dev/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-app-svc
  namespace: default
spec:
  selector:
    app: sample-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP

ArgoCD Application 생성

이제 핵심입니다. ArgoCD에게 "이 Git 저장소의 이 경로를 이 클러스터에 배포해줘"라고 알려주는 Application 리소스를 만들어야 해요. YAML로 선언적으로 만드는 걸 추천합니다 — 이것 자체도 Git으로 관리하면 더 좋고요.

# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: sample-app-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-username/my-gitops-repo.git
    targetRevision: HEAD
    path: apps/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true        # Git에서 삭제된 리소스 자동 제거
      selfHeal: true     # 드리프트 감지 시 자동 복구
    syncOptions:
    - CreateNamespace=true
# Application 생성
kubectl apply -f argocd-application.yaml

# 동기화 상태 확인
argocd app get sample-app-dev

# 수동 동기화 (처음 한 번)
argocd app sync sample-app-dev

여기서 selfHeal: true 옵션이 제가 아까 말씀드린 드리프트 자동 복구 기능이에요. 누군가 실수로 kubectl로 직접 replica 수를 바꿔도, ArgoCD가 Git에 선언된 값으로 되돌려줍니다. 처음에 이게 동작하는 걸 보고 "오오..." 했던 기억이 나네요.

Private 저장소 연결

실무에서는 당연히 Private 저장소를 쓰죠. SSH 키나 Personal Access Token으로 연결할 수 있어요.

# HTTPS + Personal Access Token 방식
argocd repo add https://github.com/your-username/my-gitops-repo.git \
  --username your-username \
  --password your-personal-access-token

# SSH 키 방식
argocd repo add git@github.com:your-username/my-gitops-repo.git \
  --ssh-private-key-path ~/.ssh/id_rsa

CI 파이프라인과 연동 — 이미지 태그 자동 업데이트

ArgoCD는 CD(Continuous Delivery) 도구입니다. CI(Continuous Integration)는 별도 도구를 써야 해요. 저는 GitHub Actions를 주로 쓰는데, 흐름은 이렇습니다.

  1. 개발자가 앱 코드를 main 브랜치에 push
  2. GitHub Actions가 Docker 이미지를 빌드하고 레지스트리에 push
  3. GitHub Actions가 GitOps 저장소의 이미지 태그를 새 버전으로 업데이트
  4. ArgoCD가 GitOps 저장소 변경을 감지하고 자동으로 클러스터에 배포
# .github/workflows/deploy.yaml
name: Build and Update GitOps Repo

on:
  push:
    branches: [main]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout app code
      uses: actions/checkout@v3

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - name: Login to Container Registry
      uses: docker/login-action@v2
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}

    - name: Build and push
      uses: docker/build-push-action@v4
      with:
        push: true
        tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

    - name: Update GitOps repo image tag
      run: |
        git clone https://x-access-token:${{ secrets.GITOPS_TOKEN }}@github.com/your-username/my-gitops-repo.git
        cd my-gitops-repo
        # sed로 이미지 태그 업데이트
        sed -i "s|image: ghcr.io/your-username/sample-app:.*|image: ghcr.io/your-username/sample-app:${{ github.sha }}|"\
          apps/dev/deployment.yaml
        git config user.email "ci@example.com"
        git config user.name "CI Bot"
        git add apps/dev/deployment.yaml
        git commit -m "chore: update image tag to ${{ github.sha }}"
        git push

근데 여기서 주의할 점이 있어요. 앱 코드 저장소와 GitOps(매니페스트) 저장소를 분리하는 게 좋습니다. 같은 저장소에 두면 앱 코드 변경과 인프라 변경 이력이 섞여서 나중에 관리가 복잡해지더라고요. 처음에 같이 뒀다가 나중에 분리하는 삽질을 했었는데... 처음부터 분리하세요 ㅎㅎ.

CI/CD 전체 파이프라인 흐름 — GitHub Actions가 이미지를 빌드하고 GitOps 저장소를 업데이트하면 ArgoCD가 자동 배포

⚠️ 삽질 모음 — 이것만 알면 시간 아낍니다

1. OutOfSync 상태가 계속 유지되는 문제

분명히 Git이랑 같은 내용인데 ArgoCD에서 계속 OutOfSync라고 뜨는 경우가 있어요. 대부분 리소스 정규화(normalization) 문제입니다. 쿠버네티스가 자동으로 추가하는 필드들(creationTimestamp, status 등)이 Git에 없는 거예요.

# Application에 ignoreDifferences 추가로 해결
spec:
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/replicas  # HPA가 관리하는 경우 replica 수 무시
  - group: ""
    kind: Service
    jsonPointers:
    - /spec/clusterIP

2. Helm 차트 사용 시 values 파일 관리

Helm을 ArgoCD와 함께 쓸 때 values 파일을 Git에 두면 됩니다.

spec:
  source:
    repoURL: https://github.com/your-username/my-gitops-repo.git
    path: charts/my-app
    helm:
      valueFiles:
      - values-dev.yaml
      - values-secrets.yaml  # Sealed Secrets 등으로 암호화된 파일

3. 시크릿(Secret) 관리 — 절대 Git에 평문으로 넣지 마세요

이건 정말 중요해요. DB 비밀번호 같은 민감 정보를 실수로 Git에 넣는 사고가 생각보다 많이 일어납니다. 저는 Sealed SecretsExternal Secrets Operator를 씁니다.

  • Sealed Secrets: 공개키로 암호화된 SealedSecret 리소스를 Git에 저장, 클러스터 내 컨트롤러가 복호화
  • External Secrets Operator: AWS Secrets Manager, HashiCorp Vault 등 외부 시크릿 저장소와 연동

4. ArgoCD 자체도 GitOps로 관리하기 — App of Apps 패턴

ArgoCD Application 리소스가 많아지면 관리가 힘들어져요. 이럴 때 App of Apps 패턴을 씁니다. 루트 Application 하나가 다른 Application들을 관리하는 계층 구조예요.

# root-application.yaml — 다른 Application들을 관리하는 루트
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-username/my-gitops-repo.git
    targetRevision: HEAD
    path: argocd-apps  # 이 폴더 안에 다른 Application YAML들이 있음
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

✅ 배포 결과 확인 및 검증

모든 설정이 끝났으면 이렇게 확인하시면 됩니다.

# ArgoCD CLI로 앱 상태 확인
argocd app list

# 상세 상태 확인
argocd app get sample-app-dev

# 동기화 이력 확인
argocd app history sample-app-dev

# kubectl로 직접 확인
kubectl get pods -n default
kubectl get svc -n default

ArgoCD UI에서 애플리케이션 상태가 Synced / Healthy로 표시되면 성공입니다. 🎉

이제 Git 저장소에서 deployment.yaml의 이미지 태그만 바꿔서 커밋하면, 몇 분 안에 (기본 polling 주기는 3분) 클러스터에 자동으로 반영되는 걸 볼 수 있어요. 처음에 이게 자동으로 되는 걸 보고 "드디어 됐다!" 하고 혼자 좋아했던 기억이 납니다 ㅎㅎ.

# 롤백도 이렇게 간단합니다
argocd app rollback sample-app-dev 

# 또는 Git에서 revert 후 push하면 ArgoCD가 자동으로 처리

ArgoCD 애플리케이션 상세 화면 — Synced/Healthy 상태와 쿠버네티스 리소스 트리 시각화

자주 묻는 질문 (FAQ)

Q. ArgoCD는 무료인가요?

네, ArgoCD는 오픈소스(Apache 2.0 라이선스)로 완전 무료입니다. CNCF(Cloud Native Computing Foundation) 졸업 프로젝트이기도 하고요.

Q. Flux와 ArgoCD 중 어떤 걸 써야 하나요?

둘 다 GitOps 도구이지만, ArgoCD는 UI가 풍부하고 직관적이라 팀에서 처음 GitOps를 도입할 때 진입 장벽이 낮습니다. Flux는 더 경량이고 CLI 친화적이에요. 저는 팀 협업 환경에서는 ArgoCD를, 단독 운영이나 자동화 중심이면 Flux를 추천합니다.

Q. 여러 클러스터를 관리할 수 있나요?

가능합니다. ArgoCD 하나로 여러 쿠버네티스 클러스터를 등록하고 관리할 수 있어요. argocd cluster add 명령어로 추가하면 됩니다.

마무리 — GitOps, 한 번 맛보면 못 돌아갑니다

ArgoCD와 GitOps를 도입하고 나서 제 일상이 꽤 달라졌어요. 배포가 무서운 이벤트에서 그냥 평범한 Git 커밋 하나로 바뀌었달까요. 팀원들도 "내가 뭘 배포했는지" Git 로그만 보면 바로 알 수 있으니 커뮤니케이션 비용도 줄었고요.

물론 처음 셋업할 때 시크릿 관리나 멀티 클러스터 권한 설정 같은 부분에서 삽질이 좀 있긴 합니다. 근데 그 초기 투자를 하고 나면 이후에는 정말 편해요.

오늘 다룬 내용을 정리하면 이렇습니다.

  • ✅ GitOps 개념과 기존 CI/CD 방식의 차이 이해
  • ✅ ArgoCD 설치 및 초기 설정
  • ✅ Application 리소스로 Git 저장소와 클러스터 연결
  • ✅ GitHub Actions와 연동한 완전 자동화 파이프라인 구축
  • ✅ 자주 겪는 문제와 해결법

다음 글에서는 ArgoCD ApplicationSet을 활용해서 여러 환경(dev/staging/prod)을 템플릿 하나로 관리하는 방법을 다룰 예정입니다. App of Apps 패턴도 더 깊이 파볼 거고요. 이 글이 도움이 됐다면 댓글로 알려주세요! 궁금한 점도 편하게 물어봐 주시고요. 😊