Infrastructure/Git

[GitLab] Spring Multi Module Project gitlab-ci.yml 작성방법

MingyuKim 2025. 3. 15.

Gitlab을 사용하면서, 내장된 CI/CD 워크플로우를 사용하기 위해 우리는 Application Repository에. gitlab-ci.yml 파일을 작성합니다. 오늘은 Spring Boot로 개발된 Multi Module Project에서 어떻게 gitlab-ci.yml 파일을 작성해야 원하는 모듈만 빌드되거나 혹은 모든 모듈이 병렬처리로 빌드시킬 수 있는지에 대한 방법을 알아보겠습니다.


Multi Module 구조 파악

포스팅에서 사용할 Multi Module의 구조는 아래와 같습니다. 

api 모듈은 데이터를 입력받아 queue에 쌓는 Producer 역할을, sms 및 mail 모듈은 rabbitmq를 listen 하여 mail과 sms 및 push를 보내주는 consumer의 역할을, batch 모듈은 부가로 처리해야 할 batch 작업을 실행하는 역하을 맡고 있습니다.

├── Dockerfile
├── README.md
├── build
├── build.gradle.kts
├── gradle
├── gradlew
├── gradlew.bat
├── http-test
├── log.config.path_IS_UNDEFINED
├── logs
├── module-api
├── module-core
├── module-database
├── module-mail
├── module-batch
├── module-rabbitmq
├── module-redis
├── module-sms
└── settings.gradle.kts

각 모듈들은 공통적인 core, database, redis, rabbitmq의 config를 주입받아 사용하기 때문에 해당 모듈 안에 코드가 변경될 경우에만 pipeline 작업을 실행하게 하고 싶었습니다.


Build Stage

핵심 기능은, 특정 파일에 대한 변경 사항을 확인하여 언제 작업을 파이프라인에 추가할지를 지정하는 것인데, 해당 기능은 gitlab-ci의 키워드 중에서 rulues:changes를 사용하여 구현할 수 있었습니다. gitlab-ci의 자세한 사용법은 GitLab 공식 기술 문서 한글판을 지원하는 인포그랩 블로그에 상세히 기술되어 있어서 어렵지 않았습니다.

 

GitLab 공식 기술 문서 한글판 by 인포그랩 | 인포그랩 | GitLab 기반 DevSecOps 구축,컨설팅,교육,CICD Pipe

GitLab의 Selected 파트너 인포그랩에서 OpenAI 기술 기반으로 자체 개발한 자동화 번역 프로그램을 통해 GitLab 공식 기술 문서의 한글판을 국내 최초로 제공합니다.

gitlab-docs.infograb.net

build_module_api:
  stage: build-and-push-image
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      changes:
        paths:
          - module-api/**/*
      variables:
        NCP_REGISTRY_IMAGE: "module-api-prod"
    - if: '$CI_COMMIT_BRANCH == "develop"'
      changes:
        paths:
          - module-api/**/*
      variables:
        NCP_REGISTRY_IMAGE: "module-api-dev"
    - when: never

  before_script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$NCP_REGISTRY\":{\"username\":\"$NCP_REGISTRY_USER\",\"password\":\"$NCP_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json

  script:
    - /kaniko/executor \
      --context "$CI_PROJECT_DIR" \
      --dockerfile "$CI_PROJECT_DIR/Dockerfile" \
      --build-arg "MODULE_NAME=module-api" \
      --build-arg "APP_PORT=8080" \
      --destination "$NCP_REGISTRY/$NCP_REGISTRY_IMAGE:$IMAGE_TAG"

빌드 stage에서는 rules.changes 키워드를 사용하여 paths에 module-api/**/* 를 지정하였습니다. 해당 내용의 의미는 module-api 하위 디렉터리 전체에 변경사항이 감지하면 rules를 충족시키기 때문에 when:never에 걸리지 않고 빌드가 실행된다는 의미입니다.

 

