通过文件导出迁移项目的故障排除

如果您在使用通过文件导出迁移项目时遇到问题,请参阅以下可能的解决方案。

故障排除命令

使用 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

由于不匹配导致项目无法导入

如果导出的项目和项目导入之间的实例运行器启用设置不匹配,项目将无法导入。并采取以下操作之一:

  • 确保源项目和目标项目中的实例运行器都已启用。
  • 导入项目时,禁用父组中的实例运行器。

导入项目中缺少用户

如果用户没有与导入的项目一起导入,请查看保存用户贡献要求。

缺少用户的常见原因是用户的公共邮箱设置未配置。 要解决此问题,请要求用户使用极狐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

以下本地工作流程可用于临时减少仓库大小以进行另一次导入尝试:

  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
    
  2. 要减小仓库大小,请处理这个 smaller-tmp-main 分支:识别和删除大文件交互式变基和修复,来减少提交次数。

    # 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"/ .
    
  3. 将这个新的、较小的文件导入极狐GitLab。
  4. 在原始仓库的完整克隆中,使用 git remote set-url origin <new-url> && git push --force --all 完成导入。
  5. 更新导入的仓库的分支保护规则及其默认分支,并删除临时的 smaller-tmp-main 分支和本地的临时数据。

解决方法 2

note 此解决方法不考虑LFS对象。

此解决方法不是尝试一次推送所有更改,而是:

  • 将项目导入与Git仓库导入分开
  • 将仓库增量推送到极狐GitLab
  1. 对要迁移的仓库进行本地克隆。在后面的步骤中,您将此克隆推送到项目导出之外。
  2. 下载导出并删除 project.bundle(其中包含 Git 仓库):

    tar -czvf new_export.tar.gz --exclude='project.bundle' @old_export.tar.gz
    
  3. 导入没有 Git 仓库的导出,要求您确认在没有仓库的情况下导入。
  4. 将此 bash 脚本保存为文件,并在添加适当的源后运行它。

    #!/bin/sh
       
    # ASSUMPTIONS:
    # - The GitLab location is "origin"
    # - The default branch is "main"
    # - This will attempt to push in chunks of 500MB (dividing the total size by 500MB).
    #   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 2GB 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
    

Sidekiq进程导出项目失败

有时Sidekiq进程可能会失败导出项目,例如,如果在执行过程中它被终止。

极狐saaa用户应联系支持以解决此问题。

自管理用户可以使用 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控制台会话

单独执行每一行,而不是一次粘贴整个代码块,这样您就可以看到每个命令返回的任何错误。

# 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 文件中。

使用群组访问令牌时通过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 而超时。 避免此问题的一种方法是减少迁移的批量大小。这样可以减少迁移超时的可能性,但会使迁移变得更慢。

错误: 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]。如果您遇到此错误:

  • 当导出文件时,您可以安全地忽略此错误。极狐GitLab会重新尝试退出的命令。
  • 当导入文件时,您必须手动重试导入。极狐GitLab不会自动重试导入。

性能问题故障排除

通读下文的当前导入/导出的性能问题。

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    |
                   +-----------------------------------+