GitLab Runner

GitLab Runner란?

GitLab CI/CD 파이프라인의 잡(Job)을 실제로 실행하는 에이전트다.

.gitlab-ci.yml에 파이프라인을 정의해두면, GitLab 서버는 잡을 큐에 올리고, Runner가 그 잡을 받아서 실행한다. GitLab 서버와 Runner는 별개로 동작한다.

GitLab 서버 (잡 스케줄링)
  ↓ 잡 할당
GitLab Runner (잡 실행)
  ↓
결과 리포트

Runner 종류 (범위)

종류설명
Shared RunnerGitLab 인스턴스 전체에서 사용. GitLab.com이 제공하는 공용 Runner
Group Runner특정 그룹과 그 하위 프로젝트에서만 사용
Project Runner특정 프로젝트에서만 사용

자체 서버나 EKS 클러스터에 Runner를 직접 설치하면 Group/Project Runner로 등록해서 쓴다.


Executor

Runner가 잡을 어떤 방식으로 실행할지 결정하는 설정이다.

Executor설명특징
shellRunner가 설치된 호스트에서 직접 실행가장 단순, 환경 격리 없음
docker각 잡을 Docker 컨테이너에서 실행잡마다 격리된 환경
kubernetes쿠버네티스 파드로 잡 실행EKS 등 K8s 클러스터에서 주로 사용
docker+machineAuto-scaling 지원 DockerGitLab.com 공용 Runner에서 사용

실무에서 EKS 환경이라면 kubernetes executor를 가장 많이 쓴다.


Kubernetes Executor 동작 방식

Runner 자체는 클러스터 안에 항상 떠 있는 파드다. 잡이 들어오면 해당 잡을 실행할 **임시 파드(helper pod)**를 동적으로 생성하고, 잡이 끝나면 삭제한다.

GitLab Runner Pod (상시 실행)
  ↓ 잡 수신
임시 Job Pod 생성 (잡 실행용)
  ↓ 잡 완료
임시 Job Pod 삭제

임시 파드는 보통 세 개의 컨테이너로 구성된다:

  • build: 실제 잡 스크립트 실행
  • helper: 아티팩트 업로드/다운로드, git clone 등 보조 작업
  • service: .gitlab-ci.ymlservices에 정의한 사이드카 (DB 등)

등록 방법

Runner를 GitLab에 연결하려면 등록 토큰이 필요하다.

# Runner 등록 (interactive)
gitlab-runner register
 
# 또는 non-interactive
gitlab-runner register \
  --url "https://gitlab.com/" \
  --registration-token "TOKEN" \
  --executor "kubernetes" \
  --description "my-runner"

등록하면 config.toml에 설정이 저장된다.


config.toml 주요 설정

Runner의 동작은 config.toml으로 제어한다. Kubernetes executor 기준:

[[runners]]
  name = "my-runner"
  url = "https://gitlab.com/"
  token = "xxxx"
  executor = "kubernetes"
  [runners.kubernetes]
    namespace = "gitlab-runner"
    image = "alpine"
    cpu_request = "100m"
    memory_request = "128Mi"
    cpu_limit = "1"
    memory_limit = "1Gi"
    [runners.kubernetes.volumes]
      [[runners.kubernetes.volumes.pvc]]
        name = "runner-cache"
        mount_path = "/cache"

.gitlab-ci.yml과의 관계

Runner는 .gitlab-ci.yml에서 tags로 특정 Runner를 선택할 수 있다.

build:
  tags:
    - my-runner   # 이 태그를 가진 Runner에서만 실행
  script:
    - ./gradlew build

Runner 등록 시 태그를 지정해두면, 잡의 태그와 매칭되는 Runner가 해당 잡을 가져간다.


캐시 (Cache)

잡 간에 파일을 재사용해서 빌드를 빠르게 한다. 의존성 파일(.gradle, node_modules 등)을 캐시해두면 매번 다운로드하지 않아도 된다.

cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - .gradle/
    - build/

Kubernetes executor에서는 캐시를 PVC나 S3에 저장한다.


아티팩트 (Artifacts)

잡이 생성한 파일을 다음 잡이나 GitLab UI에서 다운로드할 수 있게 보존한다.

build:
  script:
    - ./gradlew :prism:install
  artifacts:
    paths:
      - prism/build/install/
    expire_in: 1 week   # 보존 기간

캐시와 아티팩트의 차이:

  • 캐시: 빌드 속도 향상용, 잡 간 공유, 보장되지 않음 (없어도 잡은 실행됨)
  • 아티팩트: 잡 결과물 전달용, 파이프라인 내 다음 스테이지에 전달, 보장됨

트러블슈팅

파드가 Pending 상태에서 타임아웃

ERROR: Job failed (system failure): prepare environment:
waiting for pod running: timed out waiting for pod to start.

임시 잡 파드가 스케줄링되지 못한 경우. 원인:

  • 리소스(CPU/메모리) 부족 → 노드 스케일링 확인
  • PVC의 node affinity 불일치 → PVC 재생성 필요
  • 노드 taint와 파드 toleration 불일치 → Runner config의 toleration 설정 확인
# 임시 잡 파드 상태 확인
kubectl get pods -n gitlab-runner
 
# 스케줄링 실패 이유 확인
kubectl describe pod <job-pod-name> -n gitlab-runner

Runner가 잡을 가져가지 않음

  • Runner가 등록된 태그와 잡의 태그가 불일치하는지 확인
  • Runner가 paused 상태인지 확인 (GitLab UI → Settings → CI/CD → Runners)
  • config.tomlconcurrent 값이 낮아서 잡 처리 한도를 초과했는지 확인
concurrent = 10  # 동시에 처리할 수 있는 최대 잡 수

Docker-in-Docker (DinD) 권한 오류

잡 안에서 Docker 명령을 실행해야 할 때 (이미지 빌드 등) privileged 모드가 필요하다.

[runners.kubernetes]
  privileged = true

보안상 민감하므로 꼭 필요한 Runner에만 설정한다.