EKS CI/CD 구성
Helm차트란?
helm은 쿠버네티스를 위한 패키지 매니저이다.
그리고 이 패키지를 하나의 리소스로 묶은 것이 helm chart다.
이 helm chart는 yaml파일의 묶음(패키지)로, 이 묶음을 public 혹은 priavte registry에 push해두고, helm 명령어를 통해 helm chart를 설치하여 쿠버네티스 리소스를 배포할 수 있다.
argoCD란?
GitOps
기존의 소프트웨어를 배포하고 관리하는 방식은 문제점이 많았다.
인프라 환경을 수동적으로 관리하고, 소프트웨어와 인프라를 따로 관리하는 경우가 많았기에 이로 인해 인프라와 소프트웨어 간의 불일치가 발생하게 되었고, 배포 및 운영 과정에서 문제가 발생할 가능성이 높았다.
이러한 기존의 접근 방식에 대한 대안으로 GitOps가 탄생했다.
GitOps는 Git저장소를 사용하는 소프트웨어 배포 접근 방식이다. GitOps방식은 인프라와 소프트웨어를 함께 관리하기 때문에 Git버전 관리 시스템과 운영 환경 간의 일관성을 유지하여 소프트웨어 간의 불일치 문제를 해결한다.
도한 모든 코드와 인프라 변경 사항이 Git 저장소에 저장되기 때문에, 변경 내역을 추적하고 롤백을 쉽게 수행할 수 있다.
ArgoCD
ArgoCD는 GitOps를 구현하기 위한 도구 중 하나로 Kubernetes 애플리케이션의 자동 배포를 위한 오픈소스 도구이다.
k8s클러스터에 배포된 애플리케이션의 CI/CD 파이프라인에서 CD부분을 담당하며, Git저장소에서 변경 사항을 감지하여 자동으로 k8s클러스터에 애플리케이션을 배포할 수 있다.
ArgoCD는 애플리케이션을 배포를 위해 Declarative Application Management(선언적 애플리케이션 관리)를 사용한다.
이는 애플리케이션을 배포할 때 명시적으로 원하는 상태를 정의하고, 이를 ArgoCD가 클러스터의 상태와 비교하여 원하는 상태로 유지하도록 한다.
그렇기 때문에 애플리케이션 배포 및 관리 과정에서 발생할 수 있는 실수나 오류를 방지할 수 있다.
동작은 다음 이미지와 같다.
- 사용자가 Git 저장소에 Push한다. 이때 k8s에 배포 방식인 Helm 또는 kustomize를 사용하기 때문에 해당 배포 방식의 구조로 Git 저장소에 Push한다.
- ArgoCD는 Git저장소에 변경 상태를 감지한다.
- 변경된 내용을 k8s에 배포하여 반영한다.
참고
위 방법이 가장 기본적인 방법이지만 현재 프로젝트는 각자의 개발파트가 여러개 있기 때문에 GitOps를 중앙관리형식으로 구성한다.
자세한 내용은 아래에 다룬다.
구현하기
AWS EKS 상에 ArgoCD를 설치해야한다. 설치 sh스크립트는 다음과 같다.
#!/bin/bash
set -e
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n dev-system --set server.service.type=ClusterIP
#비번 조회
#kubectl -n dev-system get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# kubectl port-forward service/argocd-server -n dev-system 8081:443
설치가 완료되면 다음 내용을 이용하여 kubectl apply를 해준다.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: backend-services
namespace: dev-system
# Finalizer that ensures that project is not deleted until it is not referenced by any application
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
description: Project SOS
sourceRepos:
- <GitOps Repository>
destinations:
- namespace: '*'
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: '*'
kind: '*'
이제 ArgoCD는 해당 GitOps Repository를 감지하게 된다.
현재 프로젝트는 Spring서버, 파이썬서버, 스케줄링 서버 등 여러 서버가 있고 각자 레포지토리에서 작업을 진행하고 있다.
필자를 인프라를 담당하고 있었고 각자 CI/CD에 크게 집중하지 않게 진행하려면, GitOps레포지토리를 따로 만들고 여기서 버전관리를 하는것이 낫다고 판단했다.
구성도는 다음과 같다.
참고
구성도는 위의 ArgoCD부분에서 GitOps레포지토리가 추가되고 이를 트리거 한 것만 바뀌었다.
개인 레포지토리에 github actions를 구성하고, 트리거가 되면 image를 빌드하고 미리 구축한 ECR에 배포를 한다.
그 다음, GitOps레포지토리에 접근하여 values.yaml 내 버전을 변경해준다.
이러면 ArgoCD는 해당 GitOps의 변경점을 트리거하여 GitOps에 저장된 버전을 가져와 ECR에서 새로운 버전의 이미지를 Pull하고 새로 배포를 진행하게 된다.
예제 github actions workflow코드는 다음과 같다.
name: CI/CD - Build, Push, and Update ArgoCD Manifests
on:
push:
branches:
- main
workflow_dispatch:
jobs:
ci:
runs-on: ubuntu-latest
outputs:
IMAGE_TAG: ${{ steps.version.outputs.tag }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: us-east-2
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-session-name: ecrPushRole
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Read version
id: version
run: echo "tag=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Build Docker image
run: |
docker build -t ${{ steps.login-ecr.outputs.registry }}/python-repo:${{ steps.version.outputs.tag }} .
- name: Push Docker image
run: |
docker push ${{ steps.login-ecr.outputs.registry }}/python-repo:${{ steps.version.outputs.tag }}
cd:
needs: [ci]
runs-on: ubuntu-latest
steps:
- name: Checkout GitOps Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
repository: <GitOps Repository>
ref: main
token: ${{ secrets.PAT }}
- name: Update image.tag in ai_value.yaml
uses: mikefarah/yq@master
env:
IMAGE_TAG: ${{ needs.ci.outputs.IMAGE_TAG }}
with:
cmd: yq eval -i '.image.tag = env(IMAGE_TAG)' 'ai_value.yaml'
# YAML 파일을 편집하는 CLI 도구
# ai_value.yaml 안의 image.tag를 IMAGE_TAG 값으로 변경
- name: Commit manifest changes
env:
IMAGE_TAG: ${{ needs.ci.outputs.IMAGE_TAG }}
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
git add ai_value.yaml
git commit -m "ci: update python-app image tag to $IMAGE_TAG" || echo "No changes to commit"
- name: Push commit
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.PAT }}
repository: <GitOps Repository>
secrets.PAT은 GitOps 레포지토리에 접근을 위한 github actions 시크릿 키다.
GitOps에서 ECR에 접근하려면 Access Key 혹은 OIDC 인증이 필요하다.
필자는 Access Key를 사용했지만 OIDC를 추천한다고 한다.
참고
GitOps의 예제 디렉토리 구성은 다음과 같다.
gitops-repo/
python-app/
Chart.yaml
values.yaml
templates/
deployment.yaml
service.yaml
nodejs-app/
Chart.yaml
values.yaml
templates/
deployment.yaml
service.yaml
redis/
Chart.yaml
values.yaml
templates/
statefulset.yaml
service.yaml
applications/
python-app.yaml # ArgoCD Application CR
nodejs-app.yaml
redis.yaml
applications 디렉토리는 ArgoCD가 각 Helm차트의 정보를 가져갈 수 있도록 작성한 yaml파일들이다.
나머지 디렉토리들은 Helm Chart 템플릿들이다.
applications의 python-app.yaml만 확인해보자면 다음과 같다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: python-app
namespace: dev-system
spec:
project: backend-services
destination:
namespace: dev-system
server: https://kubernetes.default.svc
source:
repoURL: <GitOps Repository>
targetRevision: main # 브랜치나 태그 지정 (예: main)
path: python-app # Chart.yaml이 있는 디렉토리
helm:
valueFiles:
- values.yaml # python-app 디렉토리 안의 values.yaml 사용
syncPolicy:
automated:
prune: true
selfHeal: true
ArgoCD가 트리거 되면 repoURL을 통해 해당 레포지토리에 접근하고, path와 valueFiles로 해당 애플리케이션의 Chart.yaml, values.yaml을 가져와 해당 내용대로 배포를 진행하게 된다.