Geo 동기화 및 검증 오류 트러블슈팅
GitLab v19.2Offering: GitLab Self-Managed
Admin > Geo > Sites 또는 동기화 상태 Rake 작업에서 복제 또는 검증 실패가 발생하면 다음 일반 단계를 통해 해결을 시도할 수 있습니다: Geo는 실패를 자동으로 재시도합니다. 실패가 오래 지속된 경우 이미 여러 번 재시도가 발생했으며, 자동 재시도 간격이 실패 유형에 따라 최대 4시간까지 늘어납니다.
Admin > Geo > Sites 또는 동기화 상태 Rake 작업에서 복제 또는 검증 실패가 발생하면 다음 일반 단계를 통해 해결을 시도할 수 있습니다:
-
Geo는 실패를 자동으로 재시도합니다. 실패가 최근에 발생했고 수가 적거나, 근본 원인이 이미 해결된 것으로 의심되면 실패가 사라질 때까지 기다릴 수 있습니다.
-
실패가 오래 지속된 경우 이미 여러 번 재시도가 발생했으며, 자동 재시도 간격이 실패 유형에 따라 최대 4시간까지 늘어납니다. 근본 원인이 이미 해결된 것으로 의심되면 복제 또는 검증을 수동으로 재시도하여 대기를 피할 수 있습니다.
-
실패가 지속되면 다음 섹션을 사용하여 해결을 시도하세요.
진단 절차#
수동 재시도를 시도하기 전에 이러한 향상된 진단 절차를 사용하여 동기화 문제의 범위와 특성을 더 잘 이해할 수 있습니다.
모델 상태 확인#
이 절차는 모든 Geo 데이터 유형 모델 클래스에 대한 자세한 상태 정보를 제공하며 체크섬 실패를 파악하는 데 도움이 됩니다. 이러한 실패는 복제 가능한 객체의 체크섬을 계산할 수 없을 때 발생합니다. “기본 사이트 검증 실패”라고도 부릅니다.
체크섬 실패는 UI 또는 Rails 콘솔에서 확인할 수 있습니다.
UI
기본 사이트에서 [데이터 관리 페이지](/19.2/administration/admin_area/#data-management)를 사용하세요.
Rails 콘솔
다음 스크립트를 사용하여 각 모델 유형에 대한 자세한 정보를 출력할 수 있습니다. 출력 정보에는 다음이 포함됩니다:
-
레코드의 총 개수
-
실패, 검증됨, 대기 중인 레코드 수
-
조사를 위한 샘플 실패 레코드
ModelMapper클래스는 GitLab 18.3에서 추가되었습니다. 이전 버전에서는 Geo 데이터 유형 모델 클래스 목록을 수동으로 지정해야 합니다.
기본 사이트에서 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 레지스트리 유형에 대한 자세한 상태 정보를 제공하며 실패의 패턴을 파악하는 데 도움이 됩니다.
다음 스크립트를 실행하여 종합적인 개요를 얻습니다:
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()
이 스크립트는 각 레지스트리 유형에 대한 자세한 정보를 출력합니다. 출력 정보에는 다음이 포함됩니다:
레코드의 총 개수
-
실패, 동기화됨, 대기 중인 레코드 수
-
조사를 위한 샘플 실패 레코드
복제 또는 검증 수동 재시도#
보조 Geo 사이트의 Rails 콘솔에서 다음 작업을 수행할 수 있습니다:
개별 구성 요소 재동기화 및 재검증#
보조 사이트에서 Admin > Geo > Replication으로 이동하여 개별 항목을 강제로 재동기화하거나 재검증합니다.
그러나 이 방법이 작동하지 않으면 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로 교체합니다. 레지스트리 레코드는 추적하는 모델 레코드와 동일한 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.replicator
Replicator 인스턴스를 사용한 작업 수행#
replicator 변수에 Replicator 인스턴스를 저장한 후 여러 작업을 수행할 수 있습니다:
콘솔에서 동기화#
이 스니펫은 보조 사이트에서만 작동합니다.
콘솔에서 동기화 코드를 동기적으로 실행하므로, 리소스를 동기화하는 데 걸리는 시간을 관찰하거나 전체 오류 역추적을 볼 수 있습니다.
replicator.sync
선택 사항으로, 구성된 로그 수준보다 콘솔의 로그 수준을 더 자세하게 만든 다음 동기화를 수행합니다:
Rails.logger.level = :debug
콘솔에서 체크섬 또는 검증#
이 스니펫은 기본 또는 보조 사이트 어디서나 작동합니다.
기본 사이트에서는 리소스를 체크섬하고 결과를 기본 GitLab 데이터베이스에 저장합니다. 보조 사이트에서는 리소스를 체크섬하고, 기본 사이트에서 생성된 기본 GitLab 데이터베이스의 체크섬과 비교하여 결과를 Geo 추적 데이터베이스에 저장합니다.
콘솔에서 체크섬 및 검증 코드를 동기적으로 실행하므로, 걸리는 시간을 관찰하거나 전체 오류 역추적을 볼 수 있습니다.
replicator.verify
Sidekiq job에서 동기화#
이 스니펫은 보조 사이트에서만 작동합니다.
Sidekiq이 리소스의 동기화를 수행하도록 job을 큐에 추가합니다.
replicator.enqueue_sync
Sidekiq job에서 검증#
이 스니펫은 기본 또는 보조 사이트 어디서나 작동합니다.
Sidekiq이 리소스의 체크섬 또는 검증을 수행하도록 job을 큐에 추가합니다.
replicator.verify_async
모델 레코드 가져오기#
이 스니펫은 기본 또는 보조 사이트 어디서나 작동합니다.
replicator.model_record
레지스트리 레코드 가져오기#
이 스니펫은 레지스트리 테이블이 Geo 추적 DB에 저장되므로 보조 사이트에서만 작동합니다.
replicator.registry
Geo 데이터 유형 모델 클래스#
Geo 데이터 유형은 하나 이상의 GitLab 기능에 필요한 특정 데이터 클래스로, 관련 데이터를 저장하며 Geo에 의해 보조 사이트로 복제됩니다.
- Blob 유형:
Ci::JobArtifact
-
Ci::PipelineArtifact -
Ci::SecureFile -
LfsObject -
MergeRequestDiff -
Packages::PackageFile -
PagesDeployment -
Terraform::StateVersion -
Upload -
DependencyProxy::Manifest -
DependencyProxy::Blob -
Git 리포지터리 유형:
DesignManagement::Repository
-
ProjectRepository -
ProjectWikiRepository -
SnippetRepository -
GroupWikiRepository -
기타 유형:
ContainerRepository
주요 클래스 종류는 Registry, Model, Replicator입니다. 이 클래스 중 하나의 인스턴스가 있으면 다른 클래스를 가져올 수 있습니다. Registry와 Model은 주로 PostgreSQL DB 상태를 관리합니다. Replicator는 PostgreSQL이 아닌 데이터(파일/Git 리포지터리/컨테이너 리포지터리)를 복제하거나 검증하는 방법을 알고 있습니다.
Geo 레지스트리 클래스#
GitLab Geo의 맥락에서 레지스트리 레코드는 Geo 추적 데이터베이스의 레지스트리 테이블을 참조합니다. 각 레코드는 LFS 파일이나 프로젝트 Git 리포지터리와 같이 기본 GitLab 데이터베이스의 단일 복제 가능 항목을 추적합니다. 쿼리할 수 있는 Geo 레지스트리 테이블에 해당하는 Rails 모델은 다음과 같습니다:
- Blob 유형:
Geo::CiSecureFileRegistry
-
Geo::DependencyProxyBlobRegistry -
Geo::DependencyProxyManifestRegistry -
Geo::JobArtifactRegistry -
Geo::LfsObjectRegistry -
Geo::MergeRequestDiffRegistry -
Geo::PackageFileRegistry -
Geo::PagesDeploymentRegistry -
Geo::PipelineArtifactRegistry -
Geo::ProjectWikiRepositoryRegistry -
Geo::SnippetRepositoryRegistry -
Geo::TerraformStateVersionRegistry -
Geo::UploadRegistry -
Git 리포지터리 유형:
Geo::DesignManagementRepositoryRegistry
-
Geo::ProjectRepositoryRegistry -
Geo::ProjectWikiRepositoryRegistry -
Geo::SnippetRepositoryRegistry -
Geo::GroupWikiRepositoryRegistry -
기타 유형:
Geo::ContainerRepositoryRegistry
여러 구성 요소 재동기화 및 재검증#
History
- 대량 재동기화 및 재검증이 GitLab 16.5에서 추가됨.
구성 요소 리소스가 동기화 또는 검증에 실패하면 대량 작업을 트리거하여 복제 큐를 다시 시작할 수 있습니다. 이러한 작업은 재시도 횟수와 예약 시간을 0으로 초기화하여 최대 1시간을 기다리지 않고 시스템이 실패한 리소스를 더 빨리 처리하도록 합니다.
이러한 작업은 리소스를 즉시 처리하지 않습니다. 대신, 동기화 및 검증을 처리하는 백그라운드 job을 다시 큐에 추가합니다. 실제 복제 작업은 표준 Geo 복제 프로세스를 통해 비동기적으로 이루어집니다.
재동기화 및 재검증 작동 방식#
재동기화 또는 재검증 작업을 트리거하면 시스템이 일치하는 레코드를 pending으로 표시합니다. Geo 재동기화 및 재검증 백그라운드 워커가 이러한 레코드를 선택하여 일반 큐 우선 순위에 따라 처리합니다. 이 메커니즘을 통해 작업을 즉시 차단하지 않고도 실패한 리소스의 처리를 앞당길 수 있습니다.
성공적으로 동기화되지 않은 레코드는 재검증할 수 없습니다. 동기화된 레코드만 검증할 수 있습니다.
UI 또는 Rails 콘솔에서 대량 작업을 트리거할 수 있습니다.
UI에서#
UI에서 하나의 구성 요소의 모든 리소스에 대한 전체 재동기화를 예약할 수 있습니다:
-
오른쪽 상단 모서리에서 Admin을 선택합니다.
-
왼쪽 사이드바에서 Geo > Sites를 선택합니다.
-
Replication details 아래에서 원하는 구성 요소를 선택합니다.
선택한 구성 요소에 대한 리소스 재동기화#
-
Resync all 선택: 이미 동기화된 것과 관계없이 선택한 리소스의 모든 레코드 상태를 초기화합니다.
-
Resync all failed 선택: 동기화에 실패한 모든 레코드를 초기화합니다.
선택한 구성 요소에 대한 리소스 재검증#
-
Reverify all 선택: 이미 검증된 것과 관계없이 선택한 리소스의 모든 레코드 상태를 초기화합니다.
-
Reverify all failed 선택: 검증에 실패했지만 동기화는 성공한 모든 레코드를 초기화합니다.
모든 사이트에서 하나의 구성 요소 재검증#
기본 사이트의 체크섬이 의심스러운 경우 기본 사이트가 체크섬을 재계산하도록 해야 합니다. 기본 사이트에서 각 체크섬이 재계산된 후 이벤트가 생성되어 모든 보조 사이트로 전파되어 체크섬을 재계산하고 값을 비교하므로 “전체 재검증”이 이루어집니다. 불일치가 있으면 레지스트리가 sync failed로 표시되어 동기화 재시도가 예약됩니다.
UI에서 기본 사이트의 체크섬을 재계산할 수 있습니다:
-
오른쪽 상단 모서리에서 Admin을 선택합니다.
-
왼쪽 사이드바에서 Monitoring > Data management를 선택합니다.
-
드롭다운 목록에서 원하는 구성 요소를 선택합니다.
-
Checksum all을 선택합니다.
Resync all, Reverify all and Checksum all은 이미 동기화되었거나 검증된 것과 관계없이 모든 리소스의 업데이트를 트리거합니다. 인스턴스에 특정 객체 유형이 수천 개 있는 경우(예: CI Job Artifacts) 실행해서는 안 됩니다.
Rails 콘솔에서#
데이터를 변경하는 명령은 올바르게 실행되지 않거나 올바른 조건 하에서 실행되지 않으면 손상을 일으킬 수 있습니다.
항상 테스트 환경에서 먼저 명령을 실행하고 복원할 수 있는 백업 인스턴스를 준비해 두세요.
다음 섹션은 Rails 콘솔에서 내부 애플리케이션 명령을 사용하여 대량 복제 또는 검증을 수행하는 방법을 설명합니다.
동기화에 실패한 하나의 구성 요소의 모든 리소스 동기화#
다음 스크립트:
-
실패한 모든 리포지터리를 반복합니다.
-
마지막 실패 이유를 포함한 Geo 동기화 및 검증 메타데이터를 표시합니다.
-
리포지터리를 재동기화하려고 시도합니다.
-
실패가 발생하면 이유와 함께 보고합니다.
-
완료하는 데 시간이 걸릴 수 있습니다. 각 리포지터리 확인은 결과를 보고하기 전에 완료되어야 합니다. 세션이 시간 초과되면
screen세션을 시작하거나 Rails runner와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
오류#
메시지: Geo 기본 사이트에 파일이 없음#
동기화 실패 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"
}
동일한 오류는 특정 복제 가능 항목의 동기화 상태를 검토할 때 Admin > Geo > Sites 아래 UI에도 반영됩니다. 이 시나리오에서 특정 업로드가 누락되어 있습니다:
[
](/19.2/administration/geo/replication/troubleshooting/img/geo_uploads_file_missing_v17_11.png)
[
](/19.2/administration/geo/replication/troubleshooting/img/geo_uploads_file_missing_details_v17_11.png)
불일관성 정리#
삭제 명령을 실행하기 전에 최신 작동 백업이 있는지 확인하세요.
이러한 오류를 제거하려면 먼저 어떤 특정 리소스가 영향을 받는지 파악합니다. 그런 다음 적절한 destroy 명령을 실행하여 모든 Geo 사이트와 해당 데이터베이스에서 삭제가 전파되도록 합니다. 이전 시나리오를 기반으로 업로드가 오류를 일으키고 있으며 이것이 아래 예시로 사용됩니다.
파악된 불일관성을 해당 Geo 모델 클래스 이름에 매핑합니다. 클래스 이름은 다음 단계에서 필요합니다. 이 시나리오에서 업로드는 Upload에 해당합니다.
Geo 기본 사이트에서 Rails 콘솔을 시작합니다.
이전 단계의 Geo 모델 클래스를 기반으로 누락된 파일로 인해 검증에 실패한 모든 리소스를 쿼리합니다. limit(20)을 조정하거나 제거하여 더 많은 결과를 표시합니다. 나열된 리소스가 UI에 표시된 실패한 항목과 일치하는지 확인합니다:
Upload.verification_failed.where("verification_failure like '%File is not checksummable%'").limit(20)
=> #
선택 사항으로, 영향을 받는 리소스의 id를 사용하여 여전히 필요한지 확인합니다:
Upload.find(55)
=> #
영향을 받는 리소스를 복구해야 한다고 판단되면 다음과 같은 옵션(전체 목록이 아님)을 탐색하여 복구할 수 있습니다:
보조 사이트에 객체가 있는지 확인하고 기본 사이트로 수동으로 복사합니다.
-
이전 백업을 살펴보고 객체를 기본 사이트로 수동으로 복사합니다.
-
일부를 스팟 체크하여 레코드를 삭제해도 괜찮은지 확인합니다. 예를 들어, 모두 매우 오래된 아티팩트라면 중요한 데이터가 아닐 수 있습니다.
파악된 리소스의 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>: 스토리지에 파일이 없습니다. 표시된 경로는 누락된 파일을 파악하는 데 도움이 됩니다. 이 오류를 수정하려면 Geo 기본 사이트에 파일이 없음의 지침을 따르세요. -
File is not checksummable - is excluded from verification: 레코드가 더 이상 복제 범위에 속하지 않으므로 Geo가 검증할 수 없습니다. 이 동작은 기본 사이트가 레코드를 삭제하지 않고 복제 범위에서 제거할 때 예상되는 정상 동작입니다. 예를 들어, GitLab은 스토리지 최적화 중에 오래된MergeRequestDiff레코드를without_files상태로 이동합니다. 레지스트리 일관성 워커가 시간이 지남에 따라 이러한 레지스트리 항목을 자동으로 제거합니다.
영향을 받은 MergeRequestDiff 레지스트리 항목을 즉시 제거하려면 보조 사이트의 Rails 콘솔에서 다음 명령을 실행합니다:
Geo::MergeRequestDiffRegistry.where("verification_failure LIKE '%excluded from verification%'").find_each(&:destroy)
기본 Geo 사이트에서 업로드 검증 실패#
기본 Geo 사이트에서 일부 업로드의 검증이 verification_checksum = nil로 실패하고 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” 메시지가 포함된 로그 줄 수가 증가합니다. -
영향을 받은 리포지터리에 대해 활성 동기화 job이 실행되지 않습니다.
-
리스가 만료될 때까지 최대 8시간 동안 단일 리포지터리에 영향을 미칩니다.
진단:
Geo 관리 인터페이스를 확인하여 리포지터리가 활성 동기화 중이 아닌지 확인합니다.
geo.log에서 “Cannot obtain an exclusive lease” 메시지의 수가 증가하는지 확인합니다:
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에서 활성 동기화 job이 실행되지 않는지 확인합니다.
해결 방법:
권장 접근법은 8시간 리스 만료를 기다리는 것입니다. 수동 리스 해제는 즉각적인 동기화가 중요하고 동기화 job이 실행 중이 아님을 확인한 경우에만 사용해야 합니다.
고아 리스 키를 수동으로 해제하려면:
영향을 받은 리포지터리의 프로젝트 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
리스를 해제한 후 리포지터리 동기화는 일반 Geo 동기화 스케줄에 따라 재시도되거나 위에 표시된 것처럼 수동으로 동기화를 트리거할 수 있습니다.
오류: 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 fetch가 두 개의 별도 HTTP 요청 사이에서 계산을 수행하는 동안 JWT 토큰이 만료되고 있을 수 있습니다. GitLab 17.1.0, 17.0.5, 16.11.7에서 수정될 때까지 모든 GitLab 버전에 존재했던 이슈 464101을 참조하세요.
이 문제가 발생하고 있는지 확인하려면:
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.registry
last_sync_failure에 더 이상 fatal: could not read Username 오류가 포함되지 않으면 이 문제의 영향을 받고 있는 것입니다. 상태는 이제 2가 되어야 하며, 이는 동기화되었음을 의미합니다. 그렇다면 수정이 포함된 GitLab 버전으로 업그레이드해야 합니다. 이 문제의 심각성을 줄일 수 있는
이슈 466681에 찬성 투표하거나 댓글을 달 수 있습니다.
이 문제를 해결하려면 보조 사이트의 모든 Sidekiq 노드에 핫 패치를 적용하여 JWT 만료 시간을 연장해야 합니다:
/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 클론 패키지 파일 전송 중에 연결을 종료할 때 발생합니다. 이는 기본 유휴 타임아웃이 60초인 AWS Application Load Balancers(ALB)에서 일반적으로 발생합니다. 데이터 전송이 시작되기 전에 패키지 파일을 준비하는 데 시간이 걸리는 대형 리포지터리의 경우 ALB가 데이터가 전송되기 전에 연결을 끊어 오류를 트리거할 수 있습니다.
이 문제를 해결하려면:
기본 사이트 앞의 로드 밸런서의 유휴 타임아웃을 늘려 대형 리포지터리 클론을 수용합니다. AWS ALB의 경우 AWS Management Console의 로드 밸런서 속성에서 유휴 타임아웃 설정을 업데이트합니다.
실패한 레지스트리를 초기화합니다:
영향을 받은 리포지터리를 파악하고 초기화합니다:
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가 자동으로 동기화를 재시도할 때까지 기다리거나, 복제를 수동으로 재시도합니다.
오류: gitmodulesUrl: disallowed submodule url#
일부 프로젝트 리포지터리가 Error syncing repository: 13:creating repository: cloning repository: exit status 128 오류로 지속적으로 동기화에 실패합니다. 그러나 일부 리포지터리의 경우 Gitaly 로그의 특정 오류 메시지가 다릅니다: gitmodulesUrl: disallowed submodule url. 이 실패는 리포지터리의 .gitmodules 파일에 잘못된 서브모듈 URL이 포함되어 있을 때 발생합니다.
근본 원인: 이 문제는 형식이 잘못된 URL이 있는 .gitmodules 파일을 포함하는 Git 리포지터리의 이전 커밋으로 인해 발생합니다. 이 문제는 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 버전부터 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 : gitmodulesUrl: disallowed submodule url: https://example.gitlab.com:group/project.git
Checking objects: 100% (12/12), done.
Gitaly 로그를 확인합니다. 특정 blob SHA를 찾기 위해 gitmodulesUrl이 포함된 오류 메시지를 찾습니다.
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로 커밋을 푸시합니다.
수정 후 영향을 받은 프로젝트에서 작업 중인 모든 개발자는 현재 로컬 복사본을 제거하고 새 리포지터리를 클론해야 합니다. 그렇지 않으면 변경 사항을 푸시할 때 문제가 되는 blob을 다시 도입할 수 있습니다.
오류: fetch remote: signal: terminated: context deadline exceeded at exactly 3 hours#
Git 리포지터리를 동기화하는 동안 정확히 세 시간에 Git fetch가 실패하는 경우:
/etc/gitlab/gitlab.rb를 편집하여 기본 10800초에서 Git 타임아웃을 늘립니다:
# Git timeout in seconds
gitlab_rails['gitlab_shell_git_timeout'] = 21600
GitLab을 재구성합니다:
sudo gitlab-ctl reconfigure
오류 Failed to open TCP connection to localhost:5000 on secondary when configuring registry replication#
보조 사이트에서 컨테이너 레지스트리 복제를 구성할 때 다음 오류가 발생할 수 있습니다:
Failed to open TCP connection to localhost:5000 (Connection refused - connect(2) for \"localhost\" port 5000)"
보조 사이트에서 컨테이너 레지스트리가 활성화되지 않은 경우 발생합니다. 이를 수정하려면 보조 사이트에서 컨테이너 레지스트리가 활성화되어 있는지 확인합니다. Let’s Encrypt 통합이 비활성화된 경우 컨테이너 레지스트리도 비활성화되므로 수동으로 구성해야 합니다.
오류: Verification timed out after 28800#
가능한 근본 원인: 다양한 레지스트리 유형에서 체크섬 불일관성을 일으키는 중복 레지스트리 레코드.
진단:
여러 유형에서 중복 레지스트리를 확인합니다:
# 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
해결:
각 영향을 받은 유형에 대한 중복 레지스트리 항목을 제거합니다:
# 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#
가능한 근본 원인: 리포지터리 또는 컨테이너 레지스트리 검증 간격 변경으로 인한 체크섬 불일관성.
진단:
실패한 리포지터리 또는 컨테이너 레지스트리를 확인합니다:
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 "---"
end
failed_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
해결:
특정 프로젝트 또는 컨테이너 레지스트리에 대해 재검증을 강제로 수행합니다:
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"
end
container_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 데이터 유형마다 고유한 특성과 일반적인 실패 패턴이 있습니다. 이 섹션은 특정 객체 유형에 대한 타깃 트러블슈팅을 제공합니다.
업로드#
진단:
파일이 누락된 업로드를 파악합니다. 더 많은 결과를 보려면 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
해결:
이러한 실패를 해결하려면 기본 Geo 사이트에서 업로드 검증 실패의 단계를 따르세요.
Pages 배포#
진단:
문제가 되는 pages 배포를 검사합니다:
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
해결:
pages 배포 레코드를 삭제하기 전에 최신 작동 백업이 있는지 확인합니다. 이러한 배포를 제거해도 안전한지 팀과 조율합니다.
- 기본 사이트에서 Rails 콘솔 세션을 시작합니다.
팀과 확인하여 배포를 제거해도 안전한지 확인한 후:
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 객체#
진단:
문제가 되는 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
해결:
LFS 객체를 제거하면 해당 객체를 참조하는 모든 프로젝트에 영향을 미칩니다. 삭제하기 전에 백업이 있는지 확인하고 프로젝트 관리자와 조율합니다.
- 기본 사이트에서 Rails 콘솔 세션을 시작합니다.
파일이 누락된 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)
Job 아티팩트#
진단:
파일이 누락된 아티팩트를 확인합니다:
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
해결:
job 아티팩트 레코드를 삭제하기 전에 최신 작동 백업이 있는지 확인합니다. 이러한 아티팩트를 제거해도 안전한지 팀과 조율합니다.
- 기본 사이트에서 Rails 콘솔 세션을 시작합니다.
파일이 누락된 아티팩트를 정리합니다:
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)
패키지 파일#
이 오류는 패키지 파일이 기본 사이트의 스토리지에 없을 때 발생합니다.
영향을 받은 패키지 파일을 파악하려면:
영향을 받은 레코드를 쿼리합니다. 더 많은 결과를 보려면 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
패키지 파일 레코드를 삭제하기 전에 최신 작동 백업이 있는지 확인합니다.
이러한 패키지 파일을 제거해도 안전한지 팀과 조율합니다.
영향을 받은 패키지 파일을 제거하려면:
영향을 받은 레코드를 삭제합니다:
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)
파이프라인 아티팩트#
진단:
파일이 누락된 아티팩트를 확인합니다:
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
해결:
파이프라인 아티팩트 레코드를 삭제하기 전에 최신 작동 백업이 있는지 확인합니다. 이러한 아티팩트를 제거해도 안전한지 팀과 조율합니다.
- 기본 사이트에서 Rails 콘솔 세션을 시작합니다.
파일이 누락된 파이프라인 아티팩트를 제거합니다:
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
타임아웃으로 인한 Blob 동기화 불일치#
Blob(LFS 객체, job 아티팩트, 패키지 파일 등)은 대용량 파일이 기본 8시간 blob 다운로드 타임아웃을 초과할 때 Sync timed out after 28800으로 동기화에 실패할 수 있습니다.
이 문제를 해결하려면 다음 순서로 지원되는 옵션을 먼저 사용하세요:
-
blob 다운로드 타임아웃을 늘리고 Geo가 재시도하도록 하여 프레임워크가 다운로드, 검증 및 동기화 상태를 처리하도록 합니다.
-
blob이 여전히 동기화에 실패하면 영향을 받은 blob을 파악하고 검증한 다음 기본 사이트에서 파일을 복사합니다.
-
최후의 수단으로 Rails 콘솔에서 blob을 재동기화합니다.
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가 자동으로 재시도할 때까지 기다리거나, 복제를 수동으로 재시도합니다.
타임아웃된 blob 파악 및 검증#
타임아웃을 늘린 후에도 blob이 계속 실패하는 경우 영향을 받은 객체를 파악하고 파일이 기본 사이트에 있는지 확인합니다. 다음 예시는 LFS 객체를 사용합니다. 다른 blob 유형의 경우 일치하는 Geo 레지스트리 클래스와 모델을 사용하세요.
보조 사이트에서 영향을 받은 객체를 파악합니다:
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/에 상대적입니다. 보조 사이트의 동일한 상대 경로로 파일을 복사합니다.
blob을 동기화됨으로 표시#
파일이 보조 사이트에 있으면 동기화됨으로 표시하고 검증을 트리거합니다. 다음 예시는 LFS 객체를 사용합니다. 다른 blob 유형의 경우 일치하는 Geo 레지스트리 클래스를 사용하세요:
[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
Rails 콘솔에서 타임아웃된 blob 자동 재동기화#
이 절차는 지원되는 옵션(blob 다운로드 타임아웃 늘리기, API 및 Admin 영역의 Geo 복제 세부 정보)로 실패가 해결되지 않은 경우에만 최후의 수단으로 사용하세요. 이 절차는 Geo 프레임워크 외부에서 동기화를 실행하므로 가능한 경우 항상 지원되는 옵션을 우선 사용하세요.
다음 헬퍼는 긴 읽기 타임아웃으로 기본 사이트에서 blob을 직접 스트리밍하고(이렇게 하면 동기화 job의 고정 타임아웃을 피할 수 있음), 크기와 콘텐츠 체크섬을 기본 사이트와 비교하여 검증하고, 프레임워크의 업로더를 통해 저장하고, 레지스트리를 동기화됨으로 표시한 다음 검증을 다시 트리거합니다.
이 접근 방식은 모든 blob 유형의 복제 가능 항목(Ci::JobArtifact,
Ci::PipelineArtifact, Ci::SecureFile, LfsObject, Packages::PackageFile,
PagesDeployment, Terraform::StateVersion, Upload)에 대해 작동합니다. Git 리포지터리와
컨테이너 리포지터리는 다른 동기화 경로를 사용하므로 다루지 않습니다.
데이터를 변경하는 명령은 올바르게 실행되지 않거나 올바른 조건 하에서 실행되지 않으면 손상을 일으킬 수 있습니다.
항상 테스트 환경에서 먼저 명령을 실행하고 복원할 수 있는 백업 인스턴스를 준비해 두세요.
보조 사이트에서 Rails 콘솔 세션을 시작합니다.
헬퍼를 정의합니다:
require 'net/http'
require 'digest'
require 'tempfile'
# Content-hash attribute for each blob model. Types that are not listed, or
# whose hash attribute is nil, fall back to size-only verification.
GEO_BLOB_VERIFICATION = {
'Ci::JobArtifact' => :file_sha256,
'Ci::PipelineArtifact' => :file_sha256,
'Packages::PackageFile' => :file_sha256,
'PagesDeployment' => :file_sha256,
'Upload' => :checksum,
'Ci::SecureFile' => :checksum,
'LfsObject' => :oid
}
# Streams an HTTP GET to the block, following redirects. The Geo
# authentication header is sent only on the first request. On a redirect to a
# pre-signed object storage URL (when proxy_download is disabled) it is
# dropped, because the pre-signed URL is already authenticated.
def geo_stream_get(uri, headers, limit = 5, &block)
raise 'too many redirects' if limit < 0
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https',
open_timeout: 60, read_timeout: 86_400, write_timeout: 86_400) do |http|
request = Net::HTTP::Get.new(uri)
headers.each { |key, value| request[key] = value }
http.request(request) do |response|
case response.code.to_i
when 200
response.read_body { |chunk| yield chunk }
when 301, 302, 303, 307, 308
raise 'redirect with no Location' unless response['location']
return geo_stream_get(URI(response['location']), {}, limit - 1, &block)
else
raise "HTTP #{response.code}: #{response.message}"
end
end
end
end
def manual_geo_blob_sync(registry_class, registry_id)
registry = registry_class.find_by(id: registry_id)
return "no #{registry_class.name} ##{registry_id}" unless registry
replicator = registry.replicator
model = replicator.model_record
return 'missing model record (gone on primary?)' unless model
uploader = replicator.carrierwave_uploader
downloader = Gitlab::Geo::Replication::BlobDownloader.new(replicator: replicator)
uri = URI(downloader.resource_url)
# request_headers is a private BlobDownloader method, accessed here with
# send. It returns a short-lived Geo JWT. This relies on an internal API
# and might need updating after a GitLab upgrade.
auth = downloader.send(:request_headers)
sha_attr = GEO_BLOB_VERIFICATION[model.class.name]
want_size = model.respond_to?(:size) ? model.size : nil
want_sha = sha_attr && model.respond_to?(sha_attr) ? model.public_send(sha_attr) : nil
tmp = Tempfile.new(['geo-blob', '.bin'], '/tmp')
tmp.binmode
begin
geo_stream_get(uri, auth) { |chunk| tmp.write(chunk) }
tmp.flush
raise "size mismatch (#{tmp.size}/#{want_size})" if want_size && tmp.size != want_size
if want_sha && Digest::SHA256.file(tmp.path).hexdigest != want_sha
raise 'checksum mismatch - not marking as synced'
end
# Store the blob through the same uploader method the Geo framework
# uses (BlobDownloader#download_file), so local and object storage are
# both handled the same way.
uploader.replace_file_without_saving!(CarrierWave::SanitizedFile.new(tmp))
registry.update!(state: 2, last_synced_at: Time.current, retry_at: nil,
retry_count: 0, last_sync_failure: nil)
registry.update!(bytes: tmp.size) if registry.respond_to?(:bytes)
# The raw update! above bypasses the after_synced state-machine
# callback, so re-trigger verification explicitly to reconcile a
# previously verification_failed registry.
replicator.verify
"OK #{registry_class.name}##{registry_id} (#{tmp.size} bytes)"
ensure
tmp.close!
end
end
영향을 받은 레지스트리 레코드에 대해 헬퍼를 실행합니다. 레지스트리 클래스를
Geo 레지스트리 클래스 중 하나로, 123을
실제 레지스트리 ID로 교체합니다:
manual_geo_blob_sync(Geo::LfsObjectRegistry, 123)
선택 사항. 이 오류로 실패한 하나의 유형의 모든 blob을 재동기화하려면:
Geo::LfsObjectRegistry
.where("last_sync_failure LIKE '%Sync timed out after%'")
.pluck(:id)
.each { |id| puts manual_geo_blob_sync(Geo::LfsObjectRegistry, id) }; nil
헬퍼가 여전히 타임아웃되거나 실패하면 객체가 기본 사이트에 없거나 읽을 수 없는 상태일 수 있습니다. 자세한 내용은 Geo 기본 사이트에 파일이 없음을 참조하세요.
오류: Projects - Error during verification: Repository does not exist#
근본 원인: Git 리포지터리가 없는 프로젝트가 검증 실패를 일으킵니다.
증상:
-
검증 중에 프로젝트에 “Repository does not exist” 오류가 표시됩니다.
-
합법적으로 리포지터리가 없는 프로젝트에 대해 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)#
근본 원인: ListBucket 권한이 없어 S3 API가 404 대신 403을 반환합니다.
증상:
-
S3 엔드포인트가 있는 로그에서 403 오류
-
S3 버킷에 대한 HEAD 요청 실패
-
오브젝트 스토리지 기반 데이터 유형에 대한 동기화 실패
해결:
GitLab에서 사용하는 S3 IAM 정책에 ListBucket 권한을 추가하려면 인프라 팀의 개입이 필요합니다.
메시지: Synchronization failed - Error syncing repository#
대형 리포지터리가 이 문제의 영향을 받는 경우,
재동기화에 오랜 시간이 걸리고 Geo 사이트, 스토리지 및 네트워크 시스템에 상당한 부하를 일으킬 수 있습니다.
다음 오류 메시지는 리포지터리를 동기화할 때 일관성 검사 오류를 나타냅니다:
Synchronization failed - Error syncing repository [..] fatal: fsck error in packed object
여러 문제가 이 오류를 트리거할 수 있습니다. 예를 들어 이메일 주소 문제:
Error syncing repository: 13:fetch remote: "error: object : badEmail: invalid author/committer line - bad email
fatal: fsck error in packed object
fatal: fetch-pack: invalid index-pack output
또한 object : hasDotgit: contains '.git'가 이 오류를 트리거할 수 있습니다. 모든 리포지터리에서 하나 이상의 문제가 있을 수 있으므로 구체적인 오류를 확인합니다.
두 번째 동기화 오류도 리포지터리 확인 문제로 인해 발생할 수 있습니다:
Error syncing repository: 13:Received RST_STREAM with error code 2.
이러한 오류는 즉시 실패한 모든 리포지터리를 동기화하여 확인할 수 있습니다.
일관성 오류를 일으키는 잘못된 형식의 객체를 제거하는 것은 리포지터리 기록을 다시 작성하는 것을 포함하며, 일반적으로 옵션이 아닙니다.
이러한 일관성 검사를 무시하려면 보조 Geo 사이트의 Gitaly를 재구성하여 이러한 git fsck 문제를 무시하도록 합니다.
다음 구성 예시:
-
GitLab 16.0부터 필요한 새 구성 구조를 사용합니다.
-
다섯 가지 일반적인 검사 실패를 무시합니다.
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 원격이 보조 Geo 사이트의 파일 시스템의 리포지터리 .git/config 파일에 없음을 나타냅니다:
{
"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 사이트의 웹 인터페이스에 로그인합니다.
선택 사항. 해당 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
이 오류는 리포지터리를 사이트 간에 처음부터 복제해야 하는 경우 더 자주 발생합니다.
Geo는 여러 번 재시도하지만, 네트워크 불안정으로 전송이 지속적으로 중단되는 경우 rsync와 같은 대안적인 방법을 사용하여 git을 우회하고 Geo에서 복제에 실패하는 모든 리포지터리의 초기 복사본을 만들 수 있습니다.
각 실패한 리포지터리를 개별적으로 전송하고 각 전송 후 일관성을 확인하는 것이 좋습니다. 다른 서버로의 rsync 지침을 따라 영향을 받은 각 리포지터리를 기본에서 보조 사이트로 전송합니다.
Geo 보조 사이트에서 리포지터리 확인 실패 찾기#
모든 리포지터리 데이터 유형은 GitLab 16.3에서 Geo 셀프 서비스 프레임워크로 마이그레이션되었습니다. [Geo 셀프 서비스 프레임워크에서 이 기능을 다시 구현하는 이슈](https://gitlab.com/gitlab-org/gitlab/-/issues/426659)가 있습니다.
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 클러스터에서 리포지터리를 삭제하고 재동기화합니다. 위험을 이해하고 다음 조건이 모두 사실인 경우에만 사용하는 것을 고려합니다:
-
기본 사이트의 리포지터리에 대해
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 콘솔에서 재동기화하기 전에 correlation ID를 설정합니다. 이 ID는 이 세션에서 실행하는 명령과 관련된 모든 로그를 검색하는 데 도움이 됩니다:
Gitlab::ApplicationContext.push({})
출력 예시:
[2] pry(main)> Gitlab::ApplicationContext.push({})
=> #"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을 정상적으로 중지하되, 새 job 수신을 중지하고 현재 job 처리가 완료될 때까지 기다리도록 만들 수 있습니다.
첫 번째 단계에서는 SIGTSTP kill 신호를 보내고, 모든 job이 완료되면 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-logcursor
Sidekiq 로그를 보면서 Sidekiq job 처리가 완료되었는지 확인할 수 있습니다:
gitlab-ctl tail sidekiq
Gitaly 및 Gitaly Cluster(Praefect) 데이터를 지웁니다.
Gitaly
mv /var/opt/gitlab/git-data/repositories /var/opt/gitlab/git-data/repositories.old
sudo gitlab-ctl reconfigure
Gitaly Cluster(Praefect)
선택 사항. Praefect 내부 로드 밸런서를 비활성화합니다.
각 Praefect 서버에서 Praefect를 중지합니다:
sudo gitlab-ctl stop praefect
Praefect 데이터베이스를 초기화합니다:
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 reconfigure
Praefect 배포 노드에서 재구성을 실행하여 데이터베이스를 설정합니다:
sudo gitlab-ctl reconfigure
각 Praefect 서버에서 Praefect를 시작합니다:
sudo gitlab-ctl start praefect
선택 사항. 비활성화한 경우 Praefect 내부 로드 밸런서를 다시 활성화합니다.
더 이상 필요하지 않다고 확인하는 즉시 디스크 공간을 절약하기 위해 `/var/opt/gitlab/git-data/repositories.old`를 제거하는 것이 좋습니다.
- 선택 사항. 다른 데이터 폴더의 이름을 변경하고 새 폴더를 만듭니다.
보조 사이트에 기본 사이트에서 제거된 파일이 있을 수 있지만 이 제거가 반영되지 않았을 수 있습니다. 이 단계를 건너뛰면 이러한 파일이 Geo 보조 사이트에서 제거되지 않습니다.
업로드된 콘텐츠(파일 첨부, 아바타, 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
추적 데이터베이스를 초기화합니다.
선택 단계 3을 건너뛴 경우 `geo-postgresql`과 `postgresql` 서비스가 모두 실행 중인지 확인합니다.
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