리포지터리 크기 축소
시간이 지남에 따라 Git 리포지터리는 점점 커지게 됩니다. Git 리포지터리에 큰 파일이 추가되면:
- 모든 사람이 파일을 다운로드해야 하기 때문에 리포지터리를 가져오는 데 시간이 더 걸립니다.
- 서버의 저장 공간을 크게 차지합니다.
- Git 리포지터리 저장 공간 제한이 도달할 수 있습니다.
리포지터리를 재작성하면 원하지 않는 이력을 제거하여 리포지터리를 더 작게 만들 수 있습니다.
우리는 git filter-repo
를
git filter-branch
나
BFG보다 더 추천합니다.
리포지터리 크기 계산
리포지터리의 크기는 리포지터리에 있는 모든 파일의 누적 크기를 계산하여 결정됩니다.
이는 리포지터리의 해시된 저장 경로에서
du --summarize --bytes
명령을 실행하는 것과 유사합니다.
리포지터리 이력에서 파일 제거
GitLab은 정기적인 작업의 일환으로 도달할 수 없는 객체를 제거(prune)합니다. GitLab에서 매뉴얼으로 리포지터리 디스크 크기를 줄이려면 먼저 GitLab이 만든 브랜치, 태그 및 다른 내부 참조(refs)에서 큰 파일에 대한 참조를 제거해야 합니다. 이러한 참조(refs)에는 다음이 포함됩니다:
refs/merge-requests/*
refs/pipelines/*
refs/environments/*
refs/keep-around/*
이러한 참조(refs)는 자동으로 다운로드되지 않으며 숨겨진 참조는 광고되지 않지만, 프로젝트 내보내기를 사용하여 이러한 참조를 제거할 수 있습니다.
GitLab 리포지터리에서 파일을 제거하려면:
-
지원되는 패키지 관리자나 소스에서
git filter-repo
및 선택적으로git-sizer
를 설치하세요. -
프로젝트로부터 새로운 내보내기를 생성하고 다운로드하세요. 이 프로젝트 내보내기에는 리포지터리 및 참조를 정리할 수 있는 백업 복사본이 포함되어 있습니다.
-
tar
를 사용하여 백업을 압축 해제하세요:tar xzf project-backup.tar.gz
이것에는
git bundle
에 의해 생성된project.bundle
파일이 포함되어 있습니다. -
--bare
및--mirror
옵션을 사용하여 번들에서 리포지터리의 새로운 복사본을 클론하세요:git clone --bare --mirror /path/to/project.bundle
-
project.git
디렉터리로 이동하세요:cd project.git
-
번들 파일에서 클론하는 것은
origin
원격을 로컬 번들 파일의 URL로 설정하므로, 이를 리포지터리의 URL로 변경하세요:git remote set-url origin https://gitlab.example.com/<namespace>/<project_name>.git
-
git filter-repo
또는git-sizer
를 사용하여 리포지터리를 분석하고 결과를 검토하여 제거할 항목을 결정하세요:# git filter-repo 사용 git filter-repo --analyze head filter-repo/analysis/*-{all,deleted}-sizes.txt # git-sizer 사용 git-sizer
-
관련된
git filter-repo
옵션을 사용하여 리포지터리의 이력을 정리하세요. 두 가지 일반적인 옵션은 다음과 같습니다:-
특정 파일을 제거하려면
--path
및--invert-paths
를 사용하세요:git filter-repo --path path/to/file.ext --invert-paths
-
예를 들어 10M보다 큰 모든 파일을 제거하려면
--strip-blobs-bigger-than
을 사용하세요:git filter-repo --strip-blobs-bigger-than 10M
더 많은 예제 및 완전한 설명서는
git filter-repo
설명서를 참조하세요. -
-
내부 참조를 제거하려면 각 실행별로 생성된
commit-map
파일이 필요합니다. 각git filter-repo
실행은 새로운commit-map
을 생성하며 이전 실행에서 생성된commit-map
을 덮어씁니다. 각commit-map
파일을 백업하려면 다음 명령을 사용하세요:cp filter-repo/commit-map ./_filter_repo_commit_map_$(date +%s)
이 단계와 리포지터리 정리 단계를 실행할 때마다 이후 단계를 (Protected branches](../protected_branches.md)를 포함한 모든 내용을 반복해야 합니다.
-
변경 사항을 강제로 푸시하려면 미러 플래그를 해제해야 합니다:
git config --unset remote.origin.mirror
-
GitLab의 모든 브랜치를 덮어쓰기 위해 변경 사항을 강제로 푸시하세요:
git push origin --force 'refs/heads/*'
Protected branches로 인해 실패합니다. 진행하려면 브랜치 보호를 제거하고 푸시한 다음 다시 브랜치 보호를 활성화해야 합니다.
-
태그된 릴리스에서 큰 파일을 제거하려면 GitLab의 모든 태그에 변경 사항을 강제로 푸시하세요:
git push origin --force 'refs/tags/*'
Protected tags로 인해 실패합니다. 계속하려면 태그 보호를 제거하고 푸시한 다음 다시 태그 보호를 활성화해야 합니다.
-
더 이상 존재하지 않는 커밋에 대한 데드 링크를 방지하려면
git filter-repo
에 의해 생성된refs/replace
를 푸시하세요.git push origin --force 'refs/replace/*'
작동 방식에 대한 정보는 Git
replace
설명서를 참조하세요. - 다음 단계를 시도하기 전에 최소 30분을 기다리세요.
- 리포지터리 정리를 실행하세요. 이 프로세스는 30분 이상 된 객체만 정리합니다. 자세한 내용은 Space not being freed를 참조하세요.
리포지터리 정리
리포지터리 정리를 통해 GitLab은 해당 객체에 대한 내부 Git 참조를 제거합니다. 이 작업은 git filter-repo
를 사용하여 만든 객체 디렉터리( commit-map
파일)을 사용할 수 있게 합니다.
도입됨 in GitLab 13.6, 리포지터리를 안전하게 정리하려면 작업 중에 해당 리포지터리를 읽기 전용으로 만들어야 합니다. 이것은 자동으로 발생하지만, 진행 중인 쓰기 작업이 있으면 정리 요청을 제출할 수 없으므로 계속하기 전에 진행 중인 git push
작업을 취소하세요.
리포지터리를 정리하려면:
- 왼쪽 사이드바에서 검색 또는 이동을 선택하고 프로젝트를 찾습니다.
- 설정 > 리포지터리로 이동합니다.
-
객체 디렉터리을 업로드합니다. 예를 들어,
git filter-repo
에 의해 생성된commit-map
파일을 업로드합니다. 이 파일은filter-repo
디렉터리에 있습니다.만약
commit-map
파일이 약 250KB 또는 3000줄보다 크다면, 파일을 나누어 조각별로 업로드할 수 있습니다:split -l 3000 filter-repo/commit-map filter-repo/commit-map-
- 정리 시작을 선택합니다.
이렇게 하면:
- 이전 커밋에 대한 내부 Git 참조가 제거됩니다.
- 리포지터리에 대해
git gc --prune=30.minutes.ago
가 실행되어 참조되지 않는 객체가 제거됩니다. 리포지터리를 재패킹하면 일시적으로 리포지터리 크기가 크게 증가합니다. 이는 이전 팩 파일이 새로운 팩 파일이 생성될 때까지 제거되지 않기 때문입니다. - 프로젝트에 연결된 사용되지 않는 LFS 객체가 연결 해제되어 저장 공간이 확보됩니다.
- 디스크의 리포지터리 크기를 다시 계산합니다.
GitLab은 정리가 완료되면 리포지터리 크기가 재계산된 이메일 알림을 보냅니다.
리포지터리 크기가 줄어들지 않으면, 이는 지난 30분 동안 Git 작업에서 참조되어 있는 미사용 객체 때문일 수 있습니다. 리포지터리가 적어도 30분 동안 대기한 후 이러한 단계를 다시 실행해 보세요.
Repository Cleanup를 사용할 때 유의해야 할 사항:
- 프로젝트 통계는 캐시됩니다. 저장 공간 이용률을 줄이려면 5-10분을 기다려야 할 수 있습니다.
- 정리는 30분보다 오래된 미사용 객체를 제거합니다. 이는 지난 30분 동안 추가되거나 참조된 객체가 즉시 제거되지 않음을 의미합니다. Gitaly 서버에 액세스할 수 있다면 이 지연을 건너뛰고 즉시
git gc --prune=now
를 실행하여 모든 미사용 객체를 제거할 수 있습니다. - 이 과정은 GitLab 캐시와 데이터베이스에서 재작성된 커밋의 일부 사본을 제거하지만, 여전히 커버리지에 많은 틈이 있고 일부 사본이 무기한으로 유지될 수 있습니다. 인스턴스 캐시를 지우는 것은 일부 사본을 제거하는 데 도움이 될 수 있지만, 보안 목적으로 의존해서는 안 됩니다!
리포지터리 용량 제한
리포지터리 크기 제한:
- 관리자가 설정을 통해 설정할 수 있습니다.
- Self-managed 인스턴스에서는 관리자가 설정을 통해 설정할 수 있습니다.
- GitLab.com에 대해 설정됩니다.
프로젝트가 크기 제한에 도달하면 다음을 할 수 없습니다:
- 프로젝트에 푸시하지 못합니다.
- 새로운 Merge Request을 만들지 못합니다.
- 기존 Merge Request을 Merge하지 못합니다.
- LFS 객체를 업로드하지 못합니다.
그럼에도 불구하고 다음을 할 수 있습니다:
- 새 이슈를 만들 수 있습니다.
- 프로젝트를 복제할 수 있습니다.
리포지터리 크기 제한을 초과한 경우, 다음을 수행할 수 있습니다:
- 일부 데이터를 제거합니다.
- 새 커밋을 만듭니다.
- 리포지터리에 다시 푸시합니다.
이러한 조치가 부족한 경우 다음을 수행할 수 있습니다:
- 일부 Blob을 LFS로 이동시킵니다.
- 역사에서 일부 오래된 의존성 업데이트를 제거합니다.
유감스럽게도, 이 워크플로는 작동하지 않습니다. 커밋에서 파일을 삭제하면 이전 커밋과 Blob이 여전히 존재하기 때문에 리포지터리 크기가 실제로 줄지 않습니다. 대신에 역사를 다시 작성해야 합니다. 우리는 오픈 소스 커뮤니티 지원 도구인 git filter-repo
를 권장합니다.
git gc
가 GitLab 측에서 실행되기 전까지 “제거된” 커밋과 Blob은 여전히 존재합니다. 또한 이미 최대 크기 제한을 초과했다면 재작성된 역사를 GitLab에 푸시해야 합니다.이러한 제한을 해제하려면, Self-managed GitLab 인스턴스의 관리자가 초과한 특정 프로젝트의 제한을 높여야 합니다. 따라서 제한을 항상 미리 초과하지 않도록 주의하는 것이 좋습니다. 제한을 초과하면 임시로 높일 수 없는 경우 아래와 같이 할 수 있습니다:
- 로컬에서 필요 없는 것을 모두 정리합니다.
- 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} bytes"
공간이 해제되지 않음
이 페이지에서 정의된 과정은 리포지터리 내보내기의 크기를 줄일 수 있지만, 웹 UI와 터미널 모두에 나타나는 파일 시스템의 사용량이 변하지 않습니다.
이 과정은 리포지터리에 여전히 도달할 수 없는 많은 객체를 남겨두게 합니다. 도달할 수 없기 때문에 내보내기에 포함되지 않지만, 파일 시스템에 여전히 저장됩니다. 이러한 파일은 2주의 유예 기간 이후에 가지를 제거합니다. 가지를 잘라내어 이러한 파일을 삭제하고 저장 공간 사용 통계가 정확하도록 보장합니다.
이 과정을 빠르게 수행하려면 도달할 수 없는 객체 잘라내기 작업을 참조하세요.
Sidekiq 프로세스가 프로젝트를 내보내지 못하는 경우
가끔 Sidekiq 프로세스가 프로젝트를 내보내지 못할 수 있습니다. 예를 들어, 실행 중에 종료된 경우입니다.
GitLab.com 사용자는 지원팀에 문의하여 이 문제를 해결할 수 있습니다.
Self-managed 사용자는 Rails 콘솔을 사용하여 Sidekiq 프로세스를 우회하고 프로젝트 내보내기를 매뉴얼으로 시작할 수 있습니다:
project = Project.find(1)
current_user = User.find_by(username: '내 사용자 이름')
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: '내 사용자 이름')
RequestStore.begin!
ActiveRecord::Base.logger = Logger.new(STDOUT)
params = {}
ProjectExportWorker.new.perform(current_user.id, project.id)