文件导出项目迁移故障排除
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
如果您在使用文件导出迁移项目时遇到问题,请参考以下可能的解决方案。
故障排除命令
使用 JID 查找导入状态和更多日志信息,使用 Rails 控制台:
Project.find_by_full_path('group/project').import_state.slice(:jid, :status, :last_error)
> {"jid"=>"414dec93f941a593ea1a6894", "status"=>"finished", "last_error"=>nil}# 日志
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 "将 #{u.username} 当前为空的公开邮箱设置为 #{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 # 防止稍后重新创建可导入文件时产生干扰 mv project.bundle ../"$EXPORT"-original.bundle mv ../"$EXPORT".tar.gz ../"$EXPORT"-original.tar.gz git switch --create smaller-tmp-main -
为减少仓库大小,在此
smaller-tmp-main分支上工作: 识别并删除大文件 或交互式变基和修复 以减少提交次数。# 减少 .git/objects/pack/ 文件大小 cd project git reflog expire --expire=now --all git gc --prune=now --aggressive # 准备重新创建可导入文件 git bundle create ../project.bundle <default-branch-name> cd .. mv project/ ../"$EXPORT"-project cd .. # 重新创建可导入文件 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
-
迁移仓库的本地克隆。在后续步骤中,您将在项目导出之外推送此克隆。
-
下载导出文件并删除
project.bundle(包含 Git 仓库):tar -czvf new_export.tar.gz --exclude='project.bundle' @old_export.tar.gz -
导入没有 Git 仓库的导出文件。它会要求您确认导入没有仓库。
-
将此 bash 脚本保存为文件,并在添加适当的 origin 后运行它。
#!/bin/sh # 假设: # - GitLab 位置是 "origin" # - 默认分支是 "main" # - 这将尝试以 500 MB 的块大小推送(将总大小除以 500 MB)。 # 如果仍然超时,减小此大小以推送更小的块。 git gc SIZE=$(git count-objects -v 2> /dev/null | grep size-pack | awk '{print $2}') # 保持保守,尝试一次推送 2 GB # (假设每个提交大小相同 - 这是错误的) 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
Sidekiq 进程无法导出项目
偶尔 Sidekiq 进程可能无法导出项目,例如 如果在执行过程中被终止。
GitLab.com 用户应联系支持团队解决此问题。
GitLab 自托管管理员可以使用 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)手动执行导出步骤
您通常通过Web 界面或API导出项目。 使用这些方法导出有时可能会失败,且没有提供足够的信息进行故障排除。 在这些情况下,打开 Rails 控制台会话并循环遍历 所有定义的导出器。 单独执行每一行,而不是一次性粘贴整个代码块,这样您可以看到每个命令返回的任何错误。
# 用户需要具有导出权限
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)
## 继续使用 `e.send(:exporter_name).send(:save)` 遍历导出器列表
# 以下行应显示类似 /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)
# 在 GitLab 17.0 之前,不支持 `user` 参数。如果遇到上述错误或不确定是否要提供 `user`
# 参数,请使用以下检查:
Gitlab::ImportExport::Saver.instance_method(:initialize).parameters.include?([:keyreq, :user])
# 如果上述检查返回 false,请省略 user 参数:
s = Gitlab::ImportExport::Saver.new(exportable: p, shared: p.import_export_shared)
# 尝试上传:
s.send(:compress_and_save)
s.send(:save_upload)项目成功上传后,导出的项目位于 /var/opt/gitlab/gitlab-rails/uploads/-/system/import_export_upload/export_file/ 中的 .tar.gz 文件中。
使用组访问令牌时通过 REST API 导入失败
组访问令牌 不适用于项目或组导入操作。当组访问令牌发起导入时, 导入失败并显示以下消息:
Error adding importer user to Project members.
Validation failed: User project bots cannot be added to other groups / projects要使用导入 REST API, 请传递常规用户帐户凭据,如个人访问令牌。
错误:PG::QueryCanceled: ERROR: canceling statement due to statement timeout
某些迁移可能会因以下错误而超时:PG::QueryCanceled: ERROR: canceling statement due to statement timeout。
避免此问题的一种方法是减少迁移批处理大小。这使迁移不太可能超时,
但会使迁移变慢。
要减少批处理大小,您必须启用一个功能标志。有关更多信息,请参阅 问题 456948。
错误: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 不会自动重试导入。
性能问题故障排除
阅读以下导入/导出相关的当前性能问题。
OOM 错误
内存不足(OOM)错误通常由Sidekiq 内存杀手引起:
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
endMarked stuck import jobs as failed. JIDs: xyz +-----------+ +-----------------------------------+
|Export Job |--->| 对所有项目模型调用 ActiveRecord |
+-----------+ | `as_json` 和 `to_json` |
+-----------------------------------+
+-----------+ +-----------------------------------+
|Import Job |--->| 将所有 JSON 加载到内存中,然后 |
+-----------+ | 分批插入数据库 |
+-----------------------------------+问题和解决方案
慢速 JSON 从数据库加载/转储模型:
- 拆分工作器 |
- 批量导出
- 优化 SQL
- 远离
ActiveRecord回调(困难)
高内存使用(另请参阅一些分析):
- DB 提交最佳实践,使用更少内存
- Netflix Fast JSON API 可能有所帮助
- 批量读写磁盘和任何 SQL