IaC/CI CD Tool

7. Gitlab CI Template 활용

Somaz 2024. 6. 27. 13:37
728x90
반응형

Overview

오늘은 GitLab CI/CD에서 Template을 활용해 효율적이고 일관된 파이프라인을 구성하는 방법에 대해 알아본다.

 

GitLab에서는 반복되는 CI/CD 작업을 간결하게 관리하고 유지보수를 단순화하기 위해 템플릿 구조를 지원하며,
이를 통해 공통된 스크립트나 설정을 여러 Job에서 재사용할 수 있다.

 

이번 글에서는 GitLab에서 제공하는 공식 템플릿 참조 방식부터,
조직 내부에서 정의한 `.yml` 템플릿을 외부 프로젝트에서 참조하거나,
`.gitlab-ci.yml` 내부에 `.templates` 구조를 활용해 직접 정의하는 방식까지 다양한 활용법을 살펴보았다.

 

 

특히 이미지 빌드, 배포, Slack 알림까지 전체 워크플로우를 템플릿으로 캡슐화함으로써,
코드 중복 제거, 관리 편의성 향상, 자동화 일관성 확보라는 효과를 얻을 수 있다.

출처 : https://medium.com/globant/optimize-pipeline-implementation-using-gitlab-ci-templates-6eef54046231

 

 

 

📅 관련 글

2023.04.20 - [IaC/CI CD Tool] - 1. GitLab이란? / 개념 및 설치

2023.04.23 - [IaC/CI CD Tool] - 2. GitLab이란? / GitLab Runner 개념 및 설치

2023.04.24 - [IaC/CI CD Tool] - 3. GitLab이란? / GitLab CI/CD

2023.08.08 - [IaC/CI CD Tool] - 4. GitLab 버전 업그레이드

2023.08.08 - [IaC/CI CD Tool] - 5. GitLab ArgoCD 연동

2024.05.28 - [IaC/CI CD Tool] - 6. Gitlab CI Build(with GCP Artifact Registry, Harbor)

2024.06.18 - [IaC/CI CD Tool] - 7. Gitlab CI Template 활용

2025.01.09 - [IaC/CI CD Tool] - 8. Gitlab Repository Mirroring 방법

 

 

 


 

 

 

 

Gitlab CI Template 활용

 

Gitlab에서 제공해주는 다양한 template 들도 있다.

 

template 파일을 참조하는 방법은 아래와 같다.

# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml

include:
  - template: Terraform/Base.gitlab-ci.yml  # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml
  - template: Jobs/SAST-IaC.gitlab-ci.yml   # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml

 

 

만약 자신의 gitlab의 project에 template이 정의되어 있다면 아래와 같이 작성하면 된다.

include:
  - project: 'somaz94/server'
    ref: master
    file: '/template/my_template_file.yml'

 

 

 

참조하지 않고 `.gitlab-ci.yml` 에 정의해도 무관하다.

 

 

 

 

 

 

 


 

 

 

 

 

 

Template 작성

 

아래와 같이 build 관련한 template을 작성한다.

