Daeho's Dev Blog

Docker GitLab 백업 자동화 — gitlab-backup + NAS 이중화

가이드#GitLab#Docker#backup#NAS#cron

GitLab도 이중화 백업을 하자는 지시가 내려옴. 소스코드는 이미 NAS로 백업하고 있었으니, 이제 GitLab 서버 자체도 백업 대상이 된 거임.

찾아보니 GitLab에 백업 명령어가 내장돼 있음. gitlab-backup create. 도커 내부에서 이걸 실행하면 저장소·DB가 통째로 tar로 떨어짐. 그래서 흐름을 이렇게 잡음.

  1. 도커 내부에서 gitlab-backup create로 백업 생성
  2. 생성된 tar 파일을 docker cp로 호스트로 꺼냄
  3. 꺼낸 파일을 NAS로 보냄 (기존 백업 체계에 합류)

백업 구조

기존 이중화에 GitLab이 끼면서 전체 그림이 이렇게 됨.


  [A] 홈페이지 서버 ──── rsync ────> [B] NAS     (기존 이중화)
        │                             ▲
      rsync                           │
        │                             │ gitlab-backup tar
        ▼                             │  (이번에 추가)
  [C] GitLab 서버 ────────────────────┘

보면서 홈페이지 서버 소스코드를 NAS 로 저장 안해도 되지 않을까? 생각은 들었음.

백업 스크립트

백업 생성 → 꺼내기 → 컨테이너 내부 정리 → 데이터 디렉터리 복사 → 오래된 백업 삭제까지 한 번에 도는 스크립트. 경로는 환경에 맞게 바꾸면 됨.

#!/bin/bash

# 설정
CONTAINER_NAME="gitlab"              # GitLab 컨테이너 이름
BACKUP_DIR="/backup/gitlab"          # 호스트의 백업 저장 위치
HOST_DATA_DIR="/data/gitlab"         # 호스트에서 백업할 GitLab 데이터 경로 (volume 마운트 경로)
RETENTION_DAYS=10                    # 보관 기간 (일)

# 함수: 로그 출력
log() {
    echo "[$(date)] $1"
}

# 1. 백업 생성
log "백업 생성 중..."
docker exec -t "$CONTAINER_NAME" gitlab-backup create

# 2. 컨테이너에서 최신 백업 파일 찾기
log "최신 백업 파일 검색 중..."
LATEST_BACKUP=$(docker exec -t "$CONTAINER_NAME" ls /var/opt/gitlab/backups | grep '.tar' | tail -n 1 | tr -d '\r' | tr -d '\n')
if [ -z "$LATEST_BACKUP" ]; then
    log "최신 백업 파일을 찾지 못했습니다. 백업 파일이 생성되었는지 확인하세요."
    exit 1
fi

log "최신 백업 파일: $LATEST_BACKUP"

# 3. 최신 백업 파일 복사
log "최신 백업 파일 복사 중..."
docker cp "$CONTAINER_NAME:/var/opt/gitlab/backups/$LATEST_BACKUP" "$BACKUP_DIR"
if [ $? -eq 0 ]; then
    log "백업 파일 복사 완료: $BACKUP_DIR/$LATEST_BACKUP"

    # 4. 컨테이너 내부 백업 파일 삭제
    log "컨테이너 내부 백업 파일 삭제 중..."
    docker exec -t "$CONTAINER_NAME" rm -f "/var/opt/gitlab/backups/$LATEST_BACKUP"
    if [ $? -eq 0 ]; then
        log "컨테이너 내부 백업 파일 삭제 완료: $LATEST_BACKUP"
    else
        log "컨테이너 내부 백업 파일 삭제 실패! 수동으로 확인하세요."
    fi
else
    log "백업 파일 복사 실패! 파일을 삭제하지 않습니다."
    exit 1
fi

# 5. 호스트 데이터 디렉터리 복사
log "GitLab 데이터 디렉터리 복사 중..."
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
HOST_BACKUP_DIR="$BACKUP_DIR/gitlab_data_$TIMESTAMP"
cp -r "$HOST_DATA_DIR" "$HOST_BACKUP_DIR"
if [ $? -eq 0 ]; then
    log "GitLab 데이터 디렉터리 복사 완료: $HOST_BACKUP_DIR"
else
    log "GitLab 데이터 디렉터리 복사 실패! 수동으로 확인하세요."
    exit 1
fi

# 6. 오래된 백업 파일 삭제
log "$RETENTION_DAYS일 이상 지난 백업 파일 삭제 중..."
find "$BACKUP_DIR" -type f -name '*.tar' -mtime +$RETENTION_DAYS -exec rm -f {} \;
find "$BACKUP_DIR" -type d -name 'gitlab_data_*' -mtime +$RETENTION_DAYS -exec rm -rf {} \;
if [ $? -eq 0 ]; then
    log "$RETENTION_DAYS일 이상 지난 백업 파일 삭제 완료."
else
    log "오래된 백업 파일 삭제 실패! 수동으로 확인하세요."
fi

# 7. 완료 메시지 출력
log "자동화 작업 완료."

포인트:

  • gitlab-backup create — 저장소·DB를 tar 하나로 묶어줌. 도커면 docker exec로 실행.
  • docker cp로 꺼내고 컨테이너 내부 파일은 삭제 — 안 지우면 컨테이너 안에 백업이 쌓여서 디스크 참사남. 복사 실패 시엔 안 지우게 해둠.
  • 데이터 디렉터리도 별도 복사gitlab-backup은 설정 파일(gitlab.rb, 시크릿)까지는 안 챙겨줘서 volume 마운트된 데이터 경로를 통째로 한 번 더 뜸.
  • 보관 기간 10일find -mtime으로 오래된 tar와 데이터 복사본을 정리. 안 하면 디스크가 남아나질 않음.

이렇게 호스트에 모인 백업 파일을 기존 NAS 백업 경로에 태워서 이중화 완성.

테스트

docker compose에서 volume 경로만 스크립트의 HOST_DATA_DIR와 맞게 잡아주면 잘 동작함. 크론에 걸어서 매일 돌리면 끝.

# 매일 새벽 3시 백업
0 3 * * * /root/scripts/gitlab-backup.sh >> /var/log/gitlab-backup.log 2>&1

정리

  • GitLab 백업은 내장 명령어 gitlab-backup create 하나로 됨. 도커여도 docker exec로 해결.
  • 백업 tar는 밖으로 꺼내고 컨테이너 안은 비울 것. 설정/시크릿은 백업에 안 들어가니 데이터 디렉터리도 따로 뜰 것.
  • 보관 기간 정해서 오래된 백업 자동 삭제까지 넣어야 운영이 됨.