CICD

[CI/CD Study 3주차] ArgoCD 를 이용한 CI/CD 배포

Kimalarm 2025. 11. 2. 06:44

CoudNet@ 팀의 가시다님께서 리딩하시는 CI/CD Study 3주차 스터디 내용 정리

 

이번 주차에서는 Jenkins, Gogs, ArgoCD 를 통한 CI/CD 에 대해서 학습했습니다.
그 중 ArgoCD 배포에 대해서 작성한 글입니다.

 

실습 환경은 이전 글을 참고하시면 됩니다.
Kind 를 통해 Control Plane 1대 + Worker Node 1대로 구성하였습니다.




1. ArgoCD

 

ArgoCD 에 대한 소개 및 아키텍처 설명은 예전에 관련 글을 작성해둔 것이 있어 해당 글을 참고 바랍니다.

 

ArgoCD 란 무엇인가 ?? (핵심 용어, 구성 요소 등)

1. ArgoCD ArgoCD 는 Git 을 배포의 원천으로 사용하는 GitOps CD 도구입니다.GitOps 특성상 애플리케이션 자체의 소스코드와 배포에 해당되는 소스코드를 별개로 관리하는 것이 일반적입니다.Git 에 작성

kimalarm.tistory.com

 



1.1. ArgoCD 설치

 

kubectl create ns argocd
cat <<EOF > argocd-values.yaml
dex:
  enabled: false

server:
  service:
    type: NodePort
    nodePortHttps: 30002
  extraArgs:
    - --insecure  # HTTPS 대신 HTTP 사용
EOF

# ArgoCD 설치
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 9.0.5 -f argocd-values.yaml --namespace argocd

 

 

 

1.2. ArgoCD 설정

 

ArgoCD 또한 Jenkins 와 마찬가지로 초기 비밀번호 설정이 있습니다.
아래 명령어를 통해 비밀번호 확인 후 ArgoCD 설정을 진행합니다.

 

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

# ArgoCD UI 접속
open "http://127.0.0.1:30002"

 

ArgoCD 접속 후 Gogs 의 접근 정보를 입력합니다.

 

  • connection method : VIA HTTPS
  • Type : git
  • Project : default
  • Repo URL : Gogs 저장소 주소
  • Username : <Gogs 사용자>
  • Password : <Gogs 토큰>

 

 

 

1.3. ArgoCD 배포 실습

 

gogs 연동 확인을 위해 간단한 nginx 배포 테스트를 진행합니다.

 

Gogs 저장소에 배포를 위한 Helm Chart 생성

 

# Gogs 저장소 계정 접속 정보
MyIP=<Gogs 주소>
TOKEN=<Gogs 사용자 토큰>

# ops-deploy 클론
git clone http://devops:$TOKEN@$MyIP:3000/devops/ops-deploy.git

# 
cd ops-deploy

# Local 깃 저장소 설정
git config --local user.name "devops"
git config --local user.email "a@a.com"
git config --local init.defaultBranch main
git config --local credential.helper store
git --no-pager config --local --list
git --no-pager branch
git remote -v

 

kind 배포를 위한 Helm 파일 생성

 

# Nginx Helm 템플릿 생성

VERSION=1.26.1
mkdir nginx-chart
mkdir nginx-chart/templates

cat > nginx-chart/VERSION <<EOF
$VERSION
EOF

cat > nginx-chart/templates/configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}
data:
  index.html: |
{{ .Values.indexHtml | indent 4 }}
EOF

cat > nginx-chart/templates/deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}
    spec:
      containers:
      - name: nginx
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        ports:
        - containerPort: 80
        volumeMounts:
        - name: index-html
          mountPath: /usr/share/nginx/html/index.html
          subPath: index.html
      volumes:
      - name: index-html
        configMap:
          name: {{ .Release.Name }}
EOF

cat > nginx-chart/templates/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
spec:
  selector:
    app: {{ .Release.Name }}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30000
  type: NodePort
EOF

cat > nginx-chart/values-dev.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>DEV : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 1
EOF

cat > nginx-chart/values-prd.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>PRD : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 2
EOF

cat > nginx-chart/Chart.yaml <<EOF
apiVersion: v2
name: nginx-chart
description: A Helm chart for deploying Nginx with custom index.html
type: application
version: 1.0.0
appVersion: "$VERSION"
EOF

 

# git 푸시
git status && git add . && git commit -m "Add nginx helm chart" && git push -u origin main

 

