“지시서는 올렸는데, 왜 반응이 없지?”
지난 시간, 나는 야심 차게 .gitlab-ci.yml 파일을 작성해서 push 했다. “이제 배포가 자동으로 되겠지?”라며 기대에 차서 GitLab의 ‘CI/CD > Pipelines’ 메뉴를 클릭했다.
하지만 화면에 뜬 것은 초록색 성공 표시가 아니라, 하염없이 돌아가는 로딩 바와 ‘Pending (보류 중)’이라는 상태 메시지뿐이었다. 메시지를 클릭해 보니 이런 경고가 떠 있었다.
This job is stuck because the project doesn't have any runners online assigned to it.
해석하자면 “일감이 들어왔는데, 이걸 처리할 작업자(Runner)가 출근을 안 했습니다”라는 뜻이다. 아차, 나는 공장장(GitLab Server)에게 작업 지시서만 던져주고, 정작 공장에서 일할 직원(Runner)을 채용하지 않았던 것이다.

깃랩 러너(GitLab Runner): 파이프라인의 손과 발
GitLab은 크게 두 가지 요소로 나뉜다.
- GitLab Server (본사/공장장): 코드를 저장하고, 파이프라인 상태를 관리하고, 지시를 내린다.
- GitLab Runner (지사/현장직): 실제 서버에 설치되어, 본사의 지시를 받아 코드를 가져오고, 빌드하고, 배포 명령을 수행한다.
“그냥 GitLab이 알아서 내 서버에 접속해서 배포해주면 안 되나요?” 생각해 보면 위험천만한 일이다. 만약 인터넷에 있는 웹사이트(GitLab)가 버튼 하나 눌렀다고 해서 내 방에 있는 컴퓨터(서버)에 들어와 맘대로 명령어를 실행할 수 있다면? 그건 ‘해킹’이나 다름없다. 누군가 내 GitLab 계정을 털면 내 서버를 비트코인 채굴기로 만들어버릴 수도 있다.
그래서 GitLab은 직접 내 서버에 접속하지 않는다. 대신 내 서버 안에서만 동작하는 ‘신원 확실한 대리인(Runner)’을 심어두고, 그 대리인을 통해서만 일을 시킨다. 우리가 지금부터 할 복잡한 설치와 등록 과정은 바로 이 대리인을 인증하는 ‘보안 절차’다.
1단계: 채용 공고 확인하기 (토큰 발급)
러너를 설치하기 전에, 먼저 본사(GitLab)에서 “우리 프로젝트에 일꾼이 필요합니다”라는 ‘채용 공고’를 내고 ‘등록증(Token)’을 받아야 한다.
최신 GitLab 버전에서는 보안 강화를 위해 토큰 확인 방법이 조금 숨겨져 있다.
- GitLab 프로젝트 접속
- 왼쪽 메뉴: Settings > CI/CD
- Runners 섹션의
Expand클릭 - Project runners 섹션 우측 상단에 있는 점 3개 버튼(⋮) 클릭
이 버튼을 누르면 팝업 메뉴가 뜨는데, 여기에 우리가 찾는 정보가 있다. URL과 Registration token을 확인하고 복사해두자.
(참고: ‘Support for registration tokens is deprecated’라는 무시무시한 경고가 뜰 수 있다. 곧 사라질 방식이라는 뜻이지만, 아직은 호환성을 위해 지원하니 당황하지 말고 토큰을 복사하면 된다.)
[Tip] 프로젝트가 여러 개라면? (Group Runner) 만약 나처럼 프론트엔드, 백엔드 등 여러 프로젝트를 하나의 그룹으로 묶어 관리하고 있다면, 프로젝트마다 일일이 러너를 등록하는 건 비효율적이다. 이때는 프로젝트 설정이 아니라 그룹 설정(Group Settings) > CI/CD 메뉴에서 러너를 등록하면 된다. 이렇게 만든 ‘Group Runner’는 해당 그룹에 속한 모든 프로젝트의 일감을 혼자서 다 처리할 수 있다. (나는 프로젝트가 많아서 이 방식을 택했다.)

