9화. GitLab — 혼자 개발해도 CI/CD는 갖춘다 (셀프호스팅)

웰컴스 조회 21

코드를 GitHub에 두면 편하다. 대신 회사의 소스코드가 남의 클라우드에 있고, CI/CD를 쓰려면 또 다른 서비스와 연결해야 하고, 사용자·용량 정책은 언제든 바뀔 수 있다. GitLab은 코드 저장소에 이슈 트래커, 코드리뷰, 위키, 컨테이너 레지스트리, 그리고 CI/CD 실행기까지 전부 묶어 내 서버에 설치할 수 있는 올인원 개발 플랫폼이다. 혼자서도, 팀이 되어도 같은 구성으로 쓸 수 있다.


이런 때 유용합니다

  • 회사 소스코드를 외부 클라우드에 두기 부담스러울 때. 코드와 빌드 이력이 전부 내 서버에 있다.
  • "테스트는 언제 하지?"를 자동화하고 싶을 때. 코드를 push하면 테스트·빌드가 알아서 돌아간다.
  • 이슈 관리를 이메일과 메모장으로 하고 있을 때. 이슈 트래커와 칸반 보드가 저장소 안에 붙어 있다.
  • 도커 이미지 저장소가 필요할 때. 내장 레지스트리에 이미지가 쌓인다.
  • 나중에 팀이 늘어날 때를 대비할 때. 혼자 쓰던 구성 그대로 권한·리뷰 프로세스가 추가된다.

필요한 것은 RAM 4GB 이상(권장 8GB)의 서버다. 가장 무거운 편에 속하는 셀프호스팅이라는 점만 미리 알아두자.

GitLab은 어떤 프로그램인가

GitLab은 DevOps 전 과정을 하나로 묶은 오픈소스 플랫폼이다. GitHub과 비교하면 이해가 빠른데, GitHub이 "클라우드 서비스 중심 + 오픈소스 서버판(기능 제한)"이라면 GitLab은 "오픈소스 서버판이 거의 모든 기능을 제공"하는 쪽이다. 무료 CE(Community Edition)로도 개인·소규모 팀이 필요한 것은 대부분 된다.

기능 내용
코드 저장소 무제한 비공개 저장소, 브랜치 보호, 코드리뷰(MR)
CI/CD .gitlab-ci.yml 하나로 테스트·빌드·배포 파이프라인
이슈·보드 이슈 트래커, 라벨, 칸반 보드, 마일스톤
위키 저장소별 문서 공간
레지스트리 도커 이미지·패키지 저장소 내장
권한 관리 그룹·역할 기반 접근 제어

이 중 이 글이 다룰 핵심은 CI/CD다. 코드를 push하면 GitLab이 변경을 감지해, 등록된 Runner(실행기)에게 테스트와 빌드를 시키고 결과를 리포트한다.

전체 구성

flowchart LR
    D["개발자 - push"] --> G["GitLab - 저장소·이슈·리뷰"]
    T["팀원 - 이슈·코드리뷰"] --> G
    G -->|커밋 감지| R["GitLab Runner - 테스트·빌드"]
    R -->|결과 보고| G
    R -->|성공 시| AR["아티팩트·이미지 배포"]
    G --> BK[("백업 - data·설정")]

GitLab 본체(웹UI·저장소·DB)와 Runner는 별도 컨테이너로 운영하는 것이 권장 구성이다. Runner가 빌드 작업을 대신 실행하므로 본체 리소스가 빌드로 오염되지 않는다.

설치 방법

1단계 — GitLab 본체

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    hostname: git.example.com
    shm_size: 256m
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://git.example.com'
        gitlab_rails['gitlab_shell_ssh_port'] = 2222
    volumes:
      - /opt/docker/gitlab/config:/etc/gitlab
      - /opt/docker/gitlab/logs:/var/log/gitlab
      - /opt/docker/gitlab/data:/var/opt/gitlab
    ports:
      - "8081:80"
      - "2222:22"
    restart: unless-stopped

첫 기동에는 몇 분이 걸린다(초기화가 길다). 초기 root 비밀번호는 24시간 후 자동 삭제되는 파일에 있으니 바로 확인하자:

docker exec -it gitlab cat /etc/gitlab/initial_root_password

