GitLab CI 시작하기
러너(Runner) 설치부터 등록, 그리고 동작 확인 까지
- 대상 OS : Ubuntu / macOS
0. 목차
GitLab CIvsJenkinsGitLab이 뭐냐?- 왜?
GitLab CI를 쓰는가 GitLab시작하기 (Git 설정 / 클론)GitLab Runner설치Runner등록하기- 동작 확인 체크리스트
- 첫 번째 CI 파이프라인 만들기
1. GitLab vs Jenkins
둘다 CI/CD와 DevOps 자동화에 많이 쓰이지만 성격이 꽤 다릅니다.
| 항목 | GitLab | Jenkins |
|---|---|---|
| 형태 | 통합 DevOps 플랫폼 | CI/CD 자동화 서버 |
| 설치 방식 | SaaS + Self-hosted | Self-hosted |
| 주요 역할 | Git + CI/CD + Registry + MR 통합 | 빌드/배포 자동화 중심 |
| 설정 방식 | .gitlab-ci.yml (YAML) |
Pipeline + Plugin 설정 |
| 러닝 커브 | 비교적 쉬움 | 상대적으로 높음 |
| 플러그인 생태계 | 제한적 (내장 중심) | 매우 풍부 |
| 유지보수 | 낮음 (자동화 많음) | 높음 (직접 관리 필요) |
| 확장성 | 조금 높음 | 매우 높음 |
| 서버 관리 부담 | 낮음 | 높음 |
1-1. 언제 많이 쓰나?
GitLab
장점
- Git 저장소와 CI/CD를 통합 관리 가능
.gitlab-ci.yml기반으로 설정이 단순함- 러닝커브가 낮아 빠른 구축 가능
단점
- Jenkins 대비 커스터마이징 자유도가 낮음
- 복잡한 레거시 환경 대응에는 한계가 있음
( GitLab CI는 test → build → deploy 같은 표준 흐름에는 강하지만, 회사마다 다른 오래된 배포 방식이나 예외 절차가 많으면 구성하기 까다로울 수 있음 )
Jenkins
장점
- 커스터마이징 자유도 높음 ( 다양한 플러그인 + Pipline )
- 복잡하고 예외 많은 CI/CD 자동화 흐름을 유연하게 구성하는 데 강함
단점
- 러닝커브 및 운영 난이도가 높음
- 서버 및 플로그인 관리가 필요함
GitLab 선택 배경
- 빠르게 플랫폼을 데모 형태로 구현해야 하는 특성 +
CI/CD환경이 없었기 때문에 러닝커브가 낮은 GitLab을 사용 - 또한
GitLab을 형상관리 도구로 사용하고 있었으며,Docker Compose기반 환경에서.gitlab-ci.yml만으로CI/CD구성이 가능했기 때문에GitLab을 활용하여CI/CD를 구축하기로 하였습니다.
2. GitLab 이란?
GitLab은 코드 저장소 + CI/CD + DevOps 기능을 통한한 플랫폼
Jenkins가 유연한 CI/CD 자동화 중심이라면, GitLab은 ⤵
- 형상관리
- 빌드
- 테스트
- 배포
- Runner 관리
까지 하나의 플랫폼에서 통합 관리할 수 있는 것이 핵심입니다.
GitLab CI는 Jenkins보다 복잡한 맞춤형 파이프라인 구현은 어렵지만
운영이 쉽고 GitLab 안에서 통합 관리되며 .gitlab-ci.yml 하나로 파이프라인을 구현할 수 있습니다.
3. GitLab 시작하기
3-1. Git 기본 설정
git config --global user.name "주세용"
git config --global user.email "sy02229@naver.com"
# 확인
git config --list3-2. 레포지토리 클론
1) HTTP 방식
- 간단하지만 push 할 때마다 로그인 필요
git clone https://gitlab.com/your-name/your-project.git
2) SSH 방식 (권장)
# 1. SSH 키 생성 (서버)
ssh-keygen
# 2. 공캐 키 확인 (서버)
cat ~/.ssh/id_rsa.pub
→ 출력 내용을 복사
# 3. GitLab에 SSH 키 등록 (GitLab)
GitLab 우측 상단 프로필 → Preferences → Access(SSH Keys) → Add new key
# 4. SSH config 설정 (서버)
vi ~/.ssh/config
Host gitlab.com
HostName gitlab.com
User git
Port 22
IdentityFile ~/.ssh/id_rsa
# 5. 연결 테스트
ssh -T git@gitlab.com
→ 성공 시: Welcome to GitLab, @sy0218!
# 6. SSH로 Clone
git clone git@gitlab.com:sy0218/jsy_project.git4. GitLab Runner 설치
Runner는 GitLab이 시키는 작업을 실제로 수행하는 실행 프로그램입니다.
4-1. Ubuntu GitLab 설치
# 1. 저장소 등록
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
# 2. Runner 설치
apt install gitlab-runner -y
# 3. 확인
gitlab-runner --version4-2. macOS Gitlab 설치
# 1. Homebrew 확인 ( 없으면 설치 )
brew --version
→ 없으면 설치 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 2. Runner 설치
brew install gitlab-runner
# 3. 확인
gitlab-runner --version5. GitLab Runner 등록하기
Runner 설치만으로는 동작하지 않습니다. → GitLab 프로젝트와 Runner를 연결해야 합니다.
5-1. GitLab에서 Runner 생성
Project → Settings → CI/CD → Runners → Create Project Runner5-2. 서버에서 Runner 연결
GitLab에서 제공한 명령 실행
gitlab-runner register \
--url https://gitlab.com \
--token glrt-xxxxxxxxxxxx5-3. 프로젝트에서 Instance Runner 활성화
GitLab Runner를 서버에서 띄웠더라도, 프로젝트에서 해당 Runner를 사용하도록 설정해야 CI/CD가 정상 동작합니다.
설정 방법
- GitLab 프로젝트 접속
Settings → CI/CD → Runners이동- Instance Runners 섹션 확인
- 아래 옵션을 OFF → ON으로 변경
질문 예시
| 질문 | 입력값 |
|---|---|
| GitLab instance URL | https://gitlab.com |
| Runner name | my-runner |
| Executor | docker (권장) |
| Default Docker image | alpine:latest |
- Executor란?
- GitLab Runner가 CI 작업을 실행하는 환경
- docker
- 매번 깨끗한 컨테이너에서 실행
- 안정적
- shell
- 서버에서 직접 실행
- 가볍지만 환경이 오염될 수 있음
6. GitLab Runner 동작 확인 체크리스트
GitLab UI 확인 | 서버 확인
6-1. GitLab UI 확인
Project → Settings → CI/CD → Runners- Runner가 초록색 Online 상태인지 확인
6-2. 서버 확인
Ubuntu
gitlab-runner status
gitlab-runner start
gitlab-runner list
gitlab-runner verify
macOS
brew services list
brew services start gitlab-runner
gitlab-runner list
gitlab-runner verify6-3. 최종 체크리스트
- GitLab에서 Online 상태 확인
-
gitlab-runner status→ running -
gitlab-runner list에 Runner 표시 -
gitlab-runner verify성공
6-4. 자주 발생하는 문제
1) 네트워크 연결 확인
ping gitlab.com
curl https://gitlab.com2) Docker executor 사용 시
Docker 정상 동작 여부 확인(서버)
docker --version
docker ps7. CI 파이프라인 만들기 (베스트 프랙티스)
GitLab CI는 코드가 push/merge 될 때 자동으로
검증(lint/test) → 빌드 → 배포 → 이미지 관리(Harbor) 까지 자동화하는 시스템이다.
7-1. 전체 구조(핵심 흐름)
feature 브랜치
→ 코드 검사(lint)만 실행
→ 개발 중 빠른 오류 확인용
develop 브랜치
→ lint → test → docker build → dev 이미지 생성 & Harbor push(Harbor auto scan)
→ "개발용 배포 이미지" 만드는 단계
main 브랜치
→ develop에서 만든 이미지 기준으로
→ 운영 이미지 승격 + deploy (취약점 scan OK 전제)7-2. GitLab CI 기본 개념
1) stages (파이프라인 단계)
stages:
- setup
- lint
- test
- build
- deploy→ 실행 순서를 정의함
2) job (작업 단위)
lint:
stage: lint
script:
- echo "lint 실행"→ CI에서 실제 실행되는 작업의 최소 단위
3) script (실행 명령어)
script:
- pnpm lint
- pytest→ 실제 서버에서 실행되는 쉘 명령
4) rules (브랜치 / 조건 필터)
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'→ 언제 job을 실행할지 결정
5) workflow(파이프라인 전체 조건)
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'→ push 기준으로만 실행
6) artifacts (결과 전달)
artifacts:
paths:
- build/→ build 결과물을 다음 stage로 전달
7) allow_failure
allow_failure: true→ 실패해도 파이프라인 계속 진행 (lint, typecheck 등에 사용)
7-3. CI 실습
1) 전체 흐름
feature → lint
develop → lint + test + build + push(Harbor auto scan)
main → deploy (scan OK 전제)2) GitLab Variables 정리
CI/CD에서 필요한 민감 / 환경설정 값을 코드에 안 쓰고 따로 관리
ex..) ⤵
컨테이너 레지스트리 계정 / 비밀번호
Harbor 주소
AWS key
API key
DB 패스워드GitLab Variables 등록 위치
Project → Settings → CI/CD → Variables- 참고 자료:
https://sy02229.tistory.com/778
3) Harbor 정리
- Harbor 설치 참고:
https://sy02229.tistory.com/779
4) gitlab CI/CD 베스트 프렉티스 템플릿 구조
.gitlab-ci.yml→ 흐름 정의 (stages / workflow / include).gitlab/ci/rules.yml→ 브랜치 + 변경 기반 rules 템플릿.gitlab/ci/templates.yml→ 공통 실행 환경 (.python / .node / .docker-*).gitlab/ci/backend.yml→ backend job 정의.gitlab/ci/frontend.yml→ frontend job 정의tests/unit/→ 유닛테스트 스크립트 (backend.sh, frontend.sh ...)tests/integration/→ 통합테스트 스크립트 (API ↔ DB, 서비스 간 연동 등)tests/e2e/→ E2E 테스트 스크립트 (사용자 시나리오 전체 흐름)scripts/ci/→ CI job에서 사용하는 script (build.sh, promote.sh ...)
.gitlab-ci.yml
# ================================================================
# CI/CD 구조 원칙
# ================================================================
# 1) CI 파일 = 흐름만 담당
# 2) 민감정보 = GitLab CI/CD Variables
# 3) 빌드 = Kaniko
# 4) 이미지 관리 = Harbor + Crane
# 5) 공통 로직 = hidden job(.xxx)
# 6) main deploy = Harbor scan 기반 정상 → 실행
# 브랜치 전략
# feature/* → 변경된 것만 lint
# develop
# → MR 성공시 merge
# → 변경된 것만 lint → test → build → Harbor push (자동 scan)
# main
# → Harbor scan OK
# → deploy
# 파일 구조
# .gitlab-ci.yml → 흐름 정의 (stages / workflow / include)
# .gitlab/ci/rules.yml → 브랜치 + 변경 기반 rules 템플릿
# .gitlab/ci/templates.yml → 공통 실행 환경 (.python / .node / .docker-*)
# .gitlab/ci/backend.yml → backend job 정의
# .gitlab/ci/frontend.yml → frontend job 정의
# tests/unit/ → 유닛테스트 스크립트 (backend.sh, frontend.sh ...)
# tests/integration/ → 통합테스트 스크립트 (API ↔ DB, 서비스 간 연동 등)
# tests/e2e/ → E2E 테스트 스크립트 (사용자 시나리오 전체 흐름)
# scripts/ci/ → CI job에서 사용하는 script (build.sh, promote.sh ...)
stages:
- setup
- lint
- test
- build
- deploy
# ================================================================
# 전역 변수
# ================================================================
variables:
PIP_DISABLE_PIP_VERSION_CHECK: "1"
IMAGE_BASE: "${HARBOR_URL}/${HARBOR_PROJECT}"
# ================================================================
# 파이프라인 실행 조건
# ================================================================
workflow:
rules:
# push
- if: '$CI_PIPELINE_SOURCE == "push"'
# MR
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# web(테스트 용)
- if: '$CI_PIPELINE_SOURCE == "web"'
# ================================================================
# Include
# ================================================================
include:
- local: '.gitlab/ci/rules.yml'
- local: '.gitlab/ci/templates.yml'
- local: '.gitlab/ci/backend.yml'.gitlab/ci/rules.yml
# ================================================================
# RULES 템플릿 (변경 기반 + 브랜치)
# ================================================================
.rules:backend:
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH =~ /^feature\//'
changes:
- backend/**/*
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
changes:
- backend/**/*
.rules:frontend:
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH =~ /^feature\//'
changes:
- frontend/**/*
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
changes:
- frontend/**/*
.rules:backend:develop:mr:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
changes:
- backend/**/*
.rules:frontend:develop:mr:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
changes:
- frontend/**/*
.rules:backend:main:mr:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
changes:
- backend/**/*
.rules:frontend:main:mr:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
changes:
- frontend/**/*
.gitlab/ci/templates.yml
# ================================================================
# 공통 실행 템플릿
# ================================================================
.lint:backend:python:
image: python:3.11-slim
interruptible: true
before_script:
- pip install --no-cache-dir uv
- uv venv .venv && source .venv/bin/activate
- uv pip install ruff mypy
.python:
image: python:3.11-slim
interruptible: true
variables:
UV_CACHE_DIR: "${CI_PROJECT_DIR}/.uv-cache"
UV_LINK_MODE: "copy"
cache:
key: uv-cache-global
paths:
- .uv-cache
policy: pull
before_script:
- pip install --no-cache-dir uv
- uv venv .venv && source .venv/bin/activate
- uv pip install --cache-dir "${CI_PROJECT_DIR}/.uv-cache" "./backend[dev]"
.node:
image: node:20-bookworm-slim
interruptible: true
before_script:
- corepack enable
- corepack prepare pnpm@9.15.9 --activate
- cd frontend
- pnpm install --frozen-lockfile
# Harbor 인증
.harbor-auth-kaniko:
before_script:
- mkdir -p /kaniko/.docker
- |
cat > /kaniko/.docker/config.json <<EOF
{
"auths": {
"${HARBOR_URL}": {
"auth": "$(printf '%s:%s' "${HARBOR_USER}" "${HARBOR_PASSWORD}" | base64 -w0)"
}
}
}
EOF
# 도커 빌드(KANIKO)
.docker-build:
extends: .harbor-auth-kaniko
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- sed -i 's/\r$//' ./scripts/ci/build.sh
- chmod +x ./scripts/ci/build.sh
- sh ./scripts/ci/build.sh
# main 승격 (Harbor scan 성공)
.docker-promote:
image:
name: gcr.io/go-containerregistry/crane:debug
entrypoint: [""]
script:
- sed -i 's/\r$//' ./scripts/ci/promote.sh
- chmod +x ./scripts/ci/promote.sh
- sh ./scripts/ci/promote.sh
.gitlab/ci/backend.yml
# ================================================================
# BACKEND
# ================================================================
setup-env:
stage: setup
extends: .python
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
changes:
- backend/pyproject.toml
cache:
policy: pull-push
script:
- echo "패키지 변경됨! .uv-cache 새로 구웠음"
ruff:
stage: lint
extends: [.lint:backend:python, .rules:backend]
script:
- ruff check backend
- ruff format --check backend
allow_failure: true
mypy:
stage: lint
extends: [.lint:backend:python, .rules:backend]
script:
- mypy backend
allow_failure: true
backend:test:
stage: test
extends: [.python, .rules:backend:develop:mr]
script:
- cd $CI_PROJECT_DIR
- chmod +x tests/unit/backend.sh
- ./tests/unit/backend.sh
backend:build:
stage: build
extends: [.docker-build, .rules:backend:develop:mr]
variables:
COMPONENT: backend
needs:
- job: backend:test
backend:deploy:
stage: deploy
extends: .docker-promote
variables:
COMPONENT: backend
resource_group: deploy-backend
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
changes:
- backend/**/*
tests/unit/backend.sh, frontend.sh..
단위 테스트 추후 구현 예정 입니다.
tests/integration/backend.sh, frontend.sh..
통합 테스트 추후 구현 예정 입니다.
tests/e2e/..
e2e 테스트 추후 구현 예정 입니다.
scripts/ci/..
CI job에서 사용하는 script 입니다.
'CI|CD Pipeline' 카테고리의 다른 글
| GitLab CI/CD 트러블슈팅 모음(CI/CD) (0) | 2026.05.31 |
|---|---|
| MinIO 구축 가이드(CI/CD) (0) | 2026.05.31 |
| Harbor 설치 가이드(CI/CD) (0) | 2026.05.25 |
| GitLab Variables 등록(CI/CD) (0) | 2026.05.24 |