저는 k8s cluster에 배포하게 하기 위해 build를 docker image로 만들었고, kubernetes container 내부에서 동작하는 runner pod에서 dind 사용을 하지 않기 위해 kaniko를 사용하였지만, 빌드 스크립트는 원하는 환경에 맞추어 변경하시면 됩니다.  이제 위 build stage 내용을 복사하여 나머지 module들에 해당하는 내용으로 커스텀하면 됩니다.

 

보다 자세한 내용은 이전에 작성한 포스팅을 참고해 주세요.

 

GitLab & ArgoCD를 활용한 GitOps방식의 CI/CD 구축하기 (3) - GitlabCI & ArgoCD 연동하여 CI/CD 구축 완료하기.

GitlabCI 및 ArgoCD 설계GitlabCI에서는 이전 포스팅에서 설명한 대로, Application을 Build 하여 Conatiner Image로 만든 후 Docker Image Registry인 NCP Container Resitry에 Upload 합니다. 그 후 helm repository에 Image Tag값을

min-nine.tistory.com


Deploy Stage

해당 스테이지에서 중요한 것은 resource_group, needs 키워드입니다. 아래와  똑같은 내용을 각 모듈에 맞게 복사하여 하나의 gitlab-ci.yml 파일에서 관리하게 되면 문제점이 생기는 게 '만약 동시에 여러 멀티모듈이 helm charts의 repository에 push하게되면 어떻게 하지?'였습니다. 병렬로 build 처리를 진행하지만, 우연히 동시에 helm repository의 각 image tag가 업데이트된다면 push가 꼬여서 오류가 발생할 것이 물 보듯 뻔했습니다. 때문에 resource_group을 동일한 "multi-module-application" 이란 내용으로 지정하게 된다면, 비관적 락이 발생하여 해당 stage가 작업중일때는 다른 stage에서는 waiting 할 수 있게 처리할 수 있었습니다.

deploy_module_api:
  stage: update-helm-chart
  resource_group: multi-module-application
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      changes:
        paths:
          - module-api/**/*
      variables:
        NCP_REGISTRY_IMAGE: $MODULE_API_PROD_IMAGE_NAME
        HELM_VALUES_FILE: "prod-values.yaml"
    - if: '$CI_COMMIT_BRANCH == "develop"'
      changes:
        paths:
          - module-api/**/*
      variables:
        NCP_REGISTRY_IMAGE: $MODULE_API_DEV_IMAGE_NAME
        HELM_VALUES_FILE: "develop-values.yaml"
    - when: never

  image: alpine:latest
  variables:
    GIT_STRATEGY: none

  before_script:
    - apk add --no-cache git yq openssh-client
    - mkdir -p ~/.ssh
    - ssh-keyscan gitlab-sh.mingyu.co.kr >> ~/.ssh/known_hosts
    - chmod 700 ~/.ssh
    - chmod 644 ~/.ssh/known_hosts
    - eval $(ssh-agent -s)
    - echo "$MINGYU_GITLAB_SH_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -

  script:
    - git clone "$GITLAB_HELM_MULTIMODULE_APPLICATION_GIT_PATH"
    - cd "$GITLAB_HELM_MULTIMODULE_APPLICATION_DIR_NAME"
    - git fetch origin "$CI_COMMIT_BRANCH"
    - git checkout "$CI_COMMIT_BRANCH"
    - cd charts/module-api

    - 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"
    - cd ../../

    - git config user.email "mingyu@mingyu.co.kr"
    - git config user.name "mingyu"
    - git add "charts/module-api/$HELM_VALUES_FILE"
    - git commit -m "Update image tag to $IMAGE_TAG"
    - git push origin "$CI_COMMIT_BRANCH"

  needs:
    - build_module_api

build stage가 종료된 이후 deploy stage가 실행되어야 하기 때문에 needs 키워드를 활용하여 해당 스테이지는 build 스테이지 이후에 실행되도록 설정하였습니다.

댓글