.templates:
  .common_build_before_script: &common_build_before_script
    - echo "[INFO] Start build image."
    - echo "[INFO] CI_REGISTRY is $CI_REGISTRY"
    - echo "[INFO] CI_REGISTRY_IMAGE is $CI_REGISTRY_IMAGE"
    - echo "[INFO] BUILD_TAG is $BUILD_TAG"
    - echo "[INFO] IMAGE_URL is $IMAGE_URL"
    - echo "[INFO] BUILD_TAG_LATEST is $BUILD_TAG_LATEST"
    - echo "[INFO] IMAGE_URL_LATEST is $IMAGE_URL_LATEST"
    - echo "[INFO] CI_PROJECT_DIR is $CI_PROJECT_DIR"
    - echo "[INFO] SERVICE is $SERVICE"
    - echo "[INFO] GCP Config"
    - echo "[INFO] GCP_CI_REGISTRY is $GCP_CI_REGISTRY"
    - echo "[INFO] GCP_CI_REGISTRY_IMAGE is $GCP_CI_REGISTRY_IMAGE"
    - echo "[INFO] GCP_IMAGE_URL is $GCP_IMAGE_URL"
    - echo "[INFO] GCP_IMAGE_URL_LATEST is $GCP_IMAGE_URL_LATEST"
    - echo "[INFO] NAMESPACE is $NAMESPACE"
    - mkdir -p /kaniko/.docker
    - >
      if [[ "$NAMESPACE" =~ ^sarena- ]]; then
        echo "Configuring registry for special namespace $NAMESPACE"
        cp gcloud/config.json /kaniko/.docker/config.json
      else
        echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)\"}}}" > /kaniko/.docker/config.json
      fi

  .common_build_script: &common_build_script
    - >
      if [[ "$NAMESPACE" =~ ^sarena- ]]; then
        /kaniko/executor --cache=true --cache-ttl=24h --snapshot-mode=redo --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/$SERVICE.Dockerfile --destination $GCP_IMAGE_URL --destination $GCP_IMAGE_URL_LATEST --build-arg NODE_ENV=$NAMESPACE --skip-tls-verify
      else        
        /kaniko/executor --cache=true --cache-ttl=24h --snapshot-mode=redo --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/$SERVICE.Dockerfile --destination $IMAGE_URL --destination $IMAGE_URL_LATEST --build-arg NODE_ENV=$NAMESPACE --skip-tls-verify
      fi 
    - KANIKO_RESULT=$?
    - echo "$SERVICE" > service_status.txt
    - >
      if [ $KANIKO_RESULT -eq 0 ]; then
        echo "✅ 성공" > build_status.txt
      else
        echo "❌ 실패" > build_status.txt
      fi

 

 

그리고 아래와 같이 활용할 수 있다.

build_manual_image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.22.0-debug
  interruptible: true
  retry:
    max: 2  # Maximum of 2 retries
    when:
      - runner_system_failure
      - unknown_failure
  before_script: *common_build_before_script
  script: *common_build_script
  artifacts:
    paths:
      - build_status.txt
      - service_status.txt
  rules:
    - if: '($CI_PIPELINE_SOURCE == "web")'
  tags:
    - build-image

 

 

그리고 다른 job에도 활용이 가능하다. 

.change_files_game: &change_files_game
  changes:
    - apps/game/src/**/*

build_auto_image_game:
  stage: build
  image: 
    name: gcr.io/kaniko-project/executor:v1.22.0-debug
  interruptible: true
  retry:
    max: 2  # Maximum of 2 retries
    when:
      - runner_system_failure
      - unknown_failure
  before_script: *common_build_before_script
  script: *common_build_script
  variables:
    SERVICE: $GAME_SERVICE
    IMAGE_URL: $IMAGE_URL_GAME
    IMAGE_URL_LATEST: $IMAGE_URL_LATEST_GAME
  artifacts:
    paths:
      - build_status.txt
      - service_status.txt
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"'
      <<: *change_files_game
  tags:
    - build-image

 

 

SLACK에 메세지 보내는 템플릿도 아래와 같이 작성할 수 있다. 각각의 job마다 artifact로 결과를 받아서 메세지를 보내면 된다.

  .common_update_after_script: &common_update_after_script
    - >
      if [ "$CI_JOB_STATUS" = "success" ]; then
        echo "✅ 성공" > /builds/somaz94/server/update_status.txt
      elif [ "$CI_JOB_STATUS" = "failed" ]; then
        echo "❌ 실패" > /builds/somaz94/server/update_status.txt
      elif [ "$CI_JOB_STATUS" = "canceled" ]; then
        echo "⚠️ 취소" > /builds/somaz94/server/update_status.txt
      else
        echo "🔍 상태 불명" > /builds/somaz94/server/update_status.txt
 ...
 
 update_manual_image:
  stage: update
  image: alpine:latest
  interruptible: true
  retry:
    max: 2  # Maximum of 2 retries
    when:
      - runner_system_failure
      - unknown_failure
  before_script: *common_update_before_script
  script: *common_update_script 
  after_script: *common_update_after_script
  artifacts:
    paths:
      - update_status.txt
  rules:
    - if: $CI_PIPELINE_SOURCE == "web"
  tags:
    - deploy-image
  dependencies:
    - build_manual_image

 

 

