본문으로 건너뛰기
Advertisement

18.6 GitLab CI/CD 파이프라인 상세 구축 가이드

GitLab은 Git 저장소와 CI/CD 기능이 하나의 플랫폼에 통합된 DevOps 도구입니다. .gitlab-ci.yml 파일 하나로 테스트 → 빌드 → 배포 전 파이프라인을 선언적으로 정의할 수 있습니다.

1. GitLab CI/CD vs GitHub Actions 핵심 차이

항목GitLab CI/CDGitHub Actions
설정 파일.gitlab-ci.yml (루트).github/workflows/*.yml
실행 환경GitLab Runner (셀프 호스팅/공유)GitHub-hosted Runner
빌드 단위Stage → JobJob → Step
빌트인 Container Registry✅ 내장❌ 별도 연동 필요
비밀 변수Settings → CI/CD → VariablesSettings → Secrets

2. 파이프라인 기본 구조

GitLab CI/CD는 Stage(단계) 배열로 순서를 정의하고, 각 Job(작업) 이 해당 Stage에 속합니다.

# .gitlab-ci.yml
stages:
- test # 1단계: 테스트
- build # 2단계: Docker 이미지 빌드
- deploy # 3단계: 서버 배포

3. Stage 1: 테스트 Job

# .gitlab-ci.yml
variables:
MYSQL_ROOT_PASSWORD: testpass
MYSQL_DATABASE: testdb

test:
stage: test # stages 배열에 선언한 이름 중 하나
image: eclipse-temurin:21-jdk # Runner가 이 Docker 이미지 위에서 실행됨

services:
- mysql:8.0 # 통합 테스트용 MySQL 사이드카 컨테이너

variables: # Job 범위 환경 변수 (전역 variables 덮어쓰기 가능)
SPRING_DATASOURCE_URL: "jdbc:mysql://mysql:3306/testdb" # 서비스명이 호스트명
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: testpass

script:
- chmod +x ./gradlew
- ./gradlew test --no-daemon

artifacts:
when: always # on_success(기본) | on_failure | always
# on_success: 성공 시에만 보관
# on_failure: 실패 시에만 보관 (디버깅용)
# always : 항상 보관
reports:
junit: build/test-results/test/*.xml # GitLab 테스트 결과 통합
expire_in: 30 days # 보관 기간 (예: '1 week', '30 days', never)

cache:
key: "$CI_COMMIT_REF_SLUG" # 캐시 키 (브랜치별 독립 캐시)
policy: pull-push # pull-push(기본) | pull | push
# pull-push: 다운로드 후 업로드 모두 수행
# pull : 캐시 다운로드만 (읽기 전용, 병렬 Job에서 유용)
# push : 업로드만 (캐시를 갱신하는 전용 Job에 사용)
paths:
- .gradle/ # Gradle 캐시로 빌드 속도 향상
- build/

only: # 실행 조건 (더 유연한 rules: 키워드로 대체 권장)
- merge_requests # MR 생성/업데이트 시
- main # main 브랜치 push 시
- develop # develop 브랜치 push 시

allow_failure: false # true | false(기본)
# true: 이 Job이 실패해도 파이프라인 전체를 실패로 처리하지 않음

timeout: 10 minutes # Job 최대 실행 시간 (기본: Runner 설정값)
# 예: '1 hour', '30 minutes', '1 hour 30 minutes'

4. Stage 2: Docker 빌드 및 GitLab Container Registry 푸시

GitLab은 프로젝트마다 무료 Container Registry가 내장되어 있어 별도 Docker Hub 연동 없이 이미지를 저장할 수 있습니다.

build-image:
stage: build
image: docker:26-dind # Docker-in-Docker 이미지
services:
- docker:26-dind # DinD 소켓 연결

variables:
DOCKER_TLS_CERTDIR: "/certs" # TLS 인증서 디렉토리 (/certs | "" 중 선택)
# "" 로 설정하면 TLS 비활성화 (보안 위험, 비권장)
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

before_script: # script 실행 전 공통 준비 작업
# GitLab 내장 Container Registry에 자동 로그인 (별도 자격증명 불필요!)
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
# latest 태그도 함께 업데이트
- docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest

needs: # 선행 의존 Job 지정 (DAG 방식)
- test # test Job이 성공해야만 실행
# (needs 미사용 시 같은 stage의 모든 Job 대기)

only:
- main

retry: # 선택: Job 실패 시 자동 재시도
max: 2 # 최대 재시도 횟수 (0~2)
when: # 재시도 조건 (복수 지정 가능)
- runner_system_failure # Runner 시스템 오류 시
- stuck_or_timeout_failure # 타임아웃 또는 멈춤 시
# 다른 값: script_failure, api_failure, always

자동 제공되는 CI/CD 변수 (별도 설정 불필요)

변수명설명
$CI_REGISTRYGitLab Container Registry 호스트
$CI_REGISTRY_USERRegistry 로그인 사용자명
$CI_REGISTRY_PASSWORDRegistry 로그인 패스워드
$CI_REGISTRY_IMAGE현재 프로젝트의 Registry 경로
$CI_COMMIT_SHORT_SHA현재 커밋의 짧은 해시 (8자)
$CI_COMMIT_REF_NAME현재 브랜치명 또는 태그명

5. Stage 3: EC2 서버 자동 배포

deploy-production:
stage: deploy
image: alpine:3.19

before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
# GitLab CI/CD Variables에 등록한 SSH 개인키를 에이전트에 추가
- echo "$EC2_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan $EC2_HOST >> ~/.ssh/known_hosts

script:
- |
ssh ubuntu@$EC2_HOST << 'ENDSSH'
# GitLab Registry에서 최신 이미지 풀 (EC2도 Registry에 로그인 필요)
docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
docker stop todo-app || true
docker rm todo-app || true
docker run -d \
--name todo-app \
--restart=unless-stopped \
-p 8080:8080 \
-e SPRING_DATASOURCE_URL=$DB_URL \
-e SPRING_DATASOURCE_PASSWORD=$DB_PASSWORD \
$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
ENDSSH

environment:
name: production # GitLab Environments 탭에 표시할 이름 (자유 문자열)
# 예: production | staging | review/$CI_COMMIT_REF_SLUG
url: https://myapp.com # GitLab Environments 대시보드에 URL 표시
action: start # start(기본) | stop | prepare | access | verify
# stop: 환경 종료 Job에 사용 (리뷰 환경 정리 시 유용)

needs:
- build-image

only:
- main

when: manual # on_success(기본) | on_failure | always | manual | delayed | never
# on_success: 이전 모든 Stage 성공 시 자동 실행
# on_failure: 이전 Stage 실패 시에만 실행 (알림 Job 등)
# always : 항상 실행
# manual : UI에서 직접 클릭해야 실행
# delayed : start_in 과 함께 지정 시간 후 자동 실행
# never : 파이프라인에서 완전히 제외
주의

when: manual을 설정하면 GitLab UI의 파이프라인 페이지에서 ▶ 버튼을 직접 눌러야 배포가 실행됩니다. 자동 배포(when: on_success)와 수동 배포를 환경별로 구분하면 실수로 운영 서버에 배포하는 사고를 방지할 수 있습니다.

6. GitLab CI/CD 변수 등록 방법

Settings → CI/CD → Variables → Add variable에서 민감한 정보를 등록합니다.

변수명타입용도
EC2_HOSTVariableEC2 퍼블릭 IP
EC2_PRIVATE_KEYFileEC2 SSH 개인 키 (Masked)
DB_URLVariable (Masked)DB 연결 URL
DB_PASSWORDVariable (Masked)DB 패스워드

File 타입으로 등록된 변수는 Runner에서 실제 임시 파일로 마운트되어, ssh-add $EC2_PRIVATE_KEY처럼 파일 경로처럼 사용할 수 있습니다.

7. 전체 파이프라인 흐름 요약

git push origin main


┌──────────────┐
│ test (JUnit5 │ ← MySQL 서비스 컨테이너 기동 → Gradle 테스트 실행
│ + Testcont.) │ JUnit XML 리포트를 GitLab에 통합
└──────┬───────┘
│ 성공 시

┌──────────────┐
│ build-image │ ← Docker 빌드 → GitLab Container Registry 푸시
│ (DinD) │
└──────┬───────┘
│ 성공 시

┌──────────────┐
│ deploy │ ← (수동 승인) SSH 접속 → EC2에서 새 이미지 실행
│ (manual) │
└──────────────┘
Advertisement