root로 로그인해 비밀번호를 변경하고, 일반 사용자 계정을 만들어 root는 예비로 남겨둔다. 서버의 22번 포트를 이미 쓰고 있다면 위처럼 SSH를 2222로 우회하고 clone 주소에 반영한다.

2단계 — Runner 등록

  gitlab-runner:
    image: gitlab/gitlab-runner:latest
    container_name: gitlab-runner
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /opt/docker/gitlab-runner/config:/etc/gitlab-runner
    restart: unless-stopped

관리 영역 → CI/CD → 러너에서 등록 토큰을 발급받고:

docker exec -it gitlab-runner gitlab-runner register \
  --url https://git.example.com --token 발급토큰 --executor docker \
  --docker-image alpine:latest

3단계 — 첫 CI 파이프라인

저장소 루트에 .gitlab-ci.yml 하나면 끝난다.

stages:
  - test

test-job:
  stage: test
  image: alpine:latest
  script:
    - echo "테스트 실행"

push하는 순간 파이프라인이 도는 것을 저장소의 CI/CD 메뉴에서 볼 수 있다. 여기에 언어별 테스트, 도커 이미지 빌드, 배포 스크립트를 붙여가면 된다.

운영하며 쌓은 것들

백업은 두 조각이다. docker exec gitlab gitlab-backup create가 저장소·DB·업로드를 묶어 덤프한다. 함정은 설정 디렉터리(/etc/gitlab)는 백업에 포함되지 않는다는 것 — 이 안의 secrets 파일 없이는 백업본의 암호화 데이터를 복구할 수 없다. 백업 + 설정, 둘 다 챙긴다.

메이저 업그레이드는 계단식으로. GitLab은 최신으로 한 번에 뛰는 것을 허용하지 않는다. 공식 문서의 업그레이드 경로(예: 16.x → 17.x 중간 버전들)를 순서대로 밟아야 한다. 급하게 뛰면 DB 마이그레이션이 멈춘다.

메모리 여유를 두자. RAM 4GB 미만에서는 sidekiq(백그라운드 작업)가 OOM으로 죽는 일이 생긴다. swap을 크게 잡아두면 최소 사양에서도 버틴다.

트러블슈팅 정리

증상 원인 해결
접속하면 502 기동 중이거나 메모리 부족 기동 대기(수 분), 메모리·swap 확인
clone이 안 된다 SSH 포트·external_url 불일치 gitlab_shell_ssh_port와 주소 확인
파이프라인이 대기만 한다 Runner 미등록·오프라인 러너 상태 확인, 재등록
업데이트 후 오류 업그레이드 경로 건너뜀 공식 경로에 따라 단계적 진행
이메일 발송 실패 SMTP 미설정 gitlab.rb에 SMTP 설정 추가

자주 묻는 질문

GitHub으로 충분하지 않나?

혼자 공개 프로젝트면 GitHub이 편하다. 사내 코드 보관, 데이터 주권, CI/CD 무제한(자원 내), 계정 정책 통제가 필요해지면 GitLab 셀프호스팅이 답이다. 둘의 사용 경험은 상당히 비슷해서 이동 비용도 크지 않다.

최소 사양은?

RAM 4GB가 현실적인 최소선(반드시 swap 설정), 권장은 8GB다. 저장소와 CI 빌드 아티팩트가 쌓이는 만큼 디스크도 여유를 둔다.

CI/CD로 뭘 할 수 있나?

push 시 테스트 실행, 도커 이미지 빌드 후 내장 레지스트리 push, 서버로 SSH 접속해 배포 — 이 일련의 흐름이 .gitlab-ci.yml 하나로 자동화된다. 이 시리즈의 다른 서비스들을 업데이트하는 배포 자동화에도 쓸 수 있다.


서버 한 대로 충분하다 연재 목차 — 시리즈 소개 · 1화. Home Assistant · 2화. Frigate + go2rtc · 3화. Seafile 13 · 4화. Immich · 5화. Vaultwarden · 6화. Mailcow · 7화. MinIO · 8화. Umami

실제 운영 서버에서 러너 분리 구성으로 사용 중입니다.

