마루옵스서버 · 네트워크 · AI 기술 노트

소개문의

[Git·GitOps 21] Argo CD 첫 배포: 설치·AppProject·저장소 인증

서버가 설치된 BalticServers 데이터센터

참고 사진 · 서버가 설치된 BalticServers 데이터센터 · 사진: BalticServers.com / 원본 / CC BY-SA 3.0 · 목록용 크기 조정

첫 GitOps 배포에서는 자동 동기화부터 켜기보다, Git에 적은 리소스와 클러스터에 생길 리소스가 같은지 읽어 보는 편이 좋습니다. 이 글은 Argo CD를 설치하고 전용 namespace에 nginx Deployment와 Service를 수동으로 동기화합니다. 운영 클러스터에 바로 적용하는 설치 예제는 아닙니다.

kubectl과 kubeconfig, DNS가 동작하는 폐기 가능한 별도 실습 클러스터가 필요합니다. Argo CD 3.5.5의 릴리스와 설치 매니페스트는 2026년 10월 18일 확인했습니다. 아래 YAML은 문법과 정적 구성을 검토했으며 이 작업에서 실제 Kubernetes 클러스터에 배포한 기록은 없습니다.

Git·GitOps 전체 연재 · 21 / 24

이 글의 목차

1. 대상 클러스터를 확인하고 설치한다

kubectl config current-context
kubectl get nodes
kubectl create namespace argocd
curl -fL -o argocd-install-v3.5.5.yaml \
  https://raw.githubusercontent.com/argoproj/argo-cd/v3.5.5/manifests/install.yaml
# Review the downloaded manifest before applying to the dedicated lab cluster.
kubectl apply --server-side -n argocd -f argocd-install-v3.5.5.yaml
kubectl -n argocd get pods

server-side apply는 큰 CRD에 client-side apply의 주석 크기 제한이 걸리는 문제를 피합니다. 처음 설치하는 전용 실습 환경이므로 필드 소유권 충돌이 나면 원인을 읽고 멈춥니다. 기존 설치를 덮기 위해 --force-conflicts를 습관적으로 추가하지 않습니다.

이 설치는 클러스터 권한과 CRD를 추가합니다. AppProject를 좁힌다는 이유로 설치 자체의 권한이 작은 것은 아닙니다. 대상 namespace는 기본 설치가 참조하는 argocd를 유지합니다. 다른 버전이나 namespace를 쓸 때는 공식 업그레이드·설치 문서를 먼저 확인합니다.

2. 관리 화면에 접속한다

Pod가 준비된 뒤 kubectl을 실행하는 PC에서 포트를 전달합니다. 초기 비밀번호를 읽는 명령의 결과는 민감한 값이므로 로그나 화면 자료로 공유하지 않습니다.

kubectl -n argocd port-forward svc/argocd-server 18081:443

다른 터미널에서 초기 비밀번호를 읽습니다. 아래 Bash 명령은 Linux 또는 Git Bash 기준입니다.

kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath='{.data.password}' | base64 --decode

브라우저에서 https://localhost:18081로 접속해 admin으로 로그인하고 비밀번호를 변경합니다. 기본 자체 서명 인증서의 출처를 확인하며, 팀 운영에서는 신뢰하는 인증서와 SSO·RBAC를 구성합니다. 변경 후 초기 비밀번호 Secret은 더 이상 필요하지 않습니다.

kubectl -n argocd delete secret argocd-initial-admin-secret
kubectl create namespace gitops-demo

3. 배포 저장소를 만들고 nginx 선언을 넣는다

Gitea에 빈 gitops-config 저장소를 만들고 clone합니다. 예제 묶음의 config/environments/dev/deployment.yaml을 저장소의 environments/dev/deployment.yaml에 넣어 커밋하고 푸시합니다. 아래 digest는 공식 nginx 레지스트리에서 확인한 이미지 인덱스이며 변경 시점의 보안 업데이트를 다시 검토해야 합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gitops-web
  namespace: gitops-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gitops-web
  template:
    metadata:
      labels:
        app: gitops-web
    spec:
      containers:
        - name: web
          image: nginx@sha256:970364228b1e4f932abc22d321932816782cca8a20b85a77552a7cfe9acfb3b5
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 250m
              memory: 128Mi
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 2
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: gitops-web
  namespace: gitops-demo