나는 SLACK 웹훅을 활용했고, 워크플로우 빌더를 활용해 결과 값만 받으면 되게 작성하였다.

Build Result
[작업 상태]
- 프로젝트 : 
- 대상 환경 : 
- 배포 서비스: 
- 기준 브랜치 : 
- generator 결과 : 
- 빌드 결과 : 
- 배포 결과 : 
- Gitlab CI URL : 
- Commit Message : 
- 트리거 유저 :

 

 

 

 

Slack notify Template을 작성해준다.

  .common_notify_slack_script: &common_notify_slack_script
    - export GENERATOR_STATUS=$(cat generator_status.txt || echo "⏭️ 스킵 or ✅ Commit Massage 확인")
    - export BUILD_STATUS=$(cat build_status.txt || echo "⏭️ 스킵")
    - export UPDATE_STATUS=$(cat update_status.txt || echo "⏭️ 스킵")
    - export DEPLOY_SERVICE=$(cat service_status.txt || echo "⏭️ 스킵")
    - export CLEAN_COMMIT_MESSAGE=$(echo "$CI_COMMIT_MESSAGE" | tr -d '\n' | tr -d '\r')
    - >
      export JSON_DATA="{\"source_branch\": \"$CI_COMMIT_REF_NAME\", \"generator_result\": \"$GENERATOR_STATUS\", \"deploy_result\": \"$UPDATE_STATUS\", \"build_result\": \"$BUILD_STATUS\", \"commit_message\": \"$CLEAN_COMMIT_MESSAGE\", \"trigger_user\": \"$GITLAB_USER_LOGIN\", \"gitlab_ci_run_url\": \"$CI_PIPELINE_URL\", \"repository_name\": \"$CI_PROJECT_PATH\", \"environment\": \"$NAMESPACE\", \"deploy_service\": \"$DEPLOY_SERVICE\"}"
    - >
      echo "Sending the following data to Slack: $JSON_DATA"
    - >
      export RESPONSE=$(curl -sS -X POST -H 'Content-type: application/json' --data "$JSON_DATA" $SLACK_WEBHOOK_URL)
    - >
      echo "Slack response: $RESPONSE"

 

 

 

그리고 아래와 같이 yml 파일을 작성해준다.

notify_slack:
  stage: notify
  image: curlimages/curl:latest
  script: *common_notify_slack_script
  rules:
    - if: $CI_PIPELINE_SOURCE == "web"
    - if: $CI_PIPELINE_SOURCE == "trigger"
  tags:
    - deploy-image
  when: always

...
notify_slack_auto_game:
  stage: notify
  image: curlimages/curl:latest
  script: *common_notify_slack_script
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"'
      <<: *change_files_game
  tags:
    - deploy-image
  when: always
  dependencies:
    - build_auto_image_game
    - update_auto_image_game

 

 

 

 

이런식으로 활용하면, template을 작성해 아래와 같이 다양한 job에 적용이 가능하다.

 

 

 

 

 

 

Template 매개변수화 (Template Parameterization)

템플릿을 더욱 유연하게 만들기 위해 변수(변수화)를 사용하는 것도 중요하다.


`.gitlab-ci.yml` 에서는 `anchor(&)` 와 `alias(*)` 외에도 !reference 기능을 통해 정의된 템플릿 블록에 변수 값을 전달할 수 있다.