ArgoCD Application 등록

 

  • App Name : dev-nginx
  • Project Name : default
  • SYNC POLICY : Manual
  • SYNC OPTIONS : AUTO-CREATE NAMESPACE(Check)
  • PRUNE PROPAGATION POLICY

 

 

  • Source
  • Repo URL : 설정되어 있는 것 선택
  • Revision : HEAD
  • PATH : nginx-chart
  • DESTINATION
  • Cluster URL : <기본값>
  • NAMESPACE : dev-nginx
  • HELM
  • Values files : values-dev.yaml

 

 

Auto Sync 정책이 아니기 때문에 수동으로 Sync 를 눌러주기 전까지는 ArgoCD 가 애플리케이션을 동기화 하지 않습니다.

 

 

ArgoCD 에서 Sync 를 눌러주면 Gogs 레포지토리에 저장된 Helm 내용으로 kind 에 배포를 진행합니다.

 

 

 

1.4. 애플리케이션 Helm 업데이트

 

ArgoCD 로 배포된 Nginx Helm 의 버전을 수정해서 푸시해보겠습니다.
기존 1.26.1 -> 변경 1.26.2

 

VERSION=1.26.2

cat > nginx-chart/VERSION <<EOF
$VERSION
EOF

cat > nginx-chart/values-dev.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>DEV : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 2
EOF

cat > nginx-chart/values-prd.yaml <<EOF
indexHtml: |
  <!DOCTYPE html>
  <html>
  <head>
    <title>Welcome to Nginx!</title>
  </head>
  <body>
    <h1>Hello, Kubernetes!</h1>
    <p>PRD : Nginx version $VERSION</p>
  </body>
  </html>

image:
  repository: nginx
  tag: $VERSION

replicaCount: 2
EOF

cat > nginx-chart/Chart.yaml <<EOF
apiVersion: v2
name: nginx-chart
description: A Helm chart for deploying Nginx with custom index.html
type: application
version: 1.0.0
appVersion: "$VERSION"
EOF

 

# Git push
git status && git add . && git commit -m "Update nginx version $(cat nginx-chart/VERSION)" && git push -u origin main

 

이후, ArgoCD 에서 새로고침하여 Nginx Helm 의 차이점을 확인할 수 있습니다.

 



1.5. ArgoCD CRD YAMl 활용

 

ArgoCD UI 에서도 애플리케이션을 배포할 수 있지만, 해당 과정을 YAML 파일로 선언적 구성을 진행할 수도 있습니다.
기존에 배포된 ArgoCD 애플리케이션을 삭제한 후 실습을 진행했습니다.

 

MyIP=192.168.200.121 # Helm 이 저장된 Gogs 저장소 IP

# ArgoCD Application 배포
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values-dev.yaml
    path: nginx-chart
    repoURL: http://$MyIP:3000/devops/ops-deploy
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: dev-nginx
    server: https://kubernetes.default.svc
EOF

 

YAML 이 배포됨과 동시에 싱크가 맞춰지는 것을 알 수 있습니다.
해당 애플리케이션은 UI 에서도 확인이 가능합니다.

 

 



1.6. ArgoCD - Gogs Trigger 활용

 

ArgoCD 의 Reconciliation 설정 때문에 기본 180초마다 동기화를 확인합니다.

레포지토리에 Push 되는 즉시 ArgoCD 에 반영하고 싶다면 Gogs 에서 웹훅 설정이 필요합니다.

 

Gogs 웹훅 설정

 

  • Payload URL : ArgoCD Server 주소

 

 

ArgoCD 애플리케이션 배포

 

애플리케이션을 먼저 배포해둔 후, 변경사항을 Push 하여 즉시 변경이 되는 지 확인하겠습니다.

 

# 애플리케이션 배포
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-nginx
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values-dev.yaml
    path: nginx-chart
    repoURL: http://$MyIP:3000/devops/ops-deploy
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: dev-nginx
    server: https://kubernetes.default.svc
EOF

 

# ArgoCD 와 연동된 깃 레포지토리
cd cicd-labs/ops-deploy/nginx-chart

# 레플리카 수 변경
sed -i "s|replicaCount: 2|replicaCount: 3|g" values-dev.yaml

# 깃 푸시
git add values-dev.yaml && git commit -m "Modify nginx-chart : values-dev.yaml" && git push -u origin main

 

깃 저장소에 푸시하자마자 바로 배포되는 것을 확인했습니다.

 

 



2. Jenkins + ArgoCD + Kubernetes CI/CD 전체 과정 수행

 

 