💬 댓글 0
글보기
제목9화. GitLab — 혼자 개발해도 CI/CD는 갖춘다 (셀프호스팅)2026-10-01 00:18
카테고리리눅스(서버)
작성자
> 코드를 GitHub에 두면 편하다. 대신 회사의 소스코드가 남의 클라우드에 있고, CI/CD를 쓰려면 또 다른 서비스와 연결해야 하고, 사용자·용량 정책은 언제든 바뀔 수 있다. GitLab은 코드 저장소에 이슈 트래커, 코드리뷰, 위키, 컨테이너 레지스트리, 그리고 CI/CD 실행기까지 전부 묶어 내 서버에 설치할 수 있는 올인원 개발 플랫폼이다. 혼자서도, 팀이 되어도 같은 구성으로 쓸 수 있다.

---

## 이런 때 유용합니다

- **회사 소스코드를 외부 클라우드에 두기 부담스러울 때.** 코드와 빌드 이력이 전부 내 서버에 있다.
- **"테스트는 언제 하지?"를 자동화하고 싶을 때.** 코드를 push하면 테스트·빌드가 알아서 돌아간다.
- **이슈 관리를 이메일과 메모장으로 하고 있을 때.** 이슈 트래커와 칸반 보드가 저장소 안에 붙어 있다.
- **도커 이미지 저장소가 필요할 때.** 내장 레지스트리에 이미지가 쌓인다.
- **나중에 팀이 늘어날 때를 대비할 때.** 혼자 쓰던 구성 그대로 권한·리뷰 프로세스가 추가된다.

필요한 것은 RAM 4GB 이상(권장 8GB)의 서버다. 가장 무거운 편에 속하는 셀프호스팅이라는 점만 미리 알아두자.

## GitLab은 어떤 프로그램인가

GitLab은 **DevOps 전 과정을 하나로 묶은 오픈소스 플랫폼**이다. GitHub과 비교하면 이해가 빠른데, GitHub이 "클라우드 서비스 중심 + 오픈소스 서버판(기능 제한)"이라면 GitLab은 "오픈소스 서버판이 거의 모든 기능을 제공"하는 쪽이다. 무료 CE(Community Edition)로도 개인·소규모 팀이 필요한 것은 대부분 된다.

| 기능 | 내용 |
|---|---|
| 코드 저장소 | 무제한 비공개 저장소, 브랜치 보호, 코드리뷰(MR) |
| CI/CD | `.gitlab-ci.yml` 하나로 테스트·빌드·배포 파이프라인 |
| 이슈·보드 | 이슈 트래커, 라벨, 칸반 보드, 마일스톤 |
| 위키 | 저장소별 문서 공간 |
| 레지스트리 | 도커 이미지·패키지 저장소 내장 |
| 권한 관리 | 그룹·역할 기반 접근 제어 |

이 중 이 글이 다룰 핵심은 **CI/CD**다. 코드를 push하면 GitLab이 변경을 감지해, 등록된 **Runner**(실행기)에게 테스트와 빌드를 시키고 결과를 리포트한다.

## 전체 구성

```mermaid
flowchart LR
D["개발자 - push"] --> G["GitLab - 저장소·이슈·리뷰"]
T["팀원 - 이슈·코드리뷰"] --> G
G -->|커밋 감지| R["GitLab Runner - 테스트·빌드"]
R -->|결과 보고| G
R -->|성공 시| AR["아티팩트·이미지 배포"]
G --> BK[("백업 - data·설정")]
```

GitLab 본체(웹UI·저장소·DB)와 Runner는 별도 컨테이너로 운영하는 것이 권장 구성이다. Runner가 빌드 작업을 대신 실행하므로 본체 리소스가 빌드로 오염되지 않는다.

## 설치 방법

### 1단계 — GitLab 본체

```yaml
services:
gitlab:
image: gitlab/gitlab-ce:latest
container_name: gitlab
hostname: git.example.com
shm_size: 256m
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://git.example.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
volumes:
- /opt/docker/gitlab/config:/etc/gitlab
- /opt/docker/gitlab/logs:/var/log/gitlab
- /opt/docker/gitlab/data:/var/opt/gitlab
ports:
- "8081:80"
- "2222:22"
restart: unless-stopped
```

첫 기동에는 몇 분이 걸린다(초기화가 길다). **초기 root 비밀번호는 24시간 후 자동 삭제되는 파일**에 있으니 바로 확인하자:

```bash
docker exec -it gitlab cat /etc/gitlab/initial_root_password
```

root로 로그인해 비밀번호를 변경하고, 일반 사용자 계정을 만들어 root는 예비로 남겨둔다. 서버의 22번 포트를 이미 쓰고 있다면 위처럼 SSH를 2222로 우회하고 clone 주소에 반영한다.