2단계: 일꾼 채용하기 (Runner 설치)
이제 리눅스 서버(혹은 내 PC)에 러너를 실제로 설치해 보자. 가장 깔끔한 방법은 역시 우리가 사랑하는 ‘도커(Docker)’를 이용하는 것이다.
# 1. 깃랩 러너 컨테이너 실행
docker run -d --name gitlab-runner --restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v gitlab-runner-config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest
-v /var/run/docker.sock:/var/run/docker.sock: 이게 중요하다. 러너가 또 다른 도커 컨테이너(빌드용)를 띄워야 하기 때문에, 호스트의 도커 소켓을 공유해 주는 것이다. (Docker in Docker 방식)
3단계: 근로 계약서 작성 (Runner 등록)
러너를 설치했다고 끝이 아니다. 이 러너가 “나는 A 프로젝트의 일꾼입니다”라고 GitLab 서버에 등록(Register)하는 절차가 필요하다.
방금 띄운 러너 컨테이너 안으로 들어가서 등록 명령어를 실행하자.
# 러너 컨테이너 내부에서 등록 명령어 실행
docker exec -it gitlab-runner gitlab-runner register
이제 터미널에서 스무고개 같은 질문이 시작된다. 아까 1단계에서 본 정보를 입력하면 된다.
- Enter the GitLab instance URL:
https://gitlab.com/(또는 사설 깃랩 주소)
- Enter the registration token:
- (아까 복사한 토큰 붙여넣기)
- Enter a description for the runner:
my-runner-01(아무거나, 알아보기 쉬운 이름)
- Enter tags for the runner:
- (엔터 쳐서 생략 가능. 특정 태그가 달린 작업만 시킬 때 쓴다.)
- Enter an executor:
docker(우리는 도커 위에서 빌드할 거니까 docker라고 입력한다.)
- Enter the default Docker image:
docker:latest(기본으로 사용할 이미지)
“Runner registered successfully.” 이 메시지가 뜨면 채용 완료다! 이제 GitLab 화면을 새로고침 해보면, 초록색 불이 들어온 러너가 “준비됐습니다(Online)”라고 외치고 있을 것이다.

4단계: 파이프라인 가동!
러너가 등록되는 순간, 멈춰있던(Pending) 파이프라인이 자동으로 움직이기 시작한다. Running… 그리고 잠시 후 Passed!
내가 아무것도 안 했는데, 기계가 알아서 테스트를 돌리고, 빌드를 하고, 이미지를 만들어서 저장소에 올렸다. 터미널을 열어 로그를 확인해 보니, 내가 수동으로 칠 때보다 훨씬 빠르고 정확하게 모든 과정이 끝나 있었다.
“와… 진짜 되네?”
10분 걸리던 수동 배포가, 이제는 git push 한 번에 끝난다. 나는 이제 배포를 걸어놓고 커피를 마시러 갈 수 있게 되었다.
실무 조언: 도커 권한 문제 (Permission Denied)
처음 러너를 세팅할 때 99% 확률로 마주치는 에러가 있다. dial unix /var/run/docker.sock: connect: permission denied
러너가 도커 명령어를 쓰려고 하는데 권한이 없어서 생기는 문제다. 이럴 땐 gitlab-runner 설정 파일(config.toml)을 열어서 privileged = true 옵션을 켜줘야 한다.
# /etc/gitlab-runner/config.toml (러너 설정 파일)
[[runners]]
name = "my-runner-01"
executor = "docker"
[runners.docker]
privileged = true # 이 부분을 true로 바꿔줘야 Docker-in-Docker가 잘 된다. # …
마치며: 이제 퇴근할 수 있다
드디어 나만의 ‘배포 로봇’이 생겼다. 이제 나는 코드를 짜는 데만 집중하면 된다. 지루하고 반복적인 검증과 배포 작업은 나의 충실한 로봇(Runner)이 대신해 줄 테니까.
하지만 아직 2% 부족하다. 지금은 파이프라인이 master 브랜치에 푸시될 때마다 무조건 실행된다. “개발용 서버(dev)에는 자동으로 배포하고 싶지만, 운영 서버(prod)는 내가 ‘승인 버튼’을 눌렀을 때만 배포하고 싶다면?”
다음 시간에는 파이프라인을 더 똑똑하게 제어하는 ‘브랜치별 배포 전략’과 ‘수동 승인(Manual) 절차’에 대해 알아보자.