이번에는 Jenkins 부터 시작해서 이미지를 빌드하고 해당 이미지를 ArgoCD 를 통해 Kind 클러스터에 배포하는 일련의 과정을 수행하겠습니다.

 

 

2.1. k8s 배포용 저장소 설정

 

먼저 ArgoCD 가 바라보게될 Deployment 깃 저장소를 설정하겠습니다.

 

mkdir dev-app

# 도커 계정 정보 입력
DHUSER=<도커허브 계정>

# 버전 정보 입력
VERSION=0.0.1

# Helm 배포용 생성
cat > dev-app/VERSION <<EOF
$VERSION
EOF

cat > dev-app/timeserver.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: timeserver
spec:
  replicas: 2
  selector:
    matchLabels:
      pod: timeserver-pod
  template:
    metadata:
      labels:
        pod: timeserver-pod
    spec:
      containers:
      - name: timeserver-container
        image: docker.io/$DHUSER/dev-app:$VERSION
        livenessProbe:
          initialDelaySeconds: 30
          periodSeconds: 30
          httpGet:
            path: /healthz
            port: 80
            scheme: HTTP
          timeoutSeconds: 5
          failureThreshold: 3
          successThreshold: 1
      imagePullSecrets:
      - name: dockerhub-secret
EOF

cat > dev-app/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: timeserver
spec:
  selector:
    pod: timeserver-pod
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    nodePort: 30000
  type: NodePort
EOF

# Git Push
git add . && git commit -m "Add dev-app deployment yaml" && git push -u origin main



2.2. ArgoCD 애플리케이션 생성

 

앞서 생성한 저장소를 바라보는 애플리케이션을 생성하겠습니다.

 

echo $MyIP

cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: timeserver
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    path: dev-app
    repoURL: http://$MyIP:3000/devops/ops-deploy
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: default
    server: https://kubernetes.default.svc
EOF

# 확인

kubectl get applications -n argocd timeserver
kubectl describe applications -n argocd timeserver

 

 

 

 

2.3. 소스 코드 변경

 

dev-app 이미지를 생성하는 소스코드를 변경하여 푸시해봅니다.
여기서는 기존 Jenkinsfile 을 수정하여 이미지 태그를 변경하여 푸시하겠습니다.

 

pipeline {
    agent any
    environment {
        DOCKER_IMAGE = 'kimalarm2/dev-app' // Dockerhub 저장소 명칭
    }
    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.200.121:3000/devops/dev-app.git',  // 로컬 Gogs 저장소 주소
                 credentialsId: 'gogs-crd'  // Credentials ID
            }
        }
        stage('Read VERSION') {
            steps {
                script {
                    // VERSION 파일 읽기
                    def version = readFile('VERSION').trim()
                    echo "Version found: ${version}"
                    // 환경 변수 설정
                    env.DOCKER_TAG = version
                }
            }
        }
        stage('Docker Build and Push') {
            steps {
                script {
                    docker.withRegistry('https://index.docker.io/v1/', 'dockerhub-crd') {
                        // DOCKER_TAG 사용
                        def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                        appImage.push()
                        appImage.push("latest")
                    }
                }
            }
        }
        stage('ops-deploy Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.200.121:3000/devops/ops-deploy.git',  // 로컬 Gogs 저장소 주소
                 credentialsId: 'gogs-crd'  // Credentials ID
            }
        }
        stage('ops-deploy version update push') {
            steps {
                sh '''
                OLDVER=$(cat dev-app/VERSION)
                NEWVER=$(echo ${DOCKER_TAG})
                sed -i "s/$OLDVER/$NEWVER/" dev-app/timeserver.yaml
                sed -i "s/$OLDVER/$NEWVER/" dev-app/VERSION
                git add ./dev-app
                git config user.name "devops"
                git config user.email "a@a.com"
                git commit -m "version update ${DOCKER_TAG}"
                git push http://${GOGSCRD_USR}:${GOGSCRD_PSW}@192.168.200.121:3000/devops/ops-deploy.git // 오류 발생 시 GOGS_USER:GOGS_PSW 직접 주입
                '''
            }
        }
    }
    post {
        success {
            echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
        }
        failure {
            echo "Pipeline failed. Please check the logs."
        }
    }
}

 

# Git push
git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

 

저는 애플리케이션 버전도 0.0.6 으로 변경해서 푸시했으며, Jenkins 에서 해당 이미지로 빌드된 것을 확인했습니다.
이후 ArgoCD 에서도 0.0.6 으로 Sync 가 된 것을 확인했습니다.