Geoの同期と検証のエラーのトラブルシューティング
- プラン: Premium、Ultimate
- 提供形態: GitLab Self-Managed
Admin > Geo > Sitesまたは同期ステータスのRakeタスクでレプリケーションまたは検証の失敗に気付いた場合は、次の一般的な手順で失敗を解決できます:
- Geoは、失敗した処理を自動的に再試行します。もし失敗が新たに発生したもので数が少ない場合、または根本原因がすでに解決されたと考えられる場合は、そのまま様子を見て問題が収まるか確認することができます。
- 失敗が長期間発生していた場合、すでに多数の再試行が行われており、失敗の種類によっては自動再試行の間隔が最大で4時間まで延びている可能性があります。根本原因がすでに解決されたと考えられる場合は、待たずに手動でレプリケーションまたは検証を再試行することもできます。
- 失敗が解決しない場合は、以下のセクションを参照し、問題の解決を試みてください。
トラブルシューティングの手順
手動での再試行を行う前に、以下の拡張診断手順を使用して、同期問題のスコープや性質をより正確に把握することができます。
モデルのステータスチェック
本手順では、Geoデータ型のすべてのモデルクラスについて詳細なステータス情報を提供し、チェックサムの不整合を特定するのに役立ちます。これらの不整合は、レプリケーション対象オブジェクトのチェックサムを計算できなかった場合に発生します。また、これらは「プライマリ検証の失敗」と呼ばれることもあります。
チェックサムのエラーは、UIまたはRailsコンソールから表示できます。
プライマリサイトで、データ管理ページを使用してください。
次のスクリプトを使用すると、次のものを含むモデル型ごとの詳細情報を出力できます:
- レコードの総数
- 失敗、検証済み、および保留中のレコード数
- 調査用のサンプル失敗レコード
プライマリサイトで、Railsコンソールセッションを開始してください。
包括的な概要を取得するには、次のスクリプトを実行します:
def output_geo_verification_failures model_classes = ::Gitlab::Geo::ModelMapper.available_models model_classes.each do |klass| total = klass.count state_klass = klass.verification_state_table_class failed_examples = [] puts "\n=== #{klass.name} ===" puts "Total: #{total}" ::Geo::VerificationState::VERIFICATION_STATE_VALUES.each do |key, value| records = state_klass.where(verification_state: value) failed_examples = records if key == 'verification_failed' puts "#{key.gsub('verification_', '').camelize}: #{records.size}" end if failed_examples.any? puts "\nSample failed records:" failed_examples.limit(3).each { |record| puts " ID: #{record.id}, Checksum: #{record.verification_checksum || 'nil'}, Error: #{record.verification_failure}" } end end nil end output_geo_verification_failures
レジストリステータスチェック
この手順では、すべてのGeoレジストリタイプに関する詳細なステータス情報が提供され、失敗のパターンを特定できます。
セカンダリサイトでRailsコンソールセッションを開始します。
包括的な概要を取得するには、次のスクリプトを実行します:
def output_geo_failures() registry_classes = [ Geo::UploadRegistry, Geo::JobArtifactRegistry, Geo::PackageFileRegistry, Geo::PagesDeploymentRegistry, Geo::ProjectRepositoryRegistry, Geo::TerraformStateVersionRegistry, Geo::MergeRequestDiffRegistry, Geo::LfsObjectRegistry, Geo::PipelineArtifactRegistry, Geo::CiSecureFileRegistry, Geo::ContainerRepositoryRegistry ] registry_classes.each do |klass| puts "\n=== #{klass.name} ===" puts "Total: #{klass.count}" puts "Failed: #{klass.failed.count}" puts "Synced: #{klass.synced.count}" puts "Pending: #{klass.pending.count}" puts "Started: #{klass.with_state(:started).count}" if klass.failed.count > 0 puts "\nSample failed records:" klass.failed.limit(3).each { |record| puts " ID: #{record.id}, Error: #{record.last_sync_failure}" } end end nil end output_geo_failures()このスクリプトは、各レジストリタイプに関する詳細情報を出力します。以下を含みます:
- レコードの総数
- 失敗、同期、および保留中のレコードの数
- 調査用のサンプル失敗レコード
レプリケーションまたは検証を手動で再試行
RailsコンソールでセカンダリGeoサイトにいる場合は、以下を実行できます:
個々のコンポーネントの再同期と再検証
セカンダリサイトで、管理者 > Geo > レプリケーションにアクセスして、個々のアイテムの再同期または再検証を強制します。
ただし、これでうまくいかない場合は、Railsコンソールを使用して同じアクションを実行できます。次のセクションでは、Railsコンソールで内部アプリケーションコマンドを使用して、個々のレコードのレプリケーションまたは検証を同期的または非同期的に行う方法について説明します。
Replicatorインスタンスの取得
同期または検証操作を実行する前に、Replicatorインスタンスを取得する必要があります。
まず、実行する内容に応じて、プライマリまたはセカンダリサイトでRailsコンソールセッションを開始します。
プライマリサイト:
- リソースをチェックサムできます
セカンダリサイト:
- リソースを同期できます
- リソースをチェックサムし、そのチェックサムをプライマリサイトのチェックサムと照合して検証できます
次に、次のスニペットのいずれかを実行して、Replicatorインスタンスを取得します。
モデルレコードのIDを指定した場合
123を実際のIDに置き換えます。Packages::PackageFileをGeoデータ型モデルクラスのいずれかに置き換えます。
model_record = Packages::PackageFile.find_by(id: 123)
replicator = model_record.replicatorレジストリレコードのIDを指定した場合
432を実際のIDに置き換えます。レジストリレコードは、追跡するModelレコードと同じID値を持つ場合と持たない場合があります。Geo::PackageFileRegistryをGeoレジストリクラスのいずれかに置き換えます。
セカンダリGeoサイトの場合:
registry_record = Geo::PackageFileRegistry.find_by(id: 432)
replicator = registry_record.replicatorレジストリレコードのlast_sync_failureのエラーメッセージを指定した場合
Geo::PackageFileRegistryをGeoレジストリクラスのいずれかに置き換えます。error message hereを実際のエラーメッセージに置き換えます。
registry = Geo::PackageFileRegistry.find_by("last_sync_failure LIKE '%error message here%'")
replicator = registry.replicatorレジストリレコードのverification_failureのエラーメッセージを指定した場合
Geo::PackageFileRegistryをGeoレジストリクラスのいずれかに置き換えます。error message hereを実際のエラーメッセージに置き換えます。
registry = Geo::PackageFileRegistry.find_by("verification_failure LIKE '%error message here%'")
replicator = registry.replicatorReplicatorインスタンスを使用した操作の実行
replicator変数に格納されているReplicatorインスタンスがある場合は、多くの操作を実行できます:
コンソールでの同期
このスニペットは、セカンダリサイトでのみ機能します。
このコマンドはコンソール上で同期コードを同期的に実行します。そのため、リソースの同期にかかる時間を確認したり、エラーの完全なバックトレースを表示したりすることができます。
replicator.sync必要に応じて、コンソールのログレベルを設定済みのレベルより詳細に変更し、その後で同期処理を実行してください:
Rails.logger.level = :debugコンソールでのチェックサムまたは検証
このスニペットは、プライマリまたはセカンダリサイトで機能します。
プライマリサイトでは、リソースをチェックサムし、結果をメインのGitLabデータベースに保存します。セカンダリサイトでは、リソースをチェックサムし、メインのGitLabデータベース(プライマリサイトによって生成される)のチェックサムと照合して、結果をGeoトラッキングデータベースに保存します。
これにより、コンソールでチェックサムと検証コードが同期的に実行されるため、かかる時間を確認したり、完全なエラーバックトレースを表示したりできます。
replicator.verifySidekiqジョブでの同期
このスニペットは、セカンダリサイトでのみ機能します。
このコマンドは、Sidekiqにジョブをキューイングし、リソースの同期処理を実行させます。
replicator.enqueue_syncSidekiqジョブでの検証
このスニペットは、プライマリまたはセカンダリサイトで機能します。
Sidekiqがリソースのチェックサムまたは検証を実行するためのジョブをキューイングします。
replicator.verify_asyncモデルレコードの取得
このスニペットは、プライマリまたはセカンダリサイトで機能します。
replicator.model_recordレジストリレコードの取得
このスニペットは、セカンダリサイトでのみ機能します。これは、レジストリテーブルがGeoトラッキングDBに格納されているためです。
replicator.registryGeoデータ型モデルクラス
Geoデータ型は、関連データを格納するために1つ以上のGitLab機能に必要なデータの特定のクラスであり、Geoによってセカンダリサイトにレプリケートされます。
- Blob型:
Ci::JobArtifactCi::PipelineArtifactCi::SecureFileLfsObjectMergeRequestDiffPackages::PackageFilePagesDeploymentTerraform::StateVersionUploadDependencyProxy::ManifestDependencyProxy::Blob
- Gitリポジトリ型:
DesignManagement::RepositoryProjectRepositoryProjectWikiRepositorySnippetRepositoryGroupWikiRepository
- その他の型:
ContainerRepository
主なクラスの種類は、レジストリ、モデル、およびReplicatorです。これらのクラスのいずれかのインスタンスがある場合は、他のインスタンスを取得できます。レジストリとモデルは、主にPostgreSQL DBの状態を管理します。Replicatorは、PostgreSQL以外のデータ(ファイル/Gitリポジトリ/コンテナリポジトリ)をレプリケートまたは検証する方法を認識しています。
Geoレジストリクラス
GitLab Geoのコンテキストでは、レジストリレコードとは、Geoトラッキングデータベース内のレジストリテーブルを指します。各レコードは、LFSファイルやプロジェクトのGitリポジトリなど、メインのGitLabデータベース内の単一のレプリケート可能なファイルを追跡します。クエリできるGeoレジストリテーブルに対応するRailsモデルは次のとおりです:
- Blob型:
Geo::CiSecureFileRegistryGeo::DependencyProxyBlobRegistryGeo::DependencyProxyManifestRegistryGeo::JobArtifactRegistryGeo::LfsObjectRegistryGeo::MergeRequestDiffRegistryGeo::PackageFileRegistryGeo::PagesDeploymentRegistryGeo::PipelineArtifactRegistryGeo::ProjectWikiRepositoryRegistryGeo::SnippetRepositoryRegistryGeo::TerraformStateVersionRegistryGeo::UploadRegistry
- Gitリポジトリ型:
Geo::DesignManagementRepositoryRegistryGeo::ProjectRepositoryRegistryGeo::ProjectWikiRepositoryRegistryGeo::SnippetRepositoryRegistryGeo::GroupWikiRepositoryRegistry
- その他の型:
Geo::ContainerRepositoryRegistry
複数のコンポーネントを再度同期して再検証する
コンポーネントリソースの同期や検証に失敗した場合、一括アクションを実行してレプリケーションキューを再起動することができます。これらのアクションは再試行回数とスケジュール時間を0にリセットし、最大1時間待たずに失敗したリソースを優先的に処理させます。
再同期と再検証の仕組み
再同期または再検証アクションをトリガーすると、システムは一致するレコードをpendingとしてマークします。Geoの再同期と再検証のバックグラウンドワーカーは、これらのレコードを取得し、通常のキューの優先度に従って処理します。このメカニズムを使用すると、操作ですぐにブロックせずに、失敗したリソースの処理を迅速化できます。
UIまたはRailsコンソールから一括アクションをトリガーできます。
UIから
UIから1つのコンポーネントのすべてのリソースの完全な再同期をスケジュールできます:
- 右上隅で、管理者を選択します。
- 右上隅で、Geo > サイトを選択します。
- レプリケーションの詳細で、目的のコンポーネントを選択します。
選択したコンポーネントのリソースを再同期する
- すべて再同期を選択: これにより、選択したリソースのすべてのレコードの状態が、既に同期されているかどうかに関係なくリセットされます。
- すべての再同期に失敗しましたを選択: これにより、同期に失敗したすべてのレコードがリセットされます。
選択したコンポーネントのリソースを再検証する
- すべて再検証を選択: これにより、選択したリソースのすべてのレコードの状態が、既に検証されているかどうかに関係なくリセットされます。
- すべての失敗を再検証を選択: これにより、検証に失敗したが、同期が成功したすべてのレコードがリセットされます。
すべてのサイトで1つのコンポーネントを再検証する
プライマリサイトのチェックサムに疑問がある場合は、プライマリサイトにチェックサムを再計算させる必要があります。プライマリサイトで各チェックサムが再計算された後、すべてのセカンダリサイトに伝播されるイベントが生成され、チェックサムが再計算され、値が比較されるため、「完全な再検証」が実現されます。不一致があると、レジストリがsync failedとしてマークされ、同期の再試行がスケジュールされます。
UIからプライマリサイトのチェックサムを再計算できます:
- 右上隅で、管理者を選択します。
- モニタリング > データ管理を選択します。
- ドロップダウンリストで目的のコンポーネントを選択します。
- すべてのチェックサムを選択します。
Railsコンソールから
次のセクションでは、Railsコンソールで内部アプリケーションコマンドを使用して、一括レプリケーションまたは検証を行う方法について説明します。
同期に失敗した1つのコンポーネントのすべてのリソースを同期する
次のスクリプトは、以下を行います:
- 失敗したすべてのリポジトリをループ処理します。
- 最後の失敗の理由を含む、Geoの同期と検証のメタデータを表示します。
- リポジトリの再同期を試みます。
- 失敗が発生した場合、およびその理由を報告します。
- 完了するまでに時間がかかる場合があります。各リポジトリチェックは、結果を報告する前に完了する必要があります。セッションがタイムアウトした場合は、
screenセッションを開始するか、Railsランナーとnohupを使用して実行するなど、プロセスが実行され続けるように対策を講じてください。
このスクリプトをセカンダリGeoサイトで実行します。
Geo::ProjectRepositoryRegistry.failed.find_each do |registry|
begin
puts "ID: #{registry.id}, Project ID: #{registry.project_id}, Last Sync Failure: '#{registry.last_sync_failure}'"
registry.replicator.sync
puts "Sync initiated for registry ID: #{registry.id}"
rescue => e
puts "ID: #{registry.id}, Project ID: #{registry.project_id}, Failed: '#{e}'", e.backtrace.join("\n")
end
end; nilプライマリサイトでチェックサムに失敗したすべてのリソースを再検証する
システムは、プライマリサイトでチェックサムに失敗したすべてのリソースを自動的に再検証します。ただし、過剰な失敗発生を防ぐために段階的なバックオフ方式を採用しています。
たとえば試行された介入を完了した場合は、オプションでより早く手動で再検証をトリガーできます:
プライマリサイトのGitLab RailsノードにSSH接続します。
Railsコンソールを開きます。
UploadをGeoデータ型モデルクラスのいずれかに置き換えて、すべてのリソースをpending verificationとしてマークします:Upload.verification_state_table_class.where(verification_state: 3).each_batch do |relation| relation.update_all(verification_state: 0) end
エラー
メッセージ: The file is missing on the Geo primary site
同期エラーThe file is missing on the Geo primary siteは、セカンダリGeoサイトを初めてセットアップする際によく発生する問題です。このエラーは、プライマリサイト上のデータ不整合が原因で発生します。
GitLabの運用中に、システムエラーまたは人為的エラーが原因で、データの不整合やファイルの欠落が発生する可能性があります。たとえば、インスタンス管理者がローカルファイルシステムの複数のアーティファクトを手動で削除したとします。このような変更はデータベースに適切に伝播されず、不整合が発生します。これらの不整合は残存し、さまざまな問題を引き起こす可能性があります。これらのファイルはデータベースでまだ参照されているために、Geoセカンダリは引き続きこれらのファイルのレプリケートを試行する可能性がありますが、ファイルは存在していません。
不整合の特定
ファイルの欠落やデータの不整合がある場合、geo.logに次のようなエントリが表示されることがあります。このとき、フィールド"primary_missing_file" : trueに注目してください:
{
"bytes_downloaded" : 0,
"class" : "Geo::BlobDownloadService",
"correlation_id" : "01JT69C1ECRBEMZHA60E5SAX8E",
"download_success" : false,
"download_time_s" : 0.196,
"gitlab_host" : "gitlab.example.com",
"mark_as_synced" : false,
"message" : "Blob download",
"model_record_id" : 55,
"primary_missing_file" : true,
"reason" : "Not Found",
"replicable_name" : "upload",
"severity" : "WARN",
"status_code" : 404,
"time" : "2025-05-01T16:02:44.836Z",
"url" : "http://gitlab.example.com/api/v4/geo/retrieve/upload/55"
}特定のレプリケート可能オブジェクトの同期ステータスを確認すると、同じエラーが管理者 > Geo > サイトのUIにも反映されます。このシナリオでは、特定のアップロードファイルが欠落しています:
不整合をクリーンアップする
これらのエラーを削除するには、まず、どの特定のリソースが影響を受けているかを特定します。次に、適切なdestroyコマンドを実行して、すべてのGeoサイトとそのデータベースに削除が確実に伝播されるようにします。前述のシナリオに基づくと、アップロードがこれらのエラーの原因となっています。以下では、そのアップロードを例として使用します。
特定された不整合を、それぞれのGeoモデルクラス名にマップします。クラス名は、以下のステップで必要になります。このシナリオでは、アップロードの場合、
Uploadに対応します。GeoプライマリサイトでRailsコンソールを開始します。
前の手順のGeoモデルクラスに基づいて、ファイルが見つからないために検証が失敗したすべてのリソースをクエリします。より多くの結果を表示するには、
limit(20)を調整または削除します。リストされたリソースがUIに表示される失敗したリソースと一致することを確認してください:Upload.verification_failed.where("verification_failure like '%File is not checksummable%'").limit(20) => #<Upload:0x00007b362bb6c4e8 id: 55, size: 13346, path: "503d99159e2aa8a3ac23602058cfdf58/openbao.png", checksum: "db29d233de49b25d2085dcd8610bac787070e721baa8dcedba528a292b6e816b", model_id: 1, model_type: "Project", uploader: "FileUploader", created_at: Thu, 01 May 2025 15:54:10.549178000 UTC +00:00, store: 1, mount_point: nil, secret: "[FILTERED]", version: 2, uploaded_by_user_id: 1, organization_id: nil, namespace_id: nil, project_id: 1, verification_checksum: nil>オプションで、影響を受けるリソースの
idを使用して、それらがまだ必要かどうかを判断します:Upload.find(55) => #<Upload:0x00007b362bb6c4e8 id: 55, size: 13346, path: "503d99159e2aa8a3ac23602058cfdf58/openbao.png", checksum: "db29d233de49b25d2085dcd8610bac787070e721baa8dcedba528a292b6e816b", model_id: 1, model_type: "Project", uploader: "FileUploader", created_at: Thu, 01 May 2025 15:54:10.549178000 UTC +00:00, store: 1, mount_point: nil, secret: "[FILTERED]", version: 2, uploaded_by_user_id: 1, organization_id: nil, namespace_id: nil, project_id: 1, verification_checksum: nil>- 影響を受けたリソースを復旧する必要があると判断した場合は、次のような(以下に挙げるものに限らない)いくつかの復旧方法を検討できます:
- セカンダリサイトにオブジェクトがあるかどうかを確認し、手動でプライマリにコピーします。
- 古いバックアップを調べて、オブジェクトを手動でプライマリサイトにコピーして戻します。
- いくつかをスポットチェックして、レコードを削除しても問題なさそうか判断します。たとえば、それらがすべて古いアーティファクトであれば、重要なデータではない可能性があります。
- 影響を受けたリソースを復旧する必要があると判断した場合は、次のような(以下に挙げるものに限らない)いくつかの復旧方法を検討できます:
特定したリソースの
idを使用して、destroyを使い、個別または一括で正しく削除します。適切なGeoモデルクラス名を使用してください。個々のリソースを削除します:
Upload.find(55).destroy影響を受けるすべてのリソースを削除します:
def destroy_uploads_not_checksummable uploads = Upload.verification_failed.where("verification_failure like '%File is not checksummable%'");1 puts "Found #{uploads.count} resources that failed verification with 'File is not checksummable'." puts "Enter 'y' to continue: " prompt = STDIN.gets.chomp if prompt != 'y' puts "Exiting without action..." return end puts "Destroying all..." uploads.destroy_all end destroy_uploads_not_checksummable
影響を受けるすべてのリソースとGeoデータタイプについて、これらの手順を繰り返します。
メッセージ: "Error during verification","error":"File is not checksummable"
エラー"Error during verification","error":"File is not checksummable"は、プライマリサイトの不整合が原因で発生します。GitLab 18.9以降、エラーメッセージには原因に関する追加の詳細が含まれます:
File is not checksummable - file does not exist at: <path>: ファイルがストレージにありません。表示されるパスは、見つからないファイルを特定するのに役立ちます。File is not checksummable - <ModelClass> <ID> is excluded from verification: レコードは検証スコープから除外されています。
Geoプライマリサイトにファイルがないに記載されている手順に従ってください。
プライマリGeoサイトでのアップロードの検証に失敗
いくつかのアップロードの検証が、verification_checksum = nilのあるプライマリGeoサイトで失敗した際に、verification_failureにError during verification: undefined method `underscore' for NilClass:ClassまたはThe model which owns this Upload is missing.が含まれている場合は、これは孤立したアップロードが原因です。アップロードを所有する親レコード(アップロードの「モデル」)が何らかの理由で削除されましたが、アップロードレコードはまだ存在します。この問題は通常、アプリケーションのバグが原因です。具体的には、「モデル」の一括削除を実装する際に、関連するアップロードレコードの一括削除を忘れたことによって発生します。したがって、これらの検証エラーは実際には検証の失敗ではなく、Postgres内の不正なデータが原因で発生しているものです。
これらのエラーは、プライマリGeoサイトのgeo.logファイルで確認できます。
モデルレコードが存在しないことを確認するには、プライマリGeoサイトでRakeタスクを実行します:
sudo gitlab-rake gitlab:uploads:checkこれらのエラーを取り除くために、プライマリGeoサイトでこれらのアップロードレコードを削除するには、Railsコンソールから次のスクリプトを実行します:
def delete_orphaned_uploads(dry_run: true)
if dry_run
p "This is a dry run. Upload rows will only be printed."
else
p "This is NOT A DRY RUN! Upload rows will be deleted from the DB!"
end
subquery = Geo::UploadState.where("(verification_failure LIKE 'Error during verification: The model which owns this Upload is missing.%' OR verification_failure = 'Error during verification: undefined method `underscore'' for NilClass:Class') AND verification_checksum IS NULL")
uploads = Upload.where(upload_state: subquery)
p "Found #{uploads.count} uploads with a model that does not exist"
uploads_deleted = 0
begin
uploads.each do |upload|
if dry_run
p upload
else
uploads_deleted=uploads_deleted + 1
p upload.destroy!
end
rescue => e
puts "checking upload #{upload.id} failed with #{e.message}"
end
end
p "#{uploads_deleted} remote objects were destroyed." unless dry_run
end前述のスクリプトでは、delete_orphaned_uploadsというメソッドが定義されています。次のように呼び出すことで、ドライランを実行できます:
delete_orphaned_uploads(dry_run: true)また、孤立したアップロード行を実際に削除するには、以下のようにします:
delete_orphaned_uploads(dry_run: false)リポジトリ同期をブロックしている孤立した排他的キー
排他的リースキーが孤立した状態になると、リポジトリの同期がブロックされ、最大で8時間にわたり同期処理が実行できなくなる場合があります。
兆候:
- リポジトリの同期がブロックされる。影響を受けたリポジトリのレプリケーション状態が、
pending状態とfailed状態の間で交互に切り替わる。 geo.logにおいて、「Cannot obtain an exclusive lease」というメッセージを含むログの出力件数が増加する。- 影響を受けたリポジトリで、アクティブな同期ジョブが実行されていない。
- リースの有効期限が切れるまで、最大8時間にわたり単一のリポジトリに影響が発生する。
診断:
Geoの管理インターフェースを確認して、リポジトリがアクティブに同期されていないことを確認します。
「Cannot obtain an exclusive lease」というメッセージの数が増加していないか、
geo.logを確認します:grep "Cannot obtain an exclusive lease" /var/log/gitlab/geo/geo.logこれらのすべてのログ行に、値
geo_sync_ssf_service:project_repository:<repository id>のlease_keyフィールドが含まれていることを確認します。ここで、<repository id>は影響を受けるリポジトリの一意のIDです。影響を受けるリポジトリに対して、Sidekiqでアクティブな同期ジョブが実行されていないことを確認します。
回避策:
孤立したリースキーを手動でリリースするには、次の手順に従います:
セカンダリサイトでRailsコンソールセッションを開始します。
影響を受けるリポジトリのプロジェクトIDを見つけます(
<project-path>を実際のプロジェクトパスに置き換えます):project = Project.find_by_full_path('<project-path>') project_id = project.id同じセッションで、孤立したリースをリリースします:
replicator = Geo::ProjectRepositoryRegistry.find_by(project_id: project_id).replicator sync_service = Geo::FrameworkRepositorySyncService.new(replicator) uuid = Gitlab::ExclusiveLease.get_uuid(sync_service.lease_key) if uuid Gitlab::ExclusiveLease.cancel(sync_service.lease_key, uuid) puts "Lease released for project ID #{project_id}" else puts "No active lease found for project ID #{project_id}" endリースがリリースされたことを確認し、新しい同期をトリガーします:
replicator.sync
エラー: Error syncing repository: 13:fatal: could not read Username
last_sync_failureエラーのError syncing repository: 13:fatal: could not read Username for 'https://gitlab.example.com': terminal prompts disabledは、Geoのクローンまたはフェッチ要求の際に、JWT認証が失敗していることを示しています。
まず、システムクロックが同期されていることを確認してください。ヘルスチェックRakeタスクを実行するか、セカンダリサイトのすべてのSidekiqノードと、プライマリサイトのすべてのPumaノードでdateが同じであることを手動で確認します。
システムクロックが同期されている場合、Gitフェッチが2つの別個のHTTPリクエスト間で計算を実行している間に、JWTトークンが期限切れになる可能性があります。イシュー464101を参照してください。この問題は、GitLab 17.1.0、17.0.5、および16.11.7で修正されるまで、すべてのGitLabバージョンに存在していました。
この問題が発生しているかどうかを検証するには、以下を実行します:
Railsコンソールでコードにモンキーパッチを適用して、トークンの有効期間を1分から10分に増やします。セカンダリサイトのRailsコンソールで、これを実行します:
module Gitlab; module Geo; class BaseRequest private def geo_auth_token(message) signed_data = Gitlab::Geo::SignedData.new(geo_node: requesting_node, validity_period: 10.minutes).sign_and_encode_data(message) "#{GITLAB_GEO_AUTH_TOKEN_TYPE} #{signed_data}" end end;end;end同じRailsコンソールで、影響を受けるプロジェクトを再同期します:
Project.find_by_full_path('<mygroup/mysubgroup/myproject>').replicator.resync同期状態を確認します:
Project.find_by_full_path('<mygroup/mysubgroup/myproject>').replicator.registrylast_sync_failureにエラーfatal: could not read Usernameがもう含まれていない場合、この問題の影響を受けています。現在の状態は2になっているはずで、これは同期が完了していることを意味します。その場合は、修正が含まれているGitLabのバージョンにアップグレードしてください。また、この問題の重大度を軽減する改善が提案されているイシュー466681に、同意したりコメントを残したりすることもお勧めします。
この問題を回避するには、JWTの有効期限を延長するために、セカンダリサイトのすべてのSidekiqノードにホットパッチを適用する必要があります:
/opt/gitlab/embedded/service/gitlab-rails/ee/lib/gitlab/geo/signed_data.rbを編集します。Gitlab::Geo::SignedData.new(geo_node: requesting_node)を見つけて、, validity_period: 10.minutesを追加します:- Gitlab::Geo::SignedData.new(geo_node: requesting_node) + Gitlab::Geo::SignedData.new(geo_node: requesting_node, validity_period: 10.minutes)Sidekiqを再起動します:
sudo gitlab-ctl restart sidekiq修正を含むバージョンにアップグレードしない限り、GitLabをアップグレードするたびにこの回避策を繰り返す必要があります。
エラー: Error syncing repository: 13:creating repository: cloning repository: exit status 128
このエラーは、同期が正常に実行されないプロジェクトで表示される可能性があります。
リポジトリの作成中に終了コード128が発生した場合、Gitがクローン作成中に致命的なエラーが発生したことを意味します。これは、リポジトリの破損、ネットワークの問題、認証の問題、リソース制限、またはプロジェクトに関連付けられたGitリポジトリがないことが原因である可能性があります。このような失敗の特定の原因に関する詳細については、Gitalyのログに記録されます。
どこから始めればよいかわからない場合は、コマンドラインでgit fsckコマンドを手動で実行するすることで、プライマリサイトのソースリポジトリで整合性チェックを実行できます。
ロードバランサーからのHTTP 504によって発生する終了ステータス128
大規模なリポジトリの場合、セカンダリサイト上のGitalyログには次の表示がされることがあります:
error: RPC failed; HTTP 504 curl 22 The requested URL returned error: 504
fatal: expected 'packfile'このエラーは、ロードバランサーまたはプライマリサイトの手前にあるプロキシが、Gitクローンパックファイル転送中に接続を終了するときに発生します。これは、AWS Application Load Balancer (ALB) でよく発生します。デフォルトのアイドルタイムアウトは60秒です。Gitalyがデータ転送を開始する前にパックファイルを準備するのに時間がかかる大規模なリポジトリの場合、ALBはデータが送信される前に接続を切断し、エラーをトリガーする可能性があります。
この問題を解決するには、次の手順に従います:
大規模なリポジトリクローンに対応するために、プライマリサイトの手前にあるロードバランサーのアイドルタイムアウトを増やします。AWS ALBの場合は、AWS Management Consoleのロードバランサー属性でアイドルタイムアウト設定を更新します。
失敗したレジストリをリセットします:
セカンダリサイトでRailsコンソールセッションを開始します。
影響を受けるリポジトリを特定してリセットします:
project_ids = Geo::ProjectRepositoryRegistry.failed .where("last_sync_failure LIKE '%exit status 128%'") .pluck(:project_id) puts "Found #{project_ids.count} repositories failing with exit status 128" # state: 0 sets the registry back to pending so Geo retries the sync Geo::ProjectRepositoryRegistry.where(project_id: project_ids).update_all( state: 0, retry_count: 0, retry_at: nil, last_sync_failure: nil ) puts "Reset #{project_ids.count} registries to pending"
Geoが自動的に同期を再試行するのを待つか、manually retry replicationを実行します。
エラー: gitmodulesUrl: disallowed submodule url
一部のプロジェクトリポジトリは、エラーError syncing repository: 13:creating repository: cloning repository: exit status 128で一貫して同期に失敗します。ただし、一部のリポジトリでは、Gitalyログの特定のエラーメッセージが異なり、gitmodulesUrl: disallowed submodule urlと表示されます。このエラーは、リポジトリに.gitmodulesファイル内の無効なサブモジュールURLが含まれている場合に発生します。
根本原因: この問題は、Gitリポジトリ内の過去のコミットに含まれる、不正な形式のURLを持つ.gitmodulesファイルが原因で発生します。この問題は、Geoがプライマリからセカンダリにリポジトリをクローンしようとしたときに実行されるGitの整合性チェック(git fsck)中に発生します。
問題はリポジトリのコミット履歴にあります。.gitmodulesファイル内のサブモジュールURLには無効な形式が含まれており、パス内で:の代わりに/を使用しています:
- 無効:
https://example.gitlab.com:group/project.git - 有効:
https://example.gitlab.com/group/project.git
Geoの同期が失敗する理由:
- Gitの厳格な検証: GitLab 17.0以降および新しいGitバージョンでは、クローン処理中に、より厳格な
fsckチェックが行われます。 - 履歴データの保持: 現在の
.gitmodulesファイルが正しい場合でも、Gitはすべての過去のバージョンをリポジトリ内の 「blob」 として保持します。 - クローン時の失敗: Geoがリポジトリをクローンしようとすると、Gitの
fsckはすべてのオブジェクト(履歴を含む)を検証し、不正な形式のURLを検出した時点で失敗します。 - 完全な同期失敗: クローン全体の処理が失敗し、リポジトリがセカンダリサイトに複製されなくなります。
重要:現在の.gitmodulesファイルを編集しても、この問題は解決しません。問題のデータは、ファイルの現在のバージョンではなく、リポジトリのGit履歴内に存在しているためです。
この問題はGitLab 17.0以降で確認されている既知の問題で、より厳格なリポジトリ整合性チェックの導入によって発生します。この新しい動作はGit自体の変更によるもので、このチェック機能が追加されたことが原因です。したがって、これはGitLab GeoやGitalyに特有の問題ではありません。詳細については、イシュー468560を参照してください。
回避策
プロジェクトをバックアップする
作業を進める前に、プロジェクトのエクスポート機能を使用して、事前にプロジェクトをバックアップしておくようにしてください。
問題のあるBlob IDを特定する
影響を受けた各プロジェクトについて、以下のいずれかの方法を使用して問題のあるBlob IDを特定します:
git fsckを使用します: リポジトリをクローンし、git fsckを実行して問題を確認します:git clone https://example.gitlab.com/group/project.git cd project git fsck出力には、問題のあるblobが表示されます:
Checking object directories: 100% (256/256), done. error in blob <SHA>: gitmodulesUrl: disallowed submodule url: https://example.gitlab.com:group/project.git Checking objects: 100% (12/12), done.Gitalyログを確認します。
gitmodulesUrlを含むエラーメッセージを探して、特定のblob SHAを見つけます。
blobを消去する
影響を受ける各プロジェクトについて、前の手順で識別された問題のあるblob IDを削除します。
**重要な制限事項:**これらのリポジトリのいずれかがフォークネットワークの一部である場合、Blobの削除方法は機能しない可能性があります(オブジェクトプール内に含まれるBlobは、この方法では削除できません)。
必要に応じて.gitmodulesの不正なURLを修正する
影響を受ける各リポジトリで
.gitmodulesファイルの状態を確認します。.gitmodulesにhttps://example.gitlab.com:foo/bar.gitの代わりにhttps://example.gitlab.com/foo/bar.gitのような無効なURLがまだ含まれている場合、顧客は次のことを行う必要があります:.gitmodulesファイル内のURLを修正する- 有効なURLでコミットをプッシュする
エラー: fetch remote: signal: terminated: context deadline exceeded(ちょうど3時間後に発生)
Gitリポジトリの同期中にGitフェッチが正確に3時間後に失敗した場合:
/etc/gitlab/gitlab.rbを編集して、Gitタイムアウトをデフォルトの10800秒から増やします:# Git timeout in seconds gitlab_rails['gitlab_shell_git_timeout'] = 21600GitLabを再設定します:
sudo gitlab-ctl reconfigure
エラー: レジストリレプリケーションの構成中に、セカンダリでFailed to open TCP connection to localhost:5000が発生
セカンダリサイトでコンテナレジストリレプリケーションを構成するときに、次のエラーが発生する場合があります:
Failed to open TCP connection to localhost:5000 (Connection refused - connect(2) for \"localhost\" port 5000)"これは、セカンダリサイトでコンテナレジストリが有効になっていない場合に発生します。これを修正するには、コンテナレジストリがセカンダリサイトで有効になっていることを確認してください。Let’s Encryptインテグレーションが無効になっている場合、コンテナレジストリも無効になり、手動で構成する必要があります。
エラー: Verification timed out after 28800
**ありうる根本原因:**レジストリレコードの重複が、さまざまなレジストリタイプ間で検証の競合を引き起こしている。
診断:
セカンダリサイトでRailsコンソールセッションを開始します。
異なるタイプの重複レジストリを確認します:
# Check for duplicate upload registries upload_ids = Geo::UploadRegistry.group(:file_id).having('COUNT(*) > 1').pluck(:file_id) puts "Duplicate upload IDs count: #{upload_ids.size}" puts 'Duplicate Upload IDs:', upload_ids # Check for duplicate job artifact registries artifact_ids = Geo::JobArtifactRegistry.group(:artifact_id).having('COUNT(*) > 1').pluck(:artifact_id) puts "Duplicate artifact IDs count: #{artifact_ids.size}" puts 'Duplicate Artifact IDs:', artifact_ids # Check for duplicate package file registries package_file_ids = Geo::PackageFileRegistry.group(:package_file_id).having('COUNT(*) > 1').pluck(:package_file_id) puts "Duplicate package file IDs count: #{package_file_ids.size}" puts 'Duplicate Package File IDs:', package_file_ids # Check for duplicate LFS object registries lfs_object_ids = Geo::LfsObjectRegistry.group(:lfs_object_id).having('COUNT(*) > 1').pluck(:lfs_object_id) puts "Duplicate LFS object IDs count: #{lfs_object_ids.size}" puts 'Duplicate LFS Object IDs:', lfs_object_ids # Check for duplicate pages deployment registries pages_deployment_ids = Geo::PagesDeploymentRegistry.group(:pages_deployment_id).having('COUNT(*) > 1').pluck(:pages_deployment_id) puts "Duplicate pages deployment IDs count: #{pages_deployment_ids.size}" puts 'Duplicate Pages Deployment IDs:', pages_deployment_ids # Check for duplicate terraform state version registries terraform_state_ids = Geo::TerraformStateVersionRegistry.group(:terraform_state_version_id).having('COUNT(*) > 1').pluck(:terraform_state_version_id) puts "Duplicate terraform state version IDs count: #{terraform_state_ids.size}" puts 'Duplicate Terraform State Version IDs:', terraform_state_ids
解決策:
セカンダリサイトでRailsコンソールセッションを開始します。
影響を受けるタイプごとに、重複したレジストリエントリを削除します:
# Remove duplicate upload registries upload_ids = Geo::UploadRegistry.group(:file_id).having('COUNT(*) > 1').pluck(:file_id) if upload_ids.any? Geo::UploadRegistry.where(file_id: upload_ids).delete_all puts "Removed #{upload_ids.size} duplicate upload registry entries" end # Remove duplicate job artifact registries artifact_ids = Geo::JobArtifactRegistry.group(:artifact_id).having('COUNT(*) > 1').pluck(:artifact_id) if artifact_ids.any? Geo::JobArtifactRegistry.where(artifact_id: artifact_ids).delete_all puts "Removed #{artifact_ids.size} duplicate job artifact registry entries" end # Remove duplicate package file registries package_file_ids = Geo::PackageFileRegistry.group(:package_file_id).having('COUNT(*) > 1').pluck(:package_file_id) if package_file_ids.any? Geo::PackageFileRegistry.where(package_file_id: package_file_ids).delete_all puts "Removed #{package_file_ids.size} duplicate package file registry entries" end # Remove duplicate LFS object registries lfs_object_ids = Geo::LfsObjectRegistry.group(:lfs_object_id).having('COUNT(*) > 1').pluck(:lfs_object_id) if lfs_object_ids.any? Geo::LfsObjectRegistry.where(lfs_object_id: lfs_object_ids).delete_all puts "Removed #{lfs_object_ids.size} duplicate LFS object registry entries" end # Remove duplicate pages deployment registries pages_deployment_ids = Geo::PagesDeploymentRegistry.group(:pages_deployment_id).having('COUNT(*) > 1').pluck(:pages_deployment_id) if pages_deployment_ids.any? Geo::PagesDeploymentRegistry.where(pages_deployment_id: pages_deployment_ids).delete_all puts "Removed #{pages_deployment_ids.size} duplicate pages deployment registry entries" end # Remove duplicate terraform state version registries terraform_state_ids = Geo::TerraformStateVersionRegistry.group(:terraform_state_version_id).having('COUNT(*) > 1').pluck(:terraform_state_version_id) if terraform_state_ids.any? Geo::TerraformStateVersionRegistry.where(terraform_state_version_id: terraform_state_ids).delete_all puts "Removed #{terraform_state_ids.size} duplicate terraform state version registry entries" endすべてのレジストリタイプでクリーンアップを検証します:
# Verify no remaining duplicates upload_duplicates = Geo::UploadRegistry.group(:file_id).having('COUNT(*) > 1').count artifact_duplicates = Geo::JobArtifactRegistry.group(:artifact_id).having('COUNT(*) > 1').count package_duplicates = Geo::PackageFileRegistry.group(:package_file_id).having('COUNT(*) > 1').count lfs_duplicates = Geo::LfsObjectRegistry.group(:lfs_object_id).having('COUNT(*) > 1').count pages_duplicates = Geo::PagesDeploymentRegistry.group(:pages_deployment_id).having('COUNT(*) > 1').count terraform_duplicates = Geo::TerraformStateVersionRegistry.group(:terraform_state_version_id).having('COUNT(*) > 1').count puts "Remaining duplicates:" puts " Uploads: #{upload_duplicates.size}" puts " Job Artifacts: #{artifact_duplicates.size}" puts " Package Files: #{package_duplicates.size}" puts " LFS Objects: #{lfs_duplicates.size}" puts " Pages Deployments: #{pages_duplicates.size}" puts " Terraform State Versions: #{terraform_duplicates.size}"
エラー: Checksum does not match the primary checksum
**ありうる根本原因:**チェックサムの不整合を引き起こすリポジトリまたはコンテナレジストリの検証間隔の変更。
診断:
セカンダリサイトでRailsコンソールセッションを開始します。
失敗したリポジトリまたはコンテナレジストリを確認します:
failed_repos = Geo::ProjectRepositoryRegistry.failed.limit(100) failed_repos.each do |repo| puts "Project ID: #{repo.project_id}" puts "Primary checksum: #{repo.verification_checksum_mismatched}" puts "Secondary checksum: #{repo.verification_checksum}" puts "Error: #{repo.last_sync_failure}" puts "---" endfailed_container_repos = Geo::ContainerRepositoryRegistry.failed.limit(100) failed_container_repos.each do |repo| puts "Container Repo Id: #{repo.model_record_id}" puts "Primary checksum: #{repo.verification_checksum_mismatched}" puts "Secondary checksum: #{repo.verification_checksum}" puts "Error: #{repo.last_sync_failure}" puts "---" end
解決策:
Start a Rails console sessionをプライマリサイトで実行します。
特定のプロジェクトまたはコンテナレジストリの再検証を強制します:
project_ids = [1, 2, 3] # Replace with actual failing project IDs project_ids.each do |project_id| project = Project.find(project_id) puts "Reverifying project: #{project.full_path}" project_state = project.project_state project_state.update!(verification_state: 0) puts "Project #{project_id} marked for reverification" endcontainer_repo_ids = [1, 2, 3] container_repo_ids.each do |repo_id| container_repo = ContainerRepository.find(repo_id) puts "Reverifying container repository: #{container_repo.path}" state = container_repo.container_repository_state state.update!(verification_state: 0) puts "Container Repo #{repo_id} marked for reverification" end
Error during verification: File is not checksummableのオブジェクトタイプ固有のトラブルシューティング
Geoデータタイプが異なると、固有の特性と一般的な失敗パターンがあります。このセクションでは、特定のオブジェクトタイプを対象としたトラブルシューティングについて説明します。
アップロード
診断:
Start a Rails console sessionをプライマリサイトで実行します。
ファイルが失敗しているアップロードを特定します。
limit(5)を必要に応じて更新し、さらに多くの結果を表示します:checksummable_failures = Upload.verification_failed .where("verification_failure LIKE '%File is not checksummable%'") puts "Found #{checksummable_failures.count} uploads with missing files" checksummable_failures.limit(5).each_with_index do |record, index| puts "Record #{index + 1}:" puts " ID: #{record.id}" puts " Path: #{record.path}" puts " Model: #{record.model_type} (ID: #{record.model_id})" puts " Created: #{record.created_at}" puts "---" end
解決策:
これらの失敗を解決するには、failed verification of uploads on the primary Geo siteの手順に従ってください。
ページデプロイ
診断:
Start a Rails console sessionをプライマリサイトで実行します。
問題のあるページデプロイを調べます:
checksummable_failures = PagesDeployment.verification_failed .where("verification_failure LIKE '%File is not checksummable%'") checksummable_failures.each_with_index do |record, index| puts "Record #{index + 1}:" puts " ID: #{record.id}" puts " Project: #{record.project.full_path}" puts " Created: #{record.created_at}" puts " File exists: #{record.file.exists?}" puts "---" end
解決策:
Start a Rails console sessionをプライマリサイトで実行します。
デプロイを安全に削除できることをチームに確認した後、以下を行います:
def destroy_pages_deployments_not_checksummable(dry_run: true) deployments = PagesDeployment.verification_failed.where("verification_failure LIKE '%File is not checksummable%'") puts "Found #{deployments.count} pages deployments that failed verification with 'File is not checksummable'." if dry_run puts "DRY RUN - No changes made" deployments.each { |d| puts "Would remove: ID #{d.id}, Project: #{d.project.full_path}" } return end puts "Enter 'y' to continue: " prompt = STDIN.gets.chomp if prompt != 'y' puts "Exiting without action..." return end puts "Destroying all..." deployments.destroy_all puts "Done!" end # Run in dry run mode first destroy_pages_deployments_not_checksummable(dry_run: true)
LFSオブジェクト
診断:
Start a Rails console sessionをプライマリサイトで実行します。
問題のあるLFSオブジェクトを調べます:
checksummable_failures = LfsObject.verification_failed .where("verification_failure LIKE '%File is not checksummable%'") checksummable_failures.each_with_index do |record, index| puts "Record #{index + 1}:" puts " OID: #{record.oid}" puts " Size: #{record.size} bytes" puts " File Store: #{record.file_store}" puts " Created: #{record.created_at}" # Show associated projects associations = record.lfs_objects_projects.includes(:project) puts " Associated projects (#{associations.count}):" associations.each do |assoc| project = assoc.project if project puts " - #{project.full_path}" else puts " - Project ID: #{assoc.project_id} (not found)" end end puts "---" end
解決策:
Start a Rails console sessionをプライマリサイトで実行します。
ファイルが欠落しているLFSオブジェクトを削除します:
def destroy_lfs_not_checksummable(dry_run: true) lfs_objects = LfsObject.verification_failed.where("verification_failure like '%File is not checksummable%'") puts "Found #{lfs_objects.count} LFS objects that failed verification with 'File is not checksummable'." if dry_run puts "DRY RUN - No changes made" lfs_objects.each { |obj| puts "Would remove: OID #{obj.oid}, Size: #{obj.size}" } return end puts "Enter 'y' to continue with deletion: " prompt = STDIN.gets.chomp if prompt != 'y' puts "Exiting without action..." return end puts "Destroying all..." lfs_objects.each do |lfs_object| lfs_object.lfs_objects_projects.destroy_all lfs_object.destroy! end puts "Done!" end # Run in dry run mode first destroy_lfs_not_checksummable(dry_run: true)
ジョブアーティファクト
診断:
Start a Rails console sessionをプライマリサイトで実行します。
ファイルが欠落しているアーティファクトがないか確認します:
failed_artifacts = Ci::JobArtifact.verification_failed.where("verification_failure LIKE '%File is not checksummable%'") failed_artifacts.each do |registry| artifact = Ci::JobArtifact.find_by(id: registry.id) if artifact puts "Artifact ID: #{artifact.id}" puts "Job ID: #{artifact.job_id}" puts "Project ID: #{artifact.project_id}" puts "File exists: #{artifact.file.exists?}" puts "File path: #{artifact.file.path}" else puts "Artifact ID #{artifact.id} not found in database" end puts "---" end
解決策:
Start a Rails console sessionをプライマリサイトで実行します。
ファイルが欠落しているアーティファクトをクリーンアップします:
def cleanup_missing_artifacts(dry_run: true) missing_file_artifacts = [] Ci::JobArtifact.find_each do |artifact| unless artifact.file.exists? missing_file_artifacts << artifact.id puts "Missing file for artifact #{artifact.id}" if dry_run end end puts "Found #{missing_file_artifacts.size} artifacts with missing files" unless dry_run Ci::JobArtifact.where(id: missing_file_artifacts).destroy_all puts "Removed #{missing_file_artifacts.size} artifacts with missing files" end end # Run in dry run mode first cleanup_missing_artifacts(dry_run: true)
パッケージファイル
このエラーは、プライマリサイトのストレージからパッケージファイルが見つからない場合に発生します。
影響を受けるパッケージファイルを特定するには:
Start a Rails console sessionをプライマリサイトで実行します。
影響を受けるレコードをクエリします。
limit(5)を必要に応じて更新し、さらに多くの結果を表示します:checksummable_failures = Packages::PackageFile.verification_failed .where("verification_failure LIKE '%File is not checksummable%'") puts "Found #{checksummable_failures.count} package files with missing files" checksummable_failures.limit(5).each_with_index do |record, index| puts "Record #{index + 1}:" puts " ID: #{record.id}" puts " File Name: #{record.file_name}" puts " Package ID: #{record.package_id}" puts " Created: #{record.created_at}" puts "---" end
影響を受けるパッケージファイルを削除するには:
Start a Rails console sessionをプライマリサイトで実行します。
影響を受けるレコードを削除します:
def destroy_packages_not_checksummable(dry_run: true) packages = Packages::PackageFile.verification_failed .where("packages_package_file_states.verification_failure LIKE '%File is not checksummable%'") puts "Found #{packages.count} packages that failed verification with 'File is not checksummable'." if dry_run puts "DRY RUN - No changes made" packages.each { |p| puts "Would remove: ID #{p.id}, File: #{p.file_name}" } return end puts "Enter 'y' to continue: " prompt = STDIN.gets.chomp if prompt != 'y' puts "Exiting without action..." return end puts "Destroying all..." packages.destroy_all puts "Done!" end # Run in dry run mode first destroy_packages_not_checksummable(dry_run: true)
パイプラインアーティファクト
診断:
Start a Rails console sessionをプライマリサイトで実行します。
ファイルが欠落しているアーティファクトがないか確認します:
failed_pipeline_artifacts = Ci::PipelineArtifact.verification_failed.where("verification_failure LIKE '%checksummable%'") failed_pipeline_artifacts.each do |registry| artifact = Ci::PipelineArtifact.find_by(id: registry.id) if artifact puts "Artifact ID: #{artifact.id}" puts "Pipeline ID: #{artifact.pipeline_id}" puts "Project ID: #{artifact.project_id}" puts "File exists: #{artifact.file.exists?}" puts "File path: #{artifact.file.path}" else puts "Artifact ID #{artifact.id} not found in database" end puts "---" end
解決策:
Start a Rails console sessionをプライマリサイトで実行します。
ファイルが欠落しているパイプラインアーティファクトを削除します:
def destroy_pipeline_artifacts_not_checksummable artifacts = Ci::PipelineArtifact.verification_failed.where("verification_failure like '%File is not checksummable%'") puts "Found #{artifacts.count} pipeline artifacts that failed verification with 'File is not checksummable'." puts "Enter 'y' to continue: " prompt = STDIN.gets.chomp if prompt != 'y' puts "Exiting without action..." return end puts "Destroying all..." artifacts.destroy_all puts "Done!" end destroy_pipeline_artifacts_not_checksummable
タイムアウトにより同期していないLFSオブジェクト
大規模なファイルがデフォルトの8時間blobダウンロードタイムアウトを超過すると、LFSオブジェクトはSync timed out after 28800で同期に失敗する可能性があります。
blobダウンロードタイムアウトを増やします
GitLab 18.10以降では、blobダウンロードタイムアウトはGeoサイトごとに設定可能です。
blobダウンロードタイムアウトを増やすには、<secondary_id>をセカンダリサイトのIDに、<token>を管理者APIトークンに置き換えます:
curl --header "PRIVATE-TOKEN: <token>" \
--request PUT \
--data '{"blob_download_timeout": 43200}' \
"https://gitlab.example.com/api/v4/geo_nodes/<secondary_id>"タイムアウトを増やした後、Geoが自動的に再試行するのを待つか、manually retry replicationを実行します。
タイムアウトしたLFSオブジェクトを特定して検証する
タイムアウトを増やした後もLFSオブジェクトが失敗し続ける場合は、影響を受けるオブジェクトを特定し、ファイルがプライマリサイトに存在することを確認してください。
セカンダリサイトで影響を受けるオブジェクトを特定します:
registries = Geo::LfsObjectRegistry.failed.where("last_sync_failure LIKE '%timed out%'") puts "Found #{registries.count} LFS objects that failed with a timeout" registries.each do |registry| lfs_object = LfsObject.find_by(id: registry.lfs_object_id) size_gb = lfs_object ? (lfs_object.size / 1024.0 / 1024.0 / 1024.0).round(2) : 'unknown' puts " Registry ID: #{registry.id}, LFS Object ID: #{registry.lfs_object_id}, Size: #{size_gb} GB, Failure: #{registry.last_sync_failure}, Retries: #{registry.retry_count}" end前の手順の
lfs_object_idの値を使用して、ファイルがプライマリサイトに存在することを確認します:[lfs_object_id1, lfs_object_id2, lfs_object_id3].each do |id| lfs_object = LfsObject.find_by(id: id) if lfs_object.nil? puts "LFS Object ID: #{id} not found" next end puts "LFS Object ID: #{id}, Size: #{(lfs_object.size / 1024.0 / 1024.0 / 1024.0).round(2)} GB, File exists?: #{lfs_object.file.exists?}, Path: #{lfs_object.file.path}" end
プライマリからセカンダリにファイルをコピーする
ファイルがプライマリに存在してもセカンダリに存在しない場合は、前の手順のパスを使用してファイルを特定します:
- オブジェクトストレージの場合: パスは、設定されたLFSバケット内のオブジェクトキーです。プライマリバケットからファイルを特定してダウンロードし、セカンダリバケットの同じキーにアップロードします。
- ローカルストレージの場合: パスはプライマリサイトの
/var/opt/gitlab/gitlab-rails/shared/lfs-objects/に対して相対的です。ファイルをセカンダリサイトの同じ相対パスにコピーします。
LFSオブジェクトを同期済みとしてマークする
ファイルがセカンダリサイトに存在した後、それらを同期済みとしてマークし、検証をトリガーします:
[lfs_object_id1, lfs_object_id2, lfs_object_id3].each do |lfs_object_id|
begin
registry = Geo::LfsObjectRegistry.find_by(lfs_object_id: lfs_object_id)
if registry.nil?
puts "Registry not found for LFS Object #{lfs_object_id}"
next
end
registry.update!(
state: 2,
success: true,
last_synced_at: Time.current,
last_sync_failure: nil,
retry_count: 0,
retry_at: nil
)
registry.replicator.verify
puts "LFS Object #{lfs_object_id}: marked as synced and verification triggered"
rescue => e
puts "Error processing LFS Object #{lfs_object_id}: #{e.message}"
end
endエラー: Projects - Error during verification: Repository does not exist
根本原因: Gitリポジトリがないプロジェクトで、検証に失敗する。
兆候:
- プロジェクトの検証中に「リポジトリが存在しません」というエラーが表示される
- 正当な理由でリポジトリがないプロジェクトの場合、Geo UIに誤ったエラーレポートが表示される
- 存在しないリポジトリでの無駄な同期試行が発生している
回避策:
存在しない場合は、プライマリにプロジェクトリポジトリを作成します:
failed_projects = Project.verification_failed.where("verification_failure LIKE '%Repository does not exist%'")
puts "Found #{failed_projects.count} project repos with 'Repository does not exist' verification failure"
failed_projects.find_each do |p|
puts "#{p.full_path} #{p.ensure_repository.inspect}"
endエラー: Expected(200) <=> Actual(403 Forbidden)
根本原因: S3 APIが404ではなく403を返す原因となる、ListBucket権限がない。
兆候:
- S3エンドポイントを使用したログの403エラー
- S3バケットへのHEADリクエストの失敗
- オブジェクトストレージをバックアップしたデータタイプの同期の失敗
解決策:
これには、インフラストラクチャチームの介入が必要になり、GitLabで使用されるS3 IAMポリシーにListBucket権限を追加する必要があります。
メッセージ: Synchronization failed - Error syncing repository
次のエラーメッセージは、リポジトリの同期中に整合性チェックエラーが発生したことを示しています:
Synchronization failed - Error syncing repository [..] fatal: fsck error in packed objectいくつかの問題がこのエラーをトリガーする可能性があります。たとえば、メールアドレスに関する問題です:
Error syncing repository: 13:fetch remote: "error: object <SHA>: badEmail: invalid author/committer line - bad email
fatal: fsck error in packed object
fatal: fetch-pack: invalid index-pack outputこのエラーをトリガーする可能性のあるもう1つの問題はobject <SHA>: hasDotgit: contains '.git'です。すべてのリポジトリで複数の問題が発生している可能性があるため、特定のエラーを確認してください。
2番目の同期エラーは、リポジトリチェックの問題によっても引き起こされる可能性があります:
Error syncing repository: 13:Received RST_STREAM with error code 2.これらのエラーは、失敗したすべてのリポジトリをすぐに同期することで確認できます。
整合性エラーの原因となっている不正な形式のオブジェクトを削除するには、リポジトリの履歴を書き換える必要があります。ただし、この方法は通常推奨されません。
これらの整合性チェックを無視するには、セカンダリGeoサイト上のGitalyを再設定し、git fsckに関する問題を無視するように構成してください。次の設定例では、以下のとおりとなっています:
- GitLab 16.0から必須となった新しい設定構造を使用します。
- 5つの一般的なチェックエラーを無視します。
Gitalyのドキュメントには、Gitのその他のチェックエラーやGitLabの以前のバージョンに関する詳細が記載されています。
gitaly['configuration'] = {
git: {
config: [
{ key: "fsck.duplicateEntries", value: "ignore" },
{ key: "fsck.badFilemode", value: "ignore" },
{ key: "fsck.missingEmail", value: "ignore" },
{ key: "fsck.badEmail", value: "ignore" },
{ key: "fsck.hasDotgit", value: "ignore" },
{ key: "fetch.fsck.duplicateEntries", value: "ignore" },
{ key: "fetch.fsck.badFilemode", value: "ignore" },
{ key: "fetch.fsck.missingEmail", value: "ignore" },
{ key: "fetch.fsck.badEmail", value: "ignore" },
{ key: "fetch.fsck.hasDotgit", value: "ignore" },
{ key: "receive.fsck.duplicateEntries", value: "ignore" },
{ key: "receive.fsck.badFilemode", value: "ignore" },
{ key: "receive.fsck.missingEmail", value: "ignore" },
{ key: "receive.fsck.badEmail", value: "ignore" },
{ key: "receive.fsck.hasDotgit", value: "ignore" },
],
},
}包括的なfsckエラーの一覧は、Gitドキュメントに記載されています。
GitLab 16.1バージョン以降では、これらのイシューの一部を解決できる可能性のある拡張機能が含まれています。
Gitalyイシュー5625では、Geoが、ソースリポジトリに問題のあるコミットが含まれている場合でも、リポジトリをレプリケートすることを保証することを提案しています。
関連エラーdoes not appear to be a git repository
次のログメッセージとともに、エラーメッセージSynchronization failed - Error syncing repositoryが表示されることもあります。このエラーは、期待されるGeoリモートが.git/configファイルシステム上のセカンダリGeoサイトのリポジトリのファイルに存在しないことを示しています:
{
"created": "@1603481145.084348757",
"description": "Error received from peer unix:/var/opt/gitlab/gitaly/gitaly.socket",
…
"grpc_message": "exit status 128",
"grpc_status": 13
}
{ …
"grpc.request.fullMethod": "/gitaly.RemoteService/FindRemoteRootRef",
"grpc.request.glProjectPath": "<namespace>/<project>",
…
"level": "error",
"msg": "fatal: 'geo' does not appear to be a git repository
fatal: Could not read from remote repository. …",
}これを解決するには、次の手順に従います:
セカンダリGeoサイトのWebインターフェースでサインインします。
.gitフォルダーをバックアップします。オプション。それらのIDのいくつかが、既知のGeoレプリケーションの失敗が発生しているプロジェクトに実際に対応しているかどうかをスポットチェックします。
fatal: 'geo'をgrepの値として使用し、次のAPIコールを実行します:curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<first_failed_geo_sync_ID>"Railsコンソールを起動して、以下を実行します:
failed_project_registries = Geo::ProjectRepositoryRegistry.failed if failed_project_registries.any? puts "Found #{failed_project_registries.count} failed project repository registry entries:" failed_project_registries.each do |registry| puts "ID: #{registry.id}, Project ID: #{registry.project_id}, Last Sync Failure: '#{registry.last_sync_failure}'" end else puts "No failed project repository registry entries found." end各プロジェクトの新しい同期を実行するには、次のコマンドを実行します:
failed_project_registries.each do |registry| registry.replicator.sync puts "Sync initiated for registry ID: #{registry.id}, Project ID: #{registry.project_id}" end
バックフィル中の失敗
バックフィル中に、失敗はバックフィルキューの最後に再試行されるようにスケジュールされるため、これらの失敗はバックフィルが完了した後にのみ解消されます。
メッセージ: unexpected disconnect while reading sideband packet
不安定なネットワーキング状態により、プライマリサイトから大きなリポジトリデータをフェッチしようとすると、Gitalyが失敗する可能性があります。これらの状態により、次のエラーが発生する可能性があります:
curl 18 transfer closed with outstanding read data remaining & fetch-pack:
unexpected disconnect while reading sideband packetこのエラーは、リポジトリをサイト間でゼロからレプリケートする必要がある場合に発生しやすくなります。
GitLab Geoは複数回再試行を行いますが、通信がネットワークの一時的な障害によって継続的に中断される場合、rsyncのような代替手段を使用してgitを回避し、Geoによるレプリケーションに失敗したリポジトリの初期コピーを作成することができます。
各失敗リポジトリを個別に転送し、各転送後に整合性をチェックすることをお勧めします。プライマリサイトから影響を受ける各リポジトリをrsyncを別のサーバーにプルする手順に従って、セカンダリサイトに転送します。
Geoセカンダリサイトでリポジトリチェックの失敗を見つける
GitLab 16.2以前の場合は次のとおりです:
すべてのプロジェクトで有効になっている場合、リポジトリチェックもGeoセカンダリサイトで実行されます。メタデータは、Geoトラッキングデータベースに保存されます。
Geoセカンダリサイトでのリポジトリチェックの失敗は、必ずしもレプリケーションの問題を意味するものではありません。これらの失敗を解決するための一般的なアプローチを次に示します。
- 以下に示す影響を受けるリポジトリと、記録されたエラーを検索します。
- 特定の
git fsckエラーの原因を診断してみてください。発生しうるエラーの範囲は広いため、検索エンジンで調べてみることをおすすめします。 - 影響を受けるリポジトリの一般的な機能をテストします。セカンダリからプルし、ファイルを表示します。
- プライマリサイト側のリポジトリコピーに、同じ
git fsckエラーが発生していないか確認してください。フェイルオーバーを計画している場合は、セカンダリサイトがプライマリサイトと同じ情報を保持していることを優先して検討してください。また、プライマリのバックアップを取得したうえで、計画的フェイルオーバーガイドラインに従って作業を進めてください。 - プライマリにプッシュし、変更がセカンダリサイトにレプリケートされるかどうかを確認します。
- レプリケーションが自動的に機能しない場合は、リポジトリを同期を手動で試してください。
Railsコンソールセッションを開始して、次の基本的なトラブルシューティングの手順を実行します。
リポジトリチェックに失敗したリポジトリの数を取得
Geo::ProjectRegistry.where(last_repository_check_failed: true).countリポジトリチェックに失敗したリポジトリを見つける
Geo::ProjectRegistry.where(last_repository_check_failed: true)Gitaly Clusterからリポジトリを完全に削除して再同期する
この手順は、セカンダリサイトのGitaly Clusterからリポジトリを削除し、再同期を実行します。この操作にはリスクが伴うため、以下の条件をすべて満たし、そのリスクを十分理解している場合にのみ実施してください:
- プライマリサイト上のリポジトリで
git cloneが機能している p.replicator.sync_repository(ここで、pはプロジェクトモデルインスタンスです)が、セカンダリサイトでGitalyエラーを記録している- 標準のトラブルシューティングで問題が解決しなかった
前提条件:
- セカンダリサイトのRailsコンソールとPraefectノードの両方への管理アクセス権があることを確認してください。
- リポジトリがプライマリサイトでアクセス可能で、正しく機能していることを確認してください。
- この手順を元に戻す必要がある場合に備えて、バックアップ計画を用意しておいてください。
これを行うには、次の手順を実行します:
セカンダリサイトのRailsコンソールにサインインします。
プロジェクトモデルをインスタンス化し、次のいずれかのオプションを使用して、変数
pに保存します:影響を受けるプロジェクトIDがわかっている場合(たとえば、
60087):p = Project.find(60087)GitLabで影響を受けるプロジェクトパスがわかっている場合(たとえば、
my-group/my-project):p = Project.find_by_full_path('my-group/my-project')
プロジェクトGitリポジトリの仮想ストレージを出力し、後で使用するためにメモします:
p.repository.storage出力例:
irb(main):002:0> p.repository.storage => "default"プロジェクトGitリポジトリの相対パスを出力し、後で使用するためにメモします:
p.repository.disk_path + '.git'出力例:
irb(main):003:0> p.repository.disk_path + '.git' => "@hashed/66/b2/66b2fc8562b3432399acc2d0108fcd2782b32bd31d59226c7a03a20b32c76ee8.git"セカンダリサイトのPraefectノードにSSHで接続します。
Gitaly Clusterからリポジトリを手動で削除する手順に従って、前の手順でメモした仮想ストレージと相対パスを使用します。
セカンダリサイトのGitリポジトリが削除されました。
Railsコンソールで、再同期する前に、相関IDを設定します。このIDを使用すると、このセッションで実行するコマンドに関連するすべてのログを検索できます:
Gitlab::ApplicationContext.push({})出力例:
[2] pry(main)> Gitlab::ApplicationContext.push({}) => #<Labkit::Context:0x0000000122aa4060 @data={"correlation_id"=>"53da64ae800bd4794a2b61ab1c80b028"}>プロジェクトGitリポジトリを同期します:
p.replicator.sync_repository
Gitリポジトリがプライマリサイトからセカンダリサイトに再同期されるようになりました。Geo管理インターフェースを通じて同期処理を監視するか、Railsコンソールでリポジトリの同期ステータスを確認します。
インフラストラクチャとパフォーマンスに関する考慮事項
一部の同期の問題は、インフラストラクチャレベルの問題またはパフォーマンスの制約によって発生します。
高い並行処理数による問題
Geoの検証処理の並行処理数が過剰になると、データベースに過負荷がかかり、同期の失敗を引き起こす可能性があります。
兆候:
- データベース接続タイムアウト
- データベースサーバーでのCPU使用率が高い
- インフラストラクチャが正常であるにもかかわらず、同期の処理が遅い
診断と解決策:
UIを使用して、プライマリサイトの並行処理の設定を下げます
手動同期ステータスの更新
場合によっては、根本的な問題を解決した後、オブジェクト型を手動で同期済みとしてマークする必要がある場合があります。このシナリオは、セカンダリサイトのオブジェクトバケットに手動でファイルをアップロードすることでしか問題を修正できない場合に発生します。通常、その操作は必要ありませんが、バージョンのバグが原因で発生する可能性があります。以下に、それらの手動でアップロードされたオブジェクト型(この場合はアップロード)を同期済みとしてマークする方法を示します。
def mark_upload_synced(upload_id)
upload = Upload.find(upload_id)
registry = upload.replicator.registry
registry.start
registry.synced!
puts "Marked upload #{upload_id} as synced"
end
# Mark specific uploads as synced
upload_ids = [107221, 107320] # Replace with actual IDs
upload_ids.each { |id| mark_upload_synced(id) }Geoセカンダリサイトのレプリケーションのリセット
壊れた状態のセカンダリサイトを取得してしまい、レプリケーションの状態をリセットして最初からやり直したい場合は、次に行ういくつかの手順が役立ちます:
SidekiqとGeoログカーソルを停止します。
Sidekiqを安全に停止させることが可能です。新しいジョブの受け付けを停止し、現在実行中のジョブが処理を完了するまで待機させることができます。
最初の段階ではSIGTSTPシグナルを送信し、すべてのジョブが完了した後にSIGTERMシグナルを送信する必要があります。それ以外の場合は、
gitlab-ctl stopコマンドを使用します。gitlab-ctl status sidekiq # run: sidekiq: (pid 10180) <- this is the PID you will use kill -TSTP 10180 # change to the correct PID gitlab-ctl stop sidekiq gitlab-ctl stop geo-logcursorSidekiqログを監視して、Sidekiqジョブの処理がいつ終了したかを確認できます:
gitlab-ctl tail sidekiqGitalyとGitaly Cluster (Praefect)データをクリアします。
mv /var/opt/gitlab/git-data/repositories /var/opt/gitlab/git-data/repositories.old sudo gitlab-ctl reconfigureオプション。Praefect内部ロードバランサーを無効にします。
各PraefectサーバーでPraefectを停止します:
sudo gitlab-ctl stop praefectPraefectデータベースをリセットします:
sudo /opt/gitlab/embedded/bin/psql -U praefect -d template1 -h localhost -c "DROP DATABASE praefect_production WITH (FORCE);" sudo /opt/gitlab/embedded/bin/psql -U praefect -d template1 -h localhost -c "CREATE DATABASE praefect_production WITH OWNER=praefect ENCODING=UTF8;"各Gitalyノードからリポジトリデータの名前を変更/削除します:
sudo mv /var/opt/gitlab/git-data/repositories /var/opt/gitlab/git-data/repositories.old sudo gitlab-ctl reconfigurePraefectデプロイノードで、データベースを設定するために再構成を実行します:
sudo gitlab-ctl reconfigure各PraefectサーバーでPraefectを開始します:
sudo gitlab-ctl start praefectオプション。無効にした場合は、Praefect内部ロードバランサーを再度アクティブ化します。
オプション。他のデータフォルダーの名前を変更し、新しいフォルダーを作成します。
(ファイルの添付ファイル、アバター、またはLFSオブジェクトなどの)アップロードされたコンテンツは、次のパスのいずれかのサブフォルダーに保存されます:
/var/opt/gitlab/gitlab-rails/shared/var/opt/gitlab/gitlab-rails/uploads
それらのすべての名前を変更するには、以下を実行します:
gitlab-ctl stop mv /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared.old mkdir -p /var/opt/gitlab/gitlab-rails/shared mv /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads.old mkdir -p /var/opt/gitlab/gitlab-rails/uploads gitlab-ctl start postgresql gitlab-ctl start geo-postgresqlフォルダーを再作成し、権限と所有権が正しいことを確認するために再構成します:
gitlab-ctl reconfigureトラッキングデータベースをリセットします。
gitlab-rake db:drop:geo DISABLE_DATABASE_ENVIRONMENT_CHECK=1 # on a secondary app node gitlab-ctl reconfigure # on the tracking database node gitlab-rake db:migrate:geo # on a secondary app node以前に停止したサービスを再起動します。
gitlab-ctl start

