ファイルエクスポート・プロジェクト・マイグレーションのトラブルシューティング

ファイルエクスポートを使用したプロジェクトのマイグレーションで問題が発生した場合は、以下の解決策を参照してください。

トラブルシューティングコマンド

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

次のローカルワークフローを使用して、インポートを再度試行するためにリポジトリサイズを一時的に小さくすることができます:

  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
    

エクスポート手順の手動実行

通常、プロジェクトのエクスポートは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)

# 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 を使用するには、個人アクセストークンなど、通常のユーザーアカウントの認証情報を渡してください。

パフォーマンスに関する問題のトラブルシューティング

以下の Exporter/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

一時的ソリューション

パフォーマンスの問題は解決されていませんが、フォアグラウンドインポートを使用して、大きなプロジェクトのインポートを回避する方法があります:

顧客向け大型プロジェクトのフォアグラウンド・インポート(インフラストラッカーのインポートテンプレートを使用)。