예를 들어 아래와 같이 `before_script` 내부에서 공통 스크립트를 작성하고, `job` 마다 환경변수를 오버라이드 할 수 있다.

.templates:
  .base_env_template: &base_env_template
    variables:
      NODE_ENV: "default"
      REGION: "kr"

job_example_1:
  <<: *base_env_template
  variables:
    NODE_ENV: "dev"
    REGION: "us"

 

 

 

 

 

 

Condition 분기 및 다양한 include 전략

조건 분기(`rules, only, except`)와 함께 `include:` 를 환경 별로 나눌 수 있어, 특정 브랜치 또는 환경에서만 특정 템플릿을 포함시킬 수 있다.

 

 

 

Template 분기 로딩 예시

 

`include:` 문법은 다양한 조건 분기에 따라 동적으로 템플릿을 구성할 수 있다. 예를 들어 브랜치에 따라 서로 다른 템플릿을 불러오는 방식도 가능하다.

include:
  - local: '/ci-templates/common.yml'
  - when: '$CI_COMMIT_BRANCH == "develop"'
    local: '/ci-templates/dev.yml'
  - when: '$CI_COMMIT_BRANCH == "main"'
    local: '/ci-templates/prod.yml'
  • 이를 활용하면 개발, 스테이징, 프로덕션 별 파이프라인 차별화가 가능하며,
  • 공통 로직은 `common.yml` 로 관리하면서 환경에 따라 세부 구성이 달라진다.

 

 

 

 

 

Template Lint 및 시각화 도구

GitLab은 `.gitlab-ci.yml` 파일이 유효한지 확인할 수 있는 CI Lint 기능을 제공하며,
이를 통해 템플릿 포함 여부, 병합 결과 등을 미리 시뮬레이션할 수 있다.

 

 

CI 템플릿 디버깅 & Lint 활용법

`.gitlab-ci.yml` 이 복잡해질수록, 특히 여러 템플릿이 포함되는 구조에서는
Lint 도구를 사용하여 구문 오류나 병합 충돌 여부를 미리 검증하는 것이 좋다.

  • GitLab Web UI → CI Lint 기능 사용
    프로젝트의 CI/CD > Editor > 오른쪽 상단의 "CI Lint" 버튼을 클릭하면 현재 `.gitlab-ci.yml` 을 테스트할 수 있다.
  • API 기반으로 Lint 자동화
curl --header "PRIVATE-TOKEN: <your_access_token>" \
     --data "content=$(cat .gitlab-ci.yml)" \
     "https://gitlab.example.com/api/v4/ci/lint"

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

마무리

CI/CD 환경이 점점 복잡해지고 팀과 서비스가 많아질수록,
코드의 재사용성과 유지보수성은 더욱 중요해진다.

 

GitLab CI Template은 이러한 문제를 해결하는 훌륭한 도구로,
하나의 정의된 템플릿이 수십, 수백 개의 파이프라인에 적용될 수 있다.

 

또한 공통된 빌드 스크립트, 조건부 작업, Slack 알림 메시지 구성 등
다양한 작업을 모듈화함으로써 팀 간 표준화를 실현하고,
새로운 Job을 추가할 때도 빠르게 확장할 수 있는 기반을 마련할 수 있다.

 

앞으로 GitLab CI/CD를 설계하거나 개선할 때, 단순히 `.gitlab-ci.yml` 을 작성하는 수준을 넘어서
템플릿 기반 구조화 설계를 도입해보는 것을 추천한다.

 

작은 반복을 줄이고, 큰 효율을 만드는 시작이 될 것이다.

 

 

 

 

 

 

 

 

 

 


Reference

https://icinga.com/blog/2022/10/05/gitlab-ci-cd-job-templates/

https://about.gitlab.com/blog/2020/09/23/get-started-ci-pipeline-templates/#what-are-ci-pipeline-templates

728x90
반응형