소개
이전까지는 어플리케이션 배포를 위한 CI/CD Pipeline 구성을 Github-Action(self-hosted) 을 이용하여 모두 구성하였었습니다.
이번에는 ArgoCD 를 이용하여 GitOps 기반으로 자동 배포 파이프라인을 고도화 해보려 합니다.
GitOps 를 위해 ArgoCD 를 구축하였고, 이전 포스트까지 구현하였던 인프라와 어플리케이션 기반으로 ArgoCD 로 전환한 절차를 정리해 봅니다.
ArgoCD 란?
ArgoCD 는 GitOps 기반으로 kubernetes 에 서비스를 배포하기 위한 도구입니다. Git Repository 를 기반으로 하여 manifest 들이 변경되면 그에 맞게 kubernetes 의 Resource 들을 sync 하면서 어플리케이션 배포를 자동화할 수 있게 됩니다.
ArgoCD 를 이용하면 아래와 같은 장점을 가져갈 수 있습니다.
- 선언적으로 kubernetes resource 를 관리할 수 있습니다.
- Git Repository 변경사항에 대해 자동으로 클러스터 상태를 동기화 할 수 있습니다.
- Git 버전관리로 인해 롤백이 용이합니다.
배포 Pipeline 구성도
ArgoCD 구성을 통해 서비스 배포 파이프라인 구성은 최종적으로 아래와 같이 구성을 하고자 합니다.

배포 절차는 아래와 같이 진행이 됩니다.
- Application 개발 후 Github 으로 Push
- Github Action(self-hosted) 을 통해 CI + Image 취약점 점검
- Application 레포에 포함된 manifest 의 image tag 를 최신 값으로 수정
- Argocd 의 Auto-sync 로 manifest 와 Kubernetes 클러스터 동기화
- Application 최종 배포 완료
Application 은 기존에 만들어 두었던 node.js 로 만든 간단한 ingress traffic checker 를 사용하였습니다.
ArgoCD 설치
기본적으로 ArgoCD Docs 를 통해 상세한 설치 과정은 확인할 수 있습니다. 해당 절차를 바탕으로 설치를 진행해 봅니다.
먼저, argocd namespace 를 만듭니다.
# install argocd
kubectl create namespace argocd
helm 을 이용할 수도 있지만, 이번에는 install.yaml 을 기반으로 설치해봅니다.
# argocd 설치
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
argocd
namespace 에 argocd 를 설치하고, 해당 namespace 에 잘 설치가 되었는지 확인해봅니다.

이제 argocd 콘솔에 접속하기 위한 설정을 해줘야합니다. EKS 나 다른 클라우드 환경을 이용하고 있다면 설치시 Service type 을 LoadBalancer 로 변경하여 구성을 해줘야 합니다.
하지만 전 로컬환경에 구축한 kubernetes 환경이니 기본 ClusterIP
type 으로 구성된 Service 에 port-forward 설정을 하여 로컬에서 접속할 수 있도록 설정하였습니다.
# nohup 을 이용한 background 로 port-forward 설정
nohup kubectl -n argocd port-forward argocd-server-74759b8c98-l2rzx 40080:8080 &
port-forward 설정을 해주고 localhost:40080
으로 접속하여 콘솔 접속 가능여부를 확인합니다.

잘 접속되는지 확인이 되었다면, admin
계정 접속을 하기 위해 password 를 확인합니다. argocd 구축시 생성된 initial-admin-secrets 를 통해 초기 password 를 확인하여 로그인합니다.
# get default admin password of argocd
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
ArgoCD – Github 연동
먼저, Github Personal access Token 발행 페이지에서 아래와 같이 scopes 를 적용하여 token 을 만듭니다.

ArgoCD 에서 kubernetes cluster 에 서비스를 배포하기 위해 동기화할 manifest 는 아래와 같이 application 레포내 별도의 manifest 폴더를 만들어 kustomize 구조로 만들었습니다.
ArgoCD 는 이제 해당 폴더를 바라보면서 kubernetes 리소스를 동기화 할 것입니다.

이제 argocd settings 에서 연동할 github repository 설정을 통해 연동을 진행합니다. 여기서 미리 만들어 두었던 github personal token 을 password 로 설정합니다.

Repo 연동이 완료되면, Application 을 추가해줍니다.


추가하여 제대로 sync 가 되어 배포가 되었는지 확인합니다.

Github Action 으로 CI 구성 및 자동 배포 확인
이제 application 변경에 대한 CI 과정에서 image 가 변경되면, ArgoCD 가 최신 상태의 배포를 제대로 진행하는지 확인해봅니다.
github action 을 아래와 같이 구성하였습니다. Push 를 하게 되면 image build 를 하고 Trivy 를 통해 이미지 취약점 점검을 합니다.
완료가 되면 docker-hub 에 이미지를 푸쉬하고, 최신 image tag 를 적용한 manifest 로 업데이트합니다.
그렇게 manifest 변경이 일어나면 ArgoCD 가 위에서 완료한 설정 기반으로 auto-sync 를 하여 클러스터 내 서비스를 재배포하게 됩니다.
name: ci
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
jobs:
build:
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:${{ github.sha }}
build-args: |
DB_HOST=${{ secrets.DB_HOST }}
DB_PORT=${{ secrets.DB_PORT }}
DB_USER=${{ secrets.DB_USER }}
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
DB_DATABASE=${{ secrets.DB_DATABASE }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: "${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:${{ github.sha }}"
format: "table"
exit-code: "0"
ignore-unfixed: true
vuln-type: "os,library"
severity: "CRITICAL,HIGH"
- name: Push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:${{ github.sha }}
build-args: |
DB_HOST=${{ secrets.DB_HOST }}
DB_PORT=${{ secrets.DB_PORT }}
DB_USER=${{ secrets.DB_USER }}
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
DB_DATABASE=${{ secrets.DB_DATABASE }}
- name: Image digest
run: echo "Image pushed with digest ${{ steps.build-and-push.outputs.digest }}"
- uses: azure/setup-kubectl@v3
- uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
update-manifest:
needs: build
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
with:
repository: ${{ github.repository }}
ref: "main"
token: ${{ secrets.GIT_PAT}}
- name: Install Kustomize
run: |
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/
- name: Update image tag in manifest
env:
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
REGISTRY: ${{ secrets.DOCKER_USERNAME }}
REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
IMAGE_TAG: ${{ github.sha }}
run: |
cd manifest/overlays
kustomize edit set image $REGISTRY/$REPOSITORY=$REGISTRY/$REPOSITORY:$IMAGE_TAG
- name: Commit updated Kustomize config
run: |
git config --global user.name ${{ secrets.GIT_EMAIL }}
git config --global user.email ${{ secrets.GIT_USERNAME }}
git add -A
git commit -m "Update image tag to ${{ github.sha }} in manifest [skip ci]"
git push origin main
해당 Action workflow 를 적용하였으면 신규 Push 를 발생하여 ArgoCD 를 통해 서비스가 제대로 배포가 되었는지 확인해봅니다.