### 2단계 — Runner 등록

```yaml
gitlab-runner:
image: gitlab/gitlab-runner:latest
container_name: gitlab-runner
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/docker/gitlab-runner/config:/etc/gitlab-runner
restart: unless-stopped
```

관리 영역 → CI/CD → 러너에서 등록 토큰을 발급받고:

```bash
docker exec -it gitlab-runner gitlab-runner register \
--url https://git.example.com --token 발급토큰 --executor docker \
--docker-image alpine:latest
```

### 3단계 — 첫 CI 파이프라인

저장소 루트에 `.gitlab-ci.yml` 하나면 끝난다.

```yaml
stages:
- test

test-job:
stage: test
image: alpine:latest
script:
- echo "테스트 실행"
```

push하는 순간 파이프라인이 도는 것을 저장소의 CI/CD 메뉴에서 볼 수 있다. 여기에 언어별 테스트, 도커 이미지 빌드, 배포 스크립트를 붙여가면 된다.

## 운영하며 쌓은 것들

**백업은 두 조각이다.** `docker exec gitlab gitlab-backup create`가 저장소·DB·업로드를 묶어 덤프한다. 함정은 **설정 디렉터리(`/etc/gitlab`)는 백업에 포함되지 않는다**는 것 — 이 안의 secrets 파일 없이는 백업본의 암호화 데이터를 복구할 수 없다. 백업 + 설정, 둘 다 챙긴다.

**메이저 업그레이드는 계단식으로.** GitLab은 최신으로 한 번에 뛰는 것을 허용하지 않는다. 공식 문서의 업그레이드 경로(예: 16.x → 17.x 중간 버전들)를 순서대로 밟아야 한다. 급하게 뛰면 DB 마이그레이션이 멈춘다.

**메모리 여유를 두자.** RAM 4GB 미만에서는 sidekiq(백그라운드 작업)가 OOM으로 죽는 일이 생긴다. swap을 크게 잡아두면 최소 사양에서도 버틴다.

## 트러블슈팅 정리

| 증상 | 원인 | 해결 |
|---|---|---|
| 접속하면 502 | 기동 중이거나 메모리 부족 | 기동 대기(수 분), 메모리·swap 확인 |
| clone이 안 된다 | SSH 포트·external_url 불일치 | gitlab_shell_ssh_port와 주소 확인 |
| 파이프라인이 대기만 한다 | Runner 미등록·오프라인 | 러너 상태 확인, 재등록 |
| 업데이트 후 오류 | 업그레이드 경로 건너뜀 | 공식 경로에 따라 단계적 진행 |
| 이메일 발송 실패 | SMTP 미설정 | gitlab.rb에 SMTP 설정 추가 |

## 자주 묻는 질문

### GitHub으로 충분하지 않나?

혼자 공개 프로젝트면 GitHub이 편하다. 사내 코드 보관, 데이터 주권, CI/CD 무제한(자원 내), 계정 정책 통제가 필요해지면 GitLab 셀프호스팅이 답이다. 둘의 사용 경험은 상당히 비슷해서 이동 비용도 크지 않다.

### 최소 사양은?

RAM 4GB가 현실적인 최소선(반드시 swap 설정), 권장은 8GB다. 저장소와 CI 빌드 아티팩트가 쌓이는 만큼 디스크도 여유를 둔다.

### CI/CD로 뭘 할 수 있나?

push 시 테스트 실행, 도커 이미지 빌드 후 내장 레지스트리 push, 서버로 SSH 접속해 배포 — 이 일련의 흐름이 `.gitlab-ci.yml` 하나로 자동화된다. 이 시리즈의 다른 서비스들을 업데이트하는 배포 자동화에도 쓸 수 있다.

---

**서버 한 대로 충분하다 연재 목차** — [시리즈 소개](/info/186/) · [1화. Home Assistant](/info/185/) · [2화. Frigate + go2rtc](/info/184/) · [3화. Seafile 13](/info/183/) · [4화. Immich](/info/182/) · [5화. Vaultwarden](/info/181/) · [6화. Mailcow](/info/180/) · [7화. MinIO](/info/179/) · [8화. Umami](/info/178/)

*실제 운영 서버에서 러너 분리 구성으로 사용 중입니다.*
댓글
자동등록방지
(자동등록방지 숫자를 입력해 주세요)
목록으로