GitlabCI 및 ArgoCD 설계
GitlabCI에서는 이전 포스팅에서 설명한 대로, Application을 Build 하여 Conatiner Image로 만든 후 Docker Image Registry인 NCP Container Resitry에 Upload 합니다. 그 후 helm repository에 Image Tag값을 변경해 주면 ArgoCD의 Sync 옵션이 Auto일 경우 OutOfSync일 경우 helm repo와 자동으로 Sync 하여 배포를 완료해 주지만, 저의 경우 Sync 옵션을 Manual로 지정하였기 때문에 GitlabCI에서 ArgoCD의 Sync API까지 호출해 주도록 합니다.
GitLab CI/CD 메커니즘
GitLab의 CICD PipeLine 메커니즘은 GitLab 저장소에 내장된 자동화 도구로, 코드의 빌드, 테스트, 배포를 자동화하여 지속적인 통합(CI)과 지속적인 배포(CD)를 실현해 주는데, 주요 구성요소는 다음과 같습니다.
주요 구성 요소
- . gitlab-ci.yml 파일
파이프라인의 정의 파일로, 각 스테이지(build, test, deploy 등)와 그 안의 job들을 선언합니다. - 스테이지(Stage) & Job
- 스테이지: 파이프라인의 전체적인 단계(예: 빌드, 테스트, 배포)로, 각 스테이지는 순차적으로 실행됩니다.
- Job: 각 스테이지 내에서 실행되는 작업 단위로, 병렬 실행이 가능하며, 특정 스크립트나 명령어를 수행합니다.
- GitLab Runner
정의된 job을 실제로 실행하는 에이전트입니다. Runner는 Docker, VM, 또는 실제 머신 등 다양한 환경에서 동작할 수 있어, 유연하게 CI/CD 환경을 구성할 수 있습니다.
작동 방식
- 트리거(Trigger)
코드 커밋, Merge Request, 스케줄 등 특정 이벤트에 의해 파이프라인이 자동으로 시작됩니다. - 파이프라인 실행
. gitlab-ci.yml에 정의된 스테이지 순서대로 job들이 실행되며, 각 job은 지정된 Runner에서 독립적으로 실행됩니다. - 결과 보고
각 job의 성공, 실패 여부와 로그가 GitLab UI에 표시되어, 개발자와 시스템 엔지니어가 빌드 상태를 모니터링할 수 있습니다. - 아티팩트와 캐시
job 간에 결과물을 공유하거나, 빌드 속도를 높이기 위해 캐시를 활용할 수 있습니다.
Kaniko
Kaniko는 쿠버네티스 환경에서 컨테이너 이미지를 안전하고 효율적으로 빌드할 수 있도록 고안된 도구입니다. 기존의 Docker 빌드 방식은 Docker 데몬에 의존하기 때문에, 이미지 빌드를 위해 데몬을 실행해야 하고 이 과정에서 보안 취약점이나 권한 문제 등이 발생할 수 있습니다. 특히, 쿠버네티스 클러스터와 같은 분산 환경에서는 Docker 데몬을 실행하는 컨테이너가 추가적인 보안 리스크를 내포하게 되어 관리가 복잡해질 수 있습니다.
이에 비해 Kaniko는 Docker 데몬 없이 Dockerfile을 읽어 이미지 레이어를 순차적으로 생성하는 방식으로 동작합니다. 이로 인해 빌드 과정에서 특권 모드가 필요 없으며, 권한이 낮은 상태에서도 안전하게 이미지를 생성할 수 있습니다. 또한, Kaniko는 컨테이너 내부에서 직접 빌드 작업을 수행하고, 완성된 이미지를 외부 레지스트리로 직접 푸시하는 기능을 제공하므로 CI/CD 파이프라인에 통합하기에 매우 적합합니다.
뿐만 아니라 Kaniko는 리소스 사용 효율성이 뛰어나며, 클러스터 내부의 다른 작업에 미치는 영향이 적습니다. 이는 대규모 분산 환경이나 자동화된 빌드 시스템에서 중요한 요소로 작용합니다. 보안, 효율성, 그리고 관리의 용이성 측면에서 Kaniko는 쿠버네티스 환경에서 컨테이너 이미지 빌드를 수행할 때 선택해야 할 강력한 도구라고 할 수 있습니다. 때문에 저는 Kaniko를 사용하여 Container Image를 빌드하였습니다.
사용방법은 gitlab 공식 docs에서 확인 가능합니다.
Use kaniko to build Docker images | GitLab Docs
Use kaniko to build Docker images Tier: Free, Premium, UltimateOffering: GitLab.com, GitLab Self-Managed, GitLab Dedicated kaniko is a tool to build container images from a Dockerfile, inside a container or Kubernetes cluster. kaniko solves two problems wi
docs.gitlab.com
build-and-push-image
gitlab-ci.yaml 파일에서는 원하는 형태의 stages를 설정할 수 있습니다. 저는 "build-and-push-image", "update-helm-chart" 2가지의 stage로 구분 지어 gitlab-ci.yaml 파일을 작성하였는데, 첫 번째로 build-and-push-image를 구현해 보겠습니다.
Kaniko를 사용하기 위해서는 "gcr.io/kaniko-project/executor:v1.23.2-debug" 이미지를 사용하여야 합니다. debug이미지를 사용하는 이유는 shell이 debug 이미지에만 존재하기 때문입니다. 공식 문서에서도 debug image를 recommended 하고 있습니다.
image:
name: gcr.io/kaniko-project/executor:v1.23.2-debug
entrypoint:
- ""
Stage는 위에서 설명한 것과 같이 두 가지로 구분 지었습니다. 이미지 태그는 GitLab CI에서 제공해 주는 Default Variables를 참고하였습니다.
stages:
- build-and-push-image
- update-helm-chart
variables:
DOCKER_CONFIG: /kaniko/.docker/
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
prod와 develop 환경의 container image명이 다르기 때문에 main과 develop 브랜치로 구분 지어 추가 variables를 설정하였고, NCP의 Container Registry에 접근하기 위해 Login 관련 정보를 GitLab Groups에 환경변수로 설정해 두었습니다.
# 1) Build 스테이지: kaniko를 사용하여 Docker 이미지를 빌드하고 GitLab Registry로 푸시
build:
stage: build-and-push-image
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
NCP_REGISTRY_IMAGE: "mingyu-api"
DOCKER_ENV: "prod"
- if: '$CI_COMMIT_BRANCH == "develop"'
variables:
NCP_REGISTRY_IMAGE: "mingyu-api-dev"
DOCKER_ENV: "develop"
# rules에 해당하지 않는 브랜치에서는 실행하지 않음, only와 rules 중 하나만 사용해야 하기 때문에 when never 추가
- when: never
script:
# Docker config 생성
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$NCP_REGISTRY\":{\"username\":\"$NCP_REGISTRY_USER\",\"password\":\"$NCP_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
# Kaniko로 빌드
- /kaniko/executor
--context "$CI_PROJECT_DIR"
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
--build-arg "ENVIRONMENT=$DOCKER_ENV"
--destination "$NCP_REGISTRY/$NCP_REGISTRY_IMAGE:$IMAGE_TAG"
이 Stage에서 1~4번 항목을 처리합니다.
Update-helm-chart
해당 stage에서는 Helm repository Git 저장소를 clone 받아서 Image Tag를 Update 한 후 commit & push를 진행합니다. 또한 ArgoCD에 Sync API를 호출하여 변동된 OutOfSync를 Sync 하게 합니다.
deploy:
stage: update-helm-chart
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
NCP_REGISTRY_IMAGE: "mingyu-api"
HELM_VALUES_FILE: "custom-values/mingyu-api/prod-values.yaml"
ARGOCD_APP: "mingyu-api-prod"
- if: '$CI_COMMIT_BRANCH == "develop"'
variables:
NCP_REGISTRY_IMAGE: "mingyu-api-dev"
HELM_VALUES_FILE: "custom-values/mingyu-api/develop-values.yaml"
ARGOCD_APP: "mingyu-api-dev"
# 다른 브랜치에서는 global 변수 값("latest")을 사용하기 위해 fallback rule 추가
- when: always
image: alpine:latest # 혹은 git, yq, helm 등 툴을 사용할 수 있는 이미지
variables:
GIT_STRATEGY: none # 빌드 속도 향상 (필요 시 변경)
처음 항목에서는 Commit 한 브랜치명에 따라 개발 혹은 운영 환경에 맞추어 변수를 초기화해 줍니다.
before_script:
# 필수 패키지 설치
- apk add --no-cache git yq openssh-client curl jq
# SSH 디렉토리 및 known_hosts 파일 준비
- mkdir -p ~/.ssh
- ssh-keyscan gitlab-sh.mingyu.co.kr >> ~/.ssh/known_hosts
- chmod 700 ~/.ssh
- chmod 644 ~/.ssh/known_hosts
# SSH Private Key를 ssh-agent에 추가
- eval $(ssh-agent -s)
- echo "$MINGYU_GITLAB_SH_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
실제 script가 실행되기 전에 before_script를 사용하여 필요한 필수 패키지 및 디렉터리, ssh 설정 작업 등 사전작업을 처리합니다.
script:
# 1) GitOps 리포지토리 클론
- git clone "$GITLAB_HELM_SPRING_BOOT_GIT_PATH" # git@gitlab-sh.mingyu.co.kr:charts/backend/mingyu-spring-boot.git
- cd "$GITLAB_HELM_SPRING_BOOT_DIR_NAME" # mingyu-spring-boot
- git fetch origin "$CI_COMMIT_BRANCH"
- git checkout "$CI_COMMIT_BRANCH"
# 2) values.yaml 업데이트 (예시)
- echo "IMAGE_REPOSITORY = $NCP_REGISTRY/$NCP_REGISTRY_IMAGE"
- echo "IMAGE_TAG = $IMAGE_TAG"
- yq -i ".image.repository = \"$NCP_REGISTRY/$NCP_REGISTRY_IMAGE\"" "$HELM_VALUES_FILE"
- yq -i ".image.tag = \"$IMAGE_TAG\"" "$HELM_VALUES_FILE"
# 3) Git 커밋 & 푸시
- git config user.email "mingyu@mingyu.co.kr"
- git config user.name "mingyu"
- git add "$HELM_VALUES_FILE"
- git commit -m "Update image tag to $IMAGE_TAG"
- git push origin "$CI_COMMIT_BRANCH"
# 4) argocd sync
- echo "ArgoCD Server = $ARGOCD_SERVER"
- echo "ArgoCD USERNAME = $ARGOCD_USERNAME"
- echo "ArgoCD Password = $ARGOCD_PASSWORD"
- |
# ArgoCD 로그인하여 토큰 받기 (인증서 검증 문제가 있다면 -k 옵션 사용)
TOKEN=$(curl -sk -X POST "https://$ARGOCD_SERVER/api/v1/session" \
-H "Content-Type: application/json" \
-d "{\"username\": \"${ARGOCD_USERNAME}\", \"password\": \"${ARGOCD_PASSWORD}\"}" | jq -r '.token')
echo "ArgoCD token: $TOKEN"
# Application sync API 호출 (revision은 HEAD 혹은 원하는 값으로 지정)
curl -sk -X POST "https://$ARGOCD_SERVER/api/v1/applications/$ARGOCD_APP/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"revision\": \"${CI_COMMIT_BRANCH}\"}"
script 태그에서 해당 stage에서 처리해야 할 작업들을 기술하여 처리하도록 합니다. 필요한 Variables들은 GitLab Settings > CI/CD > Variables에서 설정 및 관리할 수 있습니다.
여기까지 완료하면, 해당 Stage에서 5~8번 항목을 완료하게 됩니다.
결과 확인
commit & push를 진행하면 gitlab PipeLies에서 작업 내역을 아래와 같이 확인할 수 있습니다.
GitLab Runner가 생성한 새로운 Build Stage를 처리해 주는 파드가 생성된 것을 확인할 수 있습니다. Build Stage가 끝나면 Deploy Stage에 해당하는 Pod가 생성돼서 Develop Stage를 처리하게 됩니다.
Build Stage까지 프로세스가 정상적으로 완료되면 아래와 같이 Stages의 두 개의 아이콘이 초록색으로 표시됩니다.
그다음, ArgoCD UI로 접속하여 Sync가 정상적으로 작동하였는지 확인합니다.
타깃 Cluster의 대시보드에 접속하거나 kubectl 명령으로 새롭게 생성된 파드가 정상적으로 동작하는지, 이미지는 최신 이미지 태그를 사용하였는지 등을 확인합니다.
제가 설계하고 구현한 PipeLine이 꼭 정답이라고는 할 수 없습니다. Application Project 1개당 Helm Repo 1개로 설정한다면 Auto Sync로 설정하면 더 간결해지기도 하고, Build를 다른 서드파티 툴 (Jenkins 등)을 활용할 수도 있습니다. 여러 방향성을 탐구해보고 상황에 맞는 CI/CD를 구축해보는 것. 그러면서 많은 안목을 넓히고 다양한 방법을 터득하는 길을 걷고 싶습니다.
'Infrastructure > CICD' 카테고리의 다른 글
GitLab & ArgoCD를 활용한 GitOps방식의 CI/CD 구축하기 (2) - ArgoCd에 Cluster 추가하기 (0) | 2025.03.08 |
---|---|
GitLab & ArgoCD를 활용한 GitOps방식의 CI/CD 구축하기 (1) - CI/CD 설계 및 Helm 생성 (1) | 2025.03.07 |
[CI/CD 구축] AWS EC2에 Docker를 활용한 Jenkins 파이프라인 구축 (2) | 2024.02.07 |
[CI/CD 구축] AWS, Docker, GitLab을 사용하여 CI/CD 구축하기 3편(끝) (0) | 2022.12.23 |
[CI/CD 구축] AWS, Docker, GitLab을 사용하여 CI/CD 구축하기 2편 (0) | 2022.12.16 |
댓글