저장소 크기 축소

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

Git 저장소는 시간이 지남에 따라 커지게 됩니다. 큰 파일을 Git 저장소에 추가하면 다음과 같은 문제가 발생합니다:

  • 모든 사람이 파일을 다운로드해야 하므로 저장소를 가져오는 속도가 느려집니다.
  • 서버의 저장 공간을 많이 차지합니다.
  • Git 저장소 저장 공간 한도에 도달할 수 있습니다(storage-limits).

저장소를 다시 작성하면 원치 않는 기록을 제거하여 저장소를 더 작게 만들 수 있습니다. 저희는 git filter-repo를 사용하기를 권장합니다(git filter-repo). git filter-branchBFG보다 낫습니다.

경고: 저장소 기록을 다시 작성하는 것은 파괴적인 작업입니다. 시작하기 전에 저장소를 백업해두는 것이 좋습니다. 저장소를 백업하는 가장 좋은 방법은 프로젝트를 내보내기(export)하는 것입니다.

저장소 크기 계산

저장소의 크기는 저장소의 모든 파일의 누적된 크기를 계산하여 결정됩니다. 이는 저장소의 해시된 저장 경로에 대해 du --summarize --bytes를 실행하는 것과 유사합니다.

저장소 기록에서 파일 제거

GitLab은 housekeeping의 일환으로 도달할 수 없는 객체를 정리(prune)합니다. GitLab에서 서버의 디스크 크기를 수동으로 줄이려면 먼저 대상에 대한 대형 파일에 대한 참조를 브랜치, 태그 GitLab에서 생성된 기타 내부 참조(REF)에서 제거해야 합니다. 이러한 참조에는 다음이 포함됩니다:

  • refs/merge-requests/*
  • refs/pipelines/*
  • refs/environments/*
  • refs/keep-around/*

참조는 자동으로 다운로드되지 않으며 숨겨진 참조는 공지되지 않습니다. 그러나 이러한 참조를 프로젝트 내보내기를 사용하여 제거할 수 있습니다.

주의: 이 프로세스는 저장소에서 비밀 정보(예: 암호 또는 키)를 제거하기에 적합하지 않습니다. 파일 내용을 포함한 커밋 정보는 데이터베이스에 캐시되어 있으며, 저장소에서 제거된 후에도 여전히 표시됩니다.

GitLab 저장소에서 파일을 제거하려면:

  1. 지원되는 패키지 관리자나 소스에서 git filter-repo 및 (선택 사항)git-sizer를 설치합니다.

  2. 프로젝트에서 새로운 내보내기를 생성하고 다운로드합니다. 이 프로젝트 내보내기에는 저장소의 백업 복사본과 우리가 사용하여 저장소에서 파일을 제거하는 데 사용할 수 있는 참조(REFS)가 포함되어 있습니다.

  3. tar를 사용하여 백업을 압축 해제합니다:

    tar xzf project-backup.tar.gz
    

    이것에는 git bundle에 의해 생성된 project.bundle 파일이 포함되어 있습니다.

  4. --bare--mirror 옵션을 사용하여 번들에서 저장소의 새로운 복사본을 복제합니다:

    git clone --bare --mirror /path/to/project.bundle
    
  5. project.git 디렉토리로 이동합니다:

    cd project.git
    
  6. 번들 파일에서 복제하면 origin 원격을 로컬 번들 파일로 설정하므로, 이것을 저장소의 URL로 변경합니다:

    git remote set-url origin https://gitlab.example.com/<namespace>/<project_name>.git
    
  7. git filter-repo 또는 git-sizer를 사용하여 저장소를 분석하고 결과를 검토하여 제거할 항목을 결정합니다:

    # git filter-repo 사용
    git filter-repo --analyze
    head filter-repo/analysis/*-{all,deleted}-sizes.txt
    
    # git-sizer 사용
    git-sizer
    
  8. 관련된 git filter-repo 옵션을 사용하여 저장소의 기록을 삭제합니다. 두 가지 일반적인 옵션은 다음과 같습니다:

    • 특정 파일을 삭제하는 --path--invert-paths:

      git filter-repo --path path/to/file.ext --invert-paths
      
    • 예를 들어 10MB보다 큰 모든 파일을 제거하는 --strip-blobs-bigger-than:

      git filter-repo --strip-blobs-bigger-than 10M
      

    더 많은 예제 및 완전한 설명서는 git filter-repo 설명서를 참조하십시오.

  9. 내부 REF를 제거하려면 각각의 실행으로 생성된 commit-map 파일이 필요합니다. 각 git filter-repo 실행은 새로운 commit-map을 생성하고 이전 실행의 commit-map을 덮어쓰게 됩니다. 각 commit-map 파일을 백업하는 데 다음 명령어를 사용할 수 있습니다:

    cp filter-repo/commit-map ./_filter_repo_commit_map_$(date +%s)
    

    이 단계 및 이후 단계(포함하여 저장소 정리 단계)를 모든 git filter-repo 명령어 실행 후 매번 반복합니다.

  10. 변경 사항을 강제로 푸시하려면 먼저 미러 플래그를 설정해제해야 합니다:

     git config --unset remote.origin.mirror
    
  11. 변경 사항을 저장하여 GitLab의 모든 브랜치를 덮어쓰려면 다음 명령어를 사용합니다:

    git push origin --force 'refs/heads/*'
    

    보호된 브랜치는 이 작업에 실패시킬 수 있습니다. 이어서 진행하려면 브랜치 보호를 제거한 다음 푸시하고 다시 브랜치 보호를 활성화해야 합니다.

  12. 태그된 릴리스에서 큰 파일을 제거하려면 GitLab의 모든 태그에 변경 사항을 강제로 푸시합니다:

    git push origin --force 'refs/tags/*'
    

    보호된 태그는 이 작업을 실패시킬 수 있습니다. 계속 진행하려면 태그 보호를 제거하고 푸시한 다음 태그 보호를 다시 활성화해야 합니다.

  13. 더 이상 존재하지 않는 커밋에 대한 링크가 dead 링크로 남지 않도록 git filter-repo에 의해 작성된 refs/replace를 푸시합니다.

    git push origin --force 'refs/replace/*'
    

    이 작업 방식에 대한 자세한 내용은 Git의 replace 문서를 참조하십시오.

  14. 다음 단계를 시도하기 전에 적어도 30분을 기다립니다.
  15. 저장소 정리를 실행합니다. 이 프로세스는 30분 이상 된 객체만 정리합니다. 자세한 내용은 Space not being freed를 참조하십시오.

저장소 정리

저장소 정리를 통해 객체 파일의 텍스트 파일을 업로드하고 GitLab이 이러한 객체에 대한 내부 Git 참조를 제거할 수 있습니다. 저장소 정리에 사용할 수 있는 commit-map 파일에 대한 객체 목록(및 commit-map 파일 내)을 생성하기 위해 git filter-repo를 사용할 수 있습니다.

GitLab 13.6에서 소개되었으며, 저장소를 안전하게 정리하려면 작업 기간 동안 읽기 전용으로 지정되어야 합니다. 이는 자동으로 발생하지만 진행 중인 쓰기가 있는 경우 정리 요청을 제출하지 못하므로 계속하기 전에 진행 중인 git push 작업을 취소해야 합니다.

경고: 내부 Git 참조를 제거하면 관련 병합 요청 커밋, 파이프라인 및 변경 세부 정보를 더 이상 사용할 수 없게 됩니다.

저장소를 정리하려면:

  1. 왼쪽 사이드바에서 검색 또는 이동을 선택하고 프로젝트를 찾습니다.
  2. 설정 > 저장소로 이동합니다.
  3. 객체 목록을 업로드합니다. 예를 들어, filter-repo 디렉토리에 있는 git filter-repo에 의해 생성된 commit-map 파일과 같은 파일입니다.

    만약 commit-map 파일이 약 250KB 또는 3000줄 이상이라면 파일을 나누어 여러 부분으로 업로드할 수 있습니다:

    split -l 3000 filter-repo/commit-map filter-repo/commit-map-
    
  4. 정리 시작을 선택합니다.

이렇게 하면:

  • 오래된 커밋에 대한 내부 Git 참조를 제거합니다.
  • 참조되지 않는 객체를 제거하기 위해 저장소에 대해 git gc --prune=30.minutes.ago를 실행합니다. 저장소를 임시로 다시 패킹하면 이전의 팩 파일이 새 팩 파일 생성 완료 전까지 제거되지 않아 저장소 크기가 크게 증가합니다.
  • 프로젝트에 연결된 사용하지 않는 LFS 객체를 설정해제하여 저장 공간을 확보합니다.
  • 디스크 상의 저장소 크기를 다시 계산합니다.

GitLab은 정리가 완료된 후 새로 계산된 저장소 크기를 포함한 이메일 알림을 보냅니다.

만약 저장소 크기가 감소하지 않는다면, 이는 지난 30분 동안 Git 작업에서 참조된 loose 객체들이 남아있기 때문일 수 있습니다. 저장소가 적어도 30분 동안 휴면 상태인 경우에 이러한 단계를 다시 실행해 보세요.

저장소 정리 사용 시, 주의해야 할 점:

  • 프로젝트 통계는 캐시됩니다. 저장 공간 이용률이 감소하는 데 5~10분이 소요될 수 있습니다.
  • 정리는 30분보다 오래된 loose 객체를 정리합니다. 이는 최근 30분 동안 추가되거나 참조된 객체가 즉시 제거되지 않음을 의미합니다. Gitaly 서버에 액세스할 수 있다면 해당 지연을 건너뛰고 git gc --prune=now를 실행하여 모든 loose 객체를 즉시 정리할 수 있습니다.
  • 이 과정은 GitLab 캐시 및 데이터베이스에 있는 다시 작성된 커밋의 일부 사본을 제거하지만 여전히 커버리지에 많은 공백이 있으며 일부 사본은 영구히 유지될 수 있습니다. 인스턴스 캐시를 지우는 것이 일부 사본을 제거하는 데 도움이 될 수 있지만 보안 목적으로 의존해서는 안 됩니다!

저장소 용량 제한

저장소 크기 제한:

프로젝트가 크기 제한에 도달하면 다음을 할 수 없습니다:

  • 프로젝트에 푸시할 수 없습니다.
  • 새 병합 요청을 생성할 수 없습니다.
  • 기존 병합 요청을 병합할 수 없습니다.
  • LFS 객체를 업로드할 수 없습니다.

그럼에도 불구하고 다음을 할 수 있습니다:

  • 새 이슈를 만들 수 있습니다.
  • 프로젝트를 복제할 수 있습니다.

만약 저장소 크기 제한을 초과한다면 다음을 할 수 있습니다:

  1. 일부 데이터를 제거합니다.
  2. 새로운 커밋을 만듭니다.
  3. 저장소에 다시 푸시합니다.

만약 이러한 조치가 충분하지 않다면 다음도 할 수 있습니다:

  • 일부 blob을 LFS로 이동시킵니다.
  • 이전 의존성 업데이트를 제거합니다.

유감스럽게도, 이러한 워크플로우는 작동하지 않습니다. 커밋에서 파일을 삭제하는 것은 실제로 저장소 크기를 줄이지 않습니다. 왜냐하면 이전 커밋과 blob이 여전히 존재하기 때문입니다. 대신에, 역사를 다시 작성해야 합니다. 우리는 오픈 소스 커뮤니티 유지보수 도구인 git filter-repo를 권장합니다.

참고: git gc가 GitLab 쪽에서 실행되기 전까지 “제거된” 커밋과 blob은 여전히 존재합니다. 또한 이미 최대 크기 제한을 초과한 경우 GitLab로 재작성된 이력을 푸시할 수 있어야 합니다.

이 제한을 해제하려면 셀프 매니지드 GitLab 인스턴스의 관리자는 해당 프로젝트의 제한을 늘려야 합니다. 따라서 제한 이하로 유지하는 것이 항상 좋습니다. 제한에 걸리면 일시적으로 증가시킬 수 없다면 유일한 옵션은 다음과 같습니다:

  1. 불필요한 것들을 로컬에서 모두 정리합니다.
  2. GitLab에서 새 프로젝트를 만들고 대신 사용합니다.

문제 해결

GUI에 표시된 리포지토리 통계가 올바르지 않음

만약 표시된 크기나 커밋 수가 .tar.gz로 내보낸 것이나 로컬 리포지토리와 다르다면, GitLab 관리자에게 갱신을 강제로 진행할 수 있도록 요청할 수 있습니다.

레일즈 콘솔을 사용하여:

p = Project.find_by_full_path('<namespace>/<project>')
pp p.statistics
p.statistics.refresh!
pp p.statistics
# 이전 값과 비교

# 프로젝트 통계를 비우는 대안적인 방법
p.repository.expire_all_method_caches
UpdateProjectStatisticsWorker.perform_async(p.id, ["commit_count","repository_size","storage_size","lfs_objects_size"])

# 별도로 총 artifact 저장 공간을 확인
builds_with_artifacts = p.builds.with_downloadable_artifacts.all

artifact_storage = 0
builds_with_artifacts.find_each do |build|
  artifact_storage += build.artifacts_size
end

puts "#{artifact_storage} 바이트"

공간이 해제되지 않음

이 페이지에서 정의된 프로세스는 리포지토리 내보내기의 크기를 줄일 수는 있지만, 웹 UI와 터미널에서 변경되지 않은 파일 시스템 사용량이 나타날 수 있습니다.

이 프로세스는 리포지토리에 도달할 수 없는 객체를 많이 남겨둡니다. 도달할 수 없기 때문에 내보내기에 포함되지 않지만, 파일 시스템에는 여전히 남아 있습니다. 이러한 파일들은 2주의 유예 기간이 지난 후 정리됩니다. 정리는 이러한 파일을 삭제하고 저장 공간 사용 통계가 정확하게 유지되도록 합니다.

이 프로세스를 가속화하기 위해 ‘도달할 수 없는 객체 정리’ 하우스키퍼 작업을 확인하세요.

Sidekiq 프로세스가 프로젝트를 내보내지 못하는 문제

Tier: Free, Premium, Ultimate Offering: Self-Managed, GitLab Dedicated

가끔 Sidekiq 프로세스가 프로젝트를 내보내지 못할 수 있습니다. 예를 들어 실행 중에 종료된 경우입니다.

GitLab.com 사용자는 지원팀에 문의하여 이 문제를 해결할 수 있습니다.

Self-Managed 사용자는 Sidekiq 프로세스를 우회하고 수동으로 프로젝트를 내보내도록 레일즈 콘솔을 사용할 수 있습니다:

project = Project.find(1)
current_user = User.find_by(username: 'my-user-name')
RequestStore.begin!
ActiveRecord::Base.logger = Logger.new(STDOUT)
params = {}

::Projects::ImportExport::ExportService.new(project, current_user, params).execute(nil)

이를 통해 내보내기를 UI를 통해 제공할 수 있지만, 사용자에게 이메일을 보내지는 않습니다. 프로젝트 내보내기 및 사용자에게 이메일을 수동으로 보내려면:

project = Project.find(1)
current_user = User.find_by(username: 'my-user-name')
RequestStore.begin!
ActiveRecord::Base.logger = Logger.new(STDOUT)
params = {}

ProjectExportWorker.new.perform(current_user.id, project.id)