파일 내보내기 프로젝트 마이그레이션 문제 해결
GitLab v19.5Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
요약
파일 내보내기로 프로젝트를 마이그레이션할 때 문제가 발생하면 아래의 해결 방법을 참고합니다. Rails 콘솔에서 JID를 사용해 가져오기 상태 정보와 추가 로그를 확인합니다: 내보낸 프로젝트와 가져오는 프로젝트 사이에 인스턴스 러너 활성화 여부가 일치하지 않으면 프로젝트 가져오기가 실패합니다.
파일 내보내기로 프로젝트를 마이그레이션할 때 문제가 발생하면 아래의 해결 방법을 참고합니다.
문제 해결 명령#
Rails 콘솔에서 JID를 사용해 가져오기 상태 정보와 추가 로그를 확인합니다:
Project.find_by_full_path('group/project').import_state.slice(:jid, :status, :last_error)
> {"jid"=>"414dec93f941a593ea1a6894", "status"=>"finished", "last_error"=>nil}
# Logs
grep JID /var/log/gitlab/sidekiq/current
grep "Import/Export error" /var/log/gitlab/sidekiq/current
grep "Import/Export backtrace" /var/log/gitlab/sidekiq/current
tail /var/log/gitlab/gitlab-rails/importer.log
불일치로 인한 프로젝트 가져오기 실패#
내보낸 프로젝트와 가져오는 프로젝트 사이에 인스턴스 러너 활성화 여부가 일치하지 않으면 프로젝트 가져오기가 실패합니다. 이슈 276930을 확인하고 다음 중 하나를 수행합니다:
- 소스 프로젝트와 대상 프로젝트 모두에서 인스턴스 러너를 활성화합니다.
- 프로젝트를 가져올 때 상위 그룹에서 인스턴스 러너를 비활성화합니다.
가져온 프로젝트에서 사용자 누락#
가져온 프로젝트와 함께 사용자가 가져와지지 않으면 사용자 기여 보존 요구 사항을 참고합니다.
사용자가 누락되는 흔한 원인은 사용자의 공개 이메일 설정이 구성되지 않은 경우입니다. 이 문제를 해결하려면 사용자에게 GitLab UI에서 이 설정을 구성하도록 안내합니다.
수동으로 구성하기에는 사용자가 너무 많다면 모든 사용자 프로필이 공개 이메일 주소를 사용하도록 Rails 콘솔에서 설정할 수 있습니다:
User.where("public_email IS NULL OR public_email = '' ").find_each do |u|
next if u.bot?
puts "Setting #{u.username}'s currently empty public email to #{u.email}…"
u.public_email = u.email
u.save!
end
대용량 리포지터리에 대한 가져오기 우회 방법#
최대 가져오기 크기 제한 때문에 가져오기가 성공하지 못할 수 있습니다. 가져오기 제한을 변경할 수 없다면 여기에 정리된 우회 방법 중 하나를 시도합니다.
우회 방법 옵션 1#
다음 로컬 워크플로로 리포지터리 크기를 일시적으로 줄인 뒤 가져오기를 다시 시도할 수 있습니다:
-
내보내기 파일에서 임시 작업 디렉터리를 만듭니다:
EXPORT=<filename-without-extension> mkdir "$EXPORT" tar -xf "$EXPORT".tar.gz --directory="$EXPORT"/ cd "$EXPORT"/ git clone project.bundle # Prevent interference with recreating an importable file later mv project.bundle ../"$EXPORT"-original.bundle mv ../"$EXPORT".tar.gz ../"$EXPORT"-original.tar.gz git switch --create smaller-tmp-main -
리포지터리 크기를 줄이려면 이
smaller-tmp-main브랜치에서 작업합니다. 대용량 파일을 식별해 제거하거나 대화형 리베이스와 fixup으로 커밋 수를 줄입니다.# Reduce the .git/objects/pack/ file size cd project git reflog expire --expire=now --all git gc --prune=now --aggressive # Prepare recreating an importable file git bundle create ../project.bundle <default-branch-name> cd .. mv project/ ../"$EXPORT"-project cd .. # Recreate an importable file tar -czf "$EXPORT"-smaller.tar.gz --directory="$EXPORT"/ . -
새로 만든 더 작은 파일을 GitLab으로 가져옵니다.
-
원본 리포지터리의 전체 클론에서
git remote set-url origin <new-url> && git push --force --all을 사용해 가져오기를 완료합니다. -
가져온 리포지터리의 브랜치 보호 규칙과 기본 브랜치를 업데이트한 다음, 임시
smaller-tmp-main브랜치와 로컬 임시 데이터를 삭제합니다.
우회 방법 옵션 2#
이 우회 방법은 LFS 객체를 고려하지 않습니다.
이 우회 방법은 모든 변경 사항을 한 번에 푸시하는 대신 다음을 수행합니다:
- 프로젝트 가져오기와 Git 리포지터리 가져오기를 분리합니다
- 리포지터리를 GitLab에 점진적으로 푸시합니다
-
마이그레이션할 리포지터리의 로컬 클론을 만듭니다. 이후 단계에서 이 클론을 프로젝트 내보내기와 별도로 푸시합니다.
-
내보내기 파일을 내려받고 Git 리포지터리가 들어 있는
project.bundle을 제거합니다:tar -czvf new_export.tar.gz --exclude='project.bundle' @old_export.tar.gz -
Git 리포지터리 없이 내보내기 파일을 가져옵니다. 리포지터리 없이 가져올 것인지 확인하는 메시지가 표시됩니다.
-
다음 bash 스크립트를 파일로 저장하고 적절한 origin을 추가한 뒤 실행합니다.
#!/bin/sh # ASSUMPTIONS: # - The GitLab location is "origin" # - The default branch is "main" # - This will attempt to push in chunks of 500 MB (dividing the total size by 500 MB). # Decrease this size to push in smaller chunks if you still receive timeouts. git gc SIZE=$(git count-objects -v 2> /dev/null | grep size-pack | awk '{print $2}') # Be conservative and try to push 2 GB at a time # (given this assumes each commit is the same size - which is wrong) BATCHES=$(($SIZE / 500000)) TOTAL_COMMITS=$(git rev-list --count HEAD) if (( BATCHES > TOTAL_COMMITS )); then BATCHES=$TOTAL_COMMITS fi INCREMENTS=$(( ($TOTAL_COMMITS / $BATCHES) - 1 )) for (( BATCH=BATCHES; BATCH>=1; BATCH-- )) do COMMIT_NUM=$(( $BATCH - $INCREMENTS )) COMMIT_SHA=$(git log -n $COMMIT_NUM --format=format:%H | tail -1) git push -u origin ${COMMIT_SHA}:refs/heads/main done git push -u origin main git push -u origin --all git push -u origin --tags
오류: 프로젝트를 가져올 때 HTTP 524 A timeout occurred 발생#
GitLab.com에서는 프로젝트 가져오기가 HTTP 524 A timeout occurred 오류로 실패할 수 있습니다.
이 오류는 크기가 수 기가바이트인 아카이브에서 발생할 수 있습니다.
각 업로드는 제한 시간 안에 끝나야 합니다. 대용량 아카이브는 이 제한을 초과할 수 있고, 그러면 업로드가 끝나기 전에 연결이 닫힙니다.
이 오류를 피하려면 아카이브를 HTTPS 위치(예: AWS S3 버킷)에 두고 가져오기 중에 GitLab 이 내려받도록 합니다. 다음 엔드포인트 중 하나를 사용합니다:
POST /api/v4/projects/remote-import은 S3 사전 서명 URL을 포함한 모든 HTTPS URL에서 동작합니다.POST /api/v4/projects/remote-import-s3은 자격 증명을 사용해 AWS S3에서 동작합니다.
GitLab 이 아카이브를 백그라운드에서 내려받으므로 아카이브 크기 때문에 시간 초과가 발생하지 않습니다.
Sidekiq 프로세스가 프로젝트를 내보내지 못하는 경우#
드물게 Sidekiq 프로세스가 프로젝트 내보내기에 실패하는 경우가 있습니다. 예를 들어 실행 중에 종료된 경우입니다.
GitLab.com 사용자는 이 문제를 해결하려면 지원에 문의합니다.
GitLab Self-Managed 관리자는 Rails 콘솔에서 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)
내보내기 단계 수동 실행#
프로젝트는 보통 웹 인터페이스나 프로젝트 가져오기 및 내보내기 API로 내보냅니다. 이 방법으로 내보내면 문제 해결에 필요한 정보 없이 실패하는 경우가 있습니다. 이럴 때는 Rails 콘솔 세션을 엽니다. 그리고 정의된 모든 내보내기 도구를 차례로 실행합니다. 블록 전체를 한 번에 붙여 넣지 말고 한 줄씩 실행해 각 명령이 반환하는 오류를 확인합니다.
# User needs to have permission to export
u = User.find_by_username('someuser')
p = Project.find_by_full_path('some/project')
e = Projects::ImportExport::ExportService.new(p,u)
e.send(:version_saver).send(:save)
e.send(:repo_saver).send(:save)
e.send(:avatar_saver).send(:save)
e.send(:project_tree_saver).send(:save)
e.send(:uploads_saver).send(:save)
e.send(:wiki_repo_saver).send(:save)
e.send(:lfs_saver).send(:save)
e.send(:snippets_repo_saver).send(:save)
e.send(:design_repo_saver).send(:save)
## continue using `e.send(:exporter_name).send(:save)` going through the list of exporters
# The following line should show you the export_path similar to /var/opt/gitlab/gitlab-rails/shared/tmp/gitlab_exports/@hashed/49/94/4994....
s = Gitlab::ImportExport::Saver.new(exportable: p, shared: p.import_export_shared, user: u)
# Prior to GitLab 17.0, the `user` parameter was not supported. If you encounter an
# error with the above or are unsure whether or not to supply the `user`
# argument, use the following check:
Gitlab::ImportExport::Saver.instance_method(:initialize).parameters.include?([:keyreq, :user])
# If the preceding check returns false, omit the user argument:
s = Gitlab::ImportExport::Saver.new(exportable: p, shared: p.import_export_shared)
# To try and upload use:
s.send(:compress_and_save)
s.send(:save_upload)
프로젝트가 정상적으로 업로드되면 내보낸 프로젝트는 /var/opt/gitlab/gitlab-rails/uploads/-/system/import_export_upload/export_file/의 .tar.gz 파일에 있습니다.
오류: PG::QueryCanceled: ERROR: canceling statement due to statement timeout#
일부 마이그레이션은 PG::QueryCanceled: ERROR: canceling statement due to statement timeout 오류로 시간 초과될 수 있습니다.
이 문제를 피하는 한 가지 방법은 마이그레이션 배치 크기를 줄이는 것입니다. 배치 크기를 줄이면 시간 초과 가능성은 낮아지지만
마이그레이션 속도는 느려집니다.
배치 크기를 줄이려면 기능 플래그가 활성화되어 있어야 합니다. 자세한 내용은 이슈 456948을 참고합니다.
오류: JSON exceeds 50 MB limit#
파일 내보내기로 프로젝트를 가져올 때 다음 오류가 발생할 수 있습니다:
JSON exceeds 50 MB limit
이 오류는 설명이나 diff가 큰 머지 리퀘스트 또는 이슈처럼 내보내기 안의 단일 레코드가 가져오기가 레코드마다 적용하는 50MB 제한을 초과할 때 발생합니다.
이 오류를 해결하려면 소스 프로젝트에서 해당 레코드의 크기를 줄인 다음 프로젝트를 다시 내보내고 가져옵니다.
오류: command exited with error code 15 and Unable to save [FILTERED] into [FILTERED]#
파일 내보내기로 프로젝트를 마이그레이션할 때 로그에 다음 오류가 나타날 수 있습니다:
command exited with error code 15 and Unable to save [FILTERED] into [FILTERED]
이 오류는 내보내기 또는 가져오기 중에 Sidekiq가 SIGTERM을 받을 때 발생하며, 주로 tar 명령을 실행하는 동안 일어납니다.
GitLab.com 및 GitLab Dedicated 같은 Kubernetes 환경에서는 메모리 또는 디스크 부족, 코드 배포, 인스턴스 업그레이드 때문에
운영체제가 SIGTERM 신호를 발생시킵니다.
근본 원인을 파악하려면 관리자가 Kubernetes가 인스턴스를 종료한 이유를 조사해야 합니다.
Kubernetes가 아닌 환경에서는 tar 명령을 실행하는 동안 인스턴스가 종료되면 이 오류가 발생할 수 있습니다.
다만 이 오류는 디스크 부족 때문에 발생하지는 않으므로 메모리 부족이 가장 유력한 원인입니다.
이 오류가 발생하면 다음과 같이 동작합니다:
- 파일을 내보낼 때는 GitLab 이 최대 재시도 횟수에 도달할 때까지 내보내기를 재시도한 뒤 내보내기를 실패로 표시합니다. GitLab.com에서는 인스턴스 부하가 적은 주말에 내보내기를 시도합니다.
- 파일을 가져올 때는 직접 가져오기를 재시도해야 합니다. GitLab은 가져오기를 자동으로 재시도하지 않습니다.
성능 문제 해결#
아래에서 Import/Export 사용 시 현재 알려진 성능 문제를 확인합니다.
OOM 오류#
메모리 부족(OOM) 오류는 대개 Sidekiq memory killer 때문에 발생합니다:
SIDEKIQ_MEMORY_KILLER_MAX_RSS = 2000000
SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS = 3000000
SIDEKIQ_MEMORY_KILLER_GRACE_TIME = 900
가져오기 상태가 started 이고 다음 Sidekiq 로그가 나타나면 메모리 문제를 뜻합니다:
WARN: Work still in progress <struct with JID>
시간 초과#
시간 초과 오류는 Gitlab::Import::StuckProjectImportJobsWorker가 프로세스를 실패로 표시하기 때문에 발생합니다:
module Gitlab
module Import
class StuckProjectImportJobsWorker
include Gitlab::Import::StuckImportJob
# ...
end
end
end
module Gitlab
module Import
module StuckImportJob
# ...
IMPORT_JOBS_EXPIRATION = 15.hours.to_i
# ...
def perform
stuck_imports_without_jid_count = mark_imports_without_jid_as_failed!
stuck_imports_with_jid_count = mark_imports_with_jid_as_failed!
track_metrics(stuck_imports_with_jid_count, stuck_imports_without_jid_count)
end
# ...
end
end
end
Marked stuck import jobs as failed. JIDs: xyz
+-----------+ +-----------------------------------+
|Export Job |--->| Calls ActiveRecord `as_json` and |
+-----------+ | `to_json` on all project models |
+-----------------------------------+
+-----------+ +-----------------------------------+
|Import Job |--->| Loads all JSON in memory, then |
+-----------+ | inserts into the DB in batches |
+-----------------------------------+
문제 및 해결 방법#
데이터베이스에서 모델을 로드하거나 덤프할 때의 느린 JSON 처리:
- 워커 분리
- 배치 내보내기
- SQL 최적화
ActiveRecord콜백에서 벗어나기(난이도 높음)
높은 메모리 사용량(분석 참고):
- 메모리를 덜 사용하는 DB 커밋 최적 지점
- Netflix Fast JSON API 활용 검토
- 디스크 및 SQL에 대한 배치 읽기/쓰기