spec:
  selector:
    app: gitops-web
  ports:
    - port: 80
      targetPort: 80

리소스 요청과 제한은 작은 학습용 시작값입니다. 실제 부하를 측정한 운영 권장값이 아닙니다. Pod Security가 제한된 클러스터에서는 기본 nginx의 root 실행과 권한이 정책에 맞는지도 확인해야 합니다.

4. Argo CD가 Gitea를 읽을 수 있게 한다

Pod에서 접근 가능한 내부 DNS와 포트로 저장소 URL을 정합니다. 이 글의 git.example.internal, YOUR_USER는 실제 값으로 바꿉니다. 15편의 localhost 터널 주소를 여기에 넣지 않습니다.

Gitea에는 이 저장소의 읽기 전용 deploy key를 등록합니다. Argo CD Settings의 Certificates에서 검증한 SSH known_hosts를 추가하고, Repositories에서 SSH URL과 개인 키를 등록합니다. 메뉴 이름이 달라졌다면 공식 private repositories 안내를 따릅니다. Connection Successful이 되는지 먼저 확인합니다.

SSH 인증 키와 서버 호스트 키는 역할이 다릅니다. HTTPS를 선택했다면 조직의 CA를 등록하고 읽기 전용 자격 증명을 사용합니다. 실제 개인 키를 Git의 bootstrap YAML에 넣지 않습니다.

5. 출처와 대상을 제한한 프로젝트를 등록한다

아래 project.yaml과 application.yaml의 저장소 URL을 같은 실제 값으로 바꿉니다. namespace는 앞에서 따로 만들었으므로 프로젝트가 cluster-scoped Namespace 생성을 허용할 필요가 없습니다.

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: git-training
  namespace: argocd
spec:
  sourceRepos:
    - 'ssh://git@git.example.internal:12222/YOUR_USER/gitops-config.git'
  destinations:
    - namespace: gitops-demo
      server: https://kubernetes.default.svc
  clusterResourceWhitelist: []
  namespaceResourceWhitelist:
    - group: apps
      kind: Deployment
    - group: ''
      kind: Service
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: gitops-web
  namespace: argocd
spec:
  project: git-training
  source:
    repoURL: 'ssh://git@git.example.internal:12222/YOUR_USER/gitops-config.git'
    targetRevision: main
    path: environments/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: gitops-demo
  syncPolicy: {}
kubectl apply -f project.yaml
kubectl apply -f application.yaml
kubectl -n argocd get applications

첫 상태는 리소스가 아직 없으므로 OutOfSync 또는 Missing일 수 있습니다. UI에서 Application의 Diff를 읽고 생성 대상이 gitops-demo의 Deployment·Service뿐인지 확인합니다. Sync를 실행하고 Synced와 Healthy 상태, 대상 리소스를 함께 확인합니다.

kubectl -n gitops-demo get deploy,svc,pods
kubectl -n gitops-demo rollout status deployment/gitops-web --timeout=120s
kubectl -n gitops-demo port-forward svc/gitops-web 18082:80

브라우저에서 http://localhost:18082 응답을 확인합니다. ImagePullBackOff는 이미지·레지스트리 연결을, ComparisonError는 저장소 인증·경로·YAML을, 권한 거절은 AppProject와 클러스터 RBAC를 구분해 살펴봅니다.

설치 기준은 Argo CD Getting Started, 범위 제한은 Projects, 인증은 Private Repositories입니다. 다음 편에서 자동 동기화와 실제 상태의 차이를 다룹니다.

이전 글 · [Git·GitOps 20] GitOps의 네 원칙과 앱·배포 설정 저장소 나누기 · 다음 글 · [Git·GitOps 22] 변경 감지와 복구: 자동 Sync·selfHeal·prune·Git revert · 연재 목록과 예제 파일

댓글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

닉네임·이메일·댓글 내용·IP 주소·브라우저 정보는 댓글 게시 및 관리에 사용됩니다. 이메일과 접속 정보는 공개 댓글에 표시하지 않습니다. 개인정보 요청은 운영자 이메일로 보내 주세요. 개인